| OLD | NEW | 
|---|
| 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 Content policy implementation, responsible for blocking things. | 19  * @fileOverview Content policy implementation, responsible for blocking things. | 
| 20  */ | 20  */ | 
| 21 | 21 | 
| 22 "use strict"; | 22 "use strict"; | 
| 23 | 23 | 
|  | 24 try | 
|  | 25 { | 
|  | 26   // Hack: SDK loader masks our Components object with a getter. | 
|  | 27   let proto = Object.getPrototypeOf(this); | 
|  | 28   let property = Object.getOwnPropertyDescriptor(proto, "Components"); | 
|  | 29   if (property && property.get) | 
|  | 30     delete proto.Components; | 
|  | 31 } | 
|  | 32 catch (e) | 
|  | 33 { | 
|  | 34   Cu.reportError(e); | 
|  | 35 } | 
|  | 36 | 
| 24 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 37 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 
| 25 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); | 38 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); | 
| 26 let {PrivateBrowsingUtils} = Cu.import("resource://gre/modules/PrivateBrowsingUt
     ils.jsm", {}); |  | 
| 27 | 39 | 
| 28 let {Utils} = require("utils"); | 40 let {Utils} = require("utils"); | 
| 29 let {Prefs} = require("prefs"); | 41 let {getFrames, isPrivate} = require("child/utils"); | 
| 30 let {FilterStorage} = require("filterStorage"); |  | 
| 31 let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses"); |  | 
| 32 let {defaultMatcher} = require("matcher"); |  | 
| 33 let {objectMouseEventHander} = require("objectTabs"); | 42 let {objectMouseEventHander} = require("objectTabs"); | 
| 34 let {RequestNotifier} = require("requestNotifier"); | 43 let {RequestNotifier} = require("requestNotifier"); | 
| 35 let {ElemHide} = require("elemHide"); |  | 
| 36 | 44 | 
| 37 /** | 45 /** | 
| 38  * Randomly generated class name, to be applied to collapsed nodes. | 46  * Randomly generated class name, to be applied to collapsed nodes. | 
| 39  */ | 47  */ | 
| 40 let collapsedClass = ""; | 48 let collapsedClass = null; | 
| 41 | 49 | 
| 42 /** | 50 /** | 
| 43  * Maps numerical content type IDs to strings. | 51  * Maps numerical content type IDs to strings. | 
| 44  * @type Map | 52  * @type Map | 
| 45  */ | 53  */ | 
| 46 let types = new Map(); | 54 let types = new Map(); | 
| 47 | 55 | 
| 48 /** | 56 /** | 
| 49  * Public policy checking functions and auxiliary objects | 57  * Checks whether a request should be allowed, hides it if necessary | 
| 50  * @class | 58  * @param wnd {nsIDOMWindow} | 
|  | 59  * @param node {nsIDOMElement} | 
|  | 60  * @param contentType {String} | 
|  | 61  * @param location {String} | 
|  | 62  * @return {Boolean} false if the request should be blocked | 
| 51  */ | 63  */ | 
| 52 var Policy = exports.Policy = | 64 function shouldAllow(window, node, contentType, location) | 
| 53 { | 65 { | 
| 54   /** | 66   let response = sendSyncMessage("AdblockPlus:ShouldAllow", { | 
| 55    * Set of explicitly supported content types | 67     contentType: contentType, | 
| 56    * @type Set | 68     location: location, | 
| 57    */ | 69     frames: getFrames(window), | 
| 58   contentTypes: new Set([ | 70     isPrivate: isPrivate(window) | 
| 59     "OTHER", "SCRIPT", "IMAGE", "STYLESHEET", "OBJECT", "SUBDOCUMENT", "DOCUMENT
     ", | 71   }); | 
| 60     "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT", "MEDIA", "ELEMHIDE", "POPUP", | 72   if (response.length == 0) | 
| 61     "GENERICHIDE", "GENERICBLOCK" | 73     return true; | 
| 62   ]), |  | 
| 63 | 74 | 
| 64   /** | 75   let {allow, collapse, hits} = response[0]; | 
| 65    * Set of content types that aren't associated with a visual document area | 76   for (let {frameIndex, contentType, docDomain, thirdParty, location, filter} of
      hits) | 
| 66    * @type Set | 77   { | 
| 67    */ | 78     let context = node; | 
| 68   nonVisualTypes: new Set([ | 79     if (typeof frameIndex == "number") | 
| 69     "SCRIPT", "STYLESHEET", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT", | 80     { | 
| 70     "ELEMHIDE", "POPUP", "GENERICHIDE", "GENERICBLOCK" | 81       context = window; | 
| 71   ]), | 82       for (let i = 0; i < frameIndex; i++) | 
|  | 83         context = context.parent; | 
|  | 84       context = context.document; | 
|  | 85     } | 
|  | 86     RequestNotifier.addNodeData(context, window.top, contentType, docDomain, thi
     rdParty, location, filter); | 
|  | 87   } | 
| 72 | 88 | 
| 73   /** | 89   if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) | 
| 74    * Map containing all schemes that should be ignored by content policy. |  | 
| 75    * @type Set |  | 
| 76    */ |  | 
| 77   whitelistSchemes: new Set(), |  | 
| 78 |  | 
| 79   /** |  | 
| 80    * Called on module startup, initializes various exported properties. |  | 
| 81    */ |  | 
| 82   init: function() |  | 
| 83   { | 90   { | 
| 84     // whitelisted URL schemes | 91     // Track mouse events for objects | 
| 85     for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" ")) | 92     if (allow && contentType == "OBJECT") | 
| 86       this.whitelistSchemes.add(scheme); |  | 
| 87 |  | 
| 88     // Generate class identifier used to collapse node and register correspondin
     g |  | 
| 89     // stylesheet. |  | 
| 90     let offset = "a".charCodeAt(0); |  | 
| 91     for (let i = 0; i < 20; i++) |  | 
| 92       collapsedClass +=  String.fromCharCode(offset + Math.random() * 26); |  | 
| 93 |  | 
| 94     let collapseStyle = Services.io.newURI("data:text/css," + |  | 
| 95         encodeURIComponent("." + collapsedClass + |  | 
| 96         "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarb
     azdummy) !important;}"), null, null); |  | 
| 97     Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetServi
     ce.USER_SHEET); |  | 
