| 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-2016 Eyeo GmbH | 3  * Copyright (C) 2006-2016 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 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 24 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 
| 25 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); | 25 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); | 
| 26 | 26 | 
| 27 let {Utils} = require("utils"); | 27 let {Utils} = require("utils"); | 
|  | 28 let {port} = require("messaging"); | 
| 28 let {Prefs} = require("prefs"); | 29 let {Prefs} = require("prefs"); | 
| 29 let {FilterStorage} = require("filterStorage"); | 30 let {FilterStorage} = require("filterStorage"); | 
| 30 let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses"); | 31 let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses"); | 
| 31 let {defaultMatcher} = require("matcher"); | 32 let {defaultMatcher} = require("matcher"); | 
| 32 let {ElemHide} = require("elemHide"); | 33 let {ElemHide} = require("elemHide"); | 
| 33 | 34 | 
| 34 /** | 35 /** | 
| 35  * Public policy checking functions and auxiliary objects | 36  * Public policy checking functions and auxiliary objects | 
| 36  * @class | 37  * @class | 
| 37  */ | 38  */ | 
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 80 | 81 | 
| 81   /** | 82   /** | 
| 82    * Called on module startup, initializes various exported properties. | 83    * Called on module startup, initializes various exported properties. | 
| 83    */ | 84    */ | 
| 84   init: function() | 85   init: function() | 
| 85   { | 86   { | 
| 86     // whitelisted URL schemes | 87     // whitelisted URL schemes | 
| 87     for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" ")) | 88     for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" ")) | 
| 88       this.whitelistSchemes.add(scheme); | 89       this.whitelistSchemes.add(scheme); | 
| 89 | 90 | 
| 90     Utils.addChildMessageListener("AdblockPlus:ShouldAllow", message => this.sho
     uldAllow(message)); | 91     port.on("shouldAllow", (message, sender) => this.shouldAllow(message)); | 
| 91 | 92 | 
| 92     // Generate class identifier used to collapse nodes and register | 93     // Generate class identifier used to collapse nodes and register | 
| 93     // corresponding stylesheet. | 94     // corresponding stylesheet. | 
| 94     let collapsedClass = ""; | 95     let collapsedClass = ""; | 
| 95     let offset = "a".charCodeAt(0); | 96     let offset = "a".charCodeAt(0); | 
| 96     for (let i = 0; i < 20; i++) | 97     for (let i = 0; i < 20; i++) | 
| 97       collapsedClass +=  String.fromCharCode(offset + Math.random() * 26); | 98       collapsedClass +=  String.fromCharCode(offset + Math.random() * 26); | 
| 98     Utils.addChildMessageListener("AdblockPlus:GetCollapsedClass", () => collaps
     edClass); | 99     port.on("getCollapsedClass", (message, sender) => collapsedClass); | 
| 99 | 100 | 
| 100     let collapseStyle = Services.io.newURI("data:text/css," + | 101     let collapseStyle = Services.io.newURI("data:text/css," + | 
| 101         encodeURIComponent("." + collapsedClass + | 102         encodeURIComponent("." + collapsedClass + | 
| 102         "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarb
     azdummy) !important;}"), null, null); | 103         "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarb
     azdummy) !important;}"), null, null); | 
| 103     Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetServi
     ce.USER_SHEET); | 104     Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetServi
     ce.USER_SHEET); | 
