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