/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ (function(){ "use strict"; var scope; if ((typeof exports) !== "undefined"){ scope = exports; } else { window.scope.intercept = {}; scope = window.scope.intercept; } const {changedFunctions, setRandomSupply} = require("./modifiedAPI"); const randomSupplies = require("./randomSupplies"); const getWrapped = require("sdk/getWrapped"); const logging = require("./logging"); const settings = require("./settings"); setRandomSupply(randomSupplies.nonPersistent); var apiNames = Object.keys(changedFunctions); var undef; function setRandomSupplyByType(type){ switch (type){ case "persistent": setRandomSupply(randomSupplies.persistent); break; case "constant": setRandomSupply(randomSupplies.constant); break; case "white": setRandomSupply(randomSupplies.white); break; default: setRandomSupply(randomSupplies.nonPersistent); } } scope.setRandomSupplyByType = setRandomSupplyByType; function getURL(window){ var href; try { href = window.location.href; } catch (e){ // unable to read location due to SOP // since we are not able to do anything in that case we can allow everything return "about:SOP"; } if (!href || href === "about:blank"){ if (window !== window.parent){ return getURL(window.parent); } else if (window.opener){ return getURL(window.opener); } } return window.location.href; } scope.preIntercept = function preIntercept({subject: window}, apis){ if (!settings.isStillDefault){ scope.intercept({subject: window}, apis); } else { let forceLoad = true; let preIntercepted = false; let intercepted = false; const forEachFunction = function(callback){ apiNames.forEach(function(name){ const changedFunction = changedFunctions[name]; ( Array.isArray(changedFunction.object)? changedFunction.object: [changedFunction.object] ).forEach(function(object){ var constructor = getWrapped(window)[object]; if (constructor){ callback({name, changedFunction, constructor}); } }); }); }; let originalPropertyDescriptors = {}; const doPreIntercept = function(){ if (!preIntercepted){ forEachFunction(function({name, constructor}){ var map = originalPropertyDescriptors[name] || new WeakMap(); originalPropertyDescriptors[name] = map; map.set(constructor, Object.getOwnPropertyDescriptor(constructor.prototype, name)); Object.defineProperty( constructor.prototype, name, { enumerable: true, configureable: true, get: exportFunction(function(){ if (forceLoad){ undoPreIntercept(); settings.forceLoad(); doRealIntercept(); var descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name); return descriptor.value || descriptor.get(); } else { var url = getURL(window); if (!url){ return undef; } var error = new Error(); apis.notify({ url, errorStack: error.stack, messageId: "preBlock", timestamp: new Date(), functionName: name, dataURL: false }); return; } }, window), set: exportFunction(function(){}, window) } ); }); preIntercepted = true; } }; const undoPreIntercept = function(){ if (preIntercepted){ preIntercepted = false; forEachFunction(function({name, constructor}){ Object.defineProperty( constructor.prototype, name, originalPropertyDescriptors[name].get(constructor) ); }); } }; const doRealIntercept = function(){ if (!intercepted){ scope.intercept({subject: window}, apis); intercepted = true; } }; logging.verbose("pre intercept until settings are loaded"); doPreIntercept(); settings.onloaded(function(){ undoPreIntercept(); doRealIntercept(); }); } }; scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){ var siteStatus = check({url: getURL(window)}); logging.verbose("status for page", window, siteStatus); if (siteStatus.mode !== "allow"){ apiNames.forEach(function(name){ var changedFunction = changedFunctions[name]; var functionStatus = changedFunction.getStatus(undefined, siteStatus); logging.verbose("status for", name, ":", functionStatus); if (functionStatus.active){ ( Array.isArray(changedFunction.object)? changedFunction.object: [changedFunction.object] ).forEach(function(object){ var constructor = getWrapped(window)[object]; if (constructor){ var original = constructor.prototype[name]; Object.defineProperty( constructor.prototype, name, { enumerable: true, configureable: true, get: exportFunction(function(){ var url = getURL(window); if (!url){ return undef; } var error = new Error(); if (checkStack(error.stack)){ return original; } var funcStatus = changedFunction.getStatus(this, siteStatus); function notifyCallback(messageId){ notify({ url, errorStack: error.stack, messageId, timestamp: new Date(), functionName: name, dataURL: prefs("storeImageForInspection") && prefs("showNotifications")? ( this instanceof HTMLCanvasElement? this.toDataURL(): ( this.canvas instanceof HTMLCanvasElement? this.canvas.toDataURL(): false ) ): false }); } if (funcStatus.active){ if (funcStatus.mode === "ask"){ funcStatus.mode = ask({ window: window, type: changedFunction.type, canvas: this, errorStack: error.stack }); } switch (funcStatus.mode){ case "allow": return original; case "fake": setRandomSupplyByType(prefs("rng")); var fake = changedFunction.fakeGenerator( prefs, notifyCallback, window, original ); switch (fake){ case true: return original; case false: return undef; default: return exportFunction(fake, getWrapped(window)); } //case "block": default: return undef; } } else { return original; } }, window), set: exportFunction(function(value){ Object.defineProperty( constructor.prototype, name, { value, writable: true, configurable: true, enumerable: true } ); }, window) } ); } }); } }); } }; }());