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

Delta Between Two Patch Sets: include.preload.js

Issue 29338588: Issue 3699 - Patch DOM API in order to prevent disabling the injected stylesheet (Closed)
Left Patch Set: Created March 17, 2016, 7:47 p.m.
Right Patch Set: Use MutationObserver again Created March 18, 2016, 5: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 | « no previous file | 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 <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
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 }, 324 },
325 325
326 disconnect: function() 326 disconnect: function()
327 { 327 {
328 this.document.removeEventListener("DOMContentLoaded", this.trace); 328 this.document.removeEventListener("DOMContentLoaded", this.trace);
329 this.observer.disconnect(); 329 this.observer.disconnect();
330 clearTimeout(this.timeout); 330 clearTimeout(this.timeout);
331 } 331 }
332 }; 332 };
333 333
334 function runInPage(document, arg, fn) 334 function reinjectStyleSheetWhenRemoved(document, style)
335 { 335 {
336 var MutationObserver = window.MutationObserver ||
337 window.WebKitMutationObserver;
338 if (!MutationObserver)
339 return null;
340
341 var parentNode = style.parentNode;
342 var observer = new MutationObserver(function()
343 {
344 if (style.parentNode != parentNode)
345 parentNode.appendChild(style);
346 });
347
348 observer.observe(parentNode, {childList: true});
349 return observer;
350 }
351
352 function protectStyleSheet(document, style)
353 {
354 var id = Math.random().toString(36).substr(2)
355 style.id = id;
356
357 var code = [
358 "(function()",
359 "{",
360 ' var style = document.getElementById("' + id + '") ||',
361 ' document.documentElement.shadowRoot.getElementById("' + id + '");',
Sebastian Noack 2016/03/18 17:58:54 (Yes, I know that this line is 6 characters too lo
kzar 2016/03/18 19:18:32 Acknowledged.
362 ' style.removeAttribute("id");'
363 ];
364
365 var disableables = ["style", "style.sheet"];
366 for (var i = 0; i < disableables.length; i++)
367 {
368 code.push(" Object.defineProperty(" + disableables[i] + ', "disabled", '
369 + "{value: false, enumerable: true});") ;
370 }
371
372 var methods = ["deleteRule", "removeRule"];
373 for (var j = 0; j < methods.length; j++)
kzar 2016/03/18 19:18:32 Nit: Just re-use the existing `i` variable instead
Sebastian Noack 2016/03/18 19:23:55 I'm not sure whether it's a good idea to make the
374 {
375 var method = methods[j];
376 if (method in CSSStyleSheet.prototype)
377 {
378 var origin = "CSSStyleSheet.prototype." + method;
379 code.push(" var " + method + " = " + origin + ";",
380 " " + origin + " = function(index)",
381 " {",
382 " if (this != style.sheet)",
383 " " + method + ".apply(this, index);",
384 " }");
385 }
386 }
387
388 code.push("})();");
389
336 var script = document.createElement("script"); 390 var script = document.createElement("script");
337 script.async = false; 391 script.async = false;
338 script.textContent = "(" + fn + ")(" + arg + ");"; 392 script.textContent = code.join("\n");
339 document.documentElement.appendChild(script); 393 document.documentElement.appendChild(script);
340 document.documentElement.removeChild(script); 394 document.documentElement.removeChild(script);
341 } 395 }
342 396
343 function protectStylesheet(document, style)
344 {
345 var id = Math.random().toString(36).substr(2);
346 style.id = id;
347
348 runInPage(document, '"' + id + '"', function(id)
349 {
350 var style = document.getElementById(id) ||
351 document.documentElement.shadowRoot.getElementById(id);
352 style.removeAttribute("id");
353
354 var removeChild = Node.prototype.removeChild;
355 Node.prototype.removeChild = function(child)
356 {
357 if (child != style)
358 removeChild.call(this, child);
359 };
360
361 var remove = Element.prototype.remove;
362 if (remove)
363 Element.prototype.remove = function()
364 {
365 if (this != style)
366 remove.call(this);
367 };
368
369 var deleteRule = CSSStyleSheet.prototype.deleteRule;
370 CSSStyleSheet.prototype.deleteRule = function(index)
371 {
372 if (this != style.sheet)
373 deleteRule.call(this, index);
374 };
375
376 var removeRule = CSSStyleSheet.prototype.removeRule;
377 if (removeRule)
378 CSSStyleSheet.prototype.removeRule = function(index)
379 {
380 if (this != style.sheet)
381 removeRule.call(this, index);
382 };
383
384 Object.defineProperty(
385 style, 'disabled',
386 {
387 value: false,
388 enumerable: true
389 }
390 );
391
392 Object.defineProperty(
393 style.sheet, 'disabled',
394 {
395 value: false,
396 enumerable: true
397 }
398 );
399 });
400 }
401
402 function init(document) 397 function init(document)
403 { 398 {
404 var shadow = null; 399 var shadow = null;
405 var style = null; 400 var style = null;
401 var observer = null;
406 var tracer = null; 402 var tracer = null;
407 var propertyFilters = new CSSPropertyFilters(window, addElemHideSelectors); 403 var propertyFilters = new CSSPropertyFilters(window, addElemHideSelectors);
408 404
409 // Use Shadow DOM if available to don't mess with web pages that rely on 405 // Use Shadow DOM if available to don't mess with web pages that rely on
410 // the order of their own <style> tags (#309). 406 // the order of their own <style> tags (#309).
411 // 407 //
412 // However, creating a shadow root breaks running CSS transitions. So we 408 // However, creating a shadow root breaks running CSS transitions. So we
413 // have to create the shadow root before transistions might start (#452). 409 // have to create the shadow root before transistions might start (#452).
414 // 410 //
415 // Also, using shadow DOM causes issues on some Google websites, 411 // Also, using shadow DOM causes issues on some Google websites,
(...skipping 18 matching lines...) Expand all
434 style = document.createElement("style"); 430 style = document.createElement("style");
435 (shadow || document.head || document.documentElement).appendChild(style); 431 (shadow || document.head || document.documentElement).appendChild(style);
436 432
437 // It can happen that the frame already navigated to a different 433 // It can happen that the frame already navigated to a different
438 // document while we were waiting for the background page to respond. 434 // document while we were waiting for the background page to respond.
439 // In that case the sheet property will stay null, after addind the 435 // In that case the sheet property will stay null, after addind the
440 // <style> element to the shadow DOM. 436 // <style> element to the shadow DOM.
441 if (!style.sheet) 437 if (!style.sheet)
442 return; 438 return;
443 439
444 protectStylesheet(document, style); 440 observer = reinjectStyleSheetWhenRemoved(document, style);
441 protectStyleSheet(document, style);
445 } 442 }
446 443
447 // If using shadow DOM, we have to add the ::content pseudo-element 444 // If using shadow DOM, we have to add the ::content pseudo-element
448 // before each selector, in order to match elements within the 445 // before each selector, in order to match elements within the
449 // insertion point. 446 // insertion point.
450 if (shadow) 447 if (shadow)
451 { 448 {
452 var preparedSelectors = []; 449 var preparedSelectors = [];
453 for (var i = 0; i < selectors.length; i++) 450 for (var i = 0; i < selectors.length; i++)
454 { 451 {
(...skipping 15 matching lines...) Expand all
470 467
471 var updateStylesheet = function() 468 var updateStylesheet = function()
472 { 469 {
473 var selectors = null; 470 var selectors = null;
474 var CSSPropertyFiltersLoaded = false; 471 var CSSPropertyFiltersLoaded = false;
475 472
476 var checkLoaded = function() 473 var checkLoaded = function()
477 { 474 {
478 if (!selectors || !CSSPropertyFiltersLoaded) 475 if (!selectors || !CSSPropertyFiltersLoaded)
479 return; 476 return;
477
478 if (observer)
479 observer.disconnect();
480 observer = null;
480 481
481 if (tracer) 482 if (tracer)
482 tracer.disconnect(); 483 tracer.disconnect();
483 tracer = null; 484 tracer = null;
484 485
485 if (style && style.parentElement) 486 if (style && style.parentElement)
486 style.parentElement.removeChild(style); 487 style.parentElement.removeChild(style);
487 style = null; 488 style = null;
488 489
489 addElemHideSelectors(selectors.selectors); 490 addElemHideSelectors(selectors.selectors);
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 }, true); 551 }, true);
551 552
552 return updateStylesheet; 553 return updateStylesheet;
553 } 554 }
554 555
555 if (document instanceof HTMLDocument) 556 if (document instanceof HTMLDocument)
556 { 557 {
557 checkSitekey(); 558 checkSitekey();
558 window.updateStylesheet = init(document); 559 window.updateStylesheet = init(document);
559 } 560 }
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld