| Index: lib/child/contentPolicy.js |
| =================================================================== |
| --- a/lib/child/contentPolicy.js |
| +++ b/lib/child/contentPolicy.js |
| @@ -50,16 +50,43 @@ let collapsedClass = null; |
| /** |
| * 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(); |
|
tschuster
2015/12/01 16:42:40
So, this is cleaned up by DeleteNodes, which is ca
Wladimir Palant
2015/12/01 19:15:31
Nodes can be removed without navigating away (yes,
|
| + |
| +/** |
| + * 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; |
| + |
| +addMessageListener("AdblockPlus:DeleteNodes", onDeleteNodes); |
| +addMessageListener("AdblockPlus:RefilterNodes", onRefilterNodes); |
| + |
| +onShutdown.add(() => { |
| + removeMessageListener("AdblockPlus:DeleteNodes", onDeleteNodes); |
| + removeMessageListener("AdblockPlus: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) |
| { |
| @@ -132,16 +159,73 @@ let shouldAllowAsync = exports.shouldAll |
| contentType, |
| location, |
| frames: getFrames(window), |
| isPrivate: isPrivate(window) |
| }, 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(message) |
| +{ |
| + storedNodes.delete(message.data); |
| +} |
| + |
| +/** |
| + * Called via message whenever Policy.refilterNodes() is called in the parent. |
| + */ |
| +function onRefilterNodes(message) |
| +{ |
| + let {nodesID, entry} = message.data; |
| + 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", |