From db7f54dbf6c39cf2e6f35359248bf9f408e2d134 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:26:29 -0500 Subject: [PATCH] Add support to launch element picker in embedded frames Related issue: - https://github.com/gorhill/uBlock/issues/1744 A new context menu entry, "Block element in frame...", will be present when right-clicking on a frame element. When this entry is clicked, uBO's element picker will be launched from within the embedded frame and function the same way as when launched from within the page. --- platform/chromium/vapi-background.js | 1 + src/_locales/en/messages.json | 4 ++++ src/css/epicker-ui.css | 10 ++++++--- src/js/commands.js | 1 + src/js/contextmenu.js | 32 ++++++++++++++++++++++------ src/js/epicker-ui.js | 8 +++---- src/js/messaging.js | 2 +- src/js/ublock.js | 18 +++++++++++----- 8 files changed, 56 insertions(+), 20 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 07721183117c4..9a5f4f0b3913d 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1010,6 +1010,7 @@ vAPI.messaging = { case 'extendClient': vAPI.tabs.executeScript(tabId, { file: '/js/vapi-client-extra.js', + frameId: portDetails.frameId, }).then(( ) => { callback(); }); diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 84dee24e960b5..5179f0af5e787 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/css/epicker-ui.css b/src/css/epicker-ui.css index c571462970a9b..c8759f2acb3ae 100644 --- a/src/css/epicker-ui.css +++ b/src/css/epicker-ui.css @@ -15,16 +15,18 @@ html#ublock0-epicker, #ublock0-epicker aside { background-color: var(--default-surface); border: 1px solid var(--default-surface-border); - bottom: 4px; + bottom: 2px; box-sizing: border-box; cursor: default; display: none; + max-height: calc(100vh - 4px); max-width: 36rem; min-width: 24rem; + overflow-y: auto; padding: 4px; position: fixed; - right: 4px; - width: calc(40% - 4px); + right: 2px; + width: calc(40% - 2px); } #ublock0-epicker.paused:not(.zap) aside { display: block; @@ -89,6 +91,8 @@ html#ublock0-epicker, box-sizing: border-box; font: 11px monospace; height: 8em; + max-height: 50vh; + min-height: 1em; padding: 2px; width: 100%; } diff --git a/src/js/commands.js b/src/js/commands.js index f99c0bf8cb6be..c30b8627b64d1 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -165,6 +165,7 @@ vAPI.commands.onCommand.addListener(async command => { µb.epickerArgs.mouse = false; µb.elementPickerExec( tab.id, + 0, undefined, command === 'launch-element-zapper' ); diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index 7ac5b2d4bc123..80b2ed7a7569c 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -58,7 +58,16 @@ const onBlockElement = function(details, tab) { } µBlock.epickerArgs.mouse = true; - µBlock.elementPickerExec(tab.id, tagName + '\t' + src); + µBlock.elementPickerExec(tab.id, 0, `${tagName}\t${src}`); +}; + +/******************************************************************************/ + +const onBlockElementInFrame = function(details, tab) { + if ( tab === undefined ) { return; } + if ( /^https?:\/\//.test(details.frameUrl) === false ) { return; } + µBlock.epickerArgs.mouse = false; + µBlock.elementPickerExec(tab.id, details.frameId); }; /******************************************************************************/ @@ -76,6 +85,9 @@ const onEntryClicked = function(details, tab) { if ( details.menuItemId === 'uBlock0-blockElement' ) { return onBlockElement(details, tab); } + if ( details.menuItemId === 'uBlock0-blockElementInFrame' ) { + return onBlockElementInFrame(details, tab); + } if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) { return onTemporarilyAllowLargeMediaElements(details, tab); } @@ -83,18 +95,23 @@ const onEntryClicked = function(details, tab) { /******************************************************************************/ -const menuEntries = [ - { +const menuEntries = { + blockElement: { id: 'uBlock0-blockElement', title: vAPI.i18n('pickerContextMenuEntry'), contexts: ['all'], }, - { + blockElementInFrame: { + id: 'uBlock0-blockElementInFrame', + title: vAPI.i18n('contextMenuBlockElementInFrame'), + contexts: ['frame'], + }, + temporarilyAllowLargeMediaElements: { id: 'uBlock0-temporarilyAllowLargeMediaElements', title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'), contexts: ['all'], } -]; +}; /******************************************************************************/ @@ -115,10 +132,11 @@ const update = function(tabId = undefined) { currentBits = newBits; let usedEntries = []; if ( newBits & 0x01 ) { - usedEntries.push(menuEntries[0]); + usedEntries.push(menuEntries.blockElement); + usedEntries.push(menuEntries.blockElementInFrame); } if ( newBits & 0x02 ) { - usedEntries.push(menuEntries[1]); + usedEntries.push(menuEntries.temporarilyAllowLargeMediaElements); } vAPI.contextMenu.setEntries(usedEntries, onEntryClicked); }; diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 7c8df09749caa..5b6f73a4a86ff 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -599,8 +599,8 @@ const onStartMoving = (( ) => { const move = ( ) => { timer = undefined; - const r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax); - const b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax); + const r1 = Math.min(Math.max(r0 - mx1 + mx0, 2), rMax); + const b1 = Math.min(Math.max(b0 - my1 + my0, 2), bMax); dialog.style.setProperty('right', `${r1}px`); dialog.style.setProperty('bottom', `${b1}px`); }; @@ -646,8 +646,8 @@ const onStartMoving = (( ) => { r0 = parseInt(style.right, 10); b0 = parseInt(style.bottom, 10); const rect = dialog.getBoundingClientRect(); - rMax = pickerRoot.clientWidth - 4 - rect.width ; - bMax = pickerRoot.clientHeight - 4 - rect.height; + rMax = pickerRoot.clientWidth - 2 - rect.width ; + bMax = pickerRoot.clientHeight - 2 - rect.height; dialog.classList.add('moving'); if ( isTouch ) { self.addEventListener('touchmove', moveAsync, { capture: true }); diff --git a/src/js/messaging.js b/src/js/messaging.js index c8c1751658061..b8e8cdf3264cf 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -154,7 +154,7 @@ const onMessage = function(request, sender, callback) { case 'launchElementPicker': // Launched from some auxiliary pages, clear context menu coords. µb.epickerArgs.mouse = false; - µb.elementPickerExec(request.tabId, request.targetURL, request.zap); + µb.elementPickerExec(request.tabId, 0, request.targetURL, request.zap); break; case 'gotoURL': diff --git a/src/js/ublock.js b/src/js/ublock.js index 1a0c0ac8281c2..bc765036ff47e 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -414,7 +414,12 @@ const matchBucket = function(url, hostname, bucket, start) { /******************************************************************************/ -µBlock.elementPickerExec = async function(tabId, targetElement, zap = false) { +µBlock.elementPickerExec = async function( + tabId, + frameId, + targetElement, + zap = false, +) { if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } this.epickerArgs.target = targetElement || ''; @@ -422,13 +427,16 @@ const matchBucket = function(url, hostname, bucket, start) { // https://github.com/uBlockOrigin/uBlock-issues/issues/40 // The element picker needs this library - vAPI.tabs.executeScript(tabId, { - file: '/lib/diff/swatinem_diff.js', - runAt: 'document_end', - }); + if ( zap !== true ) { + vAPI.tabs.executeScript(tabId, { + file: '/lib/diff/swatinem_diff.js', + runAt: 'document_end', + }); + } await vAPI.tabs.executeScript(tabId, { file: '/js/scriptlets/epicker.js', + frameId, runAt: 'document_end', });