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

Side by Side Diff: include.postload.js

Issue 5598654983307264: Issue 1751 - Highlight elements by injecting an overlay (Closed)
Patch Set: Rebased and addressed comments Created Jan. 13, 2015, 1:03 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 30 matching lines...) Expand all
41 function quote(value) 41 function quote(value)
42 { 42 {
43 return '"' + value.replace(/["\\\{\}\x00-\x1F\x7F]/g, escapeChar) + '"'; 43 return '"' + value.replace(/["\\\{\}\x00-\x1F\x7F]/g, escapeChar) + '"';
44 } 44 }
45 45
46 function escapeCSS(s) 46 function escapeCSS(s)
47 { 47 {
48 return s.replace(/^[\d\-]|[^\w\-\u0080-\uFFFF]/g, escapeChar); 48 return s.replace(/^[\d\-]|[^\w\-\u0080-\uFFFF]/g, escapeChar);
49 } 49 }
50 50
51 function supportsShadowRoot(element)
52 {
53 if (!("createShadowRoot" in element))
54 return false;
55
56 // There are some elements (e.g. <textarea>), which don't
57 // support author created shadow roots and throw an exception.
58 var clone = element.cloneNode(false);
59 try
60 {
61 clone.createShadowRoot();
62 }
63 catch (e)
64 {
65 return false;
66 }
67
68 // There are some elements (e.g. <input>), which support
69 // author created shadow roots, but ignore insertion points.
70 var child = document.createTextNode("");
71 clone.appendChild(child);
72
73 var shadow = document.createElement("shadow");
74 clone.shadowRoot.appendChild(shadow);
75
76 return shadow.getDistributedNodes()[0] == child;
77 }
78
79 function getOriginalStyle(element)
80 {
81 if ("_originalStyle" in element)
82 return element._originalStyle;
83
84 return element.getAttribute("style");
85 }
86
87 function highlightElement(element, shadowColor, backgroundColor) 51 function highlightElement(element, shadowColor, backgroundColor)
88 { 52 {
89 unhighlightElement(element); 53 unhighlightElement(element);
90 54
91 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shadow" ); 55 var highlightWithOverlay = function()
92 var originalBackgroundColorPriority = element.style.getPropertyPriority("backg round-color"); 56 {
57 var overlay = addElementOverlay(element);
93 58
94 var boxShadow = "inset 0px 0px 5px " + shadowColor; 59 highlightElement(overlay, shadowColor, backgroundColor);
95 60 overlay.style.pointerEvents = "none";
96 var highlightWithShadowDOM = function()
97 {
98 var style = document.createElement("style");
99 style.textContent = ":host {" +
100 "box-shadow:" + boxShadow + " !important;" +
101 "background-color:" + backgroundColor + " !important;" +
102 "}";
103
104 var root = element.createShadowRoot();
105 root.appendChild(document.createElement("shadow"));
106 root.appendChild(style);
107 61
108 element._unhighlight = function() 62 element._unhighlight = function()
109 { 63 {
110 root.removeChild(style); 64 overlay.parentNode.removeChild(overlay);
111 }; 65 };
112 }; 66 };
113 67
114 var highlightWithStyleAttribute = function() 68 var highlightWithStyleAttribute = function()
115 { 69 {
116 var originalBoxShadow = element.style.getPropertyValue("box-shadow"); 70 var originalBoxShadow = element.style.getPropertyValue("box-shadow");
71 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shado w");
117 var originalBackgroundColor = element.style.getPropertyValue("background-col or"); 72 var originalBackgroundColor = element.style.getPropertyValue("background-col or");
73 var originalBackgroundColorPriority = element.style.getPropertyPriority("bac kground-color");
118 74
119 element._originalStyle = getOriginalStyle(element); 75 element.style.setProperty("box-shadow", "inset 0px 0px 5px " + shadowColor, "important");
120
121 element.style.setProperty("box-shadow", boxShadow, "important");
122 element.style.setProperty("background-color", backgroundColor, "important"); 76 element.style.setProperty("background-color", backgroundColor, "important");
123 77
124 element._unhighlight = function() 78 element._unhighlight = function()
125 { 79 {
126 this.style.removeProperty("box-shadow"); 80 this.style.removeProperty("box-shadow");
127 this.style.setProperty( 81 this.style.setProperty(
128 "box-shadow", 82 "box-shadow",
129 originalBoxShadow, 83 originalBoxShadow,
130 originalBoxShadowPriority 84 originalBoxShadowPriority
131 ); 85 );
132 86
133 this.style.removeProperty("background-color"); 87 this.style.removeProperty("background-color");
134 this.style.setProperty( 88 this.style.setProperty(
135 "background-color", 89 "background-color",
136 originalBackgroundColor, 90 originalBackgroundColor,
137 originalBackgroundColorPriority 91 originalBackgroundColorPriority
138 ); 92 );
139 }; 93 };
140 }; 94 };
141 95
142 // Use shadow DOM if posibble to avoid side effects when the 96 if ("prisoner" in element)
143 // web page updates style while highlighted. However, if the 97 highlightWithStyleAttribute();
144 // element has important styles we can't override them with shadow DOM.
145 if (supportsShadowRoot(element) && originalBoxShadowPriority != "importa nt" &&
146 originalBackgroundColorPriority != "importa nt")
147 highlightWithShadowDOM();
148 else 98 else
149 highlightWithStyleAttribute(); 99 highlightWithOverlay();
150 } 100 }
151 101
152 102
153 function unhighlightElement(element) 103 function unhighlightElement(element)
154 { 104 {
155 if ("_unhighlight" in element) 105 if ("_unhighlight" in element)
156 { 106 {
157 element._unhighlight(); 107 element._unhighlight();
158 delete element._unhighlight; 108 delete element._unhighlight;
159 } 109 }
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 if (element.id) 222 if (element.id)
273 return true; 223 return true;
274 if (element.classList.length > 0) 224 if (element.classList.length > 0)
275 return true; 225 return true;
276 if (getURLsFromElement(element).length > 0) 226 if (getURLsFromElement(element).length > 0)
277 return true; 227 return true;
278 228
279 // We only generate filters based on the "style" attribute, 229 // We only generate filters based on the "style" attribute,
280 // if this is the only way we can generate a filter, and 230 // if this is the only way we can generate a filter, and
281 // only if there are at least two CSS properties defined. 231 // only if there are at least two CSS properties defined.
282 if (/:.+:/.test(getOriginalStyle(element))) 232 if (/:.+:/.test(element.getAttribute("style")))
283 return true; 233 return true;
284 234
285 return false; 235 return false;
286 } 236 }
287 237
288 // Gets the absolute position of an element by walking up the DOM tree, 238 // Gets the absolute position of an element by walking up the DOM tree,
289 // adding up offsets. 239 // adding up offsets.
290 // I hope there's a better way because it just seems absolutely stupid 240 // I hope there's a better way because it just seems absolutely stupid
291 // that the DOM wouldn't have a direct way to get this, given that it 241 // that the DOM wouldn't have a direct way to get this, given that it
292 // has hundreds and hundreds of other methods that do random junk. 242 // has hundreds and hundreds of other methods that do random junk.
293 function getAbsolutePosition(elt) { 243 function getAbsolutePosition(elt) {
294 var l = 0; 244 var l = 0;
295 var t = 0; 245 var t = 0;
296 for(; elt; elt = elt.offsetParent) { 246 for(; elt; elt = elt.offsetParent) {
297 l += elt.offsetLeft; 247 l += elt.offsetLeft;
298 t += elt.offsetTop; 248 t += elt.offsetTop;
299 } 249 }
300 return [l, t]; 250 return [l, t];
301 } 251 }
302 252
303 // Adds an overlay to an element, which is probably a Flash object 253 // Adds an overlay to an element, which is probably a Flash object
304 function addElementOverlay(elt) { 254 function addElementOverlay(elt) {
305 // If this element is enclosed in an object tag, we prefer to block that inste ad
306 if(!elt)
307 return null;
308
309 // If element doesn't have at least one of class name, ID or URL, give up
310 // because we don't know how to construct a filter rule for it
311 if(!isBlockable(elt))
312 return;
313
314 // If the element isn't rendered (since its or one of its ancestor's 255 // If the element isn't rendered (since its or one of its ancestor's
315 // "display" property is "none"), the overlay wouldn't match the element. 256 // "display" property is "none"), the overlay wouldn't match the element.
316 if (!elt.offsetParent) 257 if (!elt.offsetParent)
317 return; 258 return;
318 259
319 var thisStyle = getComputedStyle(elt, null); 260 var thisStyle = getComputedStyle(elt, null);
320 var overlay = document.createElement('div'); 261 var overlay = document.createElement('div');
321 overlay.prisoner = elt; 262 overlay.prisoner = elt;
322 overlay.className = "__adblockplus__overlay"; 263 overlay.className = "__adblockplus__overlay";
323 overlay.setAttribute('style', 'opacity:0.4; background-color:#ffffff; display: inline-box; ' + 'width:' + thisStyle.width + '; height:' + thisStyle.height + '; position:absolute; overflow:hidden; -webkit-box-sizing:border-box;'); 264 overlay.setAttribute('style', 'opacity:0.4; display:inline-box; position:absol ute; overflow:hidden; box-sizing:border-box;');
324 var pos = getAbsolutePosition(elt); 265 var pos = getAbsolutePosition(elt);
266 overlay.style.width = elt.offsetWidth + "px";
267 overlay.style.height = elt.offsetHeight + "px";
325 overlay.style.left = pos[0] + "px"; 268 overlay.style.left = pos[0] + "px";
326 overlay.style.top = pos[1] + "px"; 269 overlay.style.top = pos[1] + "px";
327 270
328 if (thisStyle.position != "static") 271 if (thisStyle.position != "static")
329 overlay.style.zIndex = thisStyle.zIndex; 272 overlay.style.zIndex = thisStyle.zIndex;
330 else 273 else
331 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; 274 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex;
332 275
333 // elt.parentNode.appendChild(overlay, elt); 276 // elt.parentNode.appendChild(overlay, elt);
334 document.body.appendChild(overlay); 277 document.body.appendChild(overlay);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 316
374 // Turn on the choose element to create filter thing 317 // Turn on the choose element to create filter thing
375 function clickHide_activate() { 318 function clickHide_activate() {
376 if(document == null) 319 if(document == null)
377 return; 320 return;
378 321
379 // If we are already selecting, abort now 322 // If we are already selecting, abort now
380 if (clickHide_activated || clickHideFiltersDialog) 323 if (clickHide_activated || clickHideFiltersDialog)
381 clickHide_deactivate(); 324 clickHide_deactivate();
382 325
383 // Add overlays for elements with URLs so user can easily click them 326 // Add overlays for blockable elements that don't emit mouse events that they can still be selected
384 var elts = document.querySelectorAll('object,embed,img,iframe,video,audio,pict ure'); 327 var elts = document.querySelectorAll('object,embed,iframe');
385 for(var i=0; i<elts.length; i++) 328 for(var i=0; i<elts.length; i++)
386 addElementOverlay(elts[i]); 329 {
330 var element = elts[i];
331 if (isBlockable(element))
332 addElementOverlay(element);
333 }
387 334
388 clickHide_activated = true; 335 clickHide_activated = true;
389 document.addEventListener("mouseover", clickHide_mouseOver, true); 336 document.addEventListener("mouseover", clickHide_mouseOver, true);
390 document.addEventListener("mouseout", clickHide_mouseOut, true); 337 document.addEventListener("mouseout", clickHide_mouseOut, true);
391 document.addEventListener("click", clickHide_mouseClick, true); 338 document.addEventListener("click", clickHide_mouseClick, true);
392 document.addEventListener("keydown", clickHide_keyDown, true); 339 document.addEventListener("keydown", clickHide_keyDown, true);
393 } 340 }
394 341
395 // Called when user has clicked on something and we are waiting for confirmation 342 // Called when user has clicked on something and we are waiting for confirmation
396 // on whether the user actually wants these filters 343 // on whether the user actually wants these filters
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 continue; 493 continue;
547 } 494 }
548 495
549 if (url == elt.src) 496 if (url == elt.src)
550 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s rc")) + ']'); 497 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s rc")) + ']');
551 } 498 }
552 499
553 // as last resort, create a filter based on inline styles 500 // as last resort, create a filter based on inline styles
554 if (clickHideFilters.length == 0) 501 if (clickHideFilters.length == 0)
555 { 502 {
556 var style = getOriginalStyle(elt); 503 var style = elt.getAttribute("style");
557 if (style) 504 if (style)
558 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']'); 505 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']');
559 } 506 }
560 507
561 // Show popup 508 // Show popup
562 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters); 509 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters);
563 510
564 // Highlight the elements specified by selector in yellow 511 // Highlight the elements specified by selector in yellow
565 if (selectorList.length > 0) 512 if (selectorList.length > 0)
566 highlightElements(selectorList.join(",")); 513 highlightElements(selectorList.join(","));
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 case "clickhide-activate": 633 case "clickhide-activate":
687 clickHide_activate(); 634 clickHide_activate();
688 break; 635 break;
689 case "clickhide-deactivate": 636 case "clickhide-deactivate":
690 clickHide_deactivate(); 637 clickHide_deactivate();
691 break; 638 break;
692 case "clickhide-new-filter": 639 case "clickhide-new-filter":
693 if(lastRightClickEvent) 640 if(lastRightClickEvent)
694 { 641 {
695 clickHide_activated = true; 642 clickHide_activated = true;
696 currentElement = addElementOverlay(lastRightClickEvent.target); 643 currentElement = lastRightClickEvent.target;
697 clickHide_mouseClick(lastRightClickEvent); 644 clickHide_mouseClick(lastRightClickEvent);
698 } 645 }
699 break; 646 break;
700 case "clickhide-init": 647 case "clickhide-init":
701 if (clickHideFiltersDialog) 648 if (clickHideFiltersDialog)
702 { 649 {
703 sendResponse({filters: clickHide_filters}); 650 sendResponse({filters: clickHide_filters});
704 651
705 clickHideFiltersDialog.style.width = msg.width + "px"; 652 clickHideFiltersDialog.style.width = msg.width + "px";
706 clickHideFiltersDialog.style.height = msg.height + "px"; 653 clickHideFiltersDialog.style.height = msg.height + "px";
(...skipping 16 matching lines...) Expand all
723 element.parentNode.removeChild(element); 670 element.parentNode.removeChild(element);
724 } 671 }
725 clickHide_deactivate(); 672 clickHide_deactivate();
726 break; 673 break;
727 } 674 }
728 }); 675 });
729 676
730 if (window == window.top) 677 if (window == window.top)
731 ext.backgroundPage.sendMessage({type: "report-html-page"}); 678 ext.backgroundPage.sendMessage({type: "report-html-page"});
732 } 679 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld