| 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(); |
| + |
| +/** |
| + * 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); |
| +}); |
| + |
| +/** |
| * Checks whether a request should be allowed, hides it if necessary |
| * @param wnd {nsIDOMWindow} |
| * @param node {nsIDOMElement} |
| * @param contentType {String} |
| * @param location {String} location of the request, filter key if contentType is ELEMHIDE |
| * @param [callback] {Function} If present, the request will be sent |
| * asynchronously and callback called with the |
| * response |
| @@ -113,16 +140,73 @@ let shouldAllow = exports.shouldAllow = |
| callback(processResponse(data)); |
| }); |
| } |
| else |
| return processResponse(sendSyncMessage("AdblockPlus:ShouldAllow", data)); |
| }; |
| /** |
| + * 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", |