| 98     onShutdown.add(() => |  | 
| 99     { | 93     { | 
| 100       Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService.
     USER_SHEET); | 94       node.addEventListener("mouseover", objectMouseEventHander, true); | 
| 101     }); | 95       node.addEventListener("mouseout", objectMouseEventHander, true); | 
| 102   }, |  | 
| 103 |  | 
| 104   /** |  | 
| 105    * Checks whether a node should be blocked, hides it if necessary |  | 
| 106    * @param wnd {nsIDOMWindow} |  | 
| 107    * @param node {nsIDOMElement} |  | 
| 108    * @param contentType {String} |  | 
| 109    * @param location {String} |  | 
| 110    * @param collapse {Boolean} true to force hiding of the node |  | 
| 111    * @return {Boolean} false if the node should be blocked |  | 
| 112    */ |  | 
| 113   processNode: function(wnd, node, contentType, location, collapse) |  | 
| 114   { |  | 
| 115     let topWnd = wnd.top; |  | 
| 116     if (!topWnd || !topWnd.location || !topWnd.location.href) |  | 
| 117       return true; |  | 
| 118 |  | 
| 119     // Ignore whitelisted schemes |  | 
| 120     if (!this.isBlockableScheme(location)) |  | 
| 121       return true; |  | 
| 122 |  | 
| 123     // Interpret unknown types as "other" |  | 
| 124     if (!this.contentTypes.has(contentType)) |  | 
| 125       contentType = "OTHER"; |  | 
| 126 |  | 
| 127     let originWindow = Utils.getOriginWindow(wnd); |  | 
| 128     let wndLocation = originWindow.location.href; |  | 
| 129     let docDomain = getHostname(wndLocation); |  | 
| 130     let match = null; |  | 
| 131     let [sitekey, sitekeyWnd] = getSitekey(wnd); |  | 
| 132     let nogeneric = false; |  | 
| 133 |  | 
| 134     function cleanWindowLocation(wnd) |  | 
| 135     { |  | 
| 136       let url = getWindowLocation(wnd); |  | 
| 137       let index = url.indexOf("#"); |  | 
| 138       if (index >= 0) |  | 
| 139         url = url.substring(0, index); |  | 
| 140 |  | 
| 141       return url; |  | 
| 142     } | 96     } | 
| 143 | 97 | 
| 144     function addHit(match) | 98     if (collapse) | 
| 145     { | 99       schedulePostProcess(node); | 
| 146       if (!PrivateBrowsingUtils.isContentWindowPrivate(wnd)) | 100   } | 
| 147         FilterStorage.increaseHitCount(match); |  | 
| 148     } |  | 
| 149 | 101 | 
| 150     if (!match && Prefs.enabled) | 102   return allow; | 
| 151     { | 103 } | 
| 152       let testWnd = wnd; |  | 
| 153       let testSitekey = sitekey; |  | 
| 154       let testSitekeyWnd = sitekeyWnd; |  | 
| 155       let parentWndLocation = cleanWindowLocation(testWnd); |  | 
| 156       while (true) |  | 
| 157       { |  | 
| 158         let testWndLocation = parentWndLocation; |  | 
| 159         parentWndLocation = (testWnd == testWnd.parent ? testWndLocation : clean
     WindowLocation(testWnd.parent)); |  | 
| 160         let parentDocDomain = getHostname(parentWndLocation); |  | 
| 161 |  | 
| 162         let typeMap = RegExpFilter.typeMap.DOCUMENT; |  | 
| 163         if (contentType == "ELEMHIDE") |  | 
| 164           typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE; |  | 
| 165         let whitelistMatch = defaultMatcher.matchesAny(testWndLocation, typeMap,
      parentDocDomain, false, testSitekey); |  | 
| 166         if (whitelistMatch instanceof WhitelistFilter) |  | 
| 167         { |  | 
| 168           addHit(whitelistMatch); |  | 
| 169           RequestNotifier.addNodeData(testWnd.document, topWnd, |  | 
| 170             (whitelistMatch.contentType & RegExpFilter.typeMap.DOCUMENT) ? "DOCU
     MENT" : "ELEMHIDE", |  | 
| 171             parentDocDomain, false, testWndLocation, whitelistMatch); |  | 
| 172           return true; |  | 
| 173         } |  | 
| 174 |  | 
| 175         let genericType = (contentType == "ELEMHIDE" ? "GENERICHIDE" : "GENERICB
     LOCK"); |  | 
| 176         let nogenericMatch = defaultMatcher.matchesAny(testWndLocation, |  | 
| 177             RegExpFilter.typeMap[genericType], parentDocDomain, false, testSitek
     ey); |  | 
| 178         if (nogenericMatch instanceof WhitelistFilter) |  | 
| 179         { |  | 
| 180           nogeneric = true; |  | 
| 181 |  | 
| 182           addHit(nogenericMatch); |  | 
| 183           RequestNotifier.addNodeData(testWnd.document, topWnd, genericType, |  | 
| 184                                       parentDocDomain, false, testWndLocation, |  | 
| 185                                       nogenericMatch); |  | 
| 186         } |  | 
| 187 |  | 
| 188         if (testWnd.parent == testWnd) |  | 
| 189           break; |  | 
| 190 |  | 
| 191         if (testWnd == testSitekeyWnd) |  | 
| 192           [testSitekey, testSitekeyWnd] = getSitekey(testWnd.parent); |  | 
| 193         testWnd = testWnd.parent; |  | 
| 194       } |  | 
| 195     } |  | 
| 196 |  | 
| 197     if (!match && contentType == "ELEMHIDE") |  | 
| 198     { |  | 
| 199       match = location; |  | 
| 200       location = match.text.replace(/^.*?#/, '#'); |  | 
| 201 |  | 
| 202       if (!match.isActiveOnDomain(docDomain)) |  | 
| 203         return true; |  | 
| 204 |  | 
| 205       let exception = ElemHide.getException(match, docDomain); |  | 
| 206       if (exception) |  | 
| 207       { |  | 
| 208         addHit(exception); |  | 
| 209         RequestNotifier.addNodeData(node, topWnd, contentType, docDomain, false,
      location, exception); |  | 
| 210         return true; |  | 
| 211       } |  | 
| 212 |  | 
| 213       if (nogeneric && match.isGeneric()) |  | 
| 214         return true; |  | 
| 215     } |  | 
| 216 |  | 
| 217     let thirdParty = (contentType == "ELEMHIDE" ? false : isThirdParty(location,
      docDomain)); |  | 
| 218 |  | 
| 219     if (!match && Prefs.enabled && RegExpFilter.typeMap.hasOwnProperty(contentTy
     pe)) |  | 
| 220     { |  | 
| 221       match = defaultMatcher.matchesAny(location, RegExpFilter.typeMap[contentTy
     pe], |  | 
| 222                                         docDomain, thirdParty, sitekey, nogeneri
     c); |  | 
| 223       if (match instanceof BlockingFilter && node.ownerDocument && !this.nonVisu
     alTypes.has(contentType)) |  | 
| 224       { |  | 
| 225         let prefCollapse = (match.collapse != null ? match.collapse : !Prefs.fas
     tcollapse); |  | 
| 226         if (collapse || prefCollapse) |  | 
| 227           schedulePostProcess(node); |  | 
| 228       } |  | 
| 229 |  | 
| 230       // Track mouse events for objects |  | 
| 231       if (!match && contentType == "OBJECT" && node.nodeType == Ci.nsIDOMNode.EL
     EMENT_NODE) |  | 
| 232       { |  | 
| 233         node.addEventListener("mouseover", objectMouseEventHander, true); |  | 
| 234         node.addEventListener("mouseout", objectMouseEventHander, true); |  | 
| 235       } |  | 
| 236     } |  | 
| 237 |  | 
| 238     // Store node data |  | 
| 239     RequestNotifier.addNodeData(node, topWnd, contentType, docDomain, thirdParty
     , location, match); |  | 
| 240     if (match) |  | 
| 241       addHit(match); |  | 
| 242 |  | 
| 243     return !match || match instanceof WhitelistFilter; |  | 
| 244   }, |  | 
| 245 |  | 
| 246   /** |  | 
| 247    * Checks whether the location's scheme is blockable. |  | 
| 248    * @param location  {nsIURI|String} |  | 
| 249    * @return {Boolean} |  | 
| 250    */ |  | 
| 251   isBlockableScheme: function(location) |  | 
| 252   { |  | 
| 253     let scheme; |  | 
| 254     if (typeof location == "string") |  | 
| 255     { |  | 
| 256       let match = /^([\w\-]+):/.exec(location); |  | 
| 257       scheme = match ? match[1] : null; |  | 
| 258     } |  | 
| 259     else |  | 
| 260       scheme = location.scheme; |  | 
| 261     return !this.whitelistSchemes.has(scheme); |  | 
| 262   }, |  | 
| 263 |  | 
| 264   /** |  | 
| 265    * Checks whether a page is whitelisted. |  | 
| 266    * @param {String} url |  | 
| 267    * @param {String} [parentUrl] location of the parent page |  | 
| 268    * @param {String} [sitekey] public key provided on the page |  | 
| 269    * @return {Filter} filter that matched the URL or null if not whitelisted |  | 
| 270    */ |  | 
| 271   isWhitelisted: function(url, parentUrl, sitekey) |  | 
| 272   { |  | 
| 273     if (!url) |  | 
| 274       return null; |  | 
| 275 |  | 
| 276     // Do not apply exception rules to schemes on our whitelistschemes list. |  | 
| 277     if (!this.isBlockableScheme(url)) |  | 
| 278       return null; |  | 
| 279 |  | 
| 280     if (!parentUrl) |  | 
| 281       parentUrl = url; |  | 
| 282 |  | 
| 283     // Ignore fragment identifier |  | 
| 284     let index = url.indexOf("#"); |  | 
| 285     if (index >= 0) |  | 
| 286       url = url.substring(0, index); |  | 
| 287 |  | 
| 288     let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, g
     etHostname(parentUrl), false, sitekey); |  | 
