diff options
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js')
-rw-r--r-- | data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js | 518 |
1 files changed, 0 insertions, 518 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js b/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js deleted file mode 100644 index 97ea7b1..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js +++ /dev/null @@ -1,518 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * Copyright (C) 2006-2017 eyeo GmbH - * - * Adblock Plus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - * - * Adblock Plus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * @fileOverview Content policy implementation, responsible for blocking things. - */ - -"use strict"; - -try -{ - // Hack: SDK loader masks our Components object with a getter. - let proto = Object.getPrototypeOf(this); - let property = Object.getOwnPropertyDescriptor(proto, "Components"); - if (property && property.get) - delete proto.Components; -} -catch (e) -{ - Cu.reportError(e); -} - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {port} = require("messaging"); -let {Utils} = require("utils"); -let {getFrames, isPrivate, getRequestWindow} = require("child/utils"); -let {objectMouseEventHander} = require("child/objectTabs"); -let {RequestNotifier} = require("child/requestNotifier"); - -/** - * Randomly generated class name, to be applied to collapsed nodes. - * @type Promise.<string> - */ -let collapsedClass = port.emitWithResponse("getCollapsedClass"); - -/** - * Maps numerical content type IDs to strings. - * @type Map.<number,string> - */ -let types = new Map(); - -/** - * Contains nodes stored by storeNodes() mapped by their IDs. - * @type Map.<string,DOMNode[]> - */ -let storedNodes = new Map(); - -/** - * Process-dependent prefix to be used for unique nodes identifiers returned - * by storeNodes(). - * @type string - */ -let nodesIDPrefix = Services.appinfo.processID + " "; - -/** - * Counter used to generate unique nodes identifiers in storeNodes(). - * @type number - */ -let maxNodesID = 0; - -port.on("deleteNodes", onDeleteNodes); -port.on("refilterNodes", onRefilterNodes); - -/** - * Processes parent's response to the ShouldAllow message. - * @param {nsIDOMWindow} window window that the request is associated with - * @param {nsIDOMElement} node DOM element that the request is associated with - * @param {Object|undefined} response object received as response - * @return {Boolean} false if the request should be blocked - */ -function processPolicyResponse(window, node, response) -{ - if (typeof response == "undefined") - return true; - - let {allow, collapse, hits} = response; - let isObject = false; - for (let hit of hits) - { - if (hit.contentType == "OBJECT") - isObject = true; - - let context = node; - if (typeof hit.frameIndex == "number") - { - context = window; - for (let i = 0; i < hit.frameIndex; i++) - context = context.parent; - context = context.document; - } - RequestNotifier.addNodeData(context, window.top, hit); - } - - if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - { - // Track mouse events for objects - if (allow && isObject) - { - node.addEventListener("mouseover", objectMouseEventHander, true); - node.addEventListener("mouseout", objectMouseEventHander, true); - } - - if (collapse) - schedulePostProcess(node); - } - return allow; -} - -/** - * Checks whether a request should be allowed, hides it if necessary - * @param {nsIDOMWindow} window - * @param {nsIDOMElement} node - * @param {String} contentType - * @param {String} location location of the request, filter key if contentType is ELEMHIDE - * @return {Boolean} false if the request should be blocked - */ -let shouldAllow = exports.shouldAllow = function(window, node, contentType, location) -{ - return processPolicyResponse(window, node, port.emitSync("shouldAllow", { - contentType, - location, - frames: getFrames(window), - isPrivate: isPrivate(window) - })); -}; - -/** - * Asynchronously checks whether a request should be allowed. - * @param {nsIDOMWindow} window - * @param {nsIDOMElement} node - * @param {String} contentType - * @param {String} location location of the request, filter key if contentType is ELEMHIDE - * @param {Function} callback callback to be called with a boolean value, if - * false the request should be blocked - */ -let shouldAllowAsync = exports.shouldAllowAsync = function(window, node, contentType, location, callback) -{ - port.emitWithResponse("shouldAllow", { - contentType, - location, - frames: getFrames(window), - isPrivate: isPrivate(window) - }).then(response => - { - callback(processPolicyResponse(window, node, response)); - }); -}; - -/** - * Stores nodes and generates a unique ID for them that can be used for - * Policy.refilterNodes() later. It's important that Policy.deleteNodes() is - * called later, otherwise the nodes will be leaked. - * @param {DOMNode[]} nodes list of nodes to be stored - * @return {string} unique ID for the nodes - */ -let storeNodes = exports.storeNodes = function(nodes) -{ - let id = nodesIDPrefix + (++maxNodesID); - storedNodes.set(id, nodes); - return id; -}; - -/** - * Called via message whenever Policy.deleteNodes() is called in the parent. - */ -function onDeleteNodes(id, sender) -{ - storedNodes.delete(id); -} - -/** - * Called via message whenever Policy.refilterNodes() is called in the parent. - */ -function onRefilterNodes({nodesID, entry}, sender) -{ - let nodes = storedNodes.get(nodesID); - if (nodes) - for (let node of nodes) - if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - Utils.runAsync(refilterNode.bind(this, node, entry)); -} - -/** - * Re-checks filters on an element. - */ -function refilterNode(/**Node*/ node, /**Object*/ entry) -{ - let wnd = Utils.getWindow(node); - if (!wnd || wnd.closed) - return; - - if (entry.type == "OBJECT") - { - node.removeEventListener("mouseover", objectMouseEventHander, true); - node.removeEventListener("mouseout", objectMouseEventHander, true); - } - - shouldAllow(wnd, node, entry.type, entry.location, (allow) => { - // Force node to be collapsed - if (!allow) - schedulePostProcess(node) - }); -} - -/** - * Actual nsIContentPolicy and nsIChannelEventSink implementation - * @class - */ -var PolicyImplementation = -{ - classDescription: "Adblock Plus content policy", - classID: Components.ID("cfeaabe6-1dd1-11b2-a0c6-cb5c268894c9"), - contractID: "@adblockplus.org/abp/policy;1", - xpcom_categories: ["content-policy", "net-channel-event-sinks"], - - /** - * Registers the content policy on startup. - */ - init: function() - { - // Populate types map - let iface = Ci.nsIContentPolicy; - for (let name in iface) - if (name.indexOf("TYPE_") == 0 && name != "TYPE_DATAREQUEST") - types.set(iface[name], name.substr(5)); - - let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory(this.classID, this.classDescription, this.contractID, this); - - let catMan = Utils.categoryManager; - for (let category of this.xpcom_categories) - catMan.addCategoryEntry(category, this.contractID, this.contractID, false, true); - - Services.obs.addObserver(this, "document-element-inserted", true); - - onShutdown.add(() => - { - Services.obs.removeObserver(this, "document-element-inserted"); - - for (let category of this.xpcom_categories) - catMan.deleteCategoryEntry(category, this.contractID, false); - - registrar.unregisterFactory(this.classID, this); - }); - }, - - // - // nsISupports interface implementation - // - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver, - Ci.nsIChannelEventSink, Ci.nsIFactory, Ci.nsISupportsWeakReference]), - - // - // nsIContentPolicy interface implementation - // - - shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) - { - // Ignore requests without context and top-level documents - if (!node || contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT) - return Ci.nsIContentPolicy.ACCEPT; - - // Bail out early for chrome: an resource: URLs, this is a work-around for - // https://bugzil.la/1127744 and https://bugzil.la/1247640 - let location = Utils.unwrapURL(contentLocation); - if (location.schemeIs("chrome") || location.schemeIs("resource")) - return Ci.nsIContentPolicy.ACCEPT; - - // Ignore standalone objects - if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT && node.ownerDocument && !/^text\/|[+\/]xml$/.test(node.ownerDocument.contentType)) - return Ci.nsIContentPolicy.ACCEPT; - - let wnd = Utils.getWindow(node); - if (!wnd) - return Ci.nsIContentPolicy.ACCEPT; - - // Data loaded by plugins should be associated with the document - if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST && node instanceof Ci.nsIDOMElement) - node = node.ownerDocument; - - // Fix type for objects misrepresented as frames or images - if (contentType != Ci.nsIContentPolicy.TYPE_OBJECT && (node instanceof Ci.nsIDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement)) - contentType = Ci.nsIContentPolicy.TYPE_OBJECT; - - let result = shouldAllow(wnd, node, types.get(contentType), location.spec); - return (result ? Ci.nsIContentPolicy.ACCEPT : Ci.nsIContentPolicy.REJECT_REQUEST); - }, - - shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) - { - return Ci.nsIContentPolicy.ACCEPT; - }, - - // - // nsIObserver interface implementation - // - _openers: new WeakMap(), - _alreadyLoaded: Symbol(), - - observe: function(subject, topic, data, uri) - { - switch (topic) - { - case "document-element-inserted": - { - let window = subject.defaultView; - if (!window) - return; - - let type = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .itemType; - if (type != Ci.nsIDocShellTreeItem.typeContent) - return; - - let opener = this._openers.get(window); - if (opener == this._alreadyLoaded) - { - // This window has loaded already, ignore it regardless of whether - // window.opener is still set. - return; - } - - if (opener && Cu.isDeadWrapper(opener)) - opener = null; - - if (!opener) - { - // We don't know the opener for this window yet, try to find it - opener = window.opener; - if (!opener) - return; - - // The opener might be an intermediate window, get the real one - while (opener.location == "about:blank" && opener.opener) - opener = opener.opener; - - this._openers.set(window, opener); - - let forgetPopup = event => - { - subject.removeEventListener("DOMContentLoaded", forgetPopup); - this._openers.set(window, this._alreadyLoaded); - }; - subject.addEventListener("DOMContentLoaded", forgetPopup); - } - - if (!uri) - uri = window.location.href; - if (!shouldAllow(opener, opener.document, "POPUP", uri)) - { - window.stop(); - Utils.runAsync(() => window.close()); - } - else if (uri == "about:blank") - { - // An about:blank pop-up most likely means that a load will be - // initiated asynchronously. Wait for that. - Utils.runAsync(() => - { - let channel = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIDocumentLoader) - .documentChannel; - if (channel) - this.observe(subject, topic, data, channel.URI.spec); - }); - } - break; - } - } - }, - - // - // nsIChannelEventSink interface implementation - // - - asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) - { - let async = false; - try - { - // nsILoadInfo.contentPolicyType was introduced in Gecko 35, then - // renamed to nsILoadInfo.externalContentPolicyType in Gecko 44. - let loadInfo = oldChannel.loadInfo; - let contentType = ("externalContentPolicyType" in loadInfo ? - loadInfo.externalContentPolicyType : loadInfo.contentPolicyType); - if (!contentType) - return; - - let wnd = getRequestWindow(newChannel); - if (!wnd) - return; - - if (contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT) - { - if (wnd.history.length <= 1 && wnd.opener) - { - // Special treatment for pop-up windows - this will close the window - // rather than preventing the redirect. Note that we might not have - // seen the original channel yet because the redirect happened before - // the async code in observe() had a chance to run. - this.observe(wnd.document, "document-element-inserted", null, oldChannel.URI.spec); - this.observe(wnd.document, "document-element-inserted", null, newChannel.URI.spec); - } - return; - } - - shouldAllowAsync(wnd, wnd.document, types.get(contentType), newChannel.URI.spec, function(allow) - { - callback.onRedirectVerifyCallback(allow ? Cr.NS_OK : Cr.NS_BINDING_ABORTED); - }); - async = true; - } - catch (e) - { - // We shouldn't throw exceptions here - this will prevent the redirect. - Cu.reportError(e); - } - finally - { - if (!async) - callback.onRedirectVerifyCallback(Cr.NS_OK); - } - }, - - // - // nsIFactory interface implementation - // - - createInstance: function(outer, iid) - { - if (outer) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - } -}; -PolicyImplementation.init(); - -/** - * Nodes scheduled for post-processing (might be null). - * @type Node[] - */ -let scheduledNodes = null; - -/** - * Schedules a node for post-processing. - */ -function schedulePostProcess(/**Element*/ node) -{ - if (scheduledNodes) - scheduledNodes.push(node); - else - { - scheduledNodes = [node]; - Utils.runAsync(postProcessNodes); - } -} - -/** - * Processes nodes scheduled for post-processing (typically hides them). - */ -function postProcessNodes() -{ - collapsedClass.then(cls => - { - let nodes = scheduledNodes; - scheduledNodes = null; - - // Resolving class is async initially so the nodes might have already been - // processed in the meantime. - if (!nodes) - return; - - for (let node of nodes) - { - // adjust frameset's cols/rows for frames - let parentNode = node.parentNode; - if (parentNode && parentNode instanceof Ci.nsIDOMHTMLFrameSetElement) - { - let hasCols = (parentNode.cols && parentNode.cols.indexOf(",") > 0); - let hasRows = (parentNode.rows && parentNode.rows.indexOf(",") > 0); - if ((hasCols || hasRows) && !(hasCols && hasRows)) - { - let index = -1; - for (let frame = node; frame; frame = frame.previousSibling) - if (frame instanceof Ci.nsIDOMHTMLFrameElement || frame instanceof Ci.nsIDOMHTMLFrameSetElement) - index++; - - let property = (hasCols ? "cols" : "rows"); - let weights = parentNode[property].split(","); - weights[index] = "0"; - parentNode[property] = weights.join(","); - } - } - else - node.classList.add(cls); - } - }); -} |