OLD | NEW |
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-2015 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 highlightedElementsSelector = null; | 22 var highlightedElementsSelector = null; |
23 var clickHideFiltersDialog = null; | 23 var clickHideFiltersDialog = null; |
24 var lastRightClickEvent = null; | 24 var lastRightClickEvent = null; |
25 var lastRightClickEventValid = false; | 25 var lastRightClickEventValid = false; |
26 var lastMouseOverEvent = null; | |
27 | 26 |
28 function highlightElement(element, shadowColor, backgroundColor) | 27 function highlightElement(element, shadowColor, backgroundColor) |
29 { | 28 { |
30 unhighlightElement(element); | 29 unhighlightElement(element); |
31 | 30 |
32 var highlightWithOverlay = function() | 31 var highlightWithOverlay = function() |
33 { | 32 { |
34 var overlay = addElementOverlay(element); | 33 var overlay = addElementOverlay(element); |
35 | 34 |
36 // If the element isn't displayed no overlay will be added. | 35 // If the element isn't displayed no overlay will be added. |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 | 191 |
193 case "video": | 192 case "video": |
194 case "audio": | 193 case "audio": |
195 case "picture": | 194 case "picture": |
196 return getURLsFromMediaElement(element); | 195 return getURLsFromMediaElement(element); |
197 } | 196 } |
198 | 197 |
199 return getURLsFromAttributes(element); | 198 return getURLsFromAttributes(element); |
200 } | 199 } |
201 | 200 |
| 201 function isBlockable(element) |
| 202 { |
| 203 if (element.id) |
| 204 return true; |
| 205 if (element.classList.length > 0) |
| 206 return true; |
| 207 if (getURLsFromElement(element).length > 0) |
| 208 return true; |
| 209 |
| 210 // We only generate filters based on the "style" attribute, |
| 211 // if this is the only way we can generate a filter, and |
| 212 // only if there are at least two CSS properties defined. |
| 213 if (/:.+:/.test(element.getAttribute("style"))) |
| 214 return true; |
| 215 |
| 216 return false; |
| 217 } |
| 218 |
202 // Adds an overlay to an element, which is probably a Flash object | 219 // Adds an overlay to an element, which is probably a Flash object |
203 function addElementOverlay(elt) { | 220 function addElementOverlay(elt) { |
204 var zIndex = "auto"; | 221 var zIndex = "auto"; |
205 var position = "absolute"; | 222 var position = "absolute"; |
206 | 223 |
207 for (var e = elt; e; e = e.parentElement) | 224 for (var e = elt; e; e = e.parentElement) |
208 { | 225 { |
209 var style = getComputedStyle(e); | 226 var style = getComputedStyle(e); |
210 | 227 |
211 // If the element isn't rendered (since its or one of its ancestor's | 228 // If the element isn't rendered (since its or one of its ancestor's |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 function clickHide_activate() { | 305 function clickHide_activate() { |
289 if(document == null) | 306 if(document == null) |
290 return; | 307 return; |
291 | 308 |
292 // If we are already selecting, abort now | 309 // If we are already selecting, abort now |
293 if (clickHide_activated || clickHideFiltersDialog) | 310 if (clickHide_activated || clickHideFiltersDialog) |
294 clickHide_deactivate(); | 311 clickHide_deactivate(); |
295 | 312 |
296 // Add overlays for blockable elements that don't emit mouse events, | 313 // Add overlays for blockable elements that don't emit mouse events, |
297 // so that they can still be selected. | 314 // so that they can still be selected. |
298 [].forEach.call( | 315 var elts = document.querySelectorAll('object,embed,iframe,frame'); |
299 document.querySelectorAll('object,embed,iframe,frame'), | 316 for(var i=0; i<elts.length; i++) |
300 function(element) | 317 { |
301 { | 318 var element = elts[i]; |
302 getFiltersForElement(element, function(filters) | 319 if (isBlockable(element)) |
303 { | 320 addElementOverlay(element); |
304 if (filters.length > 0) | 321 } |
305 addElementOverlay(element); | |
306 }); | |
307 } | |
308 ); | |
309 | 322 |
310 clickHide_activated = true; | 323 clickHide_activated = true; |
311 document.addEventListener("mousedown", clickHide_stopPropagation, true); | 324 document.addEventListener("mousedown", clickHide_stopPropagation, true); |
312 document.addEventListener("mouseup", clickHide_stopPropagation, true); | 325 document.addEventListener("mouseup", clickHide_stopPropagation, true); |
313 document.addEventListener("mouseenter", clickHide_stopPropagation, true); | 326 document.addEventListener("mouseenter", clickHide_stopPropagation, true); |
314 document.addEventListener("mouseleave", clickHide_stopPropagation, true); | 327 document.addEventListener("mouseleave", clickHide_stopPropagation, true); |
315 document.addEventListener("mouseover", clickHide_mouseOver, true); | 328 document.addEventListener("mouseover", clickHide_mouseOver, true); |
316 document.addEventListener("mouseout", clickHide_mouseOut, true); | 329 document.addEventListener("mouseout", clickHide_mouseOut, true); |
317 document.addEventListener("click", clickHide_mouseClick, true); | 330 document.addEventListener("click", clickHide_mouseClick, true); |
318 document.addEventListener("keydown", clickHide_keyDown, true); | 331 document.addEventListener("keydown", clickHide_keyDown, true); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 { | 394 { |
382 e.stopPropagation(); | 395 e.stopPropagation(); |
383 } | 396 } |
384 | 397 |
385 function clickHide_elementClickHandler(e) { | 398 function clickHide_elementClickHandler(e) { |
386 e.preventDefault(); | 399 e.preventDefault(); |
387 e.stopPropagation(); | 400 e.stopPropagation(); |
388 clickHide_mouseClick(e); | 401 clickHide_mouseClick(e); |
389 } | 402 } |
390 | 403 |
391 function getBlockableElementOrAncestor(element, callback) | 404 function getBlockableElementOrAncestor(element) |
392 { | 405 { |
393 // We assume that the user doesn't want to block the whole page. | |
394 // So we never consider the <html> or <body> element. | |
395 while (element && element != document.documentElement | 406 while (element && element != document.documentElement |
396 && element != document.body) | 407 && element != document.body) |
397 { | 408 { |
398 // We can't handle non-HTML (like SVG) elements, as well as | 409 if (element instanceof HTMLElement && element.localName != "area") |
399 // <area> elements (see below). So fall back to the parent element. | 410 { |
400 if (!(element instanceof HTMLElement) || element.localName == "area") | 411 // Handle <area> and their <map> elements specially, |
401 element = element.parentElement; | 412 // blocking the image they are associated with |
| 413 if (element.localName == "map") |
| 414 { |
| 415 var images = document.querySelectorAll("img[usemap]"); |
| 416 for (var i = 0; i < images.length; i++) |
| 417 { |
| 418 var image = images[i]; |
| 419 var usemap = image.getAttribute("usemap"); |
| 420 var index = usemap.indexOf("#"); |
402 | 421 |
403 // If image maps are used mouse events occur for the <area> element. | 422 if (index != -1 && usemap.substr(index + 1) == element.name) |
404 // But we have to block the image associated with the <map> element. | 423 return getBlockableElementOrAncestor(image); |
405 else if (element.localName == "map") | 424 } |
406 { | |
407 var images = document.querySelectorAll("img[usemap]"); | |
408 var image = null; | |
409 | 425 |
410 for (var i = 0; i < images.length; i++) | 426 return null; |
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 } | 427 } |
421 | 428 |
422 element = image; | 429 if (isBlockable(element)) |
| 430 return element; |
423 } | 431 } |
424 | 432 |
425 // Finally, if none of the above is true, check whether we can generate | 433 element = element.parentElement; |
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 } | 434 } |
440 | 435 |
441 // We reached the document root without finding a blockable element. | 436 return null; |
442 callback(null); | |
443 } | 437 } |
444 | 438 |
445 // Hovering over an element so highlight it | 439 // Hovering over an element so highlight it |
446 function clickHide_mouseOver(e) | 440 function clickHide_mouseOver(e) |
447 { | 441 { |
448 lastMouseOverEvent = e; | 442 if (clickHide_activated == false) |
| 443 return; |
449 | 444 |
450 getBlockableElementOrAncestor(e.target, function(element) | 445 var target = getBlockableElementOrAncestor(e.target); |
| 446 |
| 447 if (target) |
451 { | 448 { |
452 if (e == lastMouseOverEvent) | 449 currentElement = target; |
453 { | |
454 lastMouseOverEvent = null; | |
455 | 450 |
456 if (clickHide_activated) | 451 highlightElement(target, "#d6d84b", "#f8fa47"); |
457 { | 452 target.addEventListener("contextmenu", clickHide_elementClickHandler, true); |
458 if (currentElement) | 453 } |
459 unhighlightElement(currentElement); | |
460 | |
461 if (element) | |
462 { | |
463 highlightElement(element, "#d6d84b", "#f8fa47"); | |
464 element.addEventListener("contextmenu", clickHide_elementClickHandler,
true); | |
465 } | |
466 | |
467 currentElement = element; | |
468 } | |
469 } | |
470 }); | |
471 | |
472 e.stopPropagation(); | 454 e.stopPropagation(); |
473 } | 455 } |
474 | 456 |
475 // No longer hovering over this element so unhighlight it | 457 // No longer hovering over this element so unhighlight it |
476 function clickHide_mouseOut(e) | 458 function clickHide_mouseOut(e) |
477 { | 459 { |
478 if (!clickHide_activated || currentElement != e.target) | 460 if (!clickHide_activated || !currentElement) |
479 return; | 461 return; |
480 | 462 |
481 unhighlightElement(currentElement); | 463 unhighlightElement(currentElement); |
482 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle
r, true); | 464 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle
r, true); |
483 e.stopPropagation(); | 465 e.stopPropagation(); |
484 } | 466 } |
485 | 467 |
486 // Selects the currently hovered-over filter or cancels selection | 468 // Selects the currently hovered-over filter or cancels selection |
487 function clickHide_keyDown(e) | 469 function clickHide_keyDown(e) |
488 { | 470 { |
(...skipping 18 matching lines...) Expand all Loading... |
507 { | 489 { |
508 ext.backgroundPage.sendMessage( | 490 ext.backgroundPage.sendMessage( |
509 { | 491 { |
510 type: "compose-filters", | 492 type: "compose-filters", |
511 tagName: element.localName, | 493 tagName: element.localName, |
512 id: element.id, | 494 id: element.id, |
513 src: element.getAttribute("src"), | 495 src: element.getAttribute("src"), |
514 style: element.getAttribute("style"), | 496 style: element.getAttribute("style"), |
515 classes: [].slice.call(element.classList), | 497 classes: [].slice.call(element.classList), |
516 urls: getURLsFromElement(element), | 498 urls: getURLsFromElement(element), |
517 mediatype: typeMap[element.localName], | |
518 baseURL: document.location.href | 499 baseURL: document.location.href |
519 }, | 500 }, |
520 function(response) | 501 function(response) |
521 { | 502 { |
522 callback(response.filters, response.selectors); | 503 callback(response.filters, response.selectors); |
523 } | 504 } |
524 ); | 505 ); |
525 } | 506 } |
526 | 507 |
527 // When the user clicks, the currentElement is the one we want. | 508 // When the user clicks, the currentElement is the one we want. |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
686 break; | 667 break; |
687 case "clickhide-activate": | 668 case "clickhide-activate": |
688 clickHide_activate(); | 669 clickHide_activate(); |
689 break; | 670 break; |
690 case "clickhide-deactivate": | 671 case "clickhide-deactivate": |
691 clickHide_deactivate(); | 672 clickHide_deactivate(); |
692 break; | 673 break; |
693 case "clickhide-new-filter": | 674 case "clickhide-new-filter": |
694 if(lastRightClickEvent) | 675 if(lastRightClickEvent) |
695 { | 676 { |
696 getBlockableElementOrAncestor(lastRightClickEvent.target, function(ele
ment) | 677 clickHide_activated = true; |
697 { | 678 currentElement = getBlockableElementOrAncestor(lastRightClickEvent.tar
get); |
698 clickHide_activated = true; | 679 clickHide_mouseClick(lastRightClickEvent); |
699 currentElement = element; | |
700 clickHide_mouseClick(lastRightClickEvent); | |
701 }); | |
702 } | 680 } |
703 break; | 681 break; |
704 case "clickhide-init": | 682 case "clickhide-init": |
705 if (clickHideFiltersDialog) | 683 if (clickHideFiltersDialog) |
706 { | 684 { |
707 sendResponse({filters: clickHide_filters}); | 685 sendResponse({filters: clickHide_filters}); |
708 | 686 |
709 clickHideFiltersDialog.style.width = msg.width + "px"; | 687 clickHideFiltersDialog.style.width = msg.width + "px"; |
710 clickHideFiltersDialog.style.height = msg.height + "px"; | 688 clickHideFiltersDialog.style.height = msg.height + "px"; |
711 clickHideFiltersDialog.style.visibility = "visible"; | 689 clickHideFiltersDialog.style.visibility = "visible"; |
(...skipping 27 matching lines...) Expand all Loading... |
739 lastRightClickEventValid = false; | 717 lastRightClickEventValid = false; |
740 else | 718 else |
741 lastRightClickEvent = null; | 719 lastRightClickEvent = null; |
742 break; | 720 break; |
743 } | 721 } |
744 }); | 722 }); |
745 | 723 |
746 if (window == window.top) | 724 if (window == window.top) |
747 ext.backgroundPage.sendMessage({type: "report-html-page"}); | 725 ext.backgroundPage.sendMessage({type: "report-html-page"}); |
748 } | 726 } |
OLD | NEW |