| LEFT | RIGHT | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <http://adblockplus.org/>, |    2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2014 Eyeo GmbH |    3  * Copyright (C) 2006-2014 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 Element hiding implementation. |   19  * @fileOverview Element hiding implementation. | 
|   20  */ |   20  */ | 
|   21  |   21  | 
|   22 Cu.import("resource://gre/modules/Services.jsm"); |   22 Cu.import("resource://gre/modules/Services.jsm"); | 
|   23  |   23  | 
|   24 let {Utils} = require("utils"); |   24 let {Utils} = require("utils"); | 
|   25 let {IO} = require("io"); |   25 let {IO} = require("io"); | 
|   26 let {Prefs} = require("prefs"); |   26 let {Prefs} = require("prefs"); | 
|   27 let {Policy} = require("contentPolicy"); |  | 
|   28 let {ElemHideException} = require("filterClasses"); |   27 let {ElemHideException} = require("filterClasses"); | 
|   29 let {FilterNotifier} = require("filterNotifier"); |   28 let {FilterNotifier} = require("filterNotifier"); | 
|   30 let {AboutHandler} = require("elemHideHitRegistration"); |   29 let {AboutHandler} = require("elemHideHitRegistration"); | 
|   31 let {TimeLine} = require("timeline"); |   30 let {TimeLine} = require("timeline"); | 
 |   31 let Policy = null; | 
|   32  |   32  | 
|   33 /** |   33 /** | 
|   34  * Lookup table, filters by their associated key |   34  * Lookup table, filters by their associated key | 
|   35  * @type Object |   35  * @type Object | 
|   36  */ |   36  */ | 
|   37 let filterByKey = {__proto__: null}; |   37 let filterByKey = Object.create(null); | 
|   38  |   38  | 
|   39 /** |   39 /** | 
|   40  * Lookup table, keys of the filters by filter text |   40  * Lookup table, keys of the filters by filter text | 
|   41  * @type Object |   41  * @type Object | 
|   42  */ |   42  */ | 
|   43 let keyByFilter = {__proto__: null}; |   43 let keyByFilter = Object.create(null); | 
|   44  |   44  | 
|   45 /** |   45 /** | 
|   46  * Lookup table, keys are known element hiding exceptions |   46  * Lookup table, keys are known element hiding exceptions | 
|   47  * @type Object |   47  * @type Object | 
|   48  */ |   48  */ | 
|   49 let knownExceptions = {__proto__: null}; |   49 let knownExceptions = Object.create(null); | 
|   50  |   50  | 
|   51 /** |   51 /** | 
|   52  * Lookup table, lists of element hiding exceptions by selector |   52  * Lookup table, lists of element hiding exceptions by selector | 
|   53  * @type Object |   53  * @type Object | 
|   54  */ |   54  */ | 
|   55 let exceptions = {__proto__: null}; |   55 let exceptions = Object.create(null); | 
|   56  |   56  | 
|   57 /** |   57 /** | 
|   58  * Currently applied stylesheet URL |   58  * Currently applied stylesheet URL | 
|   59  * @type nsIURI |   59  * @type nsIURI | 
|   60  */ |   60  */ | 
|   61 let styleURL = null; |   61 let styleURL = null; | 
|   62  |   62  | 
|   63 /** |   63 /** | 
 |   64  * Global stylesheet that should be loaded into content windows. | 
 |   65  * @type nsIStyleSheet | 
 |   66  */ | 
 |   67 let styleSheet = null; | 
 |   68  | 
 |   69 /** | 
 |   70  * Use the new way of injecting styles per window that exists since Firefox 33. | 
 |   71  * @type boolean | 
 |   72  */ | 
 |   73 let useNew = ('preloadSheet' in Utils.styleService); | 
 |   74  | 
 |   75 /** | 
|   64  * Element hiding component |   76  * Element hiding component | 
|   65  * @class |   77  * @class | 
|   66  */ |   78  */ | 
|   67 let ElemHide = exports.ElemHide = |   79 let ElemHide = exports.ElemHide = | 
|   68 { |   80 { | 
|   69   /** |   81   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer
     ence]), | 
|   70    * Indicates whether filters have been added or removed since the last saveSty
     lesheet() call. |   82  | 
 |   83   /** | 
 |   84    * Indicates whether filters have been added or removed since the last apply()
      call. | 
|   71    * @type Boolean |   85    * @type Boolean | 
|   72    */ |   86    */ | 
|   73   isDirty: false, |   87   isDirty: false, | 
|   74  |   88  | 
|   75   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer
     ence]), |   89   /** | 
 |   90    * Inidicates whether the element hiding stylesheet is currently applied. | 
 |   91    * @type Boolean | 
 |   92    */ | 
 |   93   applied: false, | 