| 289     return (result instanceof WhitelistFilter ? result : null); |  | 
| 290   }, |  | 
| 291 |  | 
| 292   /** |  | 
| 293    * Checks whether the page loaded in a window is whitelisted for indication in
      the UI. |  | 
| 294    * @param wnd {nsIDOMWindow} |  | 
| 295    * @return {Filter} matching exception rule or null if not whitelisted |  | 
| 296    */ |  | 
| 297   isWindowWhitelisted: function(wnd) |  | 
| 298   { |  | 
| 299     return this.isWhitelisted(getWindowLocation(wnd)); |  | 
| 300   }, |  | 
| 301 |  | 
| 302   /** |  | 
| 303    * Asynchronously re-checks filters for given nodes. |  | 
| 304    * @param {Node[]} nodes |  | 
| 305    * @param {RequestEntry} entry |  | 
| 306    */ |  | 
| 307   refilterNodes: function(nodes, entry) |  | 
| 308   { |  | 
| 309     // Ignore nodes that have been blocked already |  | 
| 310     if (entry.filter && !(entry.filter instanceof WhitelistFilter)) |  | 
| 311       return; |  | 
| 312 |  | 
| 313     for (let node of nodes) |  | 
| 314       Utils.runAsync(() => refilterNode(node, entry)); |  | 
| 315   } |  | 
| 316 }; |  | 
| 317 Policy.init(); |  | 
| 318 | 104 | 
| 319 /** | 105 /** | 
| 320  * Actual nsIContentPolicy and nsIChannelEventSink implementation | 106  * Actual nsIContentPolicy and nsIChannelEventSink implementation | 
| 321  * @class | 107  * @class | 
| 322  */ | 108  */ | 
| 323 var PolicyImplementation = | 109 var PolicyImplementation = | 
| 324 { | 110 { | 
| 325   classDescription: "Adblock Plus content policy", | 111   classDescription: "Adblock Plus content policy", | 
| 326   classID: Components.ID("cfeaabe6-1dd1-11b2-a0c6-cb5c268894c9"), | 112   classID: Components.ID("cfeaabe6-1dd1-11b2-a0c6-cb5c268894c9"), | 
| 327   contractID: "@adblockplus.org/abp/policy;1", | 113   contractID: "@adblockplus.org/abp/policy;1", | 
| 328   xpcom_categories: ["content-policy", "net-channel-event-sinks"], | 114   xpcom_categories: ["content-policy", "net-channel-event-sinks"], | 
| 329 | 115 | 
| 330   /** | 116   /** | 
| 331    * Registers the content policy on startup. | 117    * Registers the content policy on startup. | 
| 332    */ | 118    */ | 
| 333   init: function() | 119   init: function() | 
| 334   { | 120   { | 
| 335     // Populate types map | 121     // Populate types map | 
| 336     let iface = Ci.nsIContentPolicy; | 122     let iface = Ci.nsIContentPolicy; | 
| 337     for (let name in iface) | 123     for (let name in iface) | 
| 338       if (name.indexOf("TYPE_") == 0 && name != "TYPE_DATAREQUEST") | 124       if (name.indexOf("TYPE_") == 0 && name != "TYPE_DATAREQUEST") | 
| 339         types.set(iface[name], name.substr(5)); | 125         types.set(iface[name], name.substr(5)); | 
| 340 | 126 | 
| 341     let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); | 127     let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); | 
| 342     registrar.registerFactory(this.classID, this.classDescription, this.contract
     ID, this); | 128     registrar.registerFactory(this.classID, this.classDescription, this.contract
     ID, this); | 
| 343 | 129 | 
| 344     let catMan = Utils.categoryManager; | 130     let catMan = Utils.categoryManager; | 
|  | 131     let addCategoryEntry = Utils.getPropertyWithoutCompatShims(catMan, "addCateg
     oryEntry"); | 
| 345     for (let category of this.xpcom_categories) | 132     for (let category of this.xpcom_categories) | 
| 346       catMan.addCategoryEntry(category, this.contractID, this.contractID, false,
      true); | 133       addCategoryEntry.call(catMan, category, this.contractID, this.contractID, 
     false, true); | 
| 347 | 134 | 
| 348     Services.obs.addObserver(this, "content-document-global-created", true); | 135     let addObserver = Utils.getPropertyWithoutCompatShims(Services.obs, "addObse
     rver"); | 
|  | 136     addObserver.call(Services.obs, this, "content-document-global-created", true
     ); | 
| 349 | 137 | 
| 350     onShutdown.add(() => | 138     onShutdown.add(() => | 
| 351     { | 139     { | 
| 352       Services.obs.removeObserver(this, "content-document-global-created"); | 140       let removeObserver = Utils.getPropertyWithoutCompatShims(Services.obs, "re
     moveObserver"); | 
|  | 141       removeObserver.call(Services.obs, this, "content-document-global-created")
     ; | 
| 353 | 142 | 
| 354       for (let category of this.xpcom_categories) | 143       for (let category of this.xpcom_categories) | 
| 355         catMan.deleteCategoryEntry(category, this.contractID, false); | 144         catMan.deleteCategoryEntry(category, this.contractID, false); | 
| 356 | 145 | 
| 357       registrar.unregisterFactory(this.classID, this); | 146       registrar.unregisterFactory(this.classID, this); | 
| 358     }); | 147     }); | 
| 359   }, | 148   }, | 
| 360 | 149 | 
| 361   // | 150   // | 
| 362   // nsISupports interface implementation | 151   // nsISupports interface implementation | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
| 385 | 174 | 
| 386     // Data loaded by plugins should be associated with the document | 175     // Data loaded by plugins should be associated with the document | 
| 387     if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST && node instan
     ceof Ci.nsIDOMElement) | 176     if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST && node instan
     ceof Ci.nsIDOMElement) | 
| 388       node = node.ownerDocument; | 177       node = node.ownerDocument; | 
| 389 | 178 | 
| 390     // Fix type for objects misrepresented as frames or images | 179     // Fix type for objects misrepresented as frames or images | 
| 391     if (contentType != Ci.nsIContentPolicy.TYPE_OBJECT && (node instanceof Ci.ns
     IDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement)) | 180     if (contentType != Ci.nsIContentPolicy.TYPE_OBJECT && (node instanceof Ci.ns
     IDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement)) | 
| 392       contentType = Ci.nsIContentPolicy.TYPE_OBJECT; | 181       contentType = Ci.nsIContentPolicy.TYPE_OBJECT; | 
| 393 | 182 | 
| 394     let location = Utils.unwrapURL(contentLocation); | 183     let location = Utils.unwrapURL(contentLocation); | 
| 395     let result = Policy.processNode(wnd, node, types.get(contentType), location.
     spec, false); | 184     let result = shouldAllow(wnd, node, types.get(contentType), location.spec); | 
| 396     return (result ? Ci.nsIContentPolicy.ACCEPT : Ci.nsIContentPolicy.REJECT_REQ
     UEST); | 185     return (result ? Ci.nsIContentPolicy.ACCEPT : Ci.nsIContentPolicy.REJECT_REQ
     UEST); | 
| 397   }, | 186   }, | 
| 398 | 187 | 
| 399   shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode
     , mimeType, extra) | 188   shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode
     , mimeType, extra) | 