| 104     onShutdown.add(() => | 105     onShutdown.add(() => | 
| 105     { | 106     { | 
| 106       Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService.
     USER_SHEET); | 107       Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService.
     USER_SHEET); | 
| 107     }); | 108     }); | 
| 108   }, | 109   }, | 
| 109 | 110 | 
| 110   /** | 111   /** | 
| 111    * Checks whether a node should be blocked, hides it if necessary | 112    * Checks whether a node should be blocked, hides it if necessary | 
| 112    * @param {Object} data  request data | 113    * @param {Object} data  request data | 
| 113    * @param {String} data.contentType | 114    * @param {String} data.contentType | 
| 114    * @param {String} data.location  location of the request, filter key if conte
     ntType is ELEMHIDE | 115    * @param {String} data.location  location of the request, filter key if conte
     ntType is ELEMHIDE | 
| 115    * @param {Object[]} data.frames | 116    * @param {Object[]} data.frames | 
| 116    * @param {Boolean} data.isPrivate  true if the request belongs to a private b
     rowsing window | 117    * @param {Boolean} data.isPrivate  true if the request belongs to a private b
     rowsing window | 
| 117    * @return {Object} An object containing properties allow, collapse and hits | 118    * @return {Object} An object containing properties allow, collapse and hits | 
| 118    *                  indicating how this request should be handled. | 119    *                  indicating how this request should be handled. | 
| 119    */ | 120    */ | 
| 120   shouldAllow: function({contentType, location, frames, isPrivate}) | 121   shouldAllow: function({contentType, location, frames, isPrivate}) | 
| 121   { | 122   { | 
| 122     let hits = []; | 123     let hits = []; | 
| 123 | 124 | 
| 124     function addHit(frameIndex, contentType, docDomain, thirdParty, location, fi
     lter) | 125     function addHit(frameIndex, contentType, docDomain, thirdParty, location, fi
     lter) | 
| 125     { | 126     { | 
| 126       if (filter && !isPrivate) | 127       if (filter && !isPrivate) | 
| 127         FilterStorage.increaseHitCount(filter, docDomain); | 128         FilterStorage.increaseHitCount(filter, docDomain, thirdParty); | 
| 128 | 129 | 
| 129       hits.push({ | 130       hits.push({ | 
| 130         frameIndex, contentType, docDomain, thirdParty, location, | 131         frameIndex, contentType, docDomain, thirdParty, location, | 
| 131         filter: filter ? filter.text : null, | 132         filter: filter ? filter.text : null, | 
| 132         filterType: filter ? filter.type : null | 133         filterType: filter ? filter.type : null | 
| 133       }); | 134       }); | 
| 134     } | 135     } | 
| 135 | 136 | 
| 136     function response(allow, collapse) | 137     function response(allow, collapse) | 
| 137     { | 138     { | 
| 138       return {allow, collapse, hits}; | 139       return {allow, collapse, hits}; | 
| 139     } | 140     } | 
| 140 | 141 | 
| 141     // Ignore whitelisted schemes | 142     // Ignore whitelisted schemes | 
| 142     if (!this.isBlockableScheme(location)) | 143     if (!this.isBlockableScheme(location)) | 
| 143       return response(true, false); | 144       return response(true, false); | 
| 144 | 145 | 
| 145     // Interpret unknown types as "other" | 146     // Interpret unknown types as "other" | 
| 146     contentType = this.contentTypes.get(contentType) || "OTHER"; | 147     contentType = this.contentTypes.get(contentType) || "OTHER"; | 
| 147 | 148 | 
|  | 149     let nogeneric = false; | 
|  | 150     if (Prefs.enabled) | 
|  | 151     { | 
|  | 152       let whitelistHit = | 
|  | 153           this.isFrameWhitelisted(frames, contentType == "ELEMHIDE"); | 
|  | 154       if (whitelistHit) | 
|  | 155       { | 
|  | 156         let [frameIndex, matchType, docDomain, thirdParty, location, filter] = w
     hitelistHit; | 
|  | 157         addHit(frameIndex, matchType, docDomain, thirdParty, location, filter); | 
|  | 158         if (matchType == "DOCUMENT" || matchType == "ELEMHIDE") | 
|  | 159           return response(true, false); | 
|  | 160         else | 
|  | 161           nogeneric = true; | 
|  | 162       } | 
|  | 163     } | 
|  | 164 | 
|  | 165     let match = null; | 
| 148     let wndLocation = frames[0].location; | 166     let wndLocation = frames[0].location; | 
| 149     let docDomain = getHostname(wndLocation); | 167     let docDomain = getHostname(wndLocation); | 
| 150     let match = null; |  | 
| 151     let [sitekey, sitekeyFrame] = getSitekey(frames); | 168     let [sitekey, sitekeyFrame] = getSitekey(frames); | 
| 152     let nogeneric = false; | 169     if (contentType == "ELEMHIDE") | 
| 153     if (!match && Prefs.enabled) |  | 
| 154     { |  | 
| 155       let testSitekey = sitekey; |  | 
| 156       let testSitekeyFrame = sitekeyFrame; |  | 
| 157       for (let i = 0; i < frames.length; i++) |  | 
| 158       { |  | 
| 159         let frame = frames[i]; |  | 
| 160         let testWndLocation = frame.location; |  | 
| 161         let parentWndLocation = frames[Math.min(i + 1, frames.length - 1)].locat
     ion; |  | 
| 162         let parentDocDomain = getHostname(parentWndLocation); |  | 
| 163 |  | 
| 164         let typeMap = RegExpFilter.typeMap.DOCUMENT; |  | 
| 165         if (contentType == "ELEMHIDE") |  | 
| 166           typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE; |  | 
| 167         let whitelistMatch = defaultMatcher.matchesAny(testWndLocation, typeMap,
      parentDocDomain, false, testSitekey); |  | 
| 168         if (whitelistMatch instanceof WhitelistFilter) |  | 
| 169         { |  | 
| 170           let whitelistType = (whitelistMatch.contentType & RegExpFilter.typeMap
     .DOCUMENT) ? "DOCUMENT" : "ELEMHIDE"; |  | 
| 171           addHit(i, whitelistType, parentDocDomain, false, testWndLocation, |  | 
| 172               whitelistMatch); |  | 
| 173           return response(true, false); |  | 
| 174         } |  | 
| 175 |  | 
| 176         let genericType = (contentType == "ELEMHIDE" ? "GENERICHIDE" : "GENERICB
     LOCK"); |  | 
| 177         let nogenericMatch = defaultMatcher.matchesAny(testWndLocation, |  | 
| 178             RegExpFilter.typeMap[genericType], parentDocDomain, false, testSitek
     ey); |  | 
| 179         if (nogenericMatch instanceof WhitelistFilter) |  | 
| 180         { |  | 
| 181           nogeneric = true; |  | 
| 182           addHit(i, genericType, parentDocDomain, false, testWndLocation, |  | 
| 183               nogenericMatch); |  | 
| 184         } |  | 
| 185 |  | 
| 186         if (frame == testSitekeyFrame) |  | 
| 187           [testSitekey, testSitekeyFrame] = getSitekey(frames.slice(i + 1)); |  | 
| 188       } |  | 
| 189     } |  | 
| 190 |  | 
| 191     if (!match && contentType == "ELEMHIDE") |  | 
| 192     { | 170     { | 
| 193       match = ElemHide.getFilterByKey(location); | 171       match = ElemHide.getFilterByKey(location); | 
| 194       location = match.text.replace(/^.*?#/, '#'); | 172       location = match.text.replace(/^.*?#/, '#'); | 
| 195 | 173 | 
| 196       if (!match.isActiveOnDomain(docDomain)) | 174       if (!match.isActiveOnDomain(docDomain)) | 
| 197         return response(true, false); | 175         return response(true, false); | 
| 198 | 176 | 
| 199       let exception = ElemHide.getException(match, docDomain); | 177       let exception = ElemHide.getException(match, docDomain); | 
| 200       if (exception) | 178       if (exception) | 
| 201       { | 179       { | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 233     { | 211     { | 
| 234       let match = /^([\w\-]+):/.exec(location); | 212       let match = /^([\w\-]+):/.exec(location); | 
| 235       scheme = match ? match[1] : null; | 213       scheme = match ? match[1] : null; | 
| 236     } | 214     } | 
| 237     else | 215     else | 
| 238       scheme = location.scheme; | 216       scheme = location.scheme; | 
| 239     return !this.whitelistSchemes.has(scheme); | 217     return !this.whitelistSchemes.has(scheme); | 
| 240   }, | 218   }, | 
| 241 | 219 | 
| 242   /** | 220   /** | 
| 243    * Checks whether a page is whitelisted. | 221    * Checks whether a top-level window is whitelisted. | 
| 244    * @param {String} url | 222    * @param {String} url | 
| 245    * @param {String} [parentUrl] location of the parent page | 223    *    URL of the document loaded into the window | 
| 246    * @param {String} [sitekey] public key provided on the page | 224    * @return {?WhitelistFilter} | 
| 247    * @return {Filter} filter that matched the URL or null if not whitelisted | 225    *    exception rule that matched the URL if any | 
| 248    */ | 226    */ | 
| 249   isWhitelisted: function(url, parentUrl, sitekey) | 227   isWhitelisted: function(url) | 
| 250   { | 228   { | 
| 251     if (!url) | 229     if (!url) | 
| 252       return null; | 230       return null; | 
| 253 | 231 | 
| 254     // Do not apply exception rules to schemes on our whitelistschemes list. | 232     // Do not apply exception rules to schemes on our whitelistschemes list. | 
| 255     if (!this.isBlockableScheme(url)) | 233     if (!this.isBlockableScheme(url)) | 
| 256       return null; | 234       return null; | 
| 257 |  | 
| 258     if (!parentUrl) |  | 
| 259       parentUrl = url; |  | 
| 260 | 235 | 
| 261     // Ignore fragment identifier | 236     // Ignore fragment identifier | 
| 262     let index = url.indexOf("#"); | 237     let index = url.indexOf("#"); | 
| 263     if (index >= 0) | 238     if (index >= 0) | 
| 264       url = url.substring(0, index); | 239       url = url.substring(0, index); | 
| 265 | 240 | 
| 266     let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, g
     etHostname(parentUrl), false, sitekey); | 241     let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, | 
|  | 242         getHostname(url), false, null); | 
| 267     return (result instanceof WhitelistFilter ? result : null); | 243     return (result instanceof WhitelistFilter ? result : null); | 
|  | 244   }, | 
|  | 245 | 
|  | 246   /** | 
|  | 247    * Checks whether a frame is whitelisted. | 
|  | 248    * @param {Array} frames | 
|  | 249    *    frame structure as returned by getFrames() in child/utils module. | 
|  | 250    * @param {boolean} isElemHide | 
|  | 251    *    true if element hiding whitelisting should be considered | 
|  | 252    * @return {?Array} | 
|  | 253    *    An array with the hit parameters: frameIndex, contentType, docDomain, | 
|  | 254    *    thirdParty, location, filter. Note that the filter could be a | 
|  | 255    *    genericblock/generichide exception rule. If nothing matched null is | 
|  | 256    *    returned. | 
|  | 257    */ | 
|  | 258   isFrameWhitelisted: function(frames, isElemHide) | 
|  | 259   { | 
|  | 260     let [sitekey, sitekeyFrame] = getSitekey(frames); | 
|  | 261     let nogenericHit = null; | 
|  | 262 | 
|  | 263     let typeMap = RegExpFilter.typeMap.DOCUMENT; | 
|  | 264     if (isElemHide) | 
|  | 265       typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE; | 
|  | 266     let genericType = (isElemHide ? "GENERICHIDE" : "GENERICBLOCK"); | 
|  | 267 | 
|  | 268     for (let i = 0; i < frames.length; i++) | 
|  | 269     { | 
|  | 270       let frame = frames[i]; | 
|  | 271       let wndLocation = frame.location; | 
|  | 272       let parentWndLocation = frames[Math.min(i + 1, frames.length - 1)].locatio
     n; | 
|  | 273       let parentDocDomain = getHostname(parentWndLocation); | 
|  | 274 | 
|  | 275       let match = defaultMatcher.matchesAny(wndLocation, typeMap, parentDocDomai
     n, false, sitekey); | 
|  | 276       if (match instanceof WhitelistFilter) | 
|  | 277       { | 
|  | 278         let whitelistType = (whitelistMatch.contentType & RegExpFilter.typeMap.D
     OCUMENT) ? "DOCUMENT" : "ELEMHIDE"; | 
|  | 279         return [i, whitelistType, parentDocDomain, false, wndLocation, match]; | 
|  | 280       } | 
|  | 281 | 
|  | 282       if (!nogenericHit) | 
|  | 283       { | 
|  | 284         match = defaultMatcher.matchesAny(wndLocation, | 
|  | 285             RegExpFilter.typeMap[genericType], parentDocDomain, false, sitekey); | 
|  | 286         if (match instanceof WhitelistFilter) | 
|  | 287           nogenericHit = [i, genericType, parentDocDomain, false, wndLocation, m
     atch]; | 
|  | 288       } | 
|  | 289 | 
|  | 290       if (frame == sitekeyFrame) | 
|  | 291         [sitekey, sitekeyFrame] = getSitekey(frames.slice(i + 1)); | 
|  | 292     } | 
|  | 293 | 
|  | 294     return nogenericHit; | 
| 268   }, | 295   }, | 
| 269 | 296 | 
| 270   /** | 297   /** | 
| 271    * Deletes nodes that were previously stored with a | 298    * Deletes nodes that were previously stored with a | 
| 272    * RequestNotifier.storeNodesForEntries() call or similar. | 299    * RequestNotifier.storeNodesForEntries() call or similar. | 
| 273    * @param {string} id  unique ID of the nodes | 300    * @param {string} id  unique ID of the nodes | 
| 274    */ | 301    */ | 
| 275   deleteNodes: function(id) | 302   deleteNodes: function(id) | 
| 276   { | 303   { | 
| 277     let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] | 304     port.emit("deleteNodes", id); | 
| 278                            .getService(Ci.nsIMessageBroadcaster); |  | 
| 279     messageManager.broadcastAsyncMessage("AdblockPlus:DeleteNodes", id); |  | 
| 280   }, | 305   }, | 
| 281 | 306 | 
| 282   /** | 307   /** | 
| 283    * Asynchronously re-checks filters for nodes given by an ID previously | 308    * Asynchronously re-checks filters for nodes given by an ID previously | 
| 284    * returned by a RequestNotifier.storeNodesForEntries() call or similar. | 309    * returned by a RequestNotifier.storeNodesForEntries() call or similar. | 
| 285    * @param {string} id  unique ID of the nodes | 310    * @param {string} id  unique ID of the nodes | 
| 286    * @param {RequestEntry} entry | 311    * @param {RequestEntry} entry | 
| 287    */ | 312    */ | 
| 288   refilterNodes: function(id, entry) | 313   refilterNodes: function(id, entry) | 
| 289   { | 314   { | 
| 290     let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] | 315     port.emit("refilterNodes", { | 
| 291                            .getService(Ci.nsIMessageBroadcaster); |  | 
| 292     messageManager.broadcastAsyncMessage("AdblockPlus:RefilterNodes", { |  | 
| 293       nodesID: id, | 316       nodesID: id, | 
| 294       entry: entry | 317       entry: entry | 
| 295     }); | 318     }); | 
| 296   } | 319   } | 
| 297 }; | 320 }; | 
| 298 Policy.init(); | 321 Policy.init(); | 
| 299 | 322 | 
| 300 /** | 323 /** | 
| 301  * Extracts the hostname from a URL (might return null). | 324  * Extracts the hostname from a URL (might return null). | 
| 302  */ | 325  */ | 
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 401   { | 424   { | 
| 402     // EffectiveTLDService throws on IP addresses, just compare the host name | 425     // EffectiveTLDService throws on IP addresses, just compare the host name | 
| 403     let host = ""; | 426     let host = ""; | 
| 404     try | 427     try | 
| 405     { | 428     { | 
| 406       host = uri.host; | 429       host = uri.host; | 
| 407     } catch (e) {} | 430     } catch (e) {} | 
| 408     return host != docDomain; | 431     return host != docDomain; | 
| 409   } | 432   } | 
| 410 } | 433 } | 
| LEFT | RIGHT | 
|---|