| Left: | ||
| Right: |
| 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-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 Hit counts for element hiding. | 19 * @fileOverview Serves CSS for element hiding and processes hits. |
| 20 */ | 20 */ |
| 21 | 21 |
| 22 try | 22 try |
| 23 { | 23 { |
| 24 // Hack: SDK loader masks our Components object with a getter. | 24 // Hack: SDK loader masks our Components object with a getter. |
| 25 let proto = Object.getPrototypeOf(this); | 25 let proto = Object.getPrototypeOf(this); |
| 26 let property = Object.getOwnPropertyDescriptor(proto, "Components"); | 26 let property = Object.getOwnPropertyDescriptor(proto, "Components"); |
| 27 if (property && property.get) | 27 if (property && property.get) |
| 28 delete proto.Components; | 28 delete proto.Components; |
| 29 } | 29 } |
| 30 catch (e) | 30 catch (e) |
| 31 { | 31 { |
| 32 Cu.reportError(e); | 32 Cu.reportError(e); |
| 33 } | 33 } |
| 34 | 34 |
| 35 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 35 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); |
| 36 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); | 36 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); |
| 37 | 37 |
| 38 let {shouldAllowAsync} = require("child/contentPolicy"); | 38 let {shouldAllowAsync} = require("child/contentPolicy"); |
| 39 let {getFrames, isPrivate} = require("child/utils"); | 39 let {getFrames, isPrivate} = require("child/utils"); |
| 40 let {RequestNotifier} = require("child/requestNotifier"); | 40 let {RequestNotifier} = require("child/requestNotifier"); |
| 41 let {port} = require("messaging"); | 41 let {port} = require("messaging"); |
| 42 let {Utils} = require("utils"); | 42 let {Utils} = require("utils"); |
| 43 | 43 |
| 44 // The allowXBL binding below won't have any effect on the element. For elements | |
| 45 // that should be hidden however we don't return any binding at all, this makes | |
| 46 // Gecko stop constructing the node - it cannot be shown. | |
| 47 const allowXBL = "<bindings xmlns='http://www.mozilla.org/xbl'><binding id='dumm y' bindToUntrustedContent='true'/></bindings>"; | |
| 48 const hideXBL = "<bindings xmlns='http://www.mozilla.org/xbl'/>"; | |
| 49 | |
| 50 const notImplemented = () => Cr.NS_ERROR_NOT_IMPLEMENTED; | 44 const notImplemented = () => Cr.NS_ERROR_NOT_IMPLEMENTED; |
| 51 | 45 |
| 52 /** | 46 /** |
| 53 * about: URL module used to count hits. | 47 * about: URL module used to count hits. |
| 54 * @class | 48 * @class |
| 55 */ | 49 */ |
| 56 let AboutHandler = | 50 let AboutHandler = |
| 57 { | 51 { |
| 58 classID: Components.ID("{55fb7be0-1dd2-11b2-98e6-9e97caf8ba67}"), | 52 classID: Components.ID("{55fb7be0-1dd2-11b2-98e6-9e97caf8ba67}"), |
| 59 classDescription: "Element hiding hit registration protocol handler", | 53 classDescription: "Element hiding hit registration protocol handler", |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 89 // About module implementation | 83 // About module implementation |
| 90 // | 84 // |
| 91 | 85 |
| 92 getURIFlags: function(uri) | 86 getURIFlags: function(uri) |
| 93 { | 87 { |
| 94 return Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; | 88 return Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; |
| 95 }, | 89 }, |
| 96 | 90 |
| 97 newChannel: function(uri, loadInfo) | 91 newChannel: function(uri, loadInfo) |
| 98 { | 92 { |
| 99 let match = /\?(?:hit(\d+)|css)$/.exec(uri.path); | 93 let match = /\?hit(\d+)$/.exec(uri.path); |
| 100 if (!match) | 94 if (match) |
| 101 throw Cr.NS_ERROR_FAILURE; | 95 return new HitRegistrationChannel(uri, loadInfo, match[1]); |
| 102 | 96 |
| 103 if (match[1]) | 97 match = /\?css(?:=(.*?))?$/.exec(uri.path); |
| 104 return new HitRegistrationChannel(uri, loadInfo, match[1]); | 98 if (match) |
| 105 else | 99 { |
| 106 return new StyleDataChannel(uri, loadInfo); | 100 return new StyleDataChannel(uri, loadInfo, |
| 101 match[1] ? decodeURIComponent(match[1]) : null); | |
| 102 } | |
| 103 | |
| 104 throw Cr.NS_ERROR_FAILURE; | |
| 107 }, | 105 }, |
| 108 | 106 |
| 109 QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule]) | 107 QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule]) |
| 110 }; | 108 }; |
| 111 AboutHandler.init(); | 109 AboutHandler.init(); |
| 112 | 110 |
| 113 /** | 111 /** |
| 114 * Base class for channel implementations, subclasses usually only need to | 112 * Base class for channel implementations, subclasses usually only need to |
| 115 * override BaseChannel._getResponse() method. | 113 * override BaseChannel._getResponse() method. |
| 116 * @constructor | 114 * @constructor |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 205 | 203 |
| 206 isPending: () => false, | 204 isPending: () => false, |
| 207 cancel: notImplemented, | 205 cancel: notImplemented, |
| 208 suspend: notImplemented, | 206 suspend: notImplemented, |
| 209 resume: notImplemented, | 207 resume: notImplemented, |
| 210 | 208 |
| 211 QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest]) | 209 QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest]) |
| 212 }; | 210 }; |
| 213 | 211 |
| 214 /** | 212 /** |
| 215 * Channel returning CSS data for the global stylesheet. | 213 * Channel returning CSS data for the global as well as site-specific stylesheet . |
| 216 * @constructor | 214 * @constructor |
| 217 */ | 215 */ |
| 218 function StyleDataChannel(uri, loadInfo) | 216 function StyleDataChannel(uri, loadInfo, domain) |
| 219 { | 217 { |
| 220 BaseChannel.call(this, uri, loadInfo); | 218 BaseChannel.call(this, uri, loadInfo); |
| 219 this._domain = domain; | |
| 221 } | 220 } |
| 222 StyleDataChannel.prototype = { | 221 StyleDataChannel.prototype = { |
| 223 __proto__: BaseChannel.prototype, | 222 __proto__: BaseChannel.prototype, |
| 224 contentType: "text/css", | 223 contentType: "text/css", |
| 224 _domain: null, | |
| 225 | 225 |
| 226 _getResponse: function() | 226 _getResponse: function() |
| 227 { | 227 { |
| 228 function escapeChar(match) | 228 function escapeChar(match) |
| 229 { | 229 { |
| 230 return "\\" + match.charCodeAt(0).toString(16) + " "; | 230 return "\\" + match.charCodeAt(0).toString(16) + " "; |
| 231 } | 231 } |
| 232 | 232 |
| 233 // Would be great to avoid sync messaging here but nsIStyleSheetService | 233 // Would be great to avoid sync messaging here but nsIStyleSheetService |
| 234 // insists on opening channels synchronously. | 234 // insists on opening channels synchronously. |
| 235 let domains = port.emitSync("getSelectors"); | 235 let [selectors, keys] = (this._domain ? |
| 236 port.emitSync("getSelectorsForDomain", this._domain) : | |
| 237 port.emitSync("getUnconditionalSelectors")); | |
| 236 | 238 |
| 237 let cssPrefix = "{-moz-binding: url(about:abp-elemhide?hit"; | 239 let cssPrefix = "{-moz-binding: url(about:abp-elemhide?hit"; |
| 238 let cssSuffix = "#dummy) !important;}\n"; | 240 let cssSuffix = "#dummy) !important;}\n"; |
| 239 let result = []; | 241 let result = []; |
| 240 | 242 |
| 241 for (let [domain, selectors] of domains) | 243 for (let i = 0; i < selectors.length; i++) |
| 242 { | 244 { |
| 243 if (domain) | 245 let selector = selectors[i]; |
| 244 { | 246 let key = keys[i]; |
| 245 result.push('@-moz-document domain("', | 247 result.push(selector.replace(/[^\x01-\x7F]/g, escapeChar), |
| 246 domain.replace(/[^\x01-\x7F]/g, escapeChar) | 248 cssPrefix, key, cssSuffix); |
| 247 .split(",").join('"),domain("'), | |
| 248 '"){\n'); | |
| 249 } | |
| 250 else | |
| 251 { | |
| 252 // Only allow unqualified rules on a few protocols to prevent them | |
| 253 // from blocking chrome content | |
| 254 result.push('@-moz-document url-prefix("http://"),', | |
| 255 'url-prefix("https://"),url-prefix("mailbox://"),', | |
| 256 'url-prefix("imap://"),url-prefix("news://"),', | |
| 257 'url-prefix("snews://"){\n'); | |
| 258 } | |
| 259 | |
| 260 for (let [selector, key] of selectors) | |
| 261 { | |
| 262 result.push(selector.replace(/[^\x01-\x7F]/g, escapeChar), | |
| 263 cssPrefix, key, cssSuffix); | |
| 264 } | |
| 265 | |
| 266 result.push("}\n"); | |
| 267 } | 249 } |
| 268 | 250 |
| 269 return result.join(""); | 251 return result.join(""); |
| 270 } | 252 } |
| 271 }; | 253 }; |
| 272 | 254 |
| 273 /** | 255 /** |
| 274 * Channel returning data for element hiding hits. | 256 * Channel returning data for element hiding hits. |
| 275 * @constructor | 257 * @constructor |
| 276 */ | 258 */ |
| 277 function HitRegistrationChannel(uri, loadInfo, key) | 259 function HitRegistrationChannel(uri, loadInfo, key) |
| 278 { | 260 { |
| 279 BaseChannel.call(this, uri, loadInfo); | 261 BaseChannel.call(this, uri, loadInfo); |
| 280 this.key = key; | 262 this.key = key; |
| 281 } | 263 } |
| 282 HitRegistrationChannel.prototype = { | 264 HitRegistrationChannel.prototype = { |
| 283 __proto__: BaseChannel.prototype, | 265 __proto__: BaseChannel.prototype, |
| 284 key: null, | 266 key: null, |
| 285 contentType: "text/xml", | 267 contentType: "text/xml", |
| 286 | 268 |
| 287 _getResponse: function() | 269 _getResponse: function() |
| 288 { | 270 { |
| 289 return new Promise((resolve, reject) => | 271 let window = Utils.getRequestWindow(this); |
| 272 port.emitWithResponse("registerElemHideHit", { | |
| 273 key: this.key, | |
| 274 frames: getFrames(window), | |
| 275 isPrivate: isPrivate(window) | |
| 276 }).then(hit => | |
| 290 { | 277 { |
| 291 let window = Utils.getRequestWindow(this); | 278 if (hit) |
| 292 shouldAllowAsync(window, window.document, "ELEMHIDE", this.key, allow => | 279 RequestNotifier.addNodeData(window.document, window.top, hit); |
|
kzar
2016/10/06 13:02:32
(Doesn't storing all the details returned by regis
Wladimir Palant
2016/10/06 13:08:48
That's how the list of blockable items works - it
| |
| 293 { | |
| 294 resolve(allow ? allowXBL : hideXBL); | |
| 295 }); | |
| 296 }); | 280 }); |
| 281 return "<bindings xmlns='http://www.mozilla.org/xbl'/>"; | |
| 297 } | 282 } |
| 298 }; | 283 }; |
| 299 | 284 |
| 300 let observer = { | 285 let observer = { |
| 301 QueryInterface: XPCOMUtils.generateQI([ | 286 QueryInterface: XPCOMUtils.generateQI([ |
| 302 Ci.nsIObserver, Ci.nsISupportsWeakReference | 287 Ci.nsIObserver, Ci.nsISupportsWeakReference |
| 303 ]), | 288 ]), |
| 304 | 289 |
| 305 topic: "document-element-inserted", | 290 topic: "document-element-inserted", |
| 306 styleURL: Utils.makeURI("about:abp-elemhide?css"), | 291 styleURL: Utils.makeURI("about:abp-elemhide?css"), |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 utils.addSheet(this.sheet, Ci.nsIStyleSheetService.USER_SHEET); | 353 utils.addSheet(this.sheet, Ci.nsIStyleSheetService.USER_SHEET); |
| 369 } | 354 } |
| 370 catch (e) | 355 catch (e) |
| 371 { | 356 { |
| 372 // Ignore NS_ERROR_ILLEGAL_VALUE - it will be thrown if we try to add | 357 // Ignore NS_ERROR_ILLEGAL_VALUE - it will be thrown if we try to add |
| 373 // the stylesheet multiple times to the same document (the observer | 358 // the stylesheet multiple times to the same document (the observer |
| 374 // will be notified twice for some documents). | 359 // will be notified twice for some documents). |
| 375 if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) | 360 if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) |
| 376 throw e; | 361 throw e; |
| 377 } | 362 } |
| 363 | |
| 364 let host = subject.location.hostname; | |
| 365 if (host) | |
| 366 { | |
| 367 try | |
| 368 { | |
| 369 utils.loadSheetUsingURIString(this.styleURL.spec + "=" + | |
| 370 encodeURIComponent(host), Ci.nsIStyleSheetService.USER_SHEET); | |
| 371 } | |
| 372 catch (e) | |
| 373 { | |
| 374 // Ignore NS_ERROR_ILLEGAL_VALUE - it will be thrown if we try to ad d | |
| 375 // the stylesheet multiple times to the same document (the observer | |
| 376 // will be notified twice for some documents). | |
| 377 if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) | |
| 378 throw e; | |
| 379 } | |
| 380 } | |
| 378 } | 381 } |
| 379 else if (filter) | 382 else if (filter) |
| 380 { | 383 { |
| 381 RequestNotifier.addNodeData(window.document, window.top, { | 384 RequestNotifier.addNodeData(window.document, window.top, { |
| 382 contentType, docDomain, thirdParty, location, filter, filterType | 385 contentType, docDomain, thirdParty, location, filter, filterType |
| 383 }); | 386 }); |
| 384 } | 387 } |
| 385 }); | 388 }); |
| 386 } | 389 } |
| 387 }; | 390 }; |
| 388 observer.init(); | 391 observer.init(); |
| OLD | NEW |