| 400   { | 189   { | 
| 401     return Ci.nsIContentPolicy.ACCEPT; | 190     return Ci.nsIContentPolicy.ACCEPT; | 
| 402   }, | 191   }, | 
| 403 | 192 | 
| 404   // | 193   // | 
| 405   // nsIObserver interface implementation | 194   // nsIObserver interface implementation | 
| 406   // | 195   // | 
| 407   observe: function(subject, topic, data, additional) | 196   observe: function(subject, topic, data, additional) | 
| 408   { | 197   { | 
| 409     switch (topic) | 198     switch (topic) | 
| 410     { | 199     { | 
| 411       case "content-document-global-created": | 200       case "content-document-global-created": | 
| 412       { | 201       { | 
| 413         if (!(subject instanceof Ci.nsIDOMWindow) || !subject.opener) | 202         if (!(subject instanceof Ci.nsIDOMWindow) || !subject.opener) | 
| 414           return; | 203           return; | 
| 415 | 204 | 
| 416         let uri = additional || subject.location.href; | 205         let uri = additional || subject.location.href; | 
| 417         if (!Policy.processNode(subject.opener, subject.opener.document, "POPUP"
     , uri, false)) | 206         if (!shouldAllow(subject.opener, subject.opener.document, "POPUP", uri)) | 
| 418         { | 207         { | 
| 419           subject.stop(); | 208           subject.stop(); | 
| 420           Utils.runAsync(() => subject.close()); | 209           Utils.runAsync(() => subject.close()); | 
| 421         } | 210         } | 
| 422         else if (uri == "about:blank") | 211         else if (uri == "about:blank") | 
| 423         { | 212         { | 
| 424           // An about:blank pop-up most likely means that a load will be | 213           // An about:blank pop-up most likely means that a load will be | 
| 425           // initiated asynchronously. Wait for that. | 214           // initiated asynchronously. Wait for that. | 
| 426           Utils.runAsync(() => | 215           Utils.runAsync(() => | 
| 427           { | 216           { | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 466           // Special treatment for pop-up windows - this will close the window | 255           // Special treatment for pop-up windows - this will close the window | 
| 467           // rather than preventing the redirect. Note that we might not have | 256           // rather than preventing the redirect. Note that we might not have | 
| 468           // seen the original channel yet because the redirect happened before | 257           // seen the original channel yet because the redirect happened before | 
| 469           // the async code in observe() had a chance to run. | 258           // the async code in observe() had a chance to run. | 
| 470           this.observe(wnd, "content-document-global-created", null, oldChannel.
     URI.spec); | 259           this.observe(wnd, "content-document-global-created", null, oldChannel.
     URI.spec); | 
| 471           this.observe(wnd, "content-document-global-created", null, newChannel.
     URI.spec); | 260           this.observe(wnd, "content-document-global-created", null, newChannel.
     URI.spec); | 
| 472         } | 261         } | 
| 473         return; | 262         return; | 
| 474       } | 263       } | 
| 475 | 264 | 
| 476       if (!Policy.processNode(wnd, wnd.document, types.get(contentType), newChan
     nel.URI.spec, false)) | 265       if (!shouldAllow(wnd, wnd.document, types.get(contentType), newChannel.URI
     .spec)) | 
| 477         result = Cr.NS_BINDING_ABORTED; | 266         result = Cr.NS_BINDING_ABORTED; | 
| 478     } | 267     } | 
| 479     catch (e) | 268     catch (e) | 
| 480     { | 269     { | 
| 481       // We shouldn't throw exceptions here - this will prevent the redirect. | 270       // We shouldn't throw exceptions here - this will prevent the redirect. | 
| 482       Cu.reportError(e); | 271       Cu.reportError(e); | 
| 483     } | 272     } | 
| 484     finally | 273     finally | 
| 485     { | 274     { | 
| 486       callback.onRedirectVerifyCallback(result); | 275       callback.onRedirectVerifyCallback(result); | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 518     scheduledNodes = [node]; | 307     scheduledNodes = [node]; | 
| 519     Utils.runAsync(postProcessNodes); | 308     Utils.runAsync(postProcessNodes); | 
| 520   } | 309   } | 
| 521 } | 310 } | 
| 522 | 311 | 
| 523 /** | 312 /** | 
| 524  * Processes nodes scheduled for post-processing (typically hides them). | 313  * Processes nodes scheduled for post-processing (typically hides them). | 
| 525  */ | 314  */ | 
| 526 function postProcessNodes() | 315 function postProcessNodes() | 
| 527 { | 316 { | 
|  | 317   if (!collapsedClass) | 
|  | 318   { | 
|  | 319     let response = sendSyncMessage("AdblockPlus:GetCollapsedClass"); | 
|  | 320     if (response.length) | 
|  | 321       collapsedClass = response[0]; | 
|  | 322   } | 
|  | 323 | 
| 528   let nodes = scheduledNodes; | 324   let nodes = scheduledNodes; | 
| 529   scheduledNodes = null; | 325   scheduledNodes = null; | 
| 530 | 326 | 
|  | 327   if (!collapsedClass) | 
|  | 328     return; | 
|  | 329 | 
| 531   for (let node of nodes) | 330   for (let node of nodes) | 
| 532   { | 331   { | 
| 533     // adjust frameset's cols/rows for frames | 332     // adjust frameset's cols/rows for frames | 
| 534     let parentNode = node.parentNode; | 333     let parentNode = node.parentNode; | 
| 535     if (parentNode && parentNode instanceof Ci.nsIDOMHTMLFrameSetElement) | 334     if (parentNode && parentNode instanceof Ci.nsIDOMHTMLFrameSetElement) | 
| 536     { | 335     { | 
| 537       let hasCols = (parentNode.cols && parentNode.cols.indexOf(",") > 0); | 336       let hasCols = (parentNode.cols && parentNode.cols.indexOf(",") > 0); | 
| 538       let hasRows = (parentNode.rows && parentNode.rows.indexOf(",") > 0); | 337       let hasRows = (parentNode.rows && parentNode.rows.indexOf(",") > 0); | 
| 539       if ((hasCols || hasRows) && !(hasCols && hasRows)) | 338       if ((hasCols || hasRows) && !(hasCols && hasRows)) | 
| 540       { | 339       { | 
| 541         let index = -1; | 340         let index = -1; | 
| 542         for (let frame = node; frame; frame = frame.previousSibling) | 341         for (let frame = node; frame; frame = frame.previousSibling) | 
| 543           if (frame instanceof Ci.nsIDOMHTMLFrameElement || frame instanceof Ci.
     nsIDOMHTMLFrameSetElement) | 342           if (frame instanceof Ci.nsIDOMHTMLFrameElement || frame instanceof Ci.
     nsIDOMHTMLFrameSetElement) | 
| 544             index++; | 343             index++; | 
| 545 | 344 | 
| 546         let property = (hasCols ? "cols" : "rows"); | 345         let property = (hasCols ? "cols" : "rows"); | 
| 547         let weights = parentNode[property].split(","); | 346         let weights = parentNode[property].split(","); | 
| 548         weights[index] = "0"; | 347         weights[index] = "0"; | 
| 549         parentNode[property] = weights.join(","); | 348         parentNode[property] = weights.join(","); | 
| 550       } | 349       } | 
| 551     } | 350     } | 
| 552     else | 351     else | 
| 553       node.classList.add(collapsedClass); | 352       node.classList.add(collapsedClass); | 
| 554   } | 353   } | 
| 555 } | 354 } | 
| 556 |  | 
| 557 /** |  | 
| 558  * Extracts the hostname from a URL (might return null). |  | 
| 559  */ |  | 
| 560 function getHostname(/**String*/ url) /**String*/ |  | 
| 561 { |  | 
| 562   try |  | 
| 563   { |  | 
| 564     return Utils.unwrapURL(url).host; |  | 
| 565   } |  | 
| 566   catch(e) |  | 
| 567   { |  | 
| 568     return null; |  | 
| 569   } |  | 
| 570 } |  | 
| 571 |  | 
| 572 /** |  | 
| 573  * Retrieves the sitekey of a window. |  | 
| 574  */ |  | 
| 575 function getSitekey(wnd) |  | 
| 576 { |  | 
| 577   let sitekey = null; |  | 
| 578 |  | 
| 579   while (true) |  | 
| 580   { |  | 
| 581     if (wnd.document && wnd.document.documentElement) |  | 
| 582     { |  | 
| 583       let keydata = wnd.document.documentElement.getAttribute("data-adblockkey")
     ; |  | 
| 584       if (keydata && keydata.indexOf("_") >= 0) |  | 
| 585       { |  | 
| 586         let [key, signature] = keydata.split("_", 2); |  | 
| 587         key = key.replace(/=/g, ""); |  | 
| 588 |  | 
| 589         // Website specifies a key but is the signature valid? |  | 
| 590         let uri = Services.io.newURI(getWindowLocation(wnd), null, null); |  | 
| 591         let host = uri.asciiHost; |  | 
| 592         if (uri.port > 0) |  | 
| 593           host += ":" + uri.port; |  | 
| 594         let params = [ |  | 
| 595           uri.path.replace(/#.*/, ""),  // REQUEST_URI |  | 
| 596           host,                         // HTTP_HOST |  | 
| 597           Utils.httpProtocol.userAgent  // HTTP_USER_AGENT |  | 
| 598         ]; |  | 
| 599         if (Utils.verifySignature(key, signature, params.join("\0"))) |  | 
| 600           return [key, wnd]; |  | 
| 601       } |  | 
| 602     } |  | 
| 603 |  | 
| 604     if (wnd === wnd.parent) |  | 
| 605       break; |  | 
| 606 |  | 
| 607     wnd = wnd.parent; |  | 
| 608   } |  | 
| 609 |  | 
| 610   return [sitekey, wnd]; |  | 
| 611 } |  | 
| 612 |  | 
| 613 /** |  | 
| 614  * Retrieves the location of a window. |  | 
| 615  * @param wnd {nsIDOMWindow} |  | 
| 616  * @return {String} window location or null on failure |  | 
| 617  */ |  | 
| 618 function getWindowLocation(wnd) |  | 
| 619 { |  | 
| 620   if ("name" in wnd && wnd.name == "messagepane") |  | 
| 621   { |  | 
| 622     // Thunderbird branch |  | 
| 623     try |  | 
| 624     { |  | 
| 625       let mailWnd = wnd.QueryInterface(Ci.nsIInterfaceRequestor) |  | 
| 626                        .getInterface(Ci.nsIWebNavigation) |  | 
| 627                        .QueryInterface(Ci.nsIDocShellTreeItem) |  | 
| 628                        .rootTreeItem |  | 
| 629                        .QueryInterface(Ci.nsIInterfaceRequestor) |  | 
| 630                        .getInterface(Ci.nsIDOMWindow); |  | 
| 631 |  | 
| 632       // Typically we get a wrapped mail window here, need to unwrap |  | 
| 633       try |  | 
| 634       { |  | 
| 635         mailWnd = mailWnd.wrappedJSObject; |  | 
| 636       } catch(e) {} |  | 
| 637 |  | 
| 638       if ("currentHeaderData" in mailWnd && "content-base" in mailWnd.currentHea
     derData) |  | 
| 639       { |  | 
| 640         return mailWnd.currentHeaderData["content-base"].headerValue; |  | 
| 641       } |  | 
| 642       else if ("currentHeaderData" in mailWnd && "from" in mailWnd.currentHeader
     Data) |  | 
| 643       { |  | 
| 644         let emailAddress = Utils.headerParser.extractHeaderAddressMailboxes(mail
     Wnd.currentHeaderData.from.headerValue); |  | 
| 645         if (emailAddress) |  | 
| 646           return 'mailto:' + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+
     $/, "").replace(/\s/g, '%20'); |  | 
| 647       } |  | 
| 648     } catch(e) {} |  | 
| 649   } |  | 
| 650 |  | 
| 651   // Firefox branch |  | 
| 652   return wnd.location.href; |  | 
| 653 } |  | 
| 654 |  | 
| 655 /** |  | 
| 656  * Checks whether the location's origin is different from document's origin. |  | 
| 657  */ |  | 
| 658 function isThirdParty(/**String*/location, /**String*/ docDomain) /**Boolean*/ |  | 
| 659 { |  | 
| 660   if (!location || !docDomain) |  | 
| 661     return true; |  | 
| 662 |  | 
| 663   let uri = Utils.makeURI(location); |  | 
| 664   try |  | 
| 665   { |  | 
| 666     return Utils.effectiveTLD.getBaseDomain(uri) != Utils.effectiveTLD.getBaseDo
     mainFromHost(docDomain); |  | 
| 667   } |  | 
| 668   catch (e) |  | 
| 669   { |  | 
| 670     // EffectiveTLDService throws on IP addresses, just compare the host name |  | 
| 671     let host = ""; |  | 
| 672     try |  | 
| 673     { |  | 
| 674       host = uri.host; |  | 
| 675     } catch (e) {} |  | 
| 676     return host != docDomain; |  | 
| 677   } |  | 
| 678 } |  | 
| 679 |  | 
| 680 /** |  | 
| 681  * Re-checks filters on an element. |  | 
| 682  */ |  | 
| 683 function refilterNode(/**Node*/ node, /**RequestEntry*/ entry) |  | 
| 684 { |  | 
| 685   let wnd = Utils.getWindow(node); |  | 
| 686   if (!wnd || wnd.closed) |  | 
| 687     return; |  | 
| 688 |  | 
| 689   if (entry.type == "OBJECT") |  | 
| 690   { |  | 
| 691     node.removeEventListener("mouseover", objectMouseEventHander, true); |  | 
| 692     node.removeEventListener("mouseout", objectMouseEventHander, true); |  | 
| 693   } |  | 
| 694   Policy.processNode(wnd, node, entry.type, entry.location, true); |  | 
| 695 } |  | 
| OLD | NEW | 
|---|