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

Delta Between Two Patch Sets: composer.postload.js

Issue 29370947: Issue 3138 - Improve how context menu "block element" handles iframes (Closed)
Left Patch Set: Rebased Created Aug. 9, 2017, 3:27 p.m.
Right Patch Set: Use messaging instead of requiring the info module Created Oct. 19, 2017, 10:52 a.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 | « no previous file | ext/background.js » ('j') | no next file with change/comment »
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 <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-present 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
20 "use strict"; 18 "use strict";
19
20 const {checkCollapse, elemhide,
21 getURLsFromElement, typeMap} = require("./include.preload");
21 22
22 // The page ID for the popup filter selection dialog (top frame only). 23 // The page ID for the popup filter selection dialog (top frame only).
23 let blockelementPopupId = null; 24 let blockelementPopupId = null;
24 25
25 // Element picking state (top frame only). 26 // Element picking state (top frame only).
26 let currentlyPickingElement = false; 27 let currentlyPickingElement = false;
27 let lastMouseOverEvent = null; 28 let lastMouseOverEvent = null;
28 29
29 // During element picking this is the currently highlighted element. When 30 // During element picking this is the currently highlighted element. When
30 // element has been picked this is the element that is due to be blocked. 31 // element has been picked this is the element that is due to be blocked.
31 let currentElement = null; 32 let currentElement = null;
32 33
33 // Highlighting state, used by the top frame during element picking and all 34 // Highlighting state, used by the top frame during element picking and all
34 // frames when the chosen element is highlighted red. 35 // frames when the chosen element is highlighted red.
35 let highlightedElementsSelector = null; 36 let highlightedElementsSelector = null;
36 let highlightedElementsInterval = null; 37 let highlightedElementsInterval = null;
37 38
38 // Last right click state stored for element blocking via the context menu. 39 // Last right click state stored for element blocking via the context menu.
39 let lastRightClickEvent = null; 40 let lastRightClickEvent = null;
40 let lastRightClickEventIsMostRecent = false; 41 let lastRightClickEventIsMostRecent = false;
41 42
43 let perFrameMessagingSupported = false;
44 browser.runtime.sendMessage(
45 {type: "app.get", what: "application"},
46 application => { perFrameMessagingSupported = application != "edge"; }
47 );
42 48
43 /* Utilities */ 49 /* Utilities */
44 50
45 function getFiltersForElement(element, callback) 51 function getFiltersForElement(element, callback)
46 { 52 {
47 let src = element.getAttribute("src"); 53 let src = element.getAttribute("src");
48 ext.backgroundPage.sendMessage({ 54 browser.runtime.sendMessage({
49 type: "composer.getFilters", 55 type: "composer.getFilters",
50 tagName: element.localName, 56 tagName: element.localName,
51 id: element.id, 57 id: element.id,
52 src: src && src.length <= 1000 ? src : null, 58 src: src && src.length <= 1000 ? src : null,
53 style: element.getAttribute("style"), 59 style: element.getAttribute("style"),
54 classes: Array.prototype.slice.call(element.classList), 60 classes: Array.prototype.slice.call(element.classList),
55 urls: getURLsFromElement(element), 61 urls: getURLsFromElement(element),
56 mediatype: typeMap.get(element.localName), 62 mediatype: typeMap.get(element.localName),
57 baseURL: document.location.href 63 baseURL: document.location.href
58 }, 64 },
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 { 395 {
390 if (!currentElement) 396 if (!currentElement)
391 return; 397 return;
392 398
393 let element = currentElement.prisoner || currentElement; 399 let element = currentElement.prisoner || currentElement;
394 getFiltersForElement(element, (filters, selectors) => 400 getFiltersForElement(element, (filters, selectors) =>
395 { 401 {
396 if (currentlyPickingElement) 402 if (currentlyPickingElement)
397 stopPickingElement(); 403 stopPickingElement();
398 404
399 ext.backgroundPage.sendMessage({ 405 browser.runtime.sendMessage({
400 type: "composer.openDialog" 406 type: "composer.openDialog"
401 }, 407 },
402 popupId => 408 popupId =>
403 { 409 {
404 ext.backgroundPage.sendMessage({ 410 browser.runtime.sendMessage({
405 type: "forward", 411 type: "forward",
406 targetPageId: popupId, 412 targetPageId: popupId,
407 payload: {type: "composer.dialog.init", filters} 413 payload: {type: "composer.dialog.init", filters}
408 }); 414 });
409 415
410 // Only the top frame keeps a record of the popup window's ID, 416 // Only the top frame keeps a record of the popup window's ID,
411 // so if this isn't the top frame we need to pass the ID on. 417 // so if this isn't the top frame we need to pass the ID on.
412 if (window == window.top) 418 if (window == window.top)
413 { 419 {
414 blockelementPopupId = popupId; 420 blockelementPopupId = popupId;
415 } 421 }
416 else 422 else
417 { 423 {
418 ext.backgroundPage.sendMessage({ 424 browser.runtime.sendMessage({
419 type: "forward", 425 type: "forward",
420 payload: {type: "composer.content.dialogOpened", popupId} 426 payload: {type: "composer.content.dialogOpened", popupId}
421 }); 427 });
422 } 428 }
423 }); 429 });
424 430
425 if (selectors.length > 0) 431 if (selectors.length > 0)
426 highlightElements(selectors.join(",")); 432 highlightElements(selectors.join(","));
427 433
428 highlightElement(currentElement, "#fd1708", "#f6a1b5"); 434 highlightElement(currentElement, "#fd1708", "#f6a1b5");
(...skipping 25 matching lines...) Expand all
454 /* Core logic */ 460 /* Core logic */
455 461
456 // We're done with the block element feature for now, tidy everything up. 462 // We're done with the block element feature for now, tidy everything up.
457 function deactivateBlockElement() 463 function deactivateBlockElement()
458 { 464 {
459 if (currentlyPickingElement) 465 if (currentlyPickingElement)
460 stopPickingElement(); 466 stopPickingElement();
461 467
462 if (blockelementPopupId != null) 468 if (blockelementPopupId != null)
463 { 469 {
464 ext.backgroundPage.sendMessage({ 470 browser.runtime.sendMessage({
465 type: "forward", 471 type: "forward",
466 targetPageId: blockelementPopupId, 472 targetPageId: blockelementPopupId,
467 payload: 473 payload:
468 { 474 {
469 type: "composer.dialog.close" 475 type: "composer.dialog.close"
470 } 476 }
471 }); 477 });
472 478
473 blockelementPopupId = null; 479 blockelementPopupId = null;
474 } 480 }
475 481
476 lastRightClickEvent = null; 482 lastRightClickEvent = null;
477 483
478 if (currentElement) 484 if (currentElement)
479 { 485 {
480 unhighlightElement(currentElement); 486 unhighlightElement(currentElement);
481 currentElement = null; 487 currentElement = null;
482 } 488 }
483 unhighlightElements(); 489 unhighlightElements();
484 490
485 let overlays = document.getElementsByClassName("__adblockplus__overlay"); 491 let overlays = document.getElementsByClassName("__adblockplus__overlay");
486 while (overlays.length > 0) 492 while (overlays.length > 0)
487 overlays[0].parentNode.removeChild(overlays[0]); 493 overlays[0].parentNode.removeChild(overlays[0]);
488 494
489 ext.onExtensionUnloaded.removeListener(deactivateBlockElement); 495 ext.onExtensionUnloaded.removeListener(deactivateBlockElement);
490 } 496 }
491 497
492 if (document instanceof HTMLDocument) 498 function initializeComposer()
493 { 499 {
500 if (typeof ext == "undefined")
501 return false;
502
494 // Use a contextmenu handler to save the last element the user right-clicked 503 // Use a contextmenu handler to save the last element the user right-clicked
495 // on. To make things easier, we actually save the DOM event. We have to do 504 // on. To make things easier, we actually save the DOM event. We have to do
496 // this because the contextMenu API only provides a URL, not the actual DOM 505 // this because the contextMenu API only provides a URL, not the actual DOM
497 // element. 506 // element.
498 // We also need to make sure that the previous right click event, 507 // We also need to make sure that the previous right click event,
499 // if there is one, is removed. We don't know which frame it is in so we must 508 // if there is one, is removed. We don't know which frame it is in so we must
500 // send a message to the other frames to clear their old right click events. 509 // send a message to the other frames to clear their old right click events.
501 document.addEventListener("contextmenu", event => 510 document.addEventListener("contextmenu", event =>
502 { 511 {
503 lastRightClickEvent = event; 512 lastRightClickEvent = event;
504 lastRightClickEventIsMostRecent = true; 513 lastRightClickEventIsMostRecent = true;
505 514
506 ext.backgroundPage.sendMessage({ 515 browser.runtime.sendMessage({
507 type: "forward", 516 type: "forward",
508 payload: 517 payload:
509 { 518 {
510 type: "composer.content.clearPreviousRightClickEvent" 519 type: "composer.content.clearPreviousRightClickEvent"
511 } 520 }
512 }); 521 });
513 }, true); 522 }, true);
514 523
515 ext.onMessage.addListener((msg, sender, sendResponse) => 524 ext.onMessage.addListener((message, sender, sendResponse) =>
516 { 525 {
517 switch (msg.type) 526 switch (message.type)
518 { 527 {
519 case "composer.content.getState": 528 case "composer.content.getState":
520 if (window == window.top) 529 if (window == window.top)
521 { 530 {
522 sendResponse({ 531 sendResponse({
523 active: currentlyPickingElement || blockelementPopupId != null 532 active: currentlyPickingElement || blockelementPopupId != null
524 }); 533 });
525 } 534 }
526 break; 535 break;
527 case "composer.content.startPickingElement": 536 case "composer.content.startPickingElement":
528 if (window == window.top) 537 if (window == window.top)
529 startPickingElement(); 538 startPickingElement();
530 break; 539 break;
531 case "composer.content.contextMenuClicked": 540 case "composer.content.contextMenuClicked":
532 let event = lastRightClickEvent; 541 let event = lastRightClickEvent;
533 let target = event && event.target; 542 let target = event && event.target;
534 543
535 // When the user attempts to block an element inside an iframe for which 544 // When the user attempts to block an element inside an iframe for which
536 // our right click event listener was trashed the best we can do is to 545 // our right click event listener was trashed the best we can do is to
537 // offer to block the entire iframe. This of course only works if the 546 // offer to block the entire iframe. This doesn't work for cross origin
538 // parent frame is considered to be of the same origin. 547 // frames, neither on Edge where per-frame messaging isn't supported
539 // Note: Since Edge doesn't yet support per-frame messaging[1] we 548 // yet[1], but it's the best we can do.
540 // can't use this workaround there yet. (The contextMenuClicked message
541 // will be sent to all of the page's frames.)
542 // [1] - https://developer.microsoft.com/en-us/microsoft-edge/platform/d ocumentation/extensions/api-support/supported-apis/ 549 // [1] - https://developer.microsoft.com/en-us/microsoft-edge/platform/d ocumentation/extensions/api-support/supported-apis/
543 if (!target && window.frameElement && typeof chrome != "undefined") 550 if (!target && window.frameElement && perFrameMessagingSupported)
kzar 2017/08/09 15:29:24 Note: This check `typeof chrome != "undefined"` is
kzar 2017/10/18 14:57:26 Done.
544 target = addElementOverlay(window.frameElement); 551 target = addElementOverlay(window.frameElement);
545 552
546 deactivateBlockElement(); 553 deactivateBlockElement();
547 if (target) 554 if (target)
548 { 555 {
549 getBlockableElementOrAncestor(target, element => 556 getBlockableElementOrAncestor(target, element =>
550 { 557 {
551 if (element) 558 if (element)
552 { 559 {
553 currentElement = element; 560 currentElement = element;
554 elementPicked(event); 561 elementPicked(event);
555 } 562 }
556 }); 563 });
557 } 564 }
558 break; 565 break;
559 case "composer.content.finished": 566 case "composer.content.finished":
560 if (currentElement && msg.remove) 567 if (currentElement && message.remove)
561 { 568 {
562 // Hide the selected element itself if an added blocking 569 // Hide the selected element itself if an added blocking
563 // filter is causing it to collapse. Note that this 570 // filter is causing it to collapse. Note that this
564 // behavior is incomplete, but the best we can do here, 571 // behavior is incomplete, but the best we can do here,
565 // e.g. if an added blocking filter matches other elements, 572 // e.g. if an added blocking filter matches other elements,
566 // the effect won't be visible until the page is is reloaded. 573 // the effect won't be visible until the page is is reloaded.
567 checkCollapse(currentElement.prisoner || currentElement); 574 checkCollapse(currentElement.prisoner || currentElement);
568 575
569 // Apply added element hiding filters. 576 // Apply added element hiding filters.
570 elemhide.apply(); 577 elemhide.apply();
571 } 578 }
572 deactivateBlockElement(); 579 deactivateBlockElement();
573 break; 580 break;
574 case "composer.content.clearPreviousRightClickEvent": 581 case "composer.content.clearPreviousRightClickEvent":
575 if (!lastRightClickEventIsMostRecent) 582 if (!lastRightClickEventIsMostRecent)
576 lastRightClickEvent = null; 583 lastRightClickEvent = null;
577 lastRightClickEventIsMostRecent = false; 584 lastRightClickEventIsMostRecent = false;
578 break; 585 break;
579 case "composer.content.dialogOpened": 586 case "composer.content.dialogOpened":
580 if (window == window.top) 587 if (window == window.top)
581 blockelementPopupId = msg.popupId; 588 blockelementPopupId = message.popupId;
582 break; 589 break;
583 case "composer.content.dialogClosed": 590 case "composer.content.dialogClosed":
584 // The onRemoved hook for the popup can create a race condition, so we 591 // The onRemoved hook for the popup can create a race condition, so we
585 // to be careful here. (This is not perfect, but best we can do.) 592 // to be careful here. (This is not perfect, but best we can do.)
586 if (window == window.top && blockelementPopupId == msg.popupId) 593 if (window == window.top && blockelementPopupId == message.popupId)
587 { 594 {
588 ext.backgroundPage.sendMessage({ 595 browser.runtime.sendMessage({
589 type: "forward", 596 type: "forward",
590 payload: 597 payload:
591 { 598 {
592 type: "composer.content.finished" 599 type: "composer.content.finished"
593 } 600 }
594 }); 601 });
595 } 602 }
596 break; 603 break;
597 } 604 }
598 }); 605 });
599 606
600 if (window == window.top) 607 if (window == window.top)
601 ext.backgroundPage.sendMessage({type: "composer.ready"}); 608 browser.runtime.sendMessage({type: "composer.ready"});
602 } 609
610 return true;
611 }
612
613 if (document instanceof HTMLDocument)
614 {
615 // There's a bug in Firefox that causes document_end content scripts to run
616 // before document_start content scripts on extension startup. In this case
617 // the ext object is undefined, we fail to initialize, and initializeComposer
618 // returns false. As a workaround, try again after a timeout.
619 // https://bugzilla.mozilla.org/show_bug.cgi?id=1395287
620 if (!initializeComposer())
621 setTimeout(initializeComposer, 2000);
622 }
LEFTRIGHT
« no previous file | ext/background.js » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld