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.
This commit is contained in:
Raymond Hill 2020-12-05 15:26:29 -05:00
parent 4b921f10e8
commit db7f54dbf6
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
8 changed files with 56 additions and 20 deletions

View File

@ -1010,6 +1010,7 @@ vAPI.messaging = {
case 'extendClient': case 'extendClient':
vAPI.tabs.executeScript(tabId, { vAPI.tabs.executeScript(tabId, {
file: '/js/vapi-client-extra.js', file: '/js/vapi-client-extra.js',
frameId: portDetails.frameId,
}).then(( ) => { }).then(( ) => {
callback(); callback();
}); });

View File

@ -1027,6 +1027,10 @@
"message": "bytes", "message": "bytes",
"description": "" "description": ""
}, },
"contextMenuBlockElementInFrame": {
"message": "Block element in frame...",
"description": "An entry in the browser's contextual menu"
},
"contextMenuTemporarilyAllowLargeMediaElements": { "contextMenuTemporarilyAllowLargeMediaElements": {
"message": "Temporarily allow large media elements", "message": "Temporarily allow large media elements",
"description": "A context menu entry, present when large media elements have been blocked on the current site" "description": "A context menu entry, present when large media elements have been blocked on the current site"

View File

@ -15,16 +15,18 @@ html#ublock0-epicker,
#ublock0-epicker aside { #ublock0-epicker aside {
background-color: var(--default-surface); background-color: var(--default-surface);
border: 1px solid var(--default-surface-border); border: 1px solid var(--default-surface-border);
bottom: 4px; bottom: 2px;
box-sizing: border-box; box-sizing: border-box;
cursor: default; cursor: default;
display: none; display: none;
max-height: calc(100vh - 4px);
max-width: 36rem; max-width: 36rem;
min-width: 24rem; min-width: 24rem;
overflow-y: auto;
padding: 4px; padding: 4px;
position: fixed; position: fixed;
right: 4px; right: 2px;
width: calc(40% - 4px); width: calc(40% - 2px);
} }
#ublock0-epicker.paused:not(.zap) aside { #ublock0-epicker.paused:not(.zap) aside {
display: block; display: block;
@ -89,6 +91,8 @@ html#ublock0-epicker,
box-sizing: border-box; box-sizing: border-box;
font: 11px monospace; font: 11px monospace;
height: 8em; height: 8em;
max-height: 50vh;
min-height: 1em;
padding: 2px; padding: 2px;
width: 100%; width: 100%;
} }

View File

@ -165,6 +165,7 @@ vAPI.commands.onCommand.addListener(async command => {
µb.epickerArgs.mouse = false; µb.epickerArgs.mouse = false;
µb.elementPickerExec( µb.elementPickerExec(
tab.id, tab.id,
0,
undefined, undefined,
command === 'launch-element-zapper' command === 'launch-element-zapper'
); );

View File

@ -58,7 +58,16 @@ const onBlockElement = function(details, tab) {
} }
µBlock.epickerArgs.mouse = true; µ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' ) { if ( details.menuItemId === 'uBlock0-blockElement' ) {
return onBlockElement(details, tab); return onBlockElement(details, tab);
} }
if ( details.menuItemId === 'uBlock0-blockElementInFrame' ) {
return onBlockElementInFrame(details, tab);
}
if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) { if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) {
return onTemporarilyAllowLargeMediaElements(details, tab); return onTemporarilyAllowLargeMediaElements(details, tab);
} }
@ -83,18 +95,23 @@ const onEntryClicked = function(details, tab) {
/******************************************************************************/ /******************************************************************************/
const menuEntries = [ const menuEntries = {
{ blockElement: {
id: 'uBlock0-blockElement', id: 'uBlock0-blockElement',
title: vAPI.i18n('pickerContextMenuEntry'), title: vAPI.i18n('pickerContextMenuEntry'),
contexts: ['all'], contexts: ['all'],
}, },
{ blockElementInFrame: {
id: 'uBlock0-blockElementInFrame',
title: vAPI.i18n('contextMenuBlockElementInFrame'),
contexts: ['frame'],
},
temporarilyAllowLargeMediaElements: {
id: 'uBlock0-temporarilyAllowLargeMediaElements', id: 'uBlock0-temporarilyAllowLargeMediaElements',
title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'), title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'),
contexts: ['all'], contexts: ['all'],
} }
]; };
/******************************************************************************/ /******************************************************************************/
@ -115,10 +132,11 @@ const update = function(tabId = undefined) {
currentBits = newBits; currentBits = newBits;
let usedEntries = []; let usedEntries = [];
if ( newBits & 0x01 ) { if ( newBits & 0x01 ) {
usedEntries.push(menuEntries[0]); usedEntries.push(menuEntries.blockElement);
usedEntries.push(menuEntries.blockElementInFrame);
} }
if ( newBits & 0x02 ) { if ( newBits & 0x02 ) {
usedEntries.push(menuEntries[1]); usedEntries.push(menuEntries.temporarilyAllowLargeMediaElements);
} }
vAPI.contextMenu.setEntries(usedEntries, onEntryClicked); vAPI.contextMenu.setEntries(usedEntries, onEntryClicked);
}; };

