| Index: lib/child/requestNotifier.js |
| =================================================================== |
| --- a/lib/child/requestNotifier.js |
| +++ b/lib/child/requestNotifier.js |
| @@ -16,33 +16,36 @@ |
| */ |
| /** |
| * @fileOverview Stores Adblock Plus data to be attached to a window. |
| */ |
| let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); |
| let {Utils} = require("utils"); |
| +let {Flasher} = require("child/flasher"); |
| let nodeData = new WeakMap(); |
| let windowStats = new WeakMap(); |
| let requestEntryMaxId = 0; |
| /** |
| * Active RequestNotifier instances by their ID |
| * @type Map.<number,RequestNotifier> |
| */ |
| let notifiers = new Map(); |
| addMessageListener("AdblockPlus:StartWindowScan", onStartScan); |
| addMessageListener("AdblockPlus:ShutdownNotifier", onNotifierShutdown); |
| +addMessageListener("AdblockPlus:FlashNodes", onFlashNodes); |
| onShutdown.add(() => { |
| removeMessageListener("AdblockPlus:StartWindowScan", onStartScan); |
| removeMessageListener("AdblockPlus:ShutdownNotifier", onNotifierShutdown); |
| + removeMessageListener("AdblockPlus:FlashNodes", onFlashNodes); |
| }); |
| function onStartScan(message) |
| { |
| let {notifierID, outerWindowID} = message.data; |
| let window = Services.wm.getOuterWindowWithId(outerWindowID); |
| if (window) |
| new RequestNotifier(window, notifierID); |
| @@ -50,28 +53,37 @@ function onStartScan(message) |
| function onNotifierShutdown(message) |
| { |
| let notifier = notifiers.get(message.data); |
| if (notifier) |
| notifier.shutdown(); |
| } |
| +function onFlashNodes(message) |
| +{ |
| + let {notifierID, requests, scrollToItem} = message.data; |
| + let notifier = notifiers.get(notifierID); |
| + if (notifier) |
| + notifier.flashNodes(requests, scrollToItem); |
| +} |
| + |
| /** |
| * Creates a notifier object for a particular window. After creation the window |
| * will first be scanned for previously saved requests. Once that scan is |
| * complete only new requests for this window will be reported. |
| * @param {Window} wnd window to attach the notifier to |
| * @param {Integer} id Parent notifier ID to be messaged |
| */ |
| function RequestNotifier(window, notifierID) |
| { |
| this.window = window; |
| this.id = notifierID; |
| notifiers.set(this.id, this); |
| + this.nodes = new Map(); |
| this.startScan(window); |
| } |
| exports.RequestNotifier = RequestNotifier; |
| RequestNotifier.prototype = |
| { |
| /** |
| * Parent notifier ID to be messaged |
| @@ -81,31 +93,42 @@ RequestNotifier.prototype = |
| /** |
| * The window this notifier is associated with. |
| * @type Window |
| */ |
| window: null, |
| /** |
| + * Nodes associated with a particular request ID. |
| + * @type Map.<number,Node> |
| + */ |
| + nodes: null, |
| + |
| + /** |
| * Shuts down the notifier once it is no longer used. The listener |
| * will no longer be called after that. |
| */ |
| shutdown: function() |
| { |
| delete this.window; |
| + delete this.nodes; |
| + this.stopFlashing(); |
| notifiers.delete(this.id); |
| }, |
| /** |
| * Notifies the parent about a new request. |
| + * @param {Node} node DOM node that the request is associated with |
| * @param {Object} entry |
| */ |
| - notifyListener: function(entry) |
| + notifyListener: function(node, entry) |
| { |
| + if (this.nodes) |
| + this.nodes.set(entry.id, node); |
| sendAsyncMessage("AdblockPlus:FoundNodeData", { |
| notifierID: this.id, |
| data: entry |
| }); |
| }, |
| onComplete: function() |
| { |
| @@ -131,17 +154,17 @@ RequestNotifier.prototype = |
| { |
| if (!this.window) |
| return; |
| let node = walker.currentNode; |
| let data = nodeData.get(node); |
| if (typeof data != "undefined") |
| for (let k in data) |
| - this.notifyListener(data[k]); |
| + this.notifyListener(node, data[k]); |
| if (walker.nextNode()) |
| Utils.runAsync(process); |
| else |
| { |
| // Done with the current window, start the scan for its frames |
| for (let i = 0; i < wnd.frames.length; i++) |
| this.startScan(wnd.frames[i]); |
| @@ -153,16 +176,47 @@ RequestNotifier.prototype = |
| this.onComplete(); |
| } |
| } |
| }.bind(this); |
| // Process each node in a separate event to allow other events to process |
| this.eventsPosted++; |
| Utils.runAsync(process); |
| + }, |
| + |
| + /** |
| + * Makes the nodes associated with the given requests blink. |
| + * @param {number[]} requests list of request IDs that were previously |
| + * reported by this notifier. |
| + * @param {Boolean} scrollToItem if true, scroll to first node |
| + */ |
| + flashNodes: function(requests, scrollToItem) |
| + { |
| + this.stopFlashing(); |
| + |
| + let nodes = []; |
| + for (let id of requests) |
| + { |
| + let node = this.nodes.get(id); |
| + if (typeof node != "undefined" && node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) |
| + nodes.push(node); |
| + } |
| + if (nodes.length) |
| + this.flasher = new Flasher(nodes, scrollToItem); |
| + }, |
| + |
| + /** |
| + * Stops flashing nodes after a previous flashNodes() call. |
| + */ |
| + stopFlashing: function() |
| + { |
| + if (this.flasher) |
| + this.flasher.stop(); |
| + this.flasher = null; |
| } |
| }; |
| /** |
| * Attaches request data to a DOM node. |
| * @param {Node} node node to attach data to |
| * @param {Window} topWnd top-level window the node belongs to |
| * @param {String} contentType request type, e.g. "IMAGE" |
| @@ -218,10 +272,10 @@ RequestNotifier.addNodeData = function(n |
| stats.filters[filter.text]++; |
| else |
| stats.filters[filter.text] = 1; |
| } |
| // Notify listeners |
| for (let notifier of notifiers.values()) |
| if (!notifier.window || notifier.window == topWnd) |
| - notifier.notifyListener(entry); |
| + notifier.notifyListener(node, entry); |
| } |