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 stylesheet. |
kzar
2016/10/06 12:10:46
Nit: I guess this comment should be updated now it
Wladimir Palant
2016/10/06 12:27:28
Done.
| |
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) : | |
kzar
2016/10/06 12:10:46
Dumb question but how does getSelectorsForDomain a
Wladimir Palant
2016/10/06 12:27:28
This is the content process - we cannot require El
kzar
2016/10/06 13:02:32
Ah I see.
| |
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); |
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 |