View File

@ -599,8 +599,8 @@ const onStartMoving = (( ) => {
const move = ( ) => { const move = ( ) => {
timer = undefined; timer = undefined;
const r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax); const r1 = Math.min(Math.max(r0 - mx1 + mx0, 2), rMax);
const b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax); const b1 = Math.min(Math.max(b0 - my1 + my0, 2), bMax);
dialog.style.setProperty('right', `${r1}px`); dialog.style.setProperty('right', `${r1}px`);
dialog.style.setProperty('bottom', `${b1}px`); dialog.style.setProperty('bottom', `${b1}px`);
}; };
@ -646,8 +646,8 @@ const onStartMoving = (( ) => {
r0 = parseInt(style.right, 10); r0 = parseInt(style.right, 10);
b0 = parseInt(style.bottom, 10); b0 = parseInt(style.bottom, 10);
const rect = dialog.getBoundingClientRect(); const rect = dialog.getBoundingClientRect();
rMax = pickerRoot.clientWidth - 4 - rect.width ; rMax = pickerRoot.clientWidth - 2 - rect.width ;
bMax = pickerRoot.clientHeight - 4 - rect.height; bMax = pickerRoot.clientHeight - 2 - rect.height;
dialog.classList.add('moving'); dialog.classList.add('moving');
if ( isTouch ) { if ( isTouch ) {
self.addEventListener('touchmove', moveAsync, { capture: true }); self.addEventListener('touchmove', moveAsync, { capture: true });

View File

@ -154,7 +154,7 @@ const onMessage = function(request, sender, callback) {
case 'launchElementPicker': case 'launchElementPicker':
// Launched from some auxiliary pages, clear context menu coords. // Launched from some auxiliary pages, clear context menu coords.
µb.epickerArgs.mouse = false; µb.epickerArgs.mouse = false;
µb.elementPickerExec(request.tabId, request.targetURL, request.zap); µb.elementPickerExec(request.tabId, 0, request.targetURL, request.zap);
break; break;
case 'gotoURL': case 'gotoURL':

View File

@ -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; } if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
this.epickerArgs.target = targetElement || ''; this.epickerArgs.target = targetElement || '';
@ -422,13 +427,16 @@ const matchBucket = function(url, hostname, bucket, start) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/40 // https://github.com/uBlockOrigin/uBlock-issues/issues/40
// The element picker needs this library // The element picker needs this library
if ( zap !== true ) {
vAPI.tabs.executeScript(tabId, { vAPI.tabs.executeScript(tabId, {
file: '/lib/diff/swatinem_diff.js', file: '/lib/diff/swatinem_diff.js',
runAt: 'document_end', runAt: 'document_end',
}); });
}
await vAPI.tabs.executeScript(tabId, { await vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/epicker.js', file: '/js/scriptlets/epicker.js',
frameId,
runAt: 'document_end', runAt: 'document_end',
}); });