| LEFT | RIGHT | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-2015 Eyeo GmbH | 3  * Copyright (C) 2006-2015 Eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 12  * GNU General Public License for more details. | 12  * GNU General Public License for more details. | 
| 13  * | 13  * | 
| 14  * You should have received a copy of the GNU General Public License | 14  * You should have received a copy of the GNU General Public License | 
| 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 16  */ | 16  */ | 
| 17 | 17 | 
| 18 /** | 18 /** | 
| 19  * @fileOverview Stores Adblock Plus data to be attached to a window. | 19  * @fileOverview Stores Adblock Plus data to be attached to a window. | 
| 20  */ | 20  */ | 
| 21 | 21 | 
| 22 Cu.import("resource://gre/modules/Services.jsm"); |  | 
| 23 |  | 
| 24 let {Utils} = require("utils"); | 22 let {Utils} = require("utils"); | 
| 25 let {BlockingFilter, WhitelistFilter, ElemHideBase, ElemHideFilter, ElemHideExce
     ption} = require("filterClasses"); | 23 let {BlockingFilter, WhitelistFilter, ElemHideBase, ElemHideFilter, ElemHideExce
     ption} = require("filterClasses"); | 
| 26 | 24 | 
| 27 let nodeData = new WeakMap(); | 25 let nodeData = new WeakMap(); | 
| 28 let windowStats = new WeakMap(); | 26 let windowStats = new WeakMap(); | 
| 29 let windowSelection = new WeakMap(); | 27 let windowSelection = new WeakMap(); | 
|  | 28 let requestNotifierMaxId = 0; | 
| 30 let requestEntryMaxId = 0; | 29 let requestEntryMaxId = 0; | 
| 31 | 30 | 
| 32 let setEntry, hasEntry, getEntry; | 31 /** | 
| 33 // Last issue(Bug 982561) preventing us from using WeakMap fixed for FF version 
     32 | 32  * Active RequestNotifier instances by their ID | 
| 34 if (Services.vc.compare(Utils.platformVersion, "32.0a1") >= 0) | 33  * @type Map | 
| 35 { | 34  */ | 
| 36   setEntry = (map, key, value) => map.set(key, value); | 35 let notifiers = new Map(); | 
| 37   hasEntry = (map, key) => map.has(key); |  | 
| 38   getEntry = (map, key) => map.get(key); |  | 
| 39 } |  | 
| 40 else |  | 
| 41 { |  | 
| 42   // Fall back to user data |  | 
| 43   let dataSeed = Math.random(); |  | 
| 44   let nodeDataProp = "abpNodeData" + dataSeed; |  | 
| 45   let windowStatsProp = "abpWindowStats" + dataSeed; |  | 
| 46   let windowSelectionProp = "abpWindowSelection" + dataSeed; |  | 
| 47   let getProp = function(map) |  | 
| 48   { |  | 
| 49     switch (map) |  | 
| 50     { |  | 
| 51       case nodeData: |  | 
| 52         return nodeDataProp; |  | 
| 53       case windowStats: |  | 
| 54         return windowStatsProp; |  | 
| 55       case windowSelection: |  | 
| 56         return windowSelectionProp; |  | 
| 57       default: |  | 
| 58         return null; |  | 
| 59     } |  | 
| 60   }; |  | 
| 61 |  | 
| 62   setEntry = (map, key, value) => key.setUserData(getProp(map), value, null); |  | 
| 63   hasEntry = (map, key) => key.getUserData(getProp(map)); |  | 
| 64   getEntry = (map, key) => key.getUserData(getProp(map)) || undefined; |  | 
| 65 } |  | 
| 66 |  | 
| 67 /** |  | 
| 68  * List of notifiers in use - these notifiers need to receive notifications on |  | 
| 69  * new requests. |  | 
| 70  * @type RequestNotifier[] |  | 
| 71  */ |  | 
| 72 let activeNotifiers = []; |  | 
| 73 | 36 | 
| 74 /** | 37 /** | 
| 75  * Creates a notifier object for a particular window. After creation the window | 38  * Creates a notifier object for a particular window. After creation the window | 
| 76  * will first be scanned for previously saved requests. Once that scan is | 39  * will first be scanned for previously saved requests. Once that scan is | 
| 77  * complete only new requests for this window will be reported. | 40  * complete only new requests for this window will be reported. | 
| 78  * @param {Window} wnd  window to attach the notifier to | 41  * @param {Window} wnd  window to attach the notifier to | 
| 79  * @param {Function} listener  listener to be called whenever a new request is f
     ound | 42  * @param {Function} listener  listener to be called whenever a new request is f
     ound | 
| 80  * @param {Object} [listenerObj]  "this" pointer to be used when calling the lis
     tener | 43  * @param {Object} [listenerObj]  "this" pointer to be used when calling the lis
     tener | 
| 81  */ | 44  */ | 
| 82 function RequestNotifier(wnd, listener, listenerObj) | 45 function RequestNotifier(wnd, listener, listenerObj) | 
| 83 { | 46 { | 
| 84   this.window = wnd; | 47   this.window = wnd; | 
| 85   this.listener = listener; | 48   this.listener = listener; | 
| 86   this.listenerObj = listenerObj || null; | 49   this.listenerObj = listenerObj || null; | 
| 87   activeNotifiers.push(this); | 50   this.id = ++requestNotifierMaxId; | 
|  | 51   notifiers.set(this.id, this); | 
| 88   if (wnd) | 52   if (wnd) | 
| 89     this.startScan(wnd); | 53     this.startScan(wnd); | 
| 90   else | 54   else | 
| 91     this.scanComplete = true; | 55     this.scanComplete = true; | 
| 92 } | 56 } | 
| 93 exports.RequestNotifier = RequestNotifier; | 57 exports.RequestNotifier = RequestNotifier; | 
| 94 | 58 | 
| 95 RequestNotifier.prototype = | 59 RequestNotifier.prototype = | 
| 96 { | 60 { | 
| 97   /** | 61   /** | 
|  | 62    * The unique ID of this notifier. | 
|  | 63    * @type Integer | 
|  | 64    */ | 
|  | 65   id: null, | 
|  | 66 | 
|  | 67   /** | 
| 98    * The window this notifier is associated with. | 68    * The window this notifier is associated with. | 
| 99    * @type Window | 69    * @type Window | 
| 100    */ | 70    */ | 
| 101   window: null, | 71   window: null, | 
| 102 | 72 | 
| 103   /** | 73   /** | 
| 104    * The listener to be called when a new request is found. | 74    * The listener to be called when a new request is found. | 
| 105    * @type Function | 75    * @type Function | 
| 106    */ | 76    */ | 
| 107   listener: null, | 77   listener: null, | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 121   /** | 91   /** | 
| 122    * Shuts down the notifier once it is no longer used. The listener | 92    * Shuts down the notifier once it is no longer used. The listener | 
| 123    * will no longer be called after that. | 93    * will no longer be called after that. | 
| 124    */ | 94    */ | 
| 125   shutdown: function() | 95   shutdown: function() | 
| 126   { | 96   { | 
| 127     delete this.window; | 97     delete this.window; | 
| 128     delete this.listener; | 98     delete this.listener; | 
| 129     delete this.listenerObj; | 99     delete this.listenerObj; | 
| 130 | 100 | 
| 131     for (let i = activeNotifiers.length - 1; i >= 0; i--) | 101     notifiers.delete(this.id); | 
| 132       if (activeNotifiers[i] == this) |  | 
| 133         activeNotifiers.splice(i, 1); |  | 
| 134   }, | 102   }, | 
| 135 | 103 | 
| 136   /** | 104   /** | 
| 137    * Notifies listener about a new request. | 105    * Notifies listener about a new request. | 
| 138    * @param {Window} wnd | 106    * @param {Window} wnd | 
| 139    * @param {Node} node | 107    * @param {Node} node | 
| 140    * @param {RequestEntry} entry | 108    * @param {RequestEntry} entry | 
| 141    */ | 109    */ | 
| 142   notifyListener: function(wnd, node, entry) | 110   notifyListener: function(wnd, node, entry) | 
| 143   { | 111   { | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
| 158   { | 126   { | 
| 159     let doc = wnd.document; | 127     let doc = wnd.document; | 
| 160     let walker = doc.createTreeWalker(doc, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, nul
     l, false); | 128     let walker = doc.createTreeWalker(doc, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, nul
     l, false); | 
| 161 | 129 | 
| 162     let process = function() | 130     let process = function() | 
| 163     { | 131     { | 
| 164       if (!this.listener) | 132       if (!this.listener) | 
| 165         return; | 133         return; | 
| 166 | 134 | 
| 167       let node = walker.currentNode; | 135       let node = walker.currentNode; | 
| 168       let data = getEntry(nodeData, node); | 136       let data = nodeData.get(node); | 
| 169       if (typeof data != "undefined") | 137       if (typeof data != "undefined") | 
| 170         for (let k in data) | 138         for (let k in data) | 
| 171           this.notifyListener(wnd, node, data[k]); | 139           this.notifyListener(wnd, node, data[k]); | 
| 172 | 140 | 
| 173       if (walker.nextNode()) | 141       if (walker.nextNode()) | 
| 174         Utils.runAsync(process); | 142         Utils.runAsync(process); | 
| 175       else | 143       else | 
| 176       { | 144       { | 
| 177         // Done with the current window, start the scan for its frames | 145         // Done with the current window, start the scan for its frames | 
| 178         for (let i = 0; i < wnd.frames.length; i++) | 146         for (let i = 0; i < wnd.frames.length; i++) | 
| 179           this.startScan(wnd.frames[i]); | 147           this.startScan(wnd.frames[i]); | 
| 180 | 148 | 
| 181         this.eventsPosted--; | 149         this.eventsPosted--; | 
| 182         if (!this.eventsPosted) | 150         if (!this.eventsPosted) | 
| 183         { | 151         { | 
| 184           this.scanComplete = true; | 152           this.scanComplete = true; | 
| 185           this.notifyListener(wnd, null, null); | 153           this.notifyListener(wnd, null, null); | 
| 186         } | 154         } | 
| 187       } | 155       } | 
| 188     }.bind(this); | 156     }.bind(this); | 
| 189 | 157 | 
| 190     // Process each node in a separate event to allow other events to process | 158     // Process each node in a separate event to allow other events to process | 
| 191     this.eventsPosted++; | 159     this.eventsPosted++; | 
| 192     Utils.runAsync(process); | 160     Utils.runAsync(process); | 
| 193   } | 161   } | 
| 194 }; | 162 }; | 
| 195 | 163 | 
| 196 RequestNotifier.storeSelection = function(/**Window*/ wnd, /**String*/ selection
     ) | 164 RequestNotifier.storeSelection = function(/**Window*/ wnd, /**String*/ selection
     ) | 
| 197 { | 165 { | 
| 198   setEntry(windowSelection, wnd.document, selection); | 166   windowSelection.set(wnd.document, selection); | 
| 199 }; | 167 }; | 
| 200 RequestNotifier.getSelection = function(/**Window*/ wnd) /**String*/ | 168 RequestNotifier.getSelection = function(/**Window*/ wnd) /**String*/ | 
| 201 { | 169 { | 
| 202   if (hasEntry(windowSelection, wnd.document)) | 170   if (windowSelection.has(wnd.document)) | 
| 203     return getEntry(windowSelection, wnd.document); | 171     return windowSelection.get(wnd.document); | 
| 204   else | 172   else | 
| 205     return null; | 173     return null; | 
| 206 }; | 174 }; | 
| 207 | 175 | 
| 208 /** | 176 /** | 
| 209  * Attaches request data to a DOM node. | 177  * Attaches request data to a DOM node. | 
| 210  * @param {Node} node   node to attach data to | 178  * @param {Node} node   node to attach data to | 
| 211  * @param {Window} topWnd   top-level window the node belongs to | 179  * @param {Window} topWnd   top-level window the node belongs to | 
| 212  * @param {String} contentType   request type, e.g. "IMAGE" | 180  * @param {String} contentType   request type, e.g. "IMAGE" | 
| 213  * @param {String} docDomain  domain of the document that initiated the request | 181  * @param {String} docDomain  domain of the document that initiated the request | 
| 214  * @param {Boolean} thirdParty  will be true if a third-party server has been re
     quested | 182  * @param {Boolean} thirdParty  will be true if a third-party server has been re
     quested | 
| 215  * @param {String} location   the address that has been requested | 183  * @param {String} location   the address that has been requested | 
| 216  * @param {Filter} filter   filter applied to the request or null if none | 184  * @param {Filter} filter   filter applied to the request or null if none | 
| 217  */ | 185  */ | 
| 218 RequestNotifier.addNodeData = function(/**Node*/ node, /**Window*/ topWnd, /**St
     ring*/ contentType, /**String*/ docDomain, /**Boolean*/ thirdParty, /**String*/ 
     location, /**Filter*/ filter) | 186 RequestNotifier.addNodeData = function(/**Node*/ node, /**Window*/ topWnd, /**St
     ring*/ contentType, /**String*/ docDomain, /**Boolean*/ thirdParty, /**String*/ 
     location, /**Filter*/ filter) | 
| 219 { | 187 { | 
| 220   return new RequestEntry(node, topWnd, contentType, docDomain, thirdParty, loca
     tion, filter); | 188   return new RequestEntry(node, topWnd, contentType, docDomain, thirdParty, loca
     tion, filter); | 
| 221 } | 189 } | 
| 222 | 190 | 
| 223 /** | 191 /** | 
| 224  * Retrieves the statistics for a window. | 192  * Retrieves the statistics for a window. | 
| 225  * @result {Object} Object with the properties items, blocked, whitelisted, hidd
     en, filters containing statistics for the window (might be null) | 193  * @result {Object} Object with the properties items, blocked, whitelisted, hidd
     en, filters containing statistics for the window (might be null) | 
| 226  */ | 194  */ | 
| 227 RequestNotifier.getWindowStatistics = function(/**Window*/ wnd) | 195 RequestNotifier.getWindowStatistics = function(/**Window*/ wnd) | 
| 228 { | 196 { | 
| 229   if (hasEntry(windowStats, wnd.document)) | 197   if (windowStats.has(wnd.document)) | 
| 230     return getEntry(windowStats, wnd.document); | 198     return windowStats.get(wnd.document); | 
| 231   else | 199   else | 
| 232     return null; | 200     return null; | 
| 233 } | 201 } | 
| 234 | 202 | 
| 235 /** | 203 /** | 
| 236  * Retrieves the request entry associated with a DOM node. | 204  * Retrieves the request entry associated with a DOM node. | 
| 237  * @param {Node} node | 205  * @param {Node} node | 
| 238  * @param {Boolean} noParent  if missing or false, the search will extend to the
      parent nodes until one is found that has data associated with it | 206  * @param {Boolean} noParent  if missing or false, the search will extend to the
      parent nodes until one is found that has data associated with it | 
| 239  * @param {Integer} [type] request type to be looking for | 207  * @param {Integer} [type] request type to be looking for | 
| 240  * @param {String} [location] request location to be looking for | 208  * @param {String} [location] request location to be looking for | 
| 241  * @result {[Node, RequestEntry]} | 209  * @result {[Node, RequestEntry]} | 
| 242  * @static | 210  * @static | 
| 243  */ | 211  */ | 
| 244 RequestNotifier.getDataForNode = function(node, noParent, type, location) | 212 RequestNotifier.getDataForNode = function(node, noParent, type, location) | 
| 245 { | 213 { | 
| 246   while (node) | 214   while (node) | 
| 247   { | 215   { | 
| 248     let data = getEntry(nodeData, node); | 216     let data = nodeData.get(node); | 
| 249     if (typeof data != "undefined") | 217     if (typeof data != "undefined") | 
| 250     { | 218     { | 
| 251       let entry = null; | 219       let entry = null; | 
| 252       // Look for matching entry | 220       // Look for matching entry | 
| 253       for (let k in data) | 221       for (let k in data) | 
| 254       { | 222       { | 
| 255         if ((!entry || entry.id < data[k].id) && | 223         if ((!entry || entry.id < data[k].id) && | 
| 256             (typeof type == "undefined" || data[k].type == type) && | 224             (typeof type == "undefined" || data[k].type == type) && | 
| 257             (typeof location == "undefined" || data[k].location == location)) | 225             (typeof location == "undefined" || data[k].location == location)) | 
| 258         { | 226         { | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
| 283   this.type = contentType; | 251   this.type = contentType; | 
| 284   this.docDomain = docDomain; | 252   this.docDomain = docDomain; | 
| 285   this.thirdParty = thirdParty; | 253   this.thirdParty = thirdParty; | 
| 286   this.location = location; | 254   this.location = location; | 
| 287   this.filter = filter; | 255   this.filter = filter; | 
| 288   this.id = ++requestEntryMaxId; | 256   this.id = ++requestEntryMaxId; | 
| 289 | 257 | 
| 290   this.attachToNode(node); | 258   this.attachToNode(node); | 
| 291 | 259 | 
| 292   // Update window statistics | 260   // Update window statistics | 
| 293   if (!hasEntry(windowStats, topWnd.document)) | 261   if (!windowStats.has(topWnd.document)) | 
| 294   { | 262   { | 
| 295     setEntry(windowStats, topWnd.document, { | 263     windowStats.set(topWnd.document, { | 
| 296       items: 0, | 264       items: 0, | 
| 297       hidden: 0, | 265       hidden: 0, | 
| 298       blocked: 0, | 266       blocked: 0, | 
| 299       whitelisted: 0, | 267       whitelisted: 0, | 
| 300       filters: {} | 268       filters: {} | 
| 301     }); | 269     }); | 
| 302   } | 270   } | 
| 303 | 271 | 
| 304   let stats = getEntry(windowStats, topWnd.document); | 272   let stats = windowStats.get(topWnd.document); | 
| 305   if (!filter || !(filter instanceof ElemHideBase)) | 273   if (!filter || !(filter instanceof ElemHideBase)) | 
| 306     stats.items++; | 274     stats.items++; | 
| 307   if (filter) | 275   if (filter) | 
| 308   { | 276   { | 
| 309     if (filter instanceof BlockingFilter) | 277     if (filter instanceof BlockingFilter) | 
| 310       stats.blocked++; | 278       stats.blocked++; | 
| 311     else if (filter instanceof WhitelistFilter || filter instanceof ElemHideExce
     ption) | 279     else if (filter instanceof WhitelistFilter || filter instanceof ElemHideExce
     ption) | 
| 312       stats.whitelisted++; | 280       stats.whitelisted++; | 
| 313     else if (filter instanceof ElemHideFilter) | 281     else if (filter instanceof ElemHideFilter) | 
| 314       stats.hidden++; | 282       stats.hidden++; | 
| 315 | 283 | 
| 316     if (filter.text in stats.filters) | 284     if (filter.text in stats.filters) | 
| 317       stats.filters[filter.text]++; | 285       stats.filters[filter.text]++; | 
| 318     else | 286     else | 
| 319       stats.filters[filter.text] = 1; | 287       stats.filters[filter.text] = 1; | 
| 320   } | 288   } | 
| 321 | 289 | 
| 322   // Notify listeners | 290   // Notify listeners | 
| 323   for (let notifier of activeNotifiers) | 291   for (let notifier of notifiers.values()) | 
| 324     if (!notifier.window || notifier.window == topWnd) | 292     if (!notifier.window || notifier.window == topWnd) | 
| 325       notifier.notifyListener(topWnd, node, this); | 293       notifier.notifyListener(topWnd, node, this); | 
| 326 } | 294 } | 
| 327 RequestEntry.prototype = | 295 RequestEntry.prototype = | 
| 328 { | 296 { | 
| 329   /** | 297   /** | 
| 330    * id of request (used to determine last entry attached to a node) | 298    * id of request (used to determine last entry attached to a node) | 
| 331    * @type integer | 299    * @type integer | 
| 332    */ | 300    */ | 
| 333   id: 0, | 301   id: 0, | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 371   get localizedDescr() | 339   get localizedDescr() | 
| 372   { | 340   { | 
| 373     return require("contentPolicy").Policy.localizedDescr.get(this.type); | 341     return require("contentPolicy").Policy.localizedDescr.get(this.type); | 
| 374   }, | 342   }, | 
| 375 | 343 | 
| 376   /** | 344   /** | 
| 377    * Attaches this request object to a DOM node. | 345    * Attaches this request object to a DOM node. | 
| 378    */ | 346    */ | 
| 379   attachToNode: function(/**Node*/ node) | 347   attachToNode: function(/**Node*/ node) | 
| 380   { | 348   { | 
| 381     let existingData = getEntry(nodeData, node); | 349     let existingData = nodeData.get(node); | 
| 382     if (typeof existingData == "undefined") | 350     if (typeof existingData == "undefined") | 
| 383     { | 351     { | 
| 384       existingData = {}; | 352       existingData = {}; | 
| 385       setEntry(nodeData, node, existingData); | 353       nodeData.set(node, existingData); | 
| 386     } | 354     } | 
| 387 | 355 | 
| 388     // Add this request to the node data | 356     // Add this request to the node data | 
| 389     existingData[this.type + " " + this.location] = this; | 357     existingData[this.type + " " + this.location] = this; | 
| 390   } | 358   } | 
| 391 }; | 359 }; | 
| LEFT | RIGHT | 
|---|