|   76  |   94  | 
|   77   /** |   95   /** | 
|   78    * Called on module startup. |   96    * Called on module startup. | 
|   79    */ |   97    */ | 
|   80   init: function() |   98   init: function() | 
|   81   { |   99   { | 
|   82     TimeLine.enter("Entered ElemHide.init()"); |  100     TimeLine.enter("Entered ElemHide.init()"); | 
|   83  |  101  | 
|   84     onShutdown.add(function() |  102     if (useNew) { | 
|   85     { |  103       // Avoid dependency issue. | 
|   86       Services.obs.removeObserver(this, "content-document-global-created"); |  104       Policy = require("contentPolicy").Policy; | 
|   87     }.bind(this)); |  105     } | 
 |  106  | 
 |  107     Prefs.addListener(function(name) | 
 |  108     { | 
 |  109       if (name == "enabled") | 
 |  110         ElemHide.apply(); | 
 |  111     }); | 
 |  112  | 
 |  113     if (useNew) | 
 |  114       Services.obs.addObserver(this, "content-document-global-created", true); | 
 |  115  | 
 |  116     onShutdown.add(() => | 
 |  117     { | 
 |  118       ElemHide.unapply(); | 
 |  119       if (useNew) | 
 |  120         Services.obs.removeObserver(this, "content-document-global-created"); | 
 |  121     }); | 
|   88  |  122  | 
|   89     TimeLine.log("done adding prefs listener"); |  123     TimeLine.log("done adding prefs listener"); | 
|   90  |  124  | 
|   91     let styleFile = IO.resolveFilePath(Prefs.data_directory); |  125     let styleFile = IO.resolveFilePath(Prefs.data_directory); | 
|   92     styleFile.append("elemhide.css"); |  126     styleFile.append("elemhide.css"); | 
|   93     styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); |  127     styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); | 
|   94     TimeLine.log("done determining stylesheet URL"); |  128     TimeLine.log("done determining stylesheet URL"); | 
|   95  |  129  | 
|   96     Services.obs.addObserver(this, "content-document-global-created", true); |  | 
|   97  |  | 
|   98     TimeLine.leave("ElemHide.init() done"); |  130     TimeLine.leave("ElemHide.init() done"); | 
|   99   }, |  | 
|  100  |  | 
|  101   /** |  | 
|  102    * Removes all known filters |  | 
|  103    */ |  | 
|  104   clear: function() |  | 
|  105   { |  | 
|  106     filterByKey = {__proto__: null}; |  | 
|  107     keyByFilter = {__proto__: null}; |  | 
|  108     knownExceptions = {__proto__: null}; |  | 
|  109     exceptions = {__proto__: null}; |  | 
|  110     ElemHide.isDirty = false; |  | 
|  111   }, |  131   }, | 
|  112  |  132  | 
|  113   observe: function (subject, topic, data, additional) |  133   observe: function (subject, topic, data, additional) | 
|  114   { |  134   { | 
|  115     if (topic != "content-document-global-created") |  135     if (topic != "content-document-global-created") | 
|  116       return; |  136       return; | 
|  117  |  137  | 
|  118     if (!Prefs.enabled) |  138     if (!Prefs.enabled) | 
|  119       return; |  139       return; | 
|  120  |  140  | 
|  121     let process = Policy.processWindow(subject, Policy.type.ELEMHIDE); |  141     if (Policy.shouldNeverBlockWindow(subject)) | 
|  122     dump(subject.document.documentURIObject.spec + " is okay " +  process + "\n"
     ); |  | 
|  123     if (process) |  | 
|  124       return; |  142       return; | 
|  125  |  143  | 
|  126     var windowUtils = subject.QueryInterface(Ci.nsIInterfaceRequestor).getInterf
     ace(Ci.nsIDOMWindowUtils); |  144     if (styleSheet) | 
|  127     windowUtils.loadSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); |  145     { | 
 |  146       try | 
 |  147       { | 
 |  148         let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor) | 
 |  149                            .getInterface(Ci.nsIDOMWindowUtils); | 
 |  150         utils.addSheet(styleSheet, Ci.nsIStyleSheetService.USER_SHEET); | 
 |  151       } | 
 |  152       catch (e) | 
 |  153       { | 
 |  154         Cu.reportError(e); | 
 |  155       } | 
 |  156     } | 
 |  157    }, | 
 |  158  | 
 |  159   /** | 
 |  160    * Removes all known filters | 
 |  161    */ | 
 |  162   clear: function() | 
 |  163   { | 
 |  164     filterByKey = Object.create(null); | 
 |  165     keyByFilter = Object.create(null); | 
 |  166     knownExceptions = Object.create(null); | 
 |  167     exceptions = Object.create(null); | 
 |  168     ElemHide.isDirty = false; | 
 |  169     ElemHide.unapply(); | 
|  128   }, |  170   }, | 
|  129  |  171  | 
|  130   /** |  172   /** | 
|  131    * Add a new element hiding filter |  173    * Add a new element hiding filter | 
|  132    * @param {ElemHideFilter} filter |  174    * @param {ElemHideFilter} filter | 
|  133    */ |  175    */ | 
|  134   add: function(filter) |  176   add: function(filter) | 
|  135   { |  177   { | 
|  136     if (filter instanceof ElemHideException) |  178     if (filter instanceof ElemHideException) | 
|  137     { |  179     { | 
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  200       return null; |  242       return null; | 
|  201  |  243  | 
|  202     let list = exceptions[filter.selector]; |  244     let list = exceptions[filter.selector]; | 
|  203     for (let i = list.length - 1; i >= 0; i--) |  245     for (let i = list.length - 1; i >= 0; i--) | 
|  204       if (list[i].isActiveOnDomain(docDomain)) |  246       if (list[i].isActiveOnDomain(docDomain)) | 
|  205         return list[i]; |  247         return list[i]; | 
|  206  |  248  | 
|  207     return null; |  249     return null; | 
|  208   }, |  250   }, | 
|  209  |  251  | 
|  210   shouldAllowLoad: function(wnd, filter) |  252   /** | 
|  211   { |  253    * Will be set to true if apply() is running (reentrance protection). | 
|  212     let uri = wnd.document.documentURIObject; |  | 
|  213     if (!uri) |  | 
|  214       return true; |  | 
|  215  |  | 
|  216     let domain = uri.host; |  | 
|  217     if (!filter.isActiveOnDomain(domain)) |  | 
|  218       return true; |  | 
|  219  |  | 
|  220     let exception = ElemHide.getException(filter, domain); |  | 
|  221     if (exception) |  | 
|  222     { |  | 
|  223       // FilterStorage.increaseHitCount(exception, wnd); |  | 
|  224       // RequestNotifier.addNodeData(node, topWnd, contentType, domain, thirdPar
     ty, locationText, exception); |  | 
|  225       return true; |  | 
|  226     } |  | 
|  227  |  | 
|  228     // RequestNotifier.addNodeData(node, topWnd, contentType, domain, thirdParty
     , locationText, match); |  | 
|  229     // if (match) |  | 
|  230     //   FilterStorage.increaseHitCount(match, wnd); |  | 
|  231     return false; |  | 
|  232   }, |  | 
|  233  |  | 
|  234   /** |  | 
|  235    * Will be set to true if saveStylesheet() is running (reentrance protection). |  | 
|  236    * @type Boolean |  254    * @type Boolean | 
|  237    */ |  255    */ | 
|  238   _applying: false, |  256   _applying: false, | 
|  239  |  257  | 
|  240   /** |  258   /** | 
|  241    * Will be set to true if an saveStylesheet() call arrives while saveStyleshee
     t() |  259    * Will be set to true if an apply() call arrives while apply() is already | 
|  242    * is already running (delayed execution). |  260    * running (delayed execution). | 
|  243    * @type Boolean |  261    * @type Boolean | 
|  244    */ |  262    */ | 
|  245   _needsApply: false, |  263   _needsApply: false, | 
|  246  |  264  | 
|  247   /** |  265   /** | 
|  248    * Generates stylesheet URL. |  266    * Generates stylesheet URL and applies it globally | 
|  249    */ |  267    */ | 
|  250   saveStylesheet: function() |  268   apply: function() | 
|  251   { |  269   { | 
|  252     if (this._applying) |  270     if (this._applying) | 
|  253     { |  271     { | 
|  254       this._needsApply = true; |  272       this._needsApply = true; | 
|  255       return; |  273       return; | 
|  256     } |  274     } | 
|  257  |  275  | 
|  258     TimeLine.enter("Entered ElemHide.saveStylesheet()"); |  276     TimeLine.enter("Entered ElemHide.apply()"); | 
 |  277  | 
 |  278     if (!ElemHide.isDirty || !Prefs.enabled) | 
 |  279     { | 
 |  280       // Nothing changed, looks like we merely got enabled/disabled | 
 |  281       if (Prefs.enabled && !ElemHide.applied) | 
 |  282       { | 
 |  283         try | 
 |  284         { | 
 |  285           if (!useNew) | 
 |  286             Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheetSe
     rvice.USER_SHEET); | 
 |  287           ElemHide.applied = true; | 
 |  288         } | 
 |  289         catch (e) | 
 |  290         { | 
 |  291           Cu.reportError(e); | 
 |  292         } | 
 |  293         TimeLine.log("Applying existing stylesheet finished"); | 
 |  294       } | 
 |  295       else if (!Prefs.enabled && ElemHide.applied) | 
 |  296       { | 
 |  297         ElemHide.unapply(); | 
 |  298         TimeLine.log("ElemHide.unapply() finished"); | 
 |  299       } | 
 |  300  | 
 |  301       TimeLine.leave("ElemHide.apply() done (no file changes)"); | 
 |  302       return; | 
 |  303     } | 
|  259  |  304  | 
|  260     IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) |  305     IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) | 
|  261     { |  306     { | 
|  262       TimeLine.enter("ElemHide.saveStylesheet() write callback"); |  307       TimeLine.enter("ElemHide.apply() write callback"); | 
|  263       this._applying = false; |  308       this._applying = false; | 
|  264  |  309  | 
|  265       // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that |  310       // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that | 
|  266       // there are no filters. If that exception is passed through XPCOM we will |  311       // there are no filters. If that exception is passed through XPCOM we will | 
|  267       // see a proper exception here, otherwise a number. |  312       // see a proper exception here, otherwise a number. | 
|  268       let noFilters = (e == Cr.NS_ERROR_NOT_AVAILABLE || (e && e.result == Cr.NS
     _ERROR_NOT_AVAILABLE)); |  313       let noFilters = (e == Cr.NS_ERROR_NOT_AVAILABLE || (e && e.result == Cr.NS
     _ERROR_NOT_AVAILABLE)); | 
|  269       if (noFilters) |  314       if (noFilters) | 
|  270       { |  315       { | 
|  271         e = null; |  316         e = null; | 
|  272         IO.removeFile(styleURL.file, function(e) {}); |  317         IO.removeFile(styleURL.file, function(e) {}); | 
|  273       } |  318       } | 
|  274       else if (e) |  319       else if (e) | 
|  275       { |  | 
|  276         Cu.reportError(e); |  320         Cu.reportError(e); | 
|  277       } |  | 
|  278  |  321  | 
|  279       if (this._needsApply) |  322       if (this._needsApply) | 
|  280       { |  323       { | 
|  281         this._needsApply = false; |  324         this._needsApply = false; | 
|  282         this.saveStylesheet(); |  325         this.apply(); | 
|  283       } |  326       } | 
|  284       else if (!e) |  327       else if (!e) | 
|  285       { |  328       { | 
|  286         ElemHide.isDirty = false; |  329         ElemHide.isDirty = false; | 
|  287  |  330  | 
 |  331         ElemHide.unapply(); | 
 |  332         TimeLine.log("ElemHide.unapply() finished"); | 
 |  333  | 
 |  334         if (!noFilters) | 
 |  335         { | 
 |  336           try | 
 |  337           { | 
 |  338             if (!useNew) | 
 |  339               Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheet
     Service.USER_SHEET); | 
 |  340             else | 
 |  341               styleSheet = Utils.styleService.preloadSheet(styleURL, Ci.nsIStyle
     SheetService.USER_SHEET); | 
 |  342             ElemHide.applied = true; | 
 |  343           } | 
 |  344           catch (e) | 
 |  345           { | 
 |  346             Cu.reportError(e); | 
 |  347           } | 
 |  348           TimeLine.log("Applying stylesheet finished"); | 
 |  349         } | 
 |  350  | 
|  288         FilterNotifier.triggerListeners("elemhideupdate"); |  351         FilterNotifier.triggerListeners("elemhideupdate"); | 
|  289       } |  352       } | 
|  290       TimeLine.leave("ElemHide.saveStylesheet() write callback done"); |  353       TimeLine.leave("ElemHide.apply() write callback done"); | 
|  291     }.bind(this), "ElemHideWrite"); |  354     }.bind(this), "ElemHideWrite"); | 
|  292  |  355  | 
|  293     this._applying = true; |  356     this._applying = true; | 
|  294  |  357  | 
|  295     TimeLine.leave("ElemHide.saveStylesheet() done", "ElemHideWrite"); |  358     TimeLine.leave("ElemHide.apply() done", "ElemHideWrite"); | 
|  296   }, |  359   }, | 
|  297  |  360  | 
|  298   _generateCSSContent: function() |  361   _generateCSSContent: function() | 
|  299   { |  362   { | 
|  300     // Grouping selectors by domains |  363     // Grouping selectors by domains | 
|  301     TimeLine.log("start grouping selectors"); |  364     TimeLine.log("start grouping selectors"); | 
|  302     let domains = {__proto__: null}; |  365     let domains = Object.create(null); | 
|  303     let hasFilters = false; |  366     let hasFilters = false; | 
|  304     for (let key in filterByKey) |  367     for (let key in filterByKey) | 
|  305     { |  368     { | 
|  306       let filter = filterByKey[key]; |  369       let filter = filterByKey[key]; | 
|  307       let domain = filter.selectorDomain || ""; |  370       let domain = filter.selectorDomain || ""; | 
|  308  |  371  | 
|  309       let list; |  372       let list; | 
|  310       if (domain in domains) |  373       if (domain in domains) | 
|  311         list = domains[domain]; |  374         list = domains[domain]; | 
|  312       else |  375       else | 
|  313       { |  376       { | 
|  314         list = {__proto__: null}; |  377         list = Object.create(null); | 
|  315         domains[domain] = list; |  378         domains[domain] = list; | 
|  316       } |  379       } | 
|  317       list[filter.selector] = key; |  380       list[filter.selector] = key; | 
|  318       hasFilters = true; |  381       hasFilters = true; | 
|  319     } |  382     } | 
|  320     TimeLine.log("done grouping selectors"); |  383     TimeLine.log("done grouping selectors"); | 
|  321  |  384  | 
|  322     if (!hasFilters) |  385     if (!hasFilters) | 
|  323       throw Cr.NS_ERROR_NOT_AVAILABLE; |  386       throw Cr.NS_ERROR_NOT_AVAILABLE; | 
|  324  |  387  | 
| (...skipping 19 matching lines...) Expand all  Loading... | 
|  344                   + 'url-prefix("news://"),url-prefix("snews://"){'; |  407                   + 'url-prefix("news://"),url-prefix("snews://"){'; | 
|  345       } |  408       } | 
|  346  |  409  | 
|  347       for (let selector in list) |  410       for (let selector in list) | 
|  348         yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "{" + cssTemplate.
     replace("%ID%", list[selector]) + "}"; |  411         yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "{" + cssTemplate.
     replace("%ID%", list[selector]) + "}"; | 
