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: Created Jan. 7, 2015, 2:31 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");
93
94 var boxShadow = "inset 0px 0px 5px " + shadowColor;
95
96 var highlightWithShadowDOM = function()
97 { 56 {
98 var style = document.createElement("style"); 57 highlightElement(overlay, shadowColor, backgroundColor);
99 style.textContent = ":host {" + 58 overlay.style.pointerEvents = "none";
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 59
108 element._unhighlight = function() 60 element._unhighlight = function()
kzar 2015/01/13 11:04:24 Could this cause a memory leak as _unhighlight fun
Sebastian Noack 2015/01/13 13:03:53 It is actually removed, see unhighlightElement().
109 { 61 {
110 root.removeChild(style); 62 overlay.parentNode.removeChild(overlay);
111 }; 63 };
112 }; 64 };
113 65
114 var highlightWithStyleAttribute = function() 66 var highlightWithStyleAttribute = function()
115 { 67 {
116 var originalBoxShadow = element.style.getPropertyValue("box-shadow"); 68 var originalBoxShadow = element.style.getPropertyValue ("box -shadow");
kzar 2015/01/13 11:04:24 Why extra spacing before opening bracket? (Maybe i
Sebastian Noack 2015/01/13 13:03:53 I find it more readable, when similar code is alig
117 var originalBackgroundColor = element.style.getPropertyValue("background-col or"); 69 var originalBoxShadowPriority = element.style.getPropertyPriority("box -shadow");
70 var originalBackgroundColor = element.style.getPropertyValue ("bac kground-color");
71 var originalBackgroundColorPriority = element.style.getPropertyPriority("bac kground-color");
118 72
119 element._originalStyle = getOriginalStyle(element); 73 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"); 74 element.style.setProperty("background-color", backgroundColor, "important");
123 75
124 element._unhighlight = function() 76 element._unhighlight = function()
125 { 77 {
126 this.style.removeProperty("box-shadow"); 78 this.style.removeProperty("box-shadow");
127 this.style.setProperty( 79 this.style.setProperty(
128 "box-shadow", 80 "box-shadow",
129 originalBoxShadow, 81 originalBoxShadow,
130 originalBoxShadowPriority 82 originalBoxShadowPriority
131 ); 83 );
132 84
133 this.style.removeProperty("background-color"); 85 this.style.removeProperty("background-color");
134 this.style.setProperty( 86 this.style.setProperty(
135 "background-color", 87 "background-color",
136 originalBackgroundColor, 88 originalBackgroundColor,
137 originalBackgroundColorPriority 89 originalBackgroundColorPriority
138 ); 90 );
139 }; 91 };
140 }; 92 };
141 93
142 // Use shadow DOM if posibble to avoid side effects when the 94 var overlay;
kzar 2015/01/13 11:04:24 Seems confusing to me that overlay is declared her
Sebastian Noack 2015/01/13 13:03:53 I moved the declaration into highlightWithOverlay(
143 // web page updates style while highlighted. However, if the 95 if (!("prisoner" in element) && (overlay = addElementOverlay(element)))
kzar 2015/01/13 11:04:24 Could you add a comment before this if statement e
Sebastian Noack 2015/01/13 13:03:53 We only highlight elements with style attributes i
144 // element has important styles we can't override them with shadow DOM. 96 highlightWithOverlay();
145 if (supportsShadowRoot(element) && originalBoxShadowPriority != "importa nt" &&
146 originalBackgroundColorPriority != "importa nt")
147 highlightWithShadowDOM();
148 else 97 else
149 highlightWithStyleAttribute(); 98 highlightWithStyleAttribute();
150 } 99 }
151 100
152 101
153 function unhighlightElement(element) 102 function unhighlightElement(element)
154 { 103 {
155 if ("_unhighlight" in element) 104 if ("_unhighlight" in element)
156 { 105 {
157 element._unhighlight(); 106 element._unhighlight();
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 if (element.id) 221 if (element.id)
273 return true; 222 return true;
274 if (element.classList.length > 0) 223 if (element.classList.length > 0)
275 return true; 224 return true;
276 if (getURLsFromElement(element).length > 0) 225 if (getURLsFromElement(element).length > 0)
277 return true; 226 return true;
278 227
279 // We only generate filters based on the "style" attribute, 228 // We only generate filters based on the "style" attribute,
280 // if this is the only way we can generate a filter, and 229 // if this is the only way we can generate a filter, and
281 // only if there are at least two CSS properties defined. 230 // only if there are at least two CSS properties defined.
282 if (/:.+:/.test(getOriginalStyle(element))) 231 if (/:.+:/.test(element.getAttribute("style")))
283 return true; 232 return true;
284 233
285 return false; 234 return false;
286 } 235 }
287 236
288 // Gets the absolute position of an element by walking up the DOM tree, 237 // Gets the absolute position of an element by walking up the DOM tree,
289 // adding up offsets. 238 // adding up offsets.
290 // I hope there's a better way because it just seems absolutely stupid 239 // 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 240 // 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. 241 // has hundreds and hundreds of other methods that do random junk.
293 function getAbsolutePosition(elt) { 242 function getAbsolutePosition(elt) {
294 var l = 0; 243 var l = 0;
295 var t = 0; 244 var t = 0;
296 for(; elt; elt = elt.offsetParent) { 245 for(; elt; elt = elt.offsetParent) {
297 l += elt.offsetLeft; 246 l += elt.offsetLeft;
298 t += elt.offsetTop; 247 t += elt.offsetTop;
299 } 248 }
300 return [l, t]; 249 return [l, t];
301 } 250 }
302 251
303 // Adds an overlay to an element, which is probably a Flash object 252 // Adds an overlay to an element, which is probably a Flash object
304 function addElementOverlay(elt) { 253 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 254 // 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. 255 // "display" property is "none"), the overlay wouldn't match the element.
316 if (!elt.offsetParent) 256 if (!elt.offsetParent)
317 return; 257 return;
318 258
319 var thisStyle = getComputedStyle(elt, null); 259 var thisStyle = getComputedStyle(elt, null);
320 var overlay = document.createElement('div'); 260 var overlay = document.createElement('div');
321 overlay.prisoner = elt; 261 overlay.prisoner = elt;
322 overlay.className = "__adblockplus__overlay"; 262 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;'); 263 overlay.setAttribute('style', 'opacity:0.4; display:inline-box; position:absol ute; overflow:hidden; box-sizing:border-box;');
kzar 2015/01/13 11:04:24 Are these changes relevant to the issue?
kzar 2015/01/13 13:26:34 What about these changes?
Sebastian Noack 2015/01/13 13:36:11 Kinda; it already prior to my changes here, it wer
324 var pos = getAbsolutePosition(elt); 264 var pos = getAbsolutePosition(elt);
265 overlay.style.width = elt.offsetWidth + "px";
266 overlay.style.height = elt.offsetHeight + "px";
325 overlay.style.left = pos[0] + "px"; 267 overlay.style.left = pos[0] + "px";
326 overlay.style.top = pos[1] + "px"; 268 overlay.style.top = pos[1] + "px";
327 269
328 if (thisStyle.position != "static") 270 if (thisStyle.position != "static")
329 overlay.style.zIndex = thisStyle.zIndex; 271 overlay.style.zIndex = thisStyle.zIndex;
330 else 272 else
331 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; 273 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex;
332 274
333 // elt.parentNode.appendChild(overlay, elt); 275 // elt.parentNode.appendChild(overlay, elt);
334 document.body.appendChild(overlay); 276 document.body.appendChild(overlay);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 315
374 // Turn on the choose element to create filter thing 316 // Turn on the choose element to create filter thing
375 function clickHide_activate() { 317 function clickHide_activate() {
376 if(document == null) 318 if(document == null)
377 return; 319 return;
378 320
379 // If we are already selecting, abort now 321 // If we are already selecting, abort now
380 if (clickHide_activated || clickHideFiltersDialog) 322 if (clickHide_activated || clickHideFiltersDialog)
381 clickHide_deactivate(); 323 clickHide_deactivate();
382 324
383 // Add overlays for elements with URLs so user can easily click them 325 // 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'); 326 var elts = document.querySelectorAll('object,embed,iframe');
385 for(var i=0; i<elts.length; i++) 327 for(var i=0; i<elts.length; i++)
386 addElementOverlay(elts[i]); 328 {
329 var element = elts[i];
330 if (isBlockable(element))
331 addElementOverlay(element);
332 }
387 333
388 clickHide_activated = true; 334 clickHide_activated = true;
389 document.addEventListener("mouseover", clickHide_mouseOver, true); 335 document.addEventListener("mouseover", clickHide_mouseOver, true);
390 document.addEventListener("mouseout", clickHide_mouseOut, true); 336 document.addEventListener("mouseout", clickHide_mouseOut, true);
391 document.addEventListener("click", clickHide_mouseClick, true); 337 document.addEventListener("click", clickHide_mouseClick, true);
392 document.addEventListener("keydown", clickHide_keyDown, true); 338 document.addEventListener("keydown", clickHide_keyDown, true);
393 } 339 }
394 340
395 // Called when user has clicked on something and we are waiting for confirmation 341 // Called when user has clicked on something and we are waiting for confirmation
396 // on whether the user actually wants these filters 342 // on whether the user actually wants these filters
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 continue; 492 continue;
547 } 493 }
548 494
549 if (url == elt.src) 495 if (url == elt.src)
550 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s rc")) + ']'); 496 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s rc")) + ']');
551 } 497 }
552 498
553 // as last resort, create a filter based on inline styles 499 // as last resort, create a filter based on inline styles
554 if (clickHideFilters.length == 0) 500 if (clickHideFilters.length == 0)
555 { 501 {
556 var style = getOriginalStyle(elt); 502 var style = elt.getAttribute("style");
557 if (style) 503 if (style)
558 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']'); 504 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']');
559 } 505 }
560 506
561 // Show popup 507 // Show popup
562 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters); 508 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters);
563 509
564 // Highlight the elements specified by selector in yellow 510 // Highlight the elements specified by selector in yellow
565 if (selectorList.length > 0) 511 if (selectorList.length > 0)
566 highlightElements(selectorList.join(",")); 512 highlightElements(selectorList.join(","));
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 case "clickhide-activate": 632 case "clickhide-activate":
687 clickHide_activate(); 633 clickHide_activate();
688 break; 634 break;
689 case "clickhide-deactivate": 635 case "clickhide-deactivate":
690 clickHide_deactivate(); 636 clickHide_deactivate();
691 break; 637 break;
692 case "clickhide-new-filter": 638 case "clickhide-new-filter":
693 if(lastRightClickEvent) 639 if(lastRightClickEvent)
694 { 640 {
695 clickHide_activated = true; 641 clickHide_activated = true;
696 currentElement = addElementOverlay(lastRightClickEvent.target); 642 currentElement = lastRightClickEvent.target;
697 clickHide_mouseClick(lastRightClickEvent); 643 clickHide_mouseClick(lastRightClickEvent);
698 } 644 }
699 break; 645 break;
700 case "clickhide-init": 646 case "clickhide-init":
701 if (clickHideFiltersDialog) 647 if (clickHideFiltersDialog)
702 { 648 {
703 sendResponse({filters: clickHide_filters}); 649 sendResponse({filters: clickHide_filters});
704 650
705 clickHideFiltersDialog.style.width = msg.width + "px"; 651 clickHideFiltersDialog.style.width = msg.width + "px";
706 clickHideFiltersDialog.style.height = msg.height + "px"; 652 clickHideFiltersDialog.style.height = msg.height + "px";
(...skipping 19 matching lines...) Expand all
726 break; 672 break;
727 default: 673 default:
728 sendResponse({}); 674 sendResponse({});
729 break; 675 break;
730 } 676 }
731 }); 677 });
732 678
733 if (window == window.top) 679 if (window == window.top)
734 ext.backgroundPage.sendMessage({type: "report-html-page"}); 680 ext.backgroundPage.sendMessage({type: "report-html-page"});
735 } 681 }
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