Fix having picker & inspector active at the same time

Related feedback:
https://github.com/uBlockOrigin/uBlock-issues/issues/3004#issuecomment-1863610146
This commit is contained in:
Raymond Hill 2023-12-20 11:23:25 -05:00
parent 698bec4f5b
commit 9a8dd66517
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
2 changed files with 91 additions and 87 deletions

View file

@ -31,28 +31,26 @@
/******************************************************************************/
if ( typeof vAPI !== 'object' ) { return; }
if ( typeof vAPI === null ) { return; }
if ( vAPI.domFilterer instanceof Object === false ) { return; }
if ( document.querySelector(`iframe[${vAPI.sessionId}]`) !== null ) { return; }
/******************************************************************************/
/******************************************************************************/
if ( vAPI.inspectorFrame ) { return; }
vAPI.inspectorFrame = true;
const inspectorUniqueId = vAPI.randomToken();
const nodeToIdMap = new WeakMap(); // No need to iterate
let blueNodes = [];
const roRedNodes = new Map(); // node => current cosmetic filter
const rwRedNodes = new Set(); // node => new cosmetic filter (toggle node)
//var roGreenNodes = new Map(); // node => current exception cosmetic filter (can't toggle)
const rwGreenNodes = new Set(); // node => new exception cosmetic filter (toggle filter)
//const roGreenNodes = new Map(); // node => current exception cosmetic filter (can't toggle)
const reHasCSSCombinators = /[ >+~]/;
/******************************************************************************/
//const getNodeId = node => nodeToIdMap.get(node) || 0;
/******************************************************************************/
const domLayout = (( ) => {
const skipTagNames = new Set([
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
@ -670,11 +668,13 @@ const shutdownInspector = ( ) => {
passive: true,
});
contentInspectorChannel.shutdown();
vAPI.userStylesheet.remove(inspectorCSS);
vAPI.userStylesheet.apply();
if ( inspectorFrame === null ) { return; }
if ( inspectorFrame ) {
inspectorFrame.remove();
inspectorFrame = null;
}
vAPI.userStylesheet.remove(inspectorCSS);
vAPI.userStylesheet.apply();
vAPI.inspectorFrame = false;
};
/******************************************************************************/
@ -826,22 +826,22 @@ const contentInspectorChannel = (( ) => {
if ( inspectorArgs === null ) { return; }
return new Promise(resolve => {
const iframe = document.createElement('iframe');
iframe.setAttribute(vAPI.sessionId, '');
iframe.setAttribute(inspectorUniqueId, '');
document.documentElement.append(iframe);
iframe.addEventListener('load', ( ) => {
iframe.setAttribute(`${vAPI.sessionId}-loaded`, '');
iframe.setAttribute(`${inspectorUniqueId}-loaded`, '');
const channel = new MessageChannel();
toFramePort = channel.port1;
toFramePort.onmessage = ev => {
const msg = ev.data || {};
if ( msg.what !== 'startInspector' ) { return; }
resolve(iframe);
};
iframe.contentWindow.postMessage(
{ what: 'startInspector' },
inspectorArgs.inspectorURL,
[ channel.port2 ]
);
resolve(iframe);
}, { once: true });
iframe.contentWindow.location = inspectorArgs.inspectorURL;
});
@ -859,26 +859,32 @@ const inspectorCSSStyle = [
'box-shadow: none',
'color-scheme: light dark',
'display: block',
'filter: none',
'height: 100%',
'left: 0',
'margin: 0',
'max-height: none',
'max-width: none',
'min-height: unset',
'min-width: unset',
'opacity: 1',
'outline: 0',
'padding: 0',
'pointer-events: none',
'position: fixed',
'top: 0',
'visibility: visible',
'transform: none',
'visibility: hidden',
'width: 100%',
'z-index: 2147483647',
''
].join(' !important;\n');
const inspectorCSS = `
:root > [${vAPI.sessionId}] {
:root > [${inspectorUniqueId}] {
${inspectorCSSStyle}
}
:root > [${vAPI.sessionId}-loaded] {
:root > [${inspectorUniqueId}-loaded] {
visibility: visible !important;
}
`;

View file

@ -30,18 +30,13 @@
/******************************************************************************/
if ( typeof vAPI !== 'object' || vAPI === null ) {
return;
}
if ( typeof vAPI !== 'object' ) { return; }
if ( typeof vAPI === null ) { return; }
/******************************************************************************/
if ( vAPI.pickerFrame ) { return; }
vAPI.pickerFrame = true;
const epickerId = vAPI.randomToken();
let pickerRoot = document.querySelector(`[${vAPI.sessionId}]`);
if ( pickerRoot !== null ) { return; }
let pickerBootArgs;
const pickerUniqueId = vAPI.randomToken();
const reCosmeticAnchor = /^#(\$|\?|\$\?)?#/;
@ -128,7 +123,7 @@ const highlightElements = function(elems, force) {
const islands = [];
for ( const elem of elems ) {
if ( elem === pickerRoot ) { continue; }
if ( elem === pickerFrame ) { continue; }
targetElements.push(elem);
const rect = getElementBoundingClientRect(elem);
// Ignore offscreen areas
@ -554,10 +549,10 @@ const filtersFrom = function(x, y) {
// https://www.reddit.com/r/uBlockOrigin/comments/qmjk36/
// Extract network candidates first.
if ( typeof x === 'number' ) {
const magicAttr = `${vAPI.sessionId}-clickblind`;
pickerRoot.setAttribute(magicAttr, '');
const magicAttr = `${pickerUniqueId}-clickblind`;
pickerFrame.setAttribute(magicAttr, '');
const elems = document.elementsFromPoint(x, y);
pickerRoot.removeAttribute(magicAttr);
pickerFrame.removeAttribute(magicAttr);
for ( const elem of elems ) {
netFilterFromElement(elem);
}
@ -737,7 +732,7 @@ const filterToDOMInterface = (( ) => {
}
const out = [];
for ( const elem of elems ) {
if ( elem === pickerRoot ) { continue; }
if ( elem === pickerFrame ) { continue; }
out.push({ elem, raw, style: vAPI.hideStyle });
}
return out;
@ -815,7 +810,7 @@ const filterToDOMInterface = (( ) => {
if ( Array.isArray(lastResultset) === false ) { return; }
const rootElem = document.documentElement;
for ( const { elem, style } of lastResultset ) {
if ( elem === pickerRoot ) { continue; }
if ( elem === pickerFrame ) { continue; }
if ( style === undefined ) { continue; }
if ( elem === rootElem && style === vAPI.hideStyle ) { continue; }
let styleToken = vAPI.epickerStyleProxies.get(style);
@ -932,9 +927,9 @@ const elementFromPoint = (( ) => {
} else {
return null;
}
if ( !pickerRoot ) { return null; }
const magicAttr = `${vAPI.sessionId}-clickblind`;
pickerRoot.setAttribute(magicAttr, '');
if ( !pickerFrame ) { return null; }
const magicAttr = `${pickerUniqueId}-clickblind`;
pickerFrame.setAttribute(magicAttr, '');
let elem = document.elementFromPoint(x, y);
if (
elem === null || /* to skip following tests */
@ -948,7 +943,7 @@ const elementFromPoint = (( ) => {
elem = null;
}
// https://github.com/uBlockOrigin/uBlock-issues/issues/380
pickerRoot.removeAttribute(magicAttr);
pickerFrame.removeAttribute(magicAttr);
return elem;
};
})();
@ -1064,7 +1059,7 @@ const onViewportChanged = function() {
// Auto-select a specific target, if any, and if possible
const startPicker = function() {
pickerRoot.focus();
pickerFrame.focus();
self.addEventListener('scroll', onViewportChanged, { passive: true });
self.addEventListener('resize', onViewportChanged, { passive: true });
@ -1101,7 +1096,7 @@ const startPicker = function() {
if ( attr === undefined ) { return; }
const elems = document.getElementsByTagName(tagName);
for ( const elem of elems ) {
if ( elem === pickerRoot ) { continue; }
if ( elem === pickerFrame ) { continue; }
const srcs = resourceURLsFromElement(elem);
if (
(srcs.length !== 0 && srcs.includes(url) === false) ||
@ -1140,19 +1135,22 @@ const quitPicker = function() {
self.removeEventListener('resize', onViewportChanged, { passive: true });
self.removeEventListener('keydown', onKeyPressed, true);
vAPI.shutdown.remove(quitPicker);
if ( pickerFramePort !== null ) {
if ( pickerFramePort ) {
pickerFramePort.close();
pickerFramePort = null;
}
if ( pickerRoot !== null ) {
pickerRoot.remove();
pickerRoot = null;
if ( pickerFrame ) {
pickerFrame.remove();
pickerFrame = null;
}
vAPI.userStylesheet.remove(pickerCSS);
vAPI.userStylesheet.apply();
vAPI.pickerFrame = false;
self.focus();
};
vAPI.shutdown.add(quitPicker);
/******************************************************************************/
const onDialogMessage = function(msg) {
@ -1232,21 +1230,6 @@ const onDialogMessage = function(msg) {
// of the iframe, and cannot interfere with its style properties. However the
// page can remove the iframe.
// fetch/process picker arguments.
{
pickerBootArgs = await vAPI.messaging.send('elementPicker', {
what: 'elementPickerArguments',
});
if ( typeof pickerBootArgs !== 'object' ) { return; }
if ( pickerBootArgs === null ) { return; }
// Restore net filter union data if origin is the same.
const eprom = pickerBootArgs.eprom || null;
if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) {
lastNetFilterHostname = eprom.lastNetFilterHostname || '';
lastNetFilterUnion = eprom.lastNetFilterUnion || '';
}
}
// The DOM filterer will not be present when cosmetic filtering is disabled.
const noCosmeticFiltering =
vAPI.domFilterer instanceof Object === false ||
@ -1285,13 +1268,13 @@ const pickerCSSStyle = [
const pickerCSS = `
:root > [${vAPI.sessionId}] {
:root > [${pickerUniqueId}] {
${pickerCSSStyle}
}
:root > [${vAPI.sessionId}-loaded] {
:root > [${pickerUniqueId}-loaded] {
visibility: visible !important;
}
:root [${vAPI.sessionId}-clickblind] {
:root [${pickerUniqueId}-clickblind] {
pointer-events: none !important;
}
`;
@ -1299,38 +1282,53 @@ const pickerCSS = `
vAPI.userStylesheet.add(pickerCSS);
vAPI.userStylesheet.apply();
pickerRoot = document.createElement('iframe');
pickerRoot.setAttribute(vAPI.sessionId, '');
document.documentElement.append(pickerRoot);
vAPI.shutdown.add(quitPicker);
let pickerBootArgs;
let pickerFramePort = null;
{
const bootstrap = async ( ) => {
pickerBootArgs = await vAPI.messaging.send('elementPicker', {
what: 'elementPickerArguments',
});
if ( typeof pickerBootArgs !== 'object' ) { return; }
if ( pickerBootArgs === null ) { return; }
// Restore net filter union data if origin is the same.
const eprom = pickerBootArgs.eprom || null;
if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) {
lastNetFilterHostname = eprom.lastNetFilterHostname || '';
lastNetFilterUnion = eprom.lastNetFilterUnion || '';
}
const url = new URL(pickerBootArgs.pickerURL);
url.searchParams.set('epid', epickerId);
if ( pickerBootArgs.zap ) {
url.searchParams.set('zap', '1');
}
pickerRoot.addEventListener('load', ( ) => {
return new Promise(resolve => {
const iframe = document.createElement('iframe');
iframe.setAttribute(pickerUniqueId, '');
document.documentElement.append(iframe);
iframe.addEventListener('load', ( ) => {
iframe.setAttribute(`${pickerUniqueId}-loaded`, '');
const channel = new MessageChannel();
pickerFramePort = channel.port1;
pickerFramePort.onmessage = ev => {
const msg = ev.data || {};
onDialogMessage(msg);
onDialogMessage(ev.data || {});
};
pickerFramePort.onmessageerror = ( ) => {
quitPicker();
};
pickerRoot.setAttribute(`${vAPI.sessionId}-loaded`, '');
pickerRoot.contentWindow.postMessage(
iframe.contentWindow.postMessage(
{ what: 'epickerStart' },
url.href,
[ channel.port2 ]
);
resolve(iframe);
}, { once: true });
pickerRoot.contentWindow.location = url.href;
iframe.contentWindow.location = url.href;
});
};
let pickerFrame = await bootstrap();
if ( Boolean(pickerFrame) === false ) {
quitPicker();
}
/******************************************************************************/