Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: lib/elemHide.js

Issue 6201308310667264: Issue 521- Inject our stylesheet on per-site basis rather than globally (Closed)
Left Patch Set: Created May 20, 2014, 6:09 p.m.
Right Patch Set: Version 1.0 Created Aug. 6, 2014, 4:30 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « lib/contentPolicy.js ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
(...skipping 10 matching lines...) Expand all
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 {ElemHideException} = require("filterClasses"); 27 let {ElemHideException} = require("filterClasses");
28 let {FilterNotifier} = require("filterNotifier"); 28 let {FilterNotifier} = require("filterNotifier");
29 let {AboutHandler} = require("elemHideHitRegistration"); 29 let {AboutHandler} = require("elemHideHitRegistration");
30 let {TimeLine} = require("timeline"); 30 let {TimeLine} = require("timeline");
31 let Policy = null;
31 32
32 /** 33 /**
33 * Lookup table, filters by their associated key 34 * Lookup table, filters by their associated key
34 * @type Object 35 * @type Object
35 */ 36 */
36 let filterByKey = {__proto__: null}; 37 let filterByKey = Object.create(null);
37 38
38 /** 39 /**
39 * Lookup table, keys of the filters by filter text 40 * Lookup table, keys of the filters by filter text
40 * @type Object 41 * @type Object
41 */ 42 */
42 let keyByFilter = {__proto__: null}; 43 let keyByFilter = Object.create(null);
43 44
44 /** 45 /**
45 * Lookup table, keys are known element hiding exceptions 46 * Lookup table, keys are known element hiding exceptions
46 * @type Object 47 * @type Object
47 */ 48 */
48 let knownExceptions = {__proto__: null}; 49 let knownExceptions = Object.create(null);
49 50
50 /** 51 /**
51 * Lookup table, lists of element hiding exceptions by selector 52 * Lookup table, lists of element hiding exceptions by selector
52 * @type Object 53 * @type Object
53 */ 54 */
54 let exceptions = {__proto__: null}; 55 let exceptions = Object.create(null);
55 56
56 /** 57 /**
57 * Currently applied stylesheet URL 58 * Currently applied stylesheet URL
58 * @type nsIURI 59 * @type nsIURI
59 */ 60 */
60 let styleURL = null; 61 let styleURL = null;
61 62
62 /** 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 /**
63 * Element hiding component 76 * Element hiding component
64 * @class 77 * @class
65 */ 78 */
66 let ElemHide = exports.ElemHide = 79 let ElemHide = exports.ElemHide =
67 { 80 {
81 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer ence]),
82
68 /** 83 /**
69 * Indicates whether filters have been added or removed since the last apply() call. 84 * Indicates whether filters have been added or removed since the last apply() call.
70 * @type Boolean 85 * @type Boolean
71 */ 86 */
72 isDirty: false, 87 isDirty: false,
73 88
74 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,
75 94
76 /** 95 /**
77 * Called on module startup. 96 * Called on module startup.
78 */ 97 */
79 init: function() 98 init: function()
80 { 99 {
81 TimeLine.enter("Entered ElemHide.init()"); 100 TimeLine.enter("Entered ElemHide.init()");
101
102 if (useNew) {
103 // Avoid dependency issue.
104 Policy = require("contentPolicy").Policy;
105 }
106
82 Prefs.addListener(function(name) 107 Prefs.addListener(function(name)
83 { 108 {
84 if (name == "enabled") 109 if (name == "enabled")
85 ElemHide.apply(); 110 ElemHide.apply();
86 }); 111 });
Wladimir Palant 2014/05/20 19:36:12 This code is no longer necessary.
87 onShutdown.add(function() 112
88 { 113 if (useNew)
89 Services.obs.removeObserver(this, "content-document-global-created"); 114 Services.obs.addObserver(this, "content-document-global-created", true);
90 }.bind(this)); 115
116 onShutdown.add(() =>
117 {
118 ElemHide.unapply();
119 if (useNew)
120 Services.obs.removeObserver(this, "content-document-global-created");
121 });
91 122
92 TimeLine.log("done adding prefs listener"); 123 TimeLine.log("done adding prefs listener");
93 124
94 let styleFile = IO.resolveFilePath(Prefs.data_directory); 125 let styleFile = IO.resolveFilePath(Prefs.data_directory);
95 styleFile.append("elemhide.css"); 126 styleFile.append("elemhide.css");
96 styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); 127 styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL);
97 TimeLine.log("done determining stylesheet URL"); 128 TimeLine.log("done determining stylesheet URL");
98 129
99 Services.obs.addObserver(this, "content-document-global-created", true);
100
101 TimeLine.leave("ElemHide.init() done"); 130 TimeLine.leave("ElemHide.init() done");
102 },
103
104 /**
105 * Removes all known filters
106 */
107 clear: function()
108 {
109 filterByKey = {__proto__: null};
110 keyByFilter = {__proto__: null};
111 knownExceptions = {__proto__: null};
112 exceptions = {__proto__: null};
113 ElemHide.isDirty = false;
114 }, 131 },
115 132
116 observe: function (subject, topic, data, additional) 133 observe: function (subject, topic, data, additional)
117 { 134 {
118 if (topic != "content-document-global-created") 135 if (topic != "content-document-global-created")
119 return; 136 return;
120 137
121 if (!Prefs.enabled) 138 if (!Prefs.enabled)
122 return; 139 return;
123 140
124 let uri = subject.document.documentURIObject; 141 if (Policy.shouldNeverBlockWindow(subject))
125 if (!uri)
126 return; 142 return;
127 143
128 switch(uri.scheme) 144 if (styleSheet)
129 { 145 {
130 case "http": 146 try
131 case "https": 147 {
132 case "mailbox": 148 let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor)
133 case "imap": 149 .getInterface(Ci.nsIDOMWindowUtils);
134 case "news": 150 utils.addSheet(styleSheet, Ci.nsIStyleSheetService.USER_SHEET);
135 case "snews": 151 }
136 break; 152 catch (e)
137 default: 153 {
138 // Avoid injecting in chrome documents 154 Cu.reportError(e);
139 return; 155 }
140 } 156 }
Wladimir Palant 2014/05/20 19:36:12 Please use Policy.isBlockableScheme() here (from c
141 157 },
142 // XXX is there some better way to test if it's disabled on that domain that we should do before? 158
143 if (!ElemHide.anySelectorForDomain(uri.host)) 159 /**
144 return; 160 * Removes all known filters
Wladimir Palant 2014/05/20 19:36:12 Wow, performance will go down the drain here. We c
145 161 */
146 var windowUtils = subject.QueryInterface(Ci.nsIInterfaceRequestor).getInterf ace(Ci.nsIDOMWindowUtils); 162 clear: function()
147 windowUtils.loadSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); 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();
148 }, 170 },
149 171
150 /** 172 /**
151 * Add a new element hiding filter 173 * Add a new element hiding filter
152 * @param {ElemHideFilter} filter 174 * @param {ElemHideFilter} filter
153 */ 175 */
154 add: function(filter) 176 add: function(filter)
155 { 177 {
156 if (filter instanceof ElemHideException) 178 if (filter instanceof ElemHideException)
157 { 179 {
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 _applying: false, 256 _applying: false,
235 257
236 /** 258 /**
237 * Will be set to true if an apply() call arrives while apply() is already 259 * Will be set to true if an apply() call arrives while apply() is already
238 * running (delayed execution). 260 * running (delayed execution).
239 * @type Boolean 261 * @type Boolean
240 */ 262 */
241 _needsApply: false, 263 _needsApply: false,
242 264
243 /** 265 /**
244 * Generates stylesheet URL. 266 * Generates stylesheet URL and applies it globally
245 */ 267 */
246 // XXX Rename
247 apply: function() 268 apply: function()
Wladimir Palant 2014/05/20 19:36:12 saveStylesheet()?
248 { 269 {
249 if (this._applying) 270 if (this._applying)
250 { 271 {
251 this._needsApply = true; 272 this._needsApply = true;
252 return; 273 return;
253 } 274 }
254 275
255 TimeLine.enter("Entered ElemHide.apply()"); 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 }
256 304
257 IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) 305 IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e)
258 { 306 {
259 TimeLine.enter("ElemHide.apply() write callback"); 307 TimeLine.enter("ElemHide.apply() write callback");
260 this._applying = false; 308 this._applying = false;
261 309
262 // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that 310 // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that
263 // 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
264 // see a proper exception here, otherwise a number. 312 // see a proper exception here, otherwise a number.
265 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));
266 if (noFilters) 314 if (noFilters)
267 { 315 {
268 e = null; 316 e = null;
269 IO.removeFile(styleURL.file, function(e) {}); 317 IO.removeFile(styleURL.file, function(e) {});
270 } 318 }
271 else if (e) 319 else if (e)
272 Cu.reportError(e); 320 Cu.reportError(e);
273 321
274 if (this._needsApply) 322 if (this._needsApply)
275 { 323 {
276 this._needsApply = false; 324 this._needsApply = false;
277 this.apply(); 325 this.apply();
278 } 326 }
279 else if (!e) 327 else if (!e)
280 { 328 {
281 ElemHide.isDirty = false; 329 ElemHide.isDirty = false;
282 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
283 FilterNotifier.triggerListeners("elemhideupdate"); 351 FilterNotifier.triggerListeners("elemhideupdate");
Wladimir Palant 2014/05/20 19:36:12 Apparently, this is dead code. This notification h
284 } 352 }
285 TimeLine.leave("ElemHide.apply() write callback done"); 353 TimeLine.leave("ElemHide.apply() write callback done");
286 }.bind(this), "ElemHideWrite"); 354 }.bind(this), "ElemHideWrite");
287 355
288 this._applying = true; 356 this._applying = true;
289 357
290 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite"); 358 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite");
291 }, 359 },
292 360
293 _generateCSSContent: function() 361 _generateCSSContent: function()
294 { 362 {
295 // Grouping selectors by domains 363 // Grouping selectors by domains
296 TimeLine.log("start grouping selectors"); 364 TimeLine.log("start grouping selectors");
297 let domains = {__proto__: null}; 365 let domains = Object.create(null);
298 let hasFilters = false; 366 let hasFilters = false;
299 for (let key in filterByKey) 367 for (let key in filterByKey)
300 { 368 {
301 let filter = filterByKey[key]; 369 let filter = filterByKey[key];
302 let domain = filter.selectorDomain || ""; 370 let domain = filter.selectorDomain || "";
303 371
304 let list; 372 let list;
305 if (domain in domains) 373 if (domain in domains)
306 list = domains[domain]; 374 list = domains[domain];
307 else 375 else
308 { 376 {
309 list = {__proto__: null}; 377 list = Object.create(null);
310 domains[domain] = list; 378 domains[domain] = list;
311 } 379 }
312 list[filter.selector] = key; 380 list[filter.selector] = key;
313 hasFilters = true; 381 hasFilters = true;
314 } 382 }
315 TimeLine.log("done grouping selectors"); 383 TimeLine.log("done grouping selectors");
316 384
317 if (!hasFilters) 385 if (!hasFilters)
318 throw Cr.NS_ERROR_NOT_AVAILABLE; 386 throw Cr.NS_ERROR_NOT_AVAILABLE;
319 387
(...skipping 23 matching lines...) Expand all
343 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]) + "}";
344 yield '}'; 412 yield '}';
345 } 413 }
346 }, 414 },
347 415
348 /** 416 /**
349 * Unapplies current stylesheet URL 417 * Unapplies current stylesheet URL
350 */ 418 */
351 unapply: function() 419 unapply: function()
352 { 420 {
353 }, 421 if (ElemHide.applied)
Wladimir Palant 2014/05/20 19:36:12 This function should be removed.
354 422 {
355 /** 423 try
356 * Retrieves the currently applied stylesheet URL 424 {
357 * @type String 425 if (!useNew)
358 */ 426 Utils.styleService.unregisterSheet(styleURL, Ci.nsIStyleSheetService.U SER_SHEET);
359 get styleURL() styleURL.spec, 427 }
Wladimir Palant 2014/05/20 19:36:12 This getter was also introduced in https://hg.adbl
428 catch (e)
429 {
430 Cu.reportError(e);
431 }
432 ElemHide.applied = false;
433 }
434 },
360 435
361 /** 436 /**
362 * Retrieves an element hiding filter by the corresponding protocol key 437 * Retrieves an element hiding filter by the corresponding protocol key
363 */ 438 */
364 getFilterByKey: function(/**String*/ key) /**Filter*/ 439 getFilterByKey: function(/**String*/ key) /**Filter*/
365 { 440 {
366 return (key in filterByKey ? filterByKey[key] : null); 441 return (key in filterByKey ? filterByKey[key] : null);
367 }, 442 },
368 443
369 /** 444 /**
(...skipping 12 matching lines...) Expand all
382 // workaround WebKit bug 132872, also see #419 457 // workaround WebKit bug 132872, also see #419
383 let domains = filter.domains; 458 let domains = filter.domains;
384 459
385 if (specificOnly && (!domains || domains[""])) 460 if (specificOnly && (!domains || domains[""]))
386 continue; 461 continue;
387 462
388 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) 463 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain))
389 result.push(filter.selector); 464 result.push(filter.selector);
390 } 465 }
391 return result; 466 return result;
392 },
393
394 anySelectorForDomain: function(/**String*/ domain)
395 {
396 for (let key in filterByKey)
397 {
398 let filter = filterByKey[key];
399
400 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain))
401 return true;
402 }
403
404 return false;
405 } 467 }
406 }; 468 };
LEFTRIGHT

Powered by Google App Engine
This is Rietveld