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 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 | 260 |
261 case "video": | 261 case "video": |
262 case "audio": | 262 case "audio": |
263 case "picture": | 263 case "picture": |
264 return getURLsFromMediaElement(element); | 264 return getURLsFromMediaElement(element); |
265 } | 265 } |
266 | 266 |
267 return getURLsFromAttributes(element); | 267 return getURLsFromAttributes(element); |
268 } | 268 } |
269 | 269 |
270 function isBlockable(element) | |
271 { | |
272 if (element.id) | |
273 return true; | |
274 if (element.classList.length > 0) | |
275 return true; | |
276 if (getURLsFromElement(element).length > 0) | |
277 return true; | |
278 | |
279 // We only generate filters based on the "style" attribute, | |
280 // if this is the only way we can generate a filter, and | |
281 // only if there are at least two CSS properties defined. | |
282 if (/:.+:/.test(getOriginalStyle(element))) | |
283 return true; | |
284 | |
285 return false; | |
286 } | |
287 | |
288 // Gets the absolute position of an element by walking up the DOM tree, | 270 // Gets the absolute position of an element by walking up the DOM tree, |
289 // adding up offsets. | 271 // adding up offsets. |
290 // I hope there's a better way because it just seems absolutely stupid | 272 // 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 | 273 // 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. | 274 // has hundreds and hundreds of other methods that do random junk. |
293 function getAbsolutePosition(elt) { | 275 function getAbsolutePosition(elt) { |
294 var l = 0; | 276 var l = 0; |
295 var t = 0; | 277 var t = 0; |
296 for(; elt; elt = elt.offsetParent) { | 278 for(; elt; elt = elt.offsetParent) { |
297 l += elt.offsetLeft; | 279 l += elt.offsetLeft; |
298 t += elt.offsetTop; | 280 t += elt.offsetTop; |
299 } | 281 } |
300 return [l, t]; | 282 return [l, t]; |
301 } | 283 } |
302 | 284 |
303 // Adds an overlay to an element, which is probably a Flash object | 285 // Adds an overlay to an element, which is probably a Flash object |
304 function addElementOverlay(elt) { | 286 function addElementOverlay(elt, callback) { |
305 // If this element is enclosed in an object tag, we prefer to block that inste
ad | 287 // If this element is enclosed in an object tag, we prefer to block that inste
ad |
306 if(!elt) | 288 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; | 289 return; |
313 | 290 |
314 // If the element isn't rendered (since its or one of its ancestor's | 291 // 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. | 292 // "display" property is "none"), the overlay wouldn't match the element. |
316 if (!elt.offsetParent) | 293 if (!elt.offsetParent) |
317 return; | 294 return; |
318 | 295 |
319 var thisStyle = getComputedStyle(elt, null); | 296 generateFilters(elt, function(filters) |
320 var overlay = document.createElement('div'); | 297 { |
321 overlay.prisoner = elt; | 298 if (filters.length == 0) |
322 overlay.className = "__adblockplus__overlay"; | 299 return; |
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;'); | |
324 var pos = getAbsolutePosition(elt); | |
325 overlay.style.left = pos[0] + "px"; | |
326 overlay.style.top = pos[1] + "px"; | |
327 | 300 |
328 if (thisStyle.position != "static") | 301 var thisStyle = getComputedStyle(elt, null); |
329 overlay.style.zIndex = thisStyle.zIndex; | 302 var overlay = document.createElement('div'); |
330 else | 303 overlay.prisoner = elt; |
331 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; | 304 overlay.className = "__adblockplus__overlay"; |
| 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;'); |
| 306 var pos = getAbsolutePosition(elt); |
| 307 overlay.style.left = pos[0] + "px"; |
| 308 overlay.style.top = pos[1] + "px"; |
332 | 309 |
333 // elt.parentNode.appendChild(overlay, elt); | 310 if (thisStyle.position != "static") |
334 document.body.appendChild(overlay); | 311 overlay.style.zIndex = thisStyle.zIndex; |
335 return overlay; | 312 else |
| 313 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; |
| 314 |
| 315 document.body.appendChild(overlay); |
| 316 |
| 317 if (callback) |
| 318 callback(overlay); |
| 319 }); |
336 } | 320 } |
337 | 321 |
338 // Show dialog asking user whether she wants to add the proposed filters derived | 322 // Show dialog asking user whether she wants to add the proposed filters derived |
339 // from selected page element | 323 // from selected page element |
340 function clickHide_showDialog(left, top, filters) | 324 function clickHide_showDialog(left, top, filters) |
341 { | 325 { |
342 // If we are already selecting, abort now | 326 // If we are already selecting, abort now |
343 if (clickHide_activated || clickHideFiltersDialog) | 327 if (clickHide_activated || clickHideFiltersDialog) |
344 clickHide_deactivate(true); | 328 clickHide_deactivate(true); |
345 | 329 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
436 overlays[0].parentNode.removeChild(overlays[0]); | 420 overlays[0].parentNode.removeChild(overlays[0]); |
437 } | 421 } |
438 } | 422 } |
439 | 423 |
440 function clickHide_elementClickHandler(ev) { | 424 function clickHide_elementClickHandler(ev) { |
441 ev.preventDefault(); | 425 ev.preventDefault(); |
442 ev.stopPropagation(); | 426 ev.stopPropagation(); |
443 clickHide_mouseClick(ev); | 427 clickHide_mouseClick(ev); |
444 } | 428 } |
445 | 429 |
| 430 function getBlockableElementOrAncestor(element, callback) |
| 431 { |
| 432 if (element && element != document.documentElement |
| 433 && element != document.body) |
| 434 { |
| 435 generateFilters(element, function(filters) |
| 436 { |
| 437 if (filters.length > 0) |
| 438 callback(element); |
| 439 else |
| 440 getBlockableElementOrAncestor(element.parentElement, callback); |
| 441 }); |
| 442 } |
| 443 else |
| 444 { |
| 445 callback(null); |
| 446 } |
| 447 } |
| 448 |
446 // Hovering over an element so highlight it | 449 // Hovering over an element so highlight it |
447 function clickHide_mouseOver(e) | 450 function clickHide_mouseOver(e) |
448 { | 451 { |
449 if (clickHide_activated == false) | 452 if (clickHide_activated == false) |
450 return; | 453 return; |
451 | 454 |
452 var target = e.target; | 455 getBlockableElementOrAncestor(e.target, function(element) |
453 while (target.parentNode && !isBlockable(target)) | 456 { |
454 target = target.parentNode; | 457 if (currentElement) |
455 if (target == document.documentElement || target == document.body) | 458 unhighlightElement(currentElement); |
456 target = null; | |
457 | 459 |
458 if (target && target instanceof HTMLElement) | 460 if (element) |
459 { | 461 { |
460 currentElement = target; | 462 highlightElement(element, "#d6d84b", "#f8fa47"); |
| 463 element.addEventListener("contextmenu", clickHide_elementClickHandler, tru
e); |
| 464 } |
461 | 465 |
462 highlightElement(target, "#d6d84b", "#f8fa47"); | 466 currentElement = element; |
463 target.addEventListener("contextmenu", clickHide_elementClickHandler, true); | 467 }); |
464 } | |
465 } | 468 } |
466 | 469 |
467 // No longer hovering over this element so unhighlight it | 470 // No longer hovering over this element so unhighlight it |
468 function clickHide_mouseOut(e) | 471 function clickHide_mouseOut(e) |
469 { | 472 { |
470 if (!clickHide_activated || !currentElement) | 473 if (!clickHide_activated || currentElement != e.target) |
471 return; | 474 return; |
472 | 475 |
473 unhighlightElement(currentElement); | 476 unhighlightElement(currentElement); |
474 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle
r, true); | 477 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle
r, true); |
475 } | 478 } |
476 | 479 |
477 // Selects the currently hovered-over filter or cancels selection | 480 // Selects the currently hovered-over filter or cancels selection |
478 function clickHide_keyDown(e) | 481 function clickHide_keyDown(e) |
479 { | 482 { |
480 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN*
/) | 483 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN*
/) |
481 clickHide_mouseClick(e); | 484 clickHide_mouseClick(e); |
482 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES
CAPE*/) | 485 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES
CAPE*/) |
483 { | 486 { |
484 ext.backgroundPage.sendMessage( | 487 ext.backgroundPage.sendMessage( |
485 { | 488 { |
486 type: "forward", | 489 type: "forward", |
487 payload: | 490 payload: |
488 { | 491 { |
489 type: "clickhide-deactivate" | 492 type: "clickhide-deactivate" |
490 } | 493 } |
491 }); | 494 }); |
492 e.preventDefault(); | 495 e.preventDefault(); |
493 e.stopPropagation(); | 496 e.stopPropagation(); |
494 } | 497 } |
495 } | 498 } |
496 | 499 |
| 500 function generateFilters(element, callback) |
| 501 { |
| 502 function addStyleAttributeFilter(filters, selectors) |
| 503 { |
| 504 var style = getOriginalStyle(element); |
| 505 if (style && filters.length == 0) |
| 506 { |
| 507 ext.backgroundPage.sendMessage( |
| 508 { |
| 509 type: "get-filters-from-selectors", |
| 510 selectors: [escapeCSS(element.localName) + '[style=' + quote(style) +
']'] |
| 511 }, |
| 512 |
| 513 function(response) |
| 514 { |
| 515 callback(response.filters, response.selectors); |
| 516 } |
| 517 ); |
| 518 } |
| 519 else |
| 520 callback(filters, selectors); |
| 521 } |
| 522 |
| 523 function addElemHideFilters(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 addElemHideFilters(filters, selectors); |
| 595 } |
| 596 ); |
| 597 } |
| 598 else |
| 599 addElemHideFilters(filters, selectors); |
| 600 } |
| 601 |
497 // When the user clicks, the currentElement is the one we want. | 602 // When the user clicks, the currentElement is the one we want. |
498 // We should have ABP rules ready for when the | 603 // We should have ABP rules ready for when the |
499 // popup asks for them. | 604 // popup asks for them. |
500 function clickHide_mouseClick(e) | 605 function clickHide_mouseClick(e) |
501 { | 606 { |
502 if (!currentElement || !clickHide_activated) | 607 if (!currentElement || !clickHide_activated) |
503 return; | 608 return; |
504 | 609 |
505 var elt = currentElement; | 610 var elt = currentElement; |
506 if (currentElement.classList.contains("__adblockplus__overlay")) | 611 if (currentElement.classList.contains("__adblockplus__overlay")) |
507 elt = currentElement.prisoner; | 612 elt = currentElement.prisoner; |
508 | 613 |
509 clickHideFilters = new Array(); | 614 generateFilters(elt, function(filters, selectors) |
510 selectorList = new Array(); | 615 { |
| 616 clickHide_showDialog(e.clientX, e.clientY, filters); |
511 | 617 |
512 var addSelector = function(selector) | 618 // Highlight the elements specified by selector in yellow |
513 { | 619 if (selectors.length > 0) |
514 if (selectorList.indexOf(selector) != -1) | 620 highlightElements(selectors.join(",")); |
515 return; | |
516 | 621 |
517 clickHideFilters.push(document.domain + "##" + selector); | 622 // Now, actually highlight the element the user clicked on in red |
518 selectorList.push(selector); | 623 highlightElement(currentElement, "#fd1708", "#f6a1b5"); |
519 }; | 624 }); |
520 | |
521 if (elt.id) | |
522 addSelector("#" + escapeCSS(elt.id)); | |
523 | |
524 if (elt.classList.length > 0) | |
525 { | |
526 var selector = ""; | |
527 | |
528 for (var i = 0; i < elt.classList.length; i++) | |
529 selector += "." + escapeCSS(elt.classList[i]); | |
530 | |
531 addSelector(selector); | |
532 } | |
533 | |
534 var urls = getURLsFromElement(elt); | |
535 for (var i = 0; i < urls.length; i++) | |
536 { | |
537 var url = urls[i]; | |
538 | |
539 if (/^https?:/i.test(url)) | |
540 { | |
541 var filter = url.replace(/^[\w\-]+:\/+(?:www\.)?/, "||"); | |
542 | |
543 if (clickHideFilters.indexOf(filter) == -1) | |
544 clickHideFilters.push(filter); | |
545 | |
546 continue; | |
547 } | |
548 | |
549 if (url == elt.src) | |
550 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s
rc")) + ']'); | |
551 } | |
552 | |
553 // as last resort, create a filter based on inline styles | |
554 if (clickHideFilters.length == 0) | |
555 { | |
556 var style = getOriginalStyle(elt); | |
557 if (style) | |
558 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']'); | |
559 } | |
560 | |
561 // Show popup | |
562 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters); | |
563 | |
564 // Highlight the elements specified by selector in yellow | |
565 if (selectorList.length > 0) | |
566 highlightElements(selectorList.join(",")); | |
567 // Now, actually highlight the element the user clicked on in red | |
568 highlightElement(currentElement, "#fd1708", "#f6a1b5"); | |
569 | 625 |
570 // Make sure the browser doesn't handle this click | 626 // Make sure the browser doesn't handle this click |
571 e.preventDefault(); | 627 e.preventDefault(); |
572 e.stopPropagation(); | 628 e.stopPropagation(); |
573 } | 629 } |
574 | 630 |
575 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js | 631 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js |
576 // and licensed under the MIT license. See jquery-*.min.js for details. | 632 // and licensed under the MIT license. See jquery-*.min.js for details. |
577 function removeDotSegments(u) { | 633 function removeDotSegments(u) { |
578 var r = '', m = []; | 634 var r = '', m = []; |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
685 break; | 741 break; |
686 case "clickhide-activate": | 742 case "clickhide-activate": |
687 clickHide_activate(); | 743 clickHide_activate(); |
688 break; | 744 break; |
689 case "clickhide-deactivate": | 745 case "clickhide-deactivate": |
690 clickHide_deactivate(); | 746 clickHide_deactivate(); |
691 break; | 747 break; |
692 case "clickhide-new-filter": | 748 case "clickhide-new-filter": |
693 if(lastRightClickEvent) | 749 if(lastRightClickEvent) |
694 { | 750 { |
695 clickHide_activated = true; | 751 var event = lastRightClickEvent; |
696 currentElement = addElementOverlay(lastRightClickEvent.target); | 752 |
697 clickHide_mouseClick(lastRightClickEvent); | 753 addElementOverlay(event.target, function(overlay) |
| 754 { |
| 755 clickHide_activated = true; |
| 756 currentElement = overlay; |
| 757 clickHide_mouseClick(event); |
| 758 }); |
698 } | 759 } |
699 break; | 760 break; |
700 case "clickhide-init": | 761 case "clickhide-init": |
701 if (clickHideFiltersDialog) | 762 if (clickHideFiltersDialog) |
702 { | 763 { |
703 sendResponse({filters: clickHide_filters}); | 764 sendResponse({filters: clickHide_filters}); |
704 | 765 |
705 clickHideFiltersDialog.style.width = msg.width + "px"; | 766 clickHideFiltersDialog.style.width = msg.width + "px"; |
706 clickHideFiltersDialog.style.height = msg.height + "px"; | 767 clickHideFiltersDialog.style.height = msg.height + "px"; |
707 clickHideFiltersDialog.style.visibility = "visible"; | 768 clickHideFiltersDialog.style.visibility = "visible"; |
(...skipping 18 matching lines...) Expand all Loading... |
726 break; | 787 break; |
727 default: | 788 default: |
728 sendResponse({}); | 789 sendResponse({}); |
729 break; | 790 break; |
730 } | 791 } |
731 }); | 792 }); |
732 | 793 |
733 if (window == window.top) | 794 if (window == window.top) |
734 ext.backgroundPage.sendMessage({type: "report-html-page"}); | 795 ext.backgroundPage.sendMessage({type: "report-html-page"}); |
735 } | 796 } |
OLD | NEW |