|  349       yield '}'; |  412       yield '}'; | 
|  350     } |  413     } | 
|  351   }, |  414   }, | 
|  352  |  415  | 
|  353   /** |  416   /** | 
 |  417    * Unapplies current stylesheet URL | 
 |  418    */ | 
 |  419   unapply: function() | 
 |  420   { | 
 |  421     if (ElemHide.applied) | 
 |  422     { | 
 |  423       try | 
 |  424       { | 
 |  425         if (!useNew) | 
 |  426           Utils.styleService.unregisterSheet(styleURL, Ci.nsIStyleSheetService.U
     SER_SHEET); | 
 |  427       } | 
 |  428       catch (e) | 
 |  429       { | 
 |  430         Cu.reportError(e); | 
 |  431       } | 
 |  432       ElemHide.applied = false; | 
 |  433     } | 
 |  434   }, | 
 |  435  | 
 |  436   /** | 
|  354    * Retrieves an element hiding filter by the corresponding protocol key |  437    * Retrieves an element hiding filter by the corresponding protocol key | 
|  355    */ |  438    */ | 
|  356   getFilterByKey: function(/**String*/ key) /**Filter*/ |  439   getFilterByKey: function(/**String*/ key) /**Filter*/ | 
|  357   { |  440   { | 
|  358     return (key in filterByKey ? filterByKey[key] : null); |  441     return (key in filterByKey ? filterByKey[key] : null); | 
|  359   }, |  442   }, | 
|  360  |  443  | 
|  361   /** |  444   /** | 
|  362    * Returns a list of all selectors active on a particular domain (currently |  445    * Returns a list of all selectors active on a particular domain (currently | 
|  363    * used only in Chrome, Opera and Safari). |  446    * used only in Chrome, Opera and Safari). | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
|  376  |  459  | 
|  377       if (specificOnly && (!domains || domains[""])) |  460       if (specificOnly && (!domains || domains[""])) | 
|  378         continue; |  461         continue; | 
|  379  |  462  | 
|  380       if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) |  463       if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) | 
|  381         result.push(filter.selector); |  464         result.push(filter.selector); | 
|  382     } |  465     } | 
|  383     return result; |  466     return result; | 
|  384   } |  467   } | 
|  385 }; |  468 }; | 
| LEFT | RIGHT |