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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 /* globals checkCollapse, elemhide, getURLsFromElement, typeMap */ |
| 19 |
18 "use strict"; | 20 "use strict"; |
19 | 21 |
20 // The page ID for the popup filter selection dialog (top frame only). | 22 // The page ID for the popup filter selection dialog (top frame only). |
21 let blockelementPopupId = null; | 23 let blockelementPopupId = null; |
22 | 24 |
23 // Element picking state (top frame only). | 25 // Element picking state (top frame only). |
24 let currentlyPickingElement = false; | 26 let currentlyPickingElement = false; |
25 let lastMouseOverEvent = null; | 27 let lastMouseOverEvent = null; |
26 | 28 |
27 // During element picking this is the currently highlighted element. When | 29 // During element picking this is the currently highlighted element. When |
28 // element has been picked this is the element that is due to be blocked. | 30 // element has been picked this is the element that is due to be blocked. |
29 let currentElement = null; | 31 let currentElement = null; |
30 | 32 |
31 // Highlighting state, used by the top frame during element picking and all | 33 // Highlighting state, used by the top frame during element picking and all |
32 // frames when the chosen element is highlighted red. | 34 // frames when the chosen element is highlighted red. |
33 let highlightedElementsSelector = null; | 35 let highlightedElementsSelector = null; |
34 let highlightedElementsInterval = null; | 36 let highlightedElementsInterval = null; |
35 | 37 |
36 // Last right click state stored for element blocking via the context menu. | 38 // Last right click state stored for element blocking via the context menu. |
37 let lastRightClickEvent = null; | 39 let lastRightClickEvent = null; |
38 let lastRightClickEventIsMostRecent = false; | 40 let lastRightClickEventIsMostRecent = false; |
39 | 41 |
40 | 42 |
41 /* Utilities */ | 43 /* Utilities */ |
42 | 44 |
43 function getFiltersForElement(element, callback) | 45 function getFiltersForElement(element, callback) |
44 { | 46 { |
45 let src = element.getAttribute("src"); | 47 let src = element.getAttribute("src"); |
46 ext.backgroundPage.sendMessage( | 48 ext.backgroundPage.sendMessage({ |
47 { | |
48 type: "composer.getFilters", | 49 type: "composer.getFilters", |
49 tagName: element.localName, | 50 tagName: element.localName, |
50 id: element.id, | 51 id: element.id, |
51 src: src && src.length <= 1000 ? src : null, | 52 src: src && src.length <= 1000 ? src : null, |
52 style: element.getAttribute("style"), | 53 style: element.getAttribute("style"), |
53 classes: Array.prototype.slice.call(element.classList), | 54 classes: Array.prototype.slice.call(element.classList), |
54 urls: getURLsFromElement(element), | 55 urls: getURLsFromElement(element), |
55 mediatype: typeMap[element.localName], | 56 mediatype: typeMap.get(element.localName), |
56 baseURL: document.location.href | 57 baseURL: document.location.href |
57 }, | 58 }, |
58 response => | 59 response => |
59 { | 60 { |
60 callback(response.filters, response.selectors); | 61 callback(response.filters, response.selectors); |
61 }); | 62 }); |
62 } | 63 } |
63 | 64 |
64 function getBlockableElementOrAncestor(element, callback) | 65 function getBlockableElementOrAncestor(element, callback) |
65 { | 66 { |
66 // We assume that the user doesn't want to block the whole page. | 67 // We assume that the user doesn't want to block the whole page. |
67 // So we never consider the <html> or <body> element. | 68 // So we never consider the <html> or <body> element. |
68 while (element && element != document.documentElement && | 69 while (element && element != document.documentElement && |
69 element != document.body) | 70 element != document.body) |
70 { | 71 { |
71 // We can't handle non-HTML (like SVG) elements, as well as | 72 // We can't handle non-HTML (like SVG) elements, as well as |
72 // <area> elements (see below). So fall back to the parent element. | 73 // <area> elements (see below). So fall back to the parent element. |
73 if (!(element instanceof HTMLElement) || element.localName == "area") | 74 if (!(element instanceof HTMLElement) || element.localName == "area") |
74 element = element.parentElement; | 75 element = element.parentElement; |
75 | |
76 // If image maps are used mouse events occur for the <area> element. | 76 // If image maps are used mouse events occur for the <area> element. |
77 // But we have to block the image associated with the <map> element. | 77 // But we have to block the image associated with the <map> element. |
78 else if (element.localName == "map") | 78 else if (element.localName == "map") |
79 { | 79 { |
80 let images = document.querySelectorAll("img[usemap]"); | 80 let images = document.querySelectorAll("img[usemap]"); |
81 let image = null; | 81 let image = null; |
82 | 82 |
83 for (let currentImage of images) | 83 for (let currentImage of images) |
84 { | 84 { |
85 let usemap = currentImage.getAttribute("usemap"); | 85 let usemap = currentImage.getAttribute("usemap"); |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 { | 381 { |
382 if (!currentElement) | 382 if (!currentElement) |
383 return; | 383 return; |
384 | 384 |
385 let element = currentElement.prisoner || currentElement; | 385 let element = currentElement.prisoner || currentElement; |
386 getFiltersForElement(element, (filters, selectors) => | 386 getFiltersForElement(element, (filters, selectors) => |
387 { | 387 { |
388 if (currentlyPickingElement) | 388 if (currentlyPickingElement) |
389 stopPickingElement(); | 389 stopPickingElement(); |
390 | 390 |
391 ext.backgroundPage.sendMessage( | 391 ext.backgroundPage.sendMessage({ |
392 { | |
393 type: "composer.openDialog" | 392 type: "composer.openDialog" |
394 }, | 393 }, |
395 popupId => | 394 popupId => |
396 { | 395 { |
397 ext.backgroundPage.sendMessage( | 396 ext.backgroundPage.sendMessage({ |
398 { | |
399 type: "forward", | 397 type: "forward", |
400 targetPageId: popupId, | 398 targetPageId: popupId, |
401 payload: | 399 payload: {type: "composer.dialog.init", filters} |
402 { | |
403 type: "composer.dialog.init", | |
404 filters: filters | |
405 } | |
406 }); | 400 }); |
407 | 401 |
408 // Only the top frame keeps a record of the popup window's ID, | 402 // Only the top frame keeps a record of the popup window's ID, |
409 // so if this isn't the top frame we need to pass the ID on. | 403 // so if this isn't the top frame we need to pass the ID on. |
410 if (window == window.top) | 404 if (window == window.top) |
411 { | |
412 blockelementPopupId = popupId; | 405 blockelementPopupId = popupId; |
413 } | |
414 else | 406 else |
415 { | 407 { |
416 ext.backgroundPage.sendMessage( | 408 ext.backgroundPage.sendMessage({ |
417 { | |
418 type: "forward", | 409 type: "forward", |
419 payload: | 410 payload: {type: "composer.content.dialogOpened", popupId} |
420 { | |
421 type: "composer.content.dialogOpened", | |
422 popupId: popupId | |
423 } | |
424 }); | 411 }); |
425 } | 412 } |
426 }); | 413 }); |
427 | 414 |
428 if (selectors.length > 0) | 415 if (selectors.length > 0) |
429 highlightElements(selectors.join(",")); | 416 highlightElements(selectors.join(",")); |
430 | 417 |
431 highlightElement(currentElement, "#fd1708", "#f6a1b5"); | 418 highlightElement(currentElement, "#fd1708", "#f6a1b5"); |
432 }); | 419 }); |
433 | 420 |
(...skipping 20 matching lines...) Expand all Loading... |
454 /* Core logic */ | 441 /* Core logic */ |
455 | 442 |
456 // We're done with the block element feature for now, tidy everything up. | 443 // We're done with the block element feature for now, tidy everything up. |
457 function deactivateBlockElement() | 444 function deactivateBlockElement() |
458 { | 445 { |
459 if (currentlyPickingElement) | 446 if (currentlyPickingElement) |
460 stopPickingElement(); | 447 stopPickingElement(); |
461 | 448 |
462 if (blockelementPopupId != null) | 449 if (blockelementPopupId != null) |
463 { | 450 { |
464 ext.backgroundPage.sendMessage( | 451 ext.backgroundPage.sendMessage({ |
465 { | |
466 type: "forward", | 452 type: "forward", |
467 targetPageId: blockelementPopupId, | 453 targetPageId: blockelementPopupId, |
468 payload: | 454 payload: |
469 { | 455 { |
470 type: "composer.dialog.close" | 456 type: "composer.dialog.close" |
471 } | 457 } |
472 }); | 458 }); |
473 | 459 |
474 blockelementPopupId = null; | 460 blockelementPopupId = null; |
475 } | 461 } |
(...skipping 21 matching lines...) Expand all Loading... |
497 // this because the contextMenu API only provides a URL, not the actual DOM | 483 // this because the contextMenu API only provides a URL, not the actual DOM |
498 // element. | 484 // element. |
499 // We also need to make sure that the previous right click event, | 485 // We also need to make sure that the previous right click event, |
500 // if there is one, is removed. We don't know which frame it is in so we must | 486 // if there is one, is removed. We don't know which frame it is in so we must |
501 // send a message to the other frames to clear their old right click events. | 487 // send a message to the other frames to clear their old right click events. |
502 document.addEventListener("contextmenu", event => | 488 document.addEventListener("contextmenu", event => |
503 { | 489 { |
504 lastRightClickEvent = event; | 490 lastRightClickEvent = event; |
505 lastRightClickEventIsMostRecent = true; | 491 lastRightClickEventIsMostRecent = true; |
506 | 492 |
507 ext.backgroundPage.sendMessage( | 493 ext.backgroundPage.sendMessage({ |
508 { | |
509 type: "forward", | 494 type: "forward", |
510 payload: | 495 payload: |
511 { | 496 { |
512 type: "composer.content.clearPreviousRightClickEvent" | 497 type: "composer.content.clearPreviousRightClickEvent" |
513 } | 498 } |
514 }); | 499 }); |
515 }, true); | 500 }, true); |
516 | 501 |
517 ext.onMessage.addListener((msg, sender, sendResponse) => | 502 ext.onMessage.addListener((msg, sender, sendResponse) => |
518 { | 503 { |
519 switch (msg.type) | 504 switch (msg.type) |
520 { | 505 { |
521 case "composer.content.getState": | 506 case "composer.content.getState": |
522 if (window == window.top) | 507 if (window == window.top) |
| 508 { |
523 sendResponse({ | 509 sendResponse({ |
524 active: currentlyPickingElement || blockelementPopupId != null | 510 active: currentlyPickingElement || blockelementPopupId != null |
525 }); | 511 }); |
| 512 } |
526 break; | 513 break; |
527 case "composer.content.startPickingElement": | 514 case "composer.content.startPickingElement": |
528 if (window == window.top) | 515 if (window == window.top) |
529 startPickingElement(); | 516 startPickingElement(); |
530 break; | 517 break; |
531 case "composer.content.contextMenuClicked": | 518 case "composer.content.contextMenuClicked": |
532 let event = lastRightClickEvent; | 519 let event = lastRightClickEvent; |
533 deactivateBlockElement(); | 520 deactivateBlockElement(); |
534 if (event) | 521 if (event) |
535 { | 522 { |
(...skipping 29 matching lines...) Expand all Loading... |
565 break; | 552 break; |
566 case "composer.content.dialogOpened": | 553 case "composer.content.dialogOpened": |
567 if (window == window.top) | 554 if (window == window.top) |
568 blockelementPopupId = msg.popupId; | 555 blockelementPopupId = msg.popupId; |
569 break; | 556 break; |
570 case "composer.content.dialogClosed": | 557 case "composer.content.dialogClosed": |
571 // The onRemoved hook for the popup can create a race condition, so we | 558 // The onRemoved hook for the popup can create a race condition, so we |
572 // to be careful here. (This is not perfect, but best we can do.) | 559 // to be careful here. (This is not perfect, but best we can do.) |
573 if (window == window.top && blockelementPopupId == msg.popupId) | 560 if (window == window.top && blockelementPopupId == msg.popupId) |
574 { | 561 { |
575 ext.backgroundPage.sendMessage( | 562 ext.backgroundPage.sendMessage({ |
576 { | |
577 type: "forward", | 563 type: "forward", |
578 payload: | 564 payload: |
579 { | 565 { |
580 type: "composer.content.finished" | 566 type: "composer.content.finished" |
581 } | 567 } |
582 }); | 568 }); |
583 } | 569 } |
584 break; | 570 break; |
585 } | 571 } |
586 }); | 572 }); |
587 | 573 |
588 if (window == window.top) | 574 if (window == window.top) |
589 ext.backgroundPage.sendMessage({type: "composer.ready"}); | 575 ext.backgroundPage.sendMessage({type: "composer.ready"}); |
590 } | 576 } |
OLD | NEW |