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

Delta Between Two Patch Sets: include.postload.js

Issue 5225119261655040: Issue 1282 - Don't generate filters conflicting with existing exception rules (Closed)
Left Patch Set: Created Dec. 15, 2014, 2:47 p.m.
Right Patch Set: Addressed comment Created March 3, 2015, 2:59 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 | « background.js ('k') | include.preload.js » ('j') | lib/filterComposer.js » ('J')
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 <https://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2015 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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 // Click-to-hide stuff 18 // Click-to-hide stuff
19 var clickHide_activated = false; 19 var clickHide_activated = false;
20 var clickHide_filters = null; 20 var clickHide_filters = null;
21 var currentElement = null; 21 var currentElement = null;
22 var clickHideFilters = null;
23 var highlightedElementsSelector = null; 22 var highlightedElementsSelector = null;
24 var clickHideFiltersDialog = null; 23 var clickHideFiltersDialog = null;
25 var lastRightClickEvent = null; 24 var lastRightClickEvent = null;
26 25 var lastRightClickEventValid = false;
27 function escapeChar(chr) 26 var lastMouseOverEvent = null;
28 {
29 var code = chr.charCodeAt(0);
30
31 // Control characters and leading digits must be escaped based on
32 // their char code in CSS. Moreover, curly brackets aren't allowed
33 // in elemhide filters, and therefore must be escaped based on their
34 // char code as well.
35 if (code <= 0x1F || code == 0x7F || /[\d\{\}]/.test(chr))
36 return "\\" + code.toString(16) + " ";
37
38 return "\\" + chr;
39 }
40
41 function quote(value)
42 {
43 return '"' + value.replace(/["\\\{\}\x00-\x1F\x7F]/g, escapeChar) + '"';
44 }
45
46 function escapeCSS(s)
47 {
48 return s.replace(/^[\d\-]|[^\w\-\u0080-\uFFFF]/g, escapeChar);
49 }
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 27
87 function highlightElement(element, shadowColor, backgroundColor) 28 function highlightElement(element, shadowColor, backgroundColor)
88 { 29 {
89 unhighlightElement(element); 30 unhighlightElement(element);
90 31
91 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shadow" ); 32 var highlightWithOverlay = function()
92 var originalBackgroundColorPriority = element.style.getPropertyPriority("backg round-color"); 33 {
93 34 var overlay = addElementOverlay(element);
94 var boxShadow = "inset 0px 0px 5px " + shadowColor; 35
95 36 // If the element isn't displayed no overlay will be added.
96 var highlightWithShadowDOM = function() 37 // Moreover, we don't need to highlight anything then.
97 { 38 if (!overlay)
98 var style = document.createElement("style"); 39 return;
99 style.textContent = ":host {" + 40
100 "box-shadow:" + boxShadow + " !important;" + 41 highlightElement(overlay, shadowColor, backgroundColor);
101 "background-color:" + backgroundColor + " !important;" + 42 overlay.style.pointerEvents = "none";
102 "}";
103
104 var root = element.createShadowRoot();
105 root.appendChild(document.createElement("shadow"));
106 root.appendChild(style);
107 43
108 element._unhighlight = function() 44 element._unhighlight = function()
109 { 45 {
110 root.removeChild(style); 46 overlay.parentNode.removeChild(overlay);
111 }; 47 };
112 }; 48 };
113 49
114 var highlightWithStyleAttribute = function() 50 var highlightWithStyleAttribute = function()
115 { 51 {
116 var originalBoxShadow = element.style.getPropertyValue("box-shadow"); 52 var originalBoxShadow = element.style.getPropertyValue("box-shadow");
53 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shado w");
117 var originalBackgroundColor = element.style.getPropertyValue("background-col or"); 54 var originalBackgroundColor = element.style.getPropertyValue("background-col or");
118 55 var originalBackgroundColorPriority = element.style.getPropertyPriority("bac kground-color");
119 element._originalStyle = getOriginalStyle(element); 56
120 57 element.style.setProperty("box-shadow", "inset 0px 0px 5px " + shadowColor, "important");
121 element.style.setProperty("box-shadow", boxShadow, "important");
122 element.style.setProperty("background-color", backgroundColor, "important"); 58 element.style.setProperty("background-color", backgroundColor, "important");
123 59
124 element._unhighlight = function() 60 element._unhighlight = function()
125 { 61 {
126 this.style.removeProperty("box-shadow"); 62 this.style.removeProperty("box-shadow");
127 this.style.setProperty( 63 this.style.setProperty(
128 "box-shadow", 64 "box-shadow",
129 originalBoxShadow, 65 originalBoxShadow,
130 originalBoxShadowPriority 66 originalBoxShadowPriority
131 ); 67 );
132 68
133 this.style.removeProperty("background-color"); 69 this.style.removeProperty("background-color");
134 this.style.setProperty( 70 this.style.setProperty(
135 "background-color", 71 "background-color",
136 originalBackgroundColor, 72 originalBackgroundColor,
137 originalBackgroundColorPriority 73 originalBackgroundColorPriority
138 ); 74 );
139 }; 75 };
140 }; 76 };
141 77
142 // Use shadow DOM if posibble to avoid side effects when the 78 if ("prisoner" in element)
143 // web page updates style while highlighted. However, if the 79 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 80 else
149 highlightWithStyleAttribute(); 81 highlightWithOverlay();
150 } 82 }
151 83
152 84
153 function unhighlightElement(element) 85 function unhighlightElement(element)
154 { 86 {
155 if ("_unhighlight" in element) 87 if ("_unhighlight" in element)
156 { 88 {
157 element._unhighlight(); 89 element._unhighlight();
158 delete element._unhighlight; 90 delete element._unhighlight;
159 } 91 }
(...skipping 22 matching lines...) Expand all
182 ); 114 );
183 115
184 highlightedElementsSelector = null; 116 highlightedElementsSelector = null;
185 } 117 }
186 } 118 }
187 119
188 function getURLsFromObjectElement(element) 120 function getURLsFromObjectElement(element)
189 { 121 {
190 var url = element.getAttribute("data"); 122 var url = element.getAttribute("data");
191 if (url) 123 if (url)
192 return [resolveURL(url)]; 124 return [url];
193 125
194 for (var i = 0; i < element.children.length; i++) 126 for (var i = 0; i < element.children.length; i++)
195 { 127 {
196 var child = element.children[i]; 128 var child = element.children[i];
197 if (child.localName != "param") 129 if (child.localName != "param")
198 continue; 130 continue;
199 131
200 var name = child.getAttribute("name"); 132 var name = child.getAttribute("name");
201 if (name != "movie" && // Adobe Flash 133 if (name != "movie" && // Adobe Flash
202 name != "source" && // Silverlight 134 name != "source" && // Silverlight
203 name != "src" && // Real Media + Quicktime 135 name != "src" && // Real Media + Quicktime
204 name != "FileName") // Windows Media 136 name != "FileName") // Windows Media
205 continue; 137 continue;
206 138
207 var value = child.getAttribute("value"); 139 var value = child.getAttribute("value");
208 if (!value) 140 if (!value)
209 continue; 141 continue;
210 142
211 return [resolveURL(value)]; 143 return [value];
212 } 144 }
213 145
214 return []; 146 return [];
215 } 147 }
216 148
217 function getURLsFromAttributes(element) 149 function getURLsFromAttributes(element)
218 { 150 {
219 var urls = []; 151 var urls = [];
220 152
221 if (element.src) 153 if (element.src)
222 urls.push(element.src); 154 urls.push(element.src);
223 155
224 if (element.srcset) 156 if (element.srcset)
225 { 157 {
226 var candidates = element.srcset.split(","); 158 var candidates = element.srcset.split(",");
227 for (var i = 0; i < candidates.length; i++) 159 for (var i = 0; i < candidates.length; i++)
228 { 160 {
229 var url = candidates[i].trim().replace(/\s+\S+$/, ""); 161 var url = candidates[i].trim().replace(/\s+\S+$/, "");
230 if (url) 162 if (url)
231 urls.push(resolveURL(url)); 163 urls.push(url);
232 } 164 }
233 } 165 }
234 166
235 return urls; 167 return urls;
236 } 168 }
237 169
238 function getURLsFromMediaElement(element) 170 function getURLsFromMediaElement(element)
239 { 171 {
240 var urls = getURLsFromAttributes(element); 172 var urls = getURLsFromAttributes(element);
241 173
(...skipping 18 matching lines...) Expand all
260 192
261 case "video": 193 case "video":
262 case "audio": 194 case "audio":
263 case "picture": 195 case "picture":
264 return getURLsFromMediaElement(element); 196 return getURLsFromMediaElement(element);
265 } 197 }
266 198
267 return getURLsFromAttributes(element); 199 return getURLsFromAttributes(element);
268 } 200 }
269 201
270 // Gets the absolute position of an element by walking up the DOM tree,
271 // adding up offsets.
272 // I hope there's a better way because it just seems absolutely stupid
273 // that the DOM wouldn't have a direct way to get this, given that it
274 // has hundreds and hundreds of other methods that do random junk.
275 function getAbsolutePosition(elt) {
276 var l = 0;
277 var t = 0;
278 for(; elt; elt = elt.offsetParent) {
279 l += elt.offsetLeft;
280 t += elt.offsetTop;
281 }
282 return [l, t];
283 }
284
285 // Adds an overlay to an element, which is probably a Flash object 202 // Adds an overlay to an element, which is probably a Flash object
286 function addElementOverlay(elt, callback) { 203 function addElementOverlay(elt) {
287 // If this element is enclosed in an object tag, we prefer to block that inste ad 204 var zIndex = "auto";
288 if(!elt) 205 var position = "absolute";
289 return; 206
290 207 for (var e = elt; e; e = e.parentElement)
291 // If the element isn't rendered (since its or one of its ancestor's 208 {
292 // "display" property is "none"), the overlay wouldn't match the element. 209 var style = getComputedStyle(e);
293 if (!elt.offsetParent) 210
294 return; 211 // If the element isn't rendered (since its or one of its ancestor's
295 212 // "display" property is "none"), the overlay wouldn't match the element.
296 generateFilters(elt, function(filters) 213 if (style.display == "none")
297 { 214 return null;
298 if (filters.length == 0) 215
299 return; 216 // If the element or one of its ancestors uses fixed postioning, the overlay
300 217 // has to use fixed postioning too. Otherwise it might not match the element .
301 var thisStyle = getComputedStyle(elt, null); 218 if (style.position == "fixed")
302 var overlay = document.createElement('div'); 219 position = "fixed";
303 overlay.prisoner = elt; 220
304 overlay.className = "__adblockplus__overlay"; 221 // Determine the effective z-index, which is the highest z-index used
305 overlay.setAttribute('style', 'opacity:0.4; background-color:#ffffff; displa y:inline-box; ' + 'width:' + thisStyle.width + '; height:' + thisStyle.height + '; position:absolute; overflow:hidden; -webkit-box-sizing:border-box;'); 222 // by the element and its offset ancestors, and increase it by one.
306 var pos = getAbsolutePosition(elt); 223 // When using a lower z-index the element would cover the overlay.
307 overlay.style.left = pos[0] + "px"; 224 // When using a higher z-index the overlay might also cover other elements.
308 overlay.style.top = pos[1] + "px"; 225 if (style.position != "static" && style.zIndex != "auto")
309 226 {
310 if (thisStyle.position != "static") 227 var curZIndex = parseInt(style.zIndex, 10) + 1;
311 overlay.style.zIndex = thisStyle.zIndex; 228
312 else 229 if (zIndex == "auto" || curZIndex > zIndex)
313 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; 230 zIndex = curZIndex;
314 231 }
315 document.body.appendChild(overlay); 232 }
316 233
317 if (callback) 234 var overlay = document.createElement('div');
318 callback(overlay); 235 overlay.prisoner = elt;
319 }); 236 overlay.className = "__adblockplus__overlay";
237 overlay.setAttribute('style', 'opacity:0.4; display:inline-box; overflow:hidde n; box-sizing:border-box;');
238 var rect = elt.getBoundingClientRect();
239 overlay.style.width = rect.width + "px";
240 overlay.style.height = rect.height + "px";
241 overlay.style.left = (rect.left + window.scrollX) + "px";
242 overlay.style.top = (rect.top + window.scrollY) + "px";
243 overlay.style.position = position;
244 overlay.style.zIndex = zIndex;
245
246 // elt.parentNode.appendChild(overlay, elt);
247 document.documentElement.appendChild(overlay);
248 return overlay;
320 } 249 }
321 250
322 // Show dialog asking user whether she wants to add the proposed filters derived 251 // Show dialog asking user whether she wants to add the proposed filters derived
323 // from selected page element 252 // from selected page element
324 function clickHide_showDialog(left, top, filters) 253 function clickHide_showDialog(left, top, filters)
325 { 254 {
326 // If we are already selecting, abort now 255 // If we are already selecting, abort now
327 if (clickHide_activated || clickHideFiltersDialog) 256 if (clickHide_activated || clickHideFiltersDialog)
328 clickHide_deactivate(true); 257 clickHide_deactivate(true);
329 258
330 clickHide_filters = filters; 259 clickHide_filters = filters;
331 260
332 clickHideFiltersDialog = document.createElement("iframe"); 261 clickHideFiltersDialog = document.createElement("iframe");
333 clickHideFiltersDialog.src = ext.getURL("block.html"); 262 clickHideFiltersDialog.src = ext.getURL("block.html");
334 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;"); 263 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;");
335 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)"; 264 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)";
336 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF; 265 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF;
337 266
338 // Position in upper-left all the time 267 // Position in upper-left all the time
339 clickHideFiltersDialog.style.left = "50px"; 268 clickHideFiltersDialog.style.left = "50px";
340 clickHideFiltersDialog.style.top = "50px"; 269 clickHideFiltersDialog.style.top = "50px";
341 270
342 // Make dialog partly transparent when mouse isn't over it so user has a bette r 271 // Make dialog partly transparent when mouse isn't over it so user has a bette r
343 // view of what's going to be blocked 272 // view of what's going to be blocked
344 clickHideFiltersDialog.onmouseout = function() 273 clickHideFiltersDialog.onmouseout = function()
345 { 274 {
346 if (clickHideFiltersDialog) 275 if (clickHideFiltersDialog)
347 clickHideFiltersDialog.style.setProperty("opacity", "0.7"); 276 clickHideFiltersDialog.style.setProperty("opacity", "0.7");
348 } 277 };
349 clickHideFiltersDialog.onmouseover = function() 278 clickHideFiltersDialog.onmouseover = function()
350 { 279 {
351 if (clickHideFiltersDialog) 280 if (clickHideFiltersDialog)
352 clickHideFiltersDialog.style.setProperty("opacity", "1.0"); 281 clickHideFiltersDialog.style.setProperty("opacity", "1.0");
353 } 282 };
354 283
355 document.body.appendChild(clickHideFiltersDialog); 284 document.documentElement.appendChild(clickHideFiltersDialog);
356 } 285 }
357 286
358 // Turn on the choose element to create filter thing 287 // Turn on the choose element to create filter thing
359 function clickHide_activate() { 288 function clickHide_activate() {
360 if(document == null) 289 if(document == null)
361 return; 290 return;
362 291
363 // If we are already selecting, abort now 292 // If we are already selecting, abort now
364 if (clickHide_activated || clickHideFiltersDialog) 293 if (clickHide_activated || clickHideFiltersDialog)
365 clickHide_deactivate(); 294 clickHide_deactivate();
366 295
367 // Add overlays for elements with URLs so user can easily click them 296 // Add overlays for blockable elements that don't emit mouse events,
368 var elts = document.querySelectorAll('object,embed,img,iframe,video,audio,pict ure'); 297 // so that they can still be selected.
369 for(var i=0; i<elts.length; i++) 298 [].forEach.call(
370 addElementOverlay(elts[i]); 299 document.querySelectorAll('object,embed,iframe,frame'),
300 function(element)
301 {
302 getFiltersForElement(element, function(filters)
303 {
304 if (filters.length > 0)
305 addElementOverlay(element);
306 });
307 }
308 );
371 309
372 clickHide_activated = true; 310 clickHide_activated = true;
311 document.addEventListener("mousedown", clickHide_stopPropagation, true);
312 document.addEventListener("mouseup", clickHide_stopPropagation, true);
313 document.addEventListener("mouseenter", clickHide_stopPropagation, true);
314 document.addEventListener("mouseleave", clickHide_stopPropagation, true);
373 document.addEventListener("mouseover", clickHide_mouseOver, true); 315 document.addEventListener("mouseover", clickHide_mouseOver, true);
374 document.addEventListener("mouseout", clickHide_mouseOut, true); 316 document.addEventListener("mouseout", clickHide_mouseOut, true);
375 document.addEventListener("click", clickHide_mouseClick, true); 317 document.addEventListener("click", clickHide_mouseClick, true);
376 document.addEventListener("keydown", clickHide_keyDown, true); 318 document.addEventListener("keydown", clickHide_keyDown, true);
319
320 ext.onExtensionUnloaded.addListener(clickHide_deactivate);
377 } 321 }
378 322
379 // Called when user has clicked on something and we are waiting for confirmation 323 // Called when user has clicked on something and we are waiting for confirmation
380 // on whether the user actually wants these filters 324 // on whether the user actually wants these filters
381 function clickHide_rulesPending() { 325 function clickHide_rulesPending() {
382 clickHide_activated = false; 326 clickHide_activated = false;
327 document.removeEventListener("mousedown", clickHide_stopPropagation, true);
328 document.removeEventListener("mouseup", clickHide_stopPropagation, true);
329 document.removeEventListener("mouseenter", clickHide_stopPropagation, true);
330 document.removeEventListener("mouseleave", clickHide_stopPropagation, true);
383 document.removeEventListener("mouseover", clickHide_mouseOver, true); 331 document.removeEventListener("mouseover", clickHide_mouseOver, true);
384 document.removeEventListener("mouseout", clickHide_mouseOut, true); 332 document.removeEventListener("mouseout", clickHide_mouseOut, true);
385 document.removeEventListener("click", clickHide_mouseClick, true); 333 document.removeEventListener("click", clickHide_mouseClick, true);
386 document.removeEventListener("keydown", clickHide_keyDown, true); 334 document.removeEventListener("keydown", clickHide_keyDown, true);
387 } 335 }
388 336
389 // Turn off click-to-hide 337 // Turn off click-to-hide
390 function clickHide_deactivate(keepOverlays) 338 function clickHide_deactivate(keepOverlays)
391 { 339 {
392 if (clickHideFiltersDialog) 340 if (clickHideFiltersDialog)
393 { 341 {
394 document.body.removeChild(clickHideFiltersDialog); 342 document.documentElement.removeChild(clickHideFiltersDialog);
395 clickHideFiltersDialog = null; 343 clickHideFiltersDialog = null;
396 } 344 }
397 345
398 clickHide_activated = false; 346 clickHide_activated = false;
399 clickHide_filters = null; 347 clickHide_filters = null;
400 if(!document) 348 if(!document)
401 return; // This can happen inside a nuked iframe...I think 349 return; // This can happen inside a nuked iframe...I think
350
351 document.removeEventListener("mousedown", clickHide_stopPropagation, true);
352 document.removeEventListener("mouseup", clickHide_stopPropagation, true);
353 document.removeEventListener("mouseenter", clickHide_stopPropagation, true);
354 document.removeEventListener("mouseleave", clickHide_stopPropagation, true);
402 document.removeEventListener("mouseover", clickHide_mouseOver, true); 355 document.removeEventListener("mouseover", clickHide_mouseOver, true);
403 document.removeEventListener("mouseout", clickHide_mouseOut, true); 356 document.removeEventListener("mouseout", clickHide_mouseOut, true);
404 document.removeEventListener("click", clickHide_mouseClick, true); 357 document.removeEventListener("click", clickHide_mouseClick, true);
405 document.removeEventListener("keydown", clickHide_keyDown, true); 358 document.removeEventListener("keydown", clickHide_keyDown, true);
406 359
407 if (!keepOverlays) 360 if (keepOverlays !== true)
408 { 361 {
362 lastRightClickEvent = null;
363
409 if (currentElement) { 364 if (currentElement) {
410 currentElement.removeEventListener("contextmenu", clickHide_elementClickH andler, true); 365 currentElement.removeEventListener("contextmenu", clickHide_elementClickH andler, true);
411 unhighlightElements(); 366 unhighlightElements();
412 unhighlightElement(currentElement); 367 unhighlightElement(currentElement);
413 currentElement = null; 368 currentElement = null;
414 clickHideFilters = null;
415 } 369 }
416 unhighlightElements(); 370 unhighlightElements();
417 371
418 var overlays = document.getElementsByClassName("__adblockplus__overlay"); 372 var overlays = document.getElementsByClassName("__adblockplus__overlay");
419 while (overlays.length > 0) 373 while (overlays.length > 0)
420 overlays[0].parentNode.removeChild(overlays[0]); 374 overlays[0].parentNode.removeChild(overlays[0]);
421 } 375
422 } 376 ext.onExtensionUnloaded.removeListener(clickHide_deactivate);
423 377 }
424 function clickHide_elementClickHandler(ev) { 378 }
425 ev.preventDefault(); 379
426 ev.stopPropagation(); 380 function clickHide_stopPropagation(e)
427 clickHide_mouseClick(ev); 381 {
382 e.stopPropagation();
383 }
384
385 function clickHide_elementClickHandler(e) {
386 e.preventDefault();
387 e.stopPropagation();
388 clickHide_mouseClick(e);
428 } 389 }
429 390
430 function getBlockableElementOrAncestor(element, callback) 391 function getBlockableElementOrAncestor(element, callback)
431 { 392 {
432 if (element && element != document.documentElement 393 // We assume that the user doesn't want to block the whole page.
433 && element != document.body) 394 // So we never consider the <html> or <body> element.
434 { 395 while (element && element != document.documentElement
435 generateFilters(element, function(filters) 396 && element != document.body)
436 { 397 {
437 if (filters.length > 0) 398 // We can't handle non-HTML (like SVG) elements, as well as
438 callback(element); 399 // <area> elements (see below). So fall back to the parent element.
439 else 400 if (!(element instanceof HTMLElement) || element.localName == "area")
440 getBlockableElementOrAncestor(element.parentElement, callback); 401 element = element.parentElement;
441 }); 402
442 } 403 // If image maps are used mouse events occur for the <area> element.
443 else 404 // But we have to block the image associated with the <map> element.
444 { 405 else if (element.localName == "map")
445 callback(null); 406 {
446 } 407 var images = document.querySelectorAll("img[usemap]");
408 var image = null;
409
410 for (var i = 0; i < images.length; i++)
411 {
412 var usemap = image.getAttribute("usemap");
413 var index = usemap.indexOf("#");
414
415 if (index != -1 && usemap.substr(index + 1) == element.name)
416 {
417 image = images[i];
418 break;
419 }
420 }
421
422 element = image;
423 }
424
425 // Finally, if none of the above is true, check whether we can generate
426 // any filters for this element. Otherwise fall back to its parent element.
427 else
428 {
429 getFiltersForElement(element, function(filters)
430 {
431 if (filters.length > 0)
432 callback(element);
433 else
434 getBlockableElementOrAncestor(element.parentElement, callback);
435 });
436
437 return;
438 }
439 }
440
441 // We reached the document root without finding a blockable element.
442 callback(null);
447 } 443 }
448 444
449 // Hovering over an element so highlight it 445 // Hovering over an element so highlight it
450 function clickHide_mouseOver(e) 446 function clickHide_mouseOver(e)
451 { 447 {
452 if (clickHide_activated == false) 448 lastMouseOverEvent = e;
453 return;
454 449
455 getBlockableElementOrAncestor(e.target, function(element) 450 getBlockableElementOrAncestor(e.target, function(element)
456 { 451 {
457 if (currentElement) 452 if (e == lastMouseOverEvent)
458 unhighlightElement(currentElement); 453 {
459 454 lastMouseOverEvent = null;
460 if (element) 455
461 { 456 if (clickHide_activated)
462 highlightElement(element, "#d6d84b", "#f8fa47"); 457 {
463 element.addEventListener("contextmenu", clickHide_elementClickHandler, tru e); 458 if (currentElement)
464 } 459 unhighlightElement(currentElement);
465 460
466 currentElement = element; 461 if (element)
462 {
463 highlightElement(element, "#d6d84b", "#f8fa47");
464 element.addEventListener("contextmenu", clickHide_elementClickHandler, true);
465 }
466
467 currentElement = element;
468 }
469 }
467 }); 470 });
471
472 e.stopPropagation();
468 } 473 }
469 474
470 // No longer hovering over this element so unhighlight it 475 // No longer hovering over this element so unhighlight it
471 function clickHide_mouseOut(e) 476 function clickHide_mouseOut(e)
472 { 477 {
473 if (!clickHide_activated || currentElement != e.target) 478 if (!clickHide_activated || currentElement != e.target)
474 return; 479 return;
475 480
476 unhighlightElement(currentElement); 481 unhighlightElement(currentElement);
477 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true); 482 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true);
483 e.stopPropagation();
478 } 484 }
479 485
480 // Selects the currently hovered-over filter or cancels selection 486 // Selects the currently hovered-over filter or cancels selection
481 function clickHide_keyDown(e) 487 function clickHide_keyDown(e)
482 { 488 {
483 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN* /) 489 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN* /)
484 clickHide_mouseClick(e); 490 clickHide_mouseClick(e);
485 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES CAPE*/) 491 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES CAPE*/)
486 { 492 {
487 ext.backgroundPage.sendMessage( 493 ext.backgroundPage.sendMessage(
488 { 494 {
489 type: "forward", 495 type: "forward",
490 payload: 496 payload:
491 { 497 {
492 type: "clickhide-deactivate" 498 type: "clickhide-deactivate"
493 } 499 }
494 }); 500 });
495 e.preventDefault(); 501 e.preventDefault();
496 e.stopPropagation(); 502 e.stopPropagation();
497 } 503 }
498 } 504 }
499 505
500 function generateFilters(element, callback) 506 function getFiltersForElement(element, callback)
501 { 507 {
502 function addStyleAttributeFilter(filters, selectors) 508 ext.backgroundPage.sendMessage(
503 { 509 {
504 var style = getOriginalStyle(element); 510 type: "compose-filters",
505 if (style && filters.length == 0) 511 tagName: element.localName,
506 { 512 id: element.id,
507 ext.backgroundPage.sendMessage( 513 src: element.getAttribute("src"),
508 { 514 style: element.getAttribute("style"),
509 type: "get-filters-from-selectors", 515 classes: [].slice.call(element.classList),
510 selectors: [escapeCSS(element.localName) + '[style=' + quote(style) + ']'] 516 urls: getURLsFromElement(element),
511 }, 517 mediatype: typeMap[element.localName],
512 518 baseURL: document.location.href
513 function(response) 519 },
514 { 520 function(response)
515 callback(filters.concat(response.filters), selectors.concat(response.s electors)); 521 {
516 } 522 callback(response.filters, response.selectors);
517 ); 523 }
518 } 524 );
519 else
520 callback(filters, selectors);
521 }
522
523 function addElemHideFilter(filters, selectors)
524 {
525 if (selectors.length > 0)
526 {
527 ext.backgroundPage.sendMessage(
528 {
529 type: "get-filters-from-selectors",
530 selectors: selectors
531 },
532
533 function(response)
534 {
535 addStyleAttributeFilter(filters.concat(response.filters), response.sel ectors);
536 }
537 );
538 }
539 else
540 addStyleAttributeFilter(filters, selectors);
541 }
542
543 var filters = [];
544 var selectors = [];
545
546 if (element.id)
547 selectors.push("#" + escapeCSS(element.id));
548
549 if (element.classList.length > 0)
550 {
551 var selector = "";
552
553 for (var i = 0; i < element.classList.length; i++)
554 selector += "." + escapeCSS(element.classList[i]);
555
556 selectors.push(selector);
557 }
558
559 var urls = getURLsFromElement(element);
560 if (urls.length > 0)
561 {
562 ext.backgroundPage.sendMessage(
563 {
564 type: "check-whitelisted-urls",
565 mediatype: typeMap[element.localName],
566 urls: urls
567 },
568
569 function(whitelisted)
570 {
571 for (var i = 0; i < urls.length; i++)
572 {
573 var url = urls[i];
574
575 if (!whitelisted[url] && /^https?:/i.test(url))
576 {
577 var filter = url.replace(/^[\w\-]+:\/+(?:www\.)?/, "||");
578
579 if (filters.indexOf(filter) == -1)
580 filters.push(filter);
581
582 continue;
583 }
584
585 if (url == element.src)
586 {
587 var selector = escapeCSS(element.localName) + '[src=' + quote(elemen t.getAttribute("src")) + ']';
588
589 if (selectors.indexOf(selector) == -1)
590 selectors.push(selector);
591 }
592 }
593
594 addElemHideFilter(filters, selectors);
595 }
596 );
597 }
598 else
599 addElemHideFilter(filters, selectors);
600 } 525 }
601 526
602 // When the user clicks, the currentElement is the one we want. 527 // When the user clicks, the currentElement is the one we want.
603 // We should have ABP rules ready for when the 528 // We should have ABP rules ready for when the
604 // popup asks for them. 529 // popup asks for them.
605 function clickHide_mouseClick(e) 530 function clickHide_mouseClick(e)
606 { 531 {
607 if (!currentElement || !clickHide_activated) 532 if (!currentElement || !clickHide_activated)
608 return; 533 return;
609 534
610 var elt = currentElement; 535 var elt = currentElement;
611 if (currentElement.classList.contains("__adblockplus__overlay")) 536 if (currentElement.classList.contains("__adblockplus__overlay"))
612 elt = currentElement.prisoner; 537 elt = currentElement.prisoner;
613 538
614 generateFilters(elt, function(filters, selectors) 539 getFiltersForElement(elt, function(filters, selectors)
615 { 540 {
616 clickHide_showDialog(e.clientX, e.clientY, filters); 541 ext.backgroundPage.sendMessage(
617 542 {
618 // Highlight the elements specified by selector in yellow 543 type: "forward",
544 payload:
545 {
546 type: "clickhide-show-dialog",
547 screenX: e.screenX,
548 screenY: e.screenY,
549 clickHideFilters: filters
550 }
551 });
552
619 if (selectors.length > 0) 553 if (selectors.length > 0)
620 highlightElements(selectors.join(",")); 554 highlightElements(selectors.join(","));
621 555
622 // Now, actually highlight the element the user clicked on in red
623 highlightElement(currentElement, "#fd1708", "#f6a1b5"); 556 highlightElement(currentElement, "#fd1708", "#f6a1b5");
624 }); 557 });
625 558
626 // Make sure the browser doesn't handle this click 559 // Make sure the browser doesn't handle this click
627 e.preventDefault(); 560 e.preventDefault();
628 e.stopPropagation(); 561 e.stopPropagation();
629 } 562 }
630 563
631 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js 564 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js
632 // and licensed under the MIT license. See jquery-*.min.js for details. 565 // and licensed under the MIT license. See jquery-*.min.js for details.
(...skipping 27 matching lines...) Expand all
660 // In Chrome 37-40, the document_end content script (this one) runs properly, wh ile the 593 // In Chrome 37-40, the document_end content script (this one) runs properly, wh ile the
661 // document_start content scripts (that defines ext) might not. Check whether va riable ext 594 // document_start content scripts (that defines ext) might not. Check whether va riable ext
662 // exists before continuing to avoid "Uncaught ReferenceError: ext is not define d". 595 // exists before continuing to avoid "Uncaught ReferenceError: ext is not define d".
663 // See https://crbug.com/416907 596 // See https://crbug.com/416907
664 if ("ext" in window && document instanceof HTMLDocument) 597 if ("ext" in window && document instanceof HTMLDocument)
665 { 598 {
666 // Use a contextmenu handler to save the last element the user right-clicked o n. 599 // Use a contextmenu handler to save the last element the user right-clicked o n.
667 // To make things easier, we actually save the DOM event. 600 // To make things easier, we actually save the DOM event.
668 // We have to do this because the contextMenu API only provides a URL, not the actual 601 // We have to do this because the contextMenu API only provides a URL, not the actual
669 // DOM element. 602 // DOM element.
670 document.addEventListener('contextmenu', function(e) { 603 document.addEventListener('contextmenu', function(e)
604 {
671 lastRightClickEvent = e; 605 lastRightClickEvent = e;
606 // We also need to ensure any old lastRightClickEvent variables in other
607 // frames are cleared.
608 lastRightClickEventValid = true;
609 ext.backgroundPage.sendMessage(
610 {
611 type: "forward",
612 payload:
613 {
614 type: "clickhide-clear-last-right-click-event"
615 }
616 });
672 }, true); 617 }, true);
673 618
674 document.addEventListener("click", function(event) 619 document.addEventListener("click", function(event)
675 { 620 {
676 // Ignore right-clicks 621 // Ignore right-clicks
677 if (event.button == 2) 622 if (event.button == 2)
678 return; 623 return;
679 624
680 // Search the link associated with the click 625 // Search the link associated with the click
681 var link = event.target; 626 var link = event.target;
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
741 break; 686 break;
742 case "clickhide-activate": 687 case "clickhide-activate":
743 clickHide_activate(); 688 clickHide_activate();
744 break; 689 break;
745 case "clickhide-deactivate": 690 case "clickhide-deactivate":
746 clickHide_deactivate(); 691 clickHide_deactivate();
747 break; 692 break;
748 case "clickhide-new-filter": 693 case "clickhide-new-filter":
749 if(lastRightClickEvent) 694 if(lastRightClickEvent)
750 { 695 {
751 var event = lastRightClickEvent; 696 getBlockableElementOrAncestor(lastRightClickEvent.target, function(ele ment)
752
753 addElementOverlay(event.target, function(overlay)
754 { 697 {
755 clickHide_activated = true; 698 clickHide_activated = true;
756 currentElement = overlay; 699 currentElement = element;
757 clickHide_mouseClick(event); 700 clickHide_mouseClick(lastRightClickEvent);
758 }); 701 });
759 } 702 }
760 break; 703 break;
761 case "clickhide-init": 704 case "clickhide-init":
762 if (clickHideFiltersDialog) 705 if (clickHideFiltersDialog)
763 { 706 {
764 sendResponse({filters: clickHide_filters}); 707 sendResponse({filters: clickHide_filters});
765 708
766 clickHideFiltersDialog.style.width = msg.width + "px"; 709 clickHideFiltersDialog.style.width = msg.width + "px";
767 clickHideFiltersDialog.style.height = msg.height + "px"; 710 clickHideFiltersDialog.style.height = msg.height + "px";
768 clickHideFiltersDialog.style.visibility = "visible"; 711 clickHideFiltersDialog.style.visibility = "visible";
769 } 712 }
770 break; 713 break;
771 case "clickhide-move": 714 case "clickhide-move":
772 if (clickHideFiltersDialog) 715 if (clickHideFiltersDialog)
773 { 716 {
774 clickHideFiltersDialog.style.left = (parseInt(clickHideFiltersDialog.s tyle.left, 10) + msg.x) + "px"; 717 clickHideFiltersDialog.style.left = (parseInt(clickHideFiltersDialog.s tyle.left, 10) + msg.x) + "px";
775 clickHideFiltersDialog.style.top = (parseInt(clickHideFiltersDialog.st yle.top, 10) + msg.y) + "px"; 718 clickHideFiltersDialog.style.top = (parseInt(clickHideFiltersDialog.st yle.top, 10) + msg.y) + "px";
776 } 719 }
777 break; 720 break;
778 case "clickhide-close": 721 case "clickhide-close":
779 if (clickHideFiltersDialog && msg.remove) 722 if (currentElement && msg.remove)
780 { 723 {
781 // Explicitly get rid of currentElement 724 // Explicitly get rid of currentElement
782 var element = currentElement.prisoner || currentElement; 725 var element = currentElement.prisoner || currentElement;
783 if (element && element.parentNode) 726 if (element && element.parentNode)
784 element.parentNode.removeChild(element); 727 element.parentNode.removeChild(element);
785 } 728 }
786 clickHide_deactivate(); 729 clickHide_deactivate();
787 break; 730 break;
788 default: 731 case "clickhide-show-dialog":
789 sendResponse({}); 732 if (window.self == window.top)
733 clickHide_showDialog(msg.screenX + window.pageXOffset,
734 msg.screenY + window.pageYOffset,
735 msg.clickHideFilters);
736 break;
737 case "clickhide-clear-last-right-click-event":
738 if (lastRightClickEventValid)
739 lastRightClickEventValid = false;
740 else
741 lastRightClickEvent = null;
790 break; 742 break;
791 } 743 }
792 }); 744 });
793 745
794 if (window == window.top) 746 if (window == window.top)
795 ext.backgroundPage.sendMessage({type: "report-html-page"}); 747 ext.backgroundPage.sendMessage({type: "report-html-page"});
796 } 748 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld