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", |