OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |