mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-06 09:37:12 +02:00
Isolate DOM inspector layers from page context
Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1411 Additionally, refactored communication mechanism between content script contexts and uBO contexts by using MessageChannel/BroadcastChannel web APIs.
This commit is contained in:
parent
2c495487f1
commit
ee83a4304a
@ -967,71 +967,11 @@ vAPI.messaging = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
broadcast: function(message) {
|
|
||||||
const messageWrapper = { broadcast: true, msg: message };
|
|
||||||
for ( const { port } of this.ports.values() ) {
|
|
||||||
try {
|
|
||||||
port.postMessage(messageWrapper);
|
|
||||||
} catch(ex) {
|
|
||||||
this.onPortDisconnect(port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( this.defaultHandler ) {
|
|
||||||
this.defaultHandler(message, null, ( ) => { });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onFrameworkMessage: function(request, port, callback) {
|
onFrameworkMessage: function(request, port, callback) {
|
||||||
const portDetails = this.ports.get(port.name) || {};
|
const portDetails = this.ports.get(port.name) || {};
|
||||||
const tabId = portDetails.tabId;
|
const tabId = portDetails.tabId;
|
||||||
const msg = request.msg;
|
const msg = request.msg;
|
||||||
switch ( msg.what ) {
|
switch ( msg.what ) {
|
||||||
case 'connectionAccepted':
|
|
||||||
case 'connectionRefused': {
|
|
||||||
const toPort = this.ports.get(msg.fromToken);
|
|
||||||
if ( toPort !== undefined ) {
|
|
||||||
msg.tabId = tabId;
|
|
||||||
toPort.port.postMessage(request);
|
|
||||||
} else {
|
|
||||||
msg.what = 'connectionBroken';
|
|
||||||
port.postMessage(request);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'connectionRequested':
|
|
||||||
msg.tabId = tabId;
|
|
||||||
for ( const { port: toPort } of this.ports.values() ) {
|
|
||||||
if ( toPort === port ) { continue; }
|
|
||||||
try {
|
|
||||||
toPort.postMessage(request);
|
|
||||||
} catch (ex) {
|
|
||||||
this.onPortDisconnect(toPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'connectionBroken':
|
|
||||||
case 'connectionCheck':
|
|
||||||
case 'connectionMessage': {
|
|
||||||
const toPort = this.ports.get(
|
|
||||||
port.name === msg.fromToken ? msg.toToken : msg.fromToken
|
|
||||||
);
|
|
||||||
if ( toPort !== undefined ) {
|
|
||||||
msg.tabId = tabId;
|
|
||||||
toPort.port.postMessage(request);
|
|
||||||
} else {
|
|
||||||
msg.what = 'connectionBroken';
|
|
||||||
port.postMessage(request);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'extendClient':
|
|
||||||
vAPI.tabs.executeScript(tabId, {
|
|
||||||
file: '/js/vapi-client-extra.js',
|
|
||||||
frameId: portDetails.frameId,
|
|
||||||
}).then(( ) => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'localStorage': {
|
case 'localStorage': {
|
||||||
if ( portDetails.privileged !== true ) { break; }
|
if ( portDetails.privileged !== true ) { break; }
|
||||||
const args = msg.args || [];
|
const args = msg.args || [];
|
||||||
|
@ -1,264 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
uBlock Origin - a browser extension to block requests.
|
|
||||||
Copyright (C) 2019-present Raymond Hill
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uBlock
|
|
||||||
*/
|
|
||||||
|
|
||||||
// For non-background page
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// Direct messaging connection ability
|
|
||||||
|
|
||||||
(( ) => {
|
|
||||||
// >>>>>>>> start of private namespace
|
|
||||||
|
|
||||||
if ( typeof vAPI !== 'object' ) { return; }
|
|
||||||
if ( vAPI.messaging instanceof Object === false ) { return; }
|
|
||||||
if ( vAPI.MessagingConnection instanceof Function ) { return; }
|
|
||||||
|
|
||||||
const listeners = new Set();
|
|
||||||
const connections = new Map();
|
|
||||||
|
|
||||||
vAPI.MessagingConnection = class {
|
|
||||||
constructor(handler, details) {
|
|
||||||
this.messaging = vAPI.messaging;
|
|
||||||
this.handler = handler;
|
|
||||||
this.id = details.id;
|
|
||||||
this.to = details.to;
|
|
||||||
this.toToken = details.toToken;
|
|
||||||
this.from = details.from;
|
|
||||||
this.fromToken = details.fromToken;
|
|
||||||
this.checkTimer = undefined;
|
|
||||||
// On Firefox it appears ports are not automatically disconnected
|
|
||||||
// when navigating to another page.
|
|
||||||
const ctor = vAPI.MessagingConnection;
|
|
||||||
if ( ctor.pagehide !== undefined ) { return; }
|
|
||||||
ctor.pagehide = ( ) => {
|
|
||||||
for ( const connection of connections.values() ) {
|
|
||||||
connection.disconnect();
|
|
||||||
connection.handler(
|
|
||||||
connection.toDetails('connectionBroken')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener('pagehide', ctor.pagehide);
|
|
||||||
}
|
|
||||||
toDetails(what, payload) {
|
|
||||||
return {
|
|
||||||
what: what,
|
|
||||||
id: this.id,
|
|
||||||
from: this.from,
|
|
||||||
fromToken: this.fromToken,
|
|
||||||
to: this.to,
|
|
||||||
toToken: this.toToken,
|
|
||||||
payload: payload
|
|
||||||
};
|
|
||||||
}
|
|
||||||
disconnect() {
|
|
||||||
if ( this.checkTimer !== undefined ) {
|
|
||||||
clearTimeout(this.checkTimer);
|
|
||||||
this.checkTimer = undefined;
|
|
||||||
}
|
|
||||||
connections.delete(this.id);
|
|
||||||
const port = this.messaging.getPort();
|
|
||||||
if ( port === null ) { return; }
|
|
||||||
port.postMessage({
|
|
||||||
channel: 'vapi',
|
|
||||||
msg: this.toDetails('connectionBroken'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
checkAsync() {
|
|
||||||
if ( this.checkTimer !== undefined ) {
|
|
||||||
clearTimeout(this.checkTimer);
|
|
||||||
}
|
|
||||||
this.checkTimer = vAPI.setTimeout(
|
|
||||||
( ) => { this.check(); },
|
|
||||||
499
|
|
||||||
);
|
|
||||||
}
|
|
||||||
check() {
|
|
||||||
this.checkTimer = undefined;
|
|
||||||
if ( connections.has(this.id) === false ) { return; }
|
|
||||||
const port = this.messaging.getPort();
|
|
||||||
if ( port === null ) { return; }
|
|
||||||
port.postMessage({
|
|
||||||
channel: 'vapi',
|
|
||||||
msg: this.toDetails('connectionCheck'),
|
|
||||||
});
|
|
||||||
this.checkAsync();
|
|
||||||
}
|
|
||||||
receive(details) {
|
|
||||||
switch ( details.what ) {
|
|
||||||
case 'connectionAccepted':
|
|
||||||
this.toToken = details.toToken;
|
|
||||||
this.handler(details);
|
|
||||||
this.checkAsync();
|
|
||||||
break;
|
|
||||||
case 'connectionBroken':
|
|
||||||
connections.delete(this.id);
|
|
||||||
this.handler(details);
|
|
||||||
break;
|
|
||||||
case 'connectionMessage':
|
|
||||||
this.handler(details);
|
|
||||||
this.checkAsync();
|
|
||||||
break;
|
|
||||||
case 'connectionCheck':
|
|
||||||
const port = this.messaging.getPort();
|
|
||||||
if ( port === null ) { return; }
|
|
||||||
if ( connections.has(this.id) ) {
|
|
||||||
this.checkAsync();
|
|
||||||
} else {
|
|
||||||
details.what = 'connectionBroken';
|
|
||||||
port.postMessage({ channel: 'vapi', msg: details });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'connectionRefused':
|
|
||||||
connections.delete(this.id);
|
|
||||||
this.handler(details);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
send(payload) {
|
|
||||||
const port = this.messaging.getPort();
|
|
||||||
if ( port === null ) { return; }
|
|
||||||
port.postMessage({
|
|
||||||
channel: 'vapi',
|
|
||||||
msg: this.toDetails('connectionMessage', payload),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static addListener(listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
vAPI.messaging.getPort(); // Ensure a port instance exists
|
|
||||||
}
|
|
||||||
static removeListener(listener) {
|
|
||||||
listeners.delete(listener);
|
|
||||||
}
|
|
||||||
static connectTo(from, to, handler) {
|
|
||||||
const port = vAPI.messaging.getPort();
|
|
||||||
if ( port === null ) { return; }
|
|
||||||
const connection = new vAPI.MessagingConnection(handler, {
|
|
||||||
id: `${from}-${to}-${vAPI.sessionId}`,
|
|
||||||
to: to,
|
|
||||||
from: from,
|
|
||||||
fromToken: port.name
|
|
||||||
});
|
|
||||||
connections.set(connection.id, connection);
|
|
||||||
port.postMessage({
|
|
||||||
channel: 'vapi',
|
|
||||||
msg: {
|
|
||||||
what: 'connectionRequested',
|
|
||||||
id: connection.id,
|
|
||||||
from: from,
|
|
||||||
fromToken: port.name,
|
|
||||||
to: to,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return connection.id;
|
|
||||||
}
|
|
||||||
static disconnectFrom(connectionId) {
|
|
||||||
const connection = connections.get(connectionId);
|
|
||||||
if ( connection === undefined ) { return; }
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
static sendTo(connectionId, payload) {
|
|
||||||
const connection = connections.get(connectionId);
|
|
||||||
if ( connection === undefined ) { return; }
|
|
||||||
connection.send(payload);
|
|
||||||
}
|
|
||||||
static canDestroyPort() {
|
|
||||||
return listeners.length === 0 && connections.size === 0;
|
|
||||||
}
|
|
||||||
static mustDestroyPort() {
|
|
||||||
if ( connections.size === 0 ) { return; }
|
|
||||||
for ( const connection of connections.values() ) {
|
|
||||||
connection.receive({ what: 'connectionBroken' });
|
|
||||||
}
|
|
||||||
connections.clear();
|
|
||||||
}
|
|
||||||
static canProcessMessage(details) {
|
|
||||||
if ( details.channel !== 'vapi' ) { return; }
|
|
||||||
switch ( details.msg.what ) {
|
|
||||||
case 'connectionAccepted':
|
|
||||||
case 'connectionBroken':
|
|
||||||
case 'connectionCheck':
|
|
||||||
case 'connectionMessage':
|
|
||||||
case 'connectionRefused': {
|
|
||||||
const connection = connections.get(details.msg.id);
|
|
||||||
if ( connection === undefined ) { break; }
|
|
||||||
connection.receive(details.msg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case 'connectionRequested':
|
|
||||||
if ( listeners.length === 0 ) { return; }
|
|
||||||
const port = vAPI.messaging.getPort();
|
|
||||||
if ( port === null ) { break; }
|
|
||||||
let listener, result;
|
|
||||||
for ( listener of listeners ) {
|
|
||||||
result = listener(details.msg);
|
|
||||||
if ( result !== undefined ) { break; }
|
|
||||||
}
|
|
||||||
if ( result === undefined ) { break; }
|
|
||||||
if ( result === true ) {
|
|
||||||
details.msg.what = 'connectionAccepted';
|
|
||||||
details.msg.toToken = port.name;
|
|
||||||
const connection = new vAPI.MessagingConnection(
|
|
||||||
listener,
|
|
||||||
details.msg
|
|
||||||
);
|
|
||||||
connections.set(connection.id, connection);
|
|
||||||
} else {
|
|
||||||
details.msg.what = 'connectionRefused';
|
|
||||||
}
|
|
||||||
port.postMessage(details);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vAPI.messaging.extensions.push(vAPI.MessagingConnection);
|
|
||||||
|
|
||||||
// <<<<<<<< end of private namespace
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
DO NOT:
|
|
||||||
- Remove the following code
|
|
||||||
- Add code beyond the following code
|
|
||||||
Reason:
|
|
||||||
- https://github.com/gorhill/uBlock/pull/3721
|
|
||||||
- uBO never uses the return value from injected content scripts
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
void 0;
|
|
@ -83,8 +83,6 @@ vAPI.messaging = {
|
|||||||
port: null,
|
port: null,
|
||||||
portTimer: null,
|
portTimer: null,
|
||||||
portTimerDelay: 10000,
|
portTimerDelay: 10000,
|
||||||
extended: undefined,
|
|
||||||
extensions: [],
|
|
||||||
msgIdGenerator: 1,
|
msgIdGenerator: 1,
|
||||||
pending: new Map(),
|
pending: new Map(),
|
||||||
shuttingDown: false,
|
shuttingDown: false,
|
||||||
@ -127,23 +125,11 @@ vAPI.messaging = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unhandled messages
|
|
||||||
this.extensions.every(ext => ext.canProcessMessage(details) !== true);
|
|
||||||
},
|
},
|
||||||
messageListenerBound: null,
|
messageListenerBound: null,
|
||||||
|
|
||||||
canDestroyPort: function() {
|
canDestroyPort: function() {
|
||||||
return this.pending.size === 0 && (
|
return this.pending.size === 0;
|
||||||
this.extensions.length === 0 ||
|
|
||||||
this.extensions.every(e => e.canDestroyPort())
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
mustDestroyPort: function() {
|
|
||||||
if ( this.extensions.length === 0 ) { return; }
|
|
||||||
this.extensions.forEach(e => e.mustDestroyPort());
|
|
||||||
this.extensions.length = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
portPoller: function() {
|
portPoller: function() {
|
||||||
@ -168,7 +154,6 @@ vAPI.messaging = {
|
|||||||
port.onDisconnect.removeListener(this.disconnectListenerBound);
|
port.onDisconnect.removeListener(this.disconnectListenerBound);
|
||||||
this.port = null;
|
this.port = null;
|
||||||
}
|
}
|
||||||
this.mustDestroyPort();
|
|
||||||
// service pending callbacks
|
// service pending callbacks
|
||||||
if ( this.pending.size !== 0 ) {
|
if ( this.pending.size !== 0 ) {
|
||||||
const pending = this.pending;
|
const pending = this.pending;
|
||||||
@ -232,22 +217,6 @@ vAPI.messaging = {
|
|||||||
port.postMessage({ channel, msgId, msg });
|
port.postMessage({ channel, msgId, msg });
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Dynamically extend capabilities.
|
|
||||||
//
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/1571
|
|
||||||
// Don't use `self` to access `vAPI`.
|
|
||||||
extend: function() {
|
|
||||||
if ( this.extended === undefined ) {
|
|
||||||
this.extended = vAPI.messaging.send('vapi', {
|
|
||||||
what: 'extendClient'
|
|
||||||
}).then(( ) =>
|
|
||||||
typeof vAPI === 'object' && this.extensions.length !== 0
|
|
||||||
).catch(( ) => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.extended;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vAPI.shutdown.add(( ) => {
|
vAPI.shutdown.add(( ) => {
|
||||||
|
@ -53,7 +53,6 @@
|
|||||||
<script src="js/vapi.js"></script>
|
<script src="js/vapi.js"></script>
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
<script src="js/vapi-client-extra.js"></script>
|
|
||||||
|
|
||||||
<script src="js/codemirror/search.js" type="module"></script>
|
<script src="js/codemirror/search.js" type="module"></script>
|
||||||
<script src="js/codemirror/search-thread.js"></script>
|
<script src="js/codemirror/search-thread.js"></script>
|
||||||
|
@ -108,7 +108,6 @@
|
|||||||
<script src="js/vapi.js"></script>
|
<script src="js/vapi.js"></script>
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
<script src="js/vapi-client-extra.js"></script>
|
|
||||||
<script src="js/theme.js" type="module"></script>
|
<script src="js/theme.js" type="module"></script>
|
||||||
<script src="js/i18n.js" type="module"></script>
|
<script src="js/i18n.js" type="module"></script>
|
||||||
<script src="js/dashboard-common.js" type="module"></script>
|
<script src="js/dashboard-common.js" type="module"></script>
|
||||||
|
38
src/css/dom-inspector.css
Normal file
38
src/css/dom-inspector.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
html#ublock0-inspector,
|
||||||
|
#ublock0-inspector body {
|
||||||
|
background: transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
#ublock0-inspector :focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#ublock0-inspector svg {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#ublock0-inspector svg > path:nth-of-type(1) {
|
||||||
|
fill: rgba(255,0,0,0.2);
|
||||||
|
stroke: #F00;
|
||||||
|
}
|
||||||
|
#ublock0-inspector svg > path:nth-of-type(2) {
|
||||||
|
fill: rgba(0,255,0,0.2);
|
||||||
|
stroke: #0F0;
|
||||||
|
}
|
||||||
|
#ublock0-inspector svg > path:nth-of-type(3) {
|
||||||
|
fill: rgba(255,0,0,0.2);
|
||||||
|
stroke: #F00;
|
||||||
|
}
|
||||||
|
#ublock0-inspector svg > path:nth-of-type(4) {
|
||||||
|
fill: rgba(0,0,255,0.1);
|
||||||
|
stroke: #FFF;
|
||||||
|
stroke-width: 0.5px;
|
||||||
|
}
|
92
src/js/dom-inspector.js
Normal file
92
src/js/dom-inspector.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2014-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const svgRoot = document.querySelector('svg');
|
||||||
|
|
||||||
|
const quit = ( ) => {
|
||||||
|
inspectorContentPort.postMessage({ what: 'quitInspector' });
|
||||||
|
inspectorContentPort.close();
|
||||||
|
inspectorContentPort.onmessage = inspectorContentPort.onmessageerror = null;
|
||||||
|
inspectorContentPort = undefined;
|
||||||
|
loggerPort.postMessage({ what: 'quitInspector' });
|
||||||
|
loggerPort.close();
|
||||||
|
loggerPort.onmessage = loggerPort.onmessageerror = null;
|
||||||
|
loggerPort = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessage = (msg, fromLogger) => {
|
||||||
|
switch ( msg.what ) {
|
||||||
|
case 'quitInspector': {
|
||||||
|
quit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'svgPaths': {
|
||||||
|
const paths = svgRoot.children;
|
||||||
|
paths[0].setAttribute('d', msg.paths[0]);
|
||||||
|
paths[1].setAttribute('d', msg.paths[1]);
|
||||||
|
paths[2].setAttribute('d', msg.paths[2]);
|
||||||
|
paths[3].setAttribute('d', msg.paths[3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if ( typeof fromLogger !== 'boolean' ) { return; }
|
||||||
|
if ( fromLogger ) {
|
||||||
|
inspectorContentPort.postMessage(msg);
|
||||||
|
} else {
|
||||||
|
loggerPort.postMessage(msg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait for the content script to establish communication
|
||||||
|
|
||||||
|
let inspectorContentPort;
|
||||||
|
|
||||||
|
let loggerPort = new globalThis.BroadcastChannel('loggerInspector');
|
||||||
|
loggerPort.onmessage = ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
onMessage(msg, true);
|
||||||
|
};
|
||||||
|
loggerPort.onmessageerror = ( ) => {
|
||||||
|
quit();
|
||||||
|
};
|
||||||
|
|
||||||
|
globalThis.addEventListener('message', ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
if ( msg.what !== 'startInspector' ) { return; }
|
||||||
|
if ( Array.isArray(ev.ports) === false ) { return; }
|
||||||
|
if ( ev.ports.length === 0 ) { return; }
|
||||||
|
inspectorContentPort = ev.ports[0];
|
||||||
|
inspectorContentPort.onmessage = ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
onMessage(msg, false);
|
||||||
|
};
|
||||||
|
inspectorContentPort.onmessageerror = ( ) => {
|
||||||
|
quit();
|
||||||
|
};
|
||||||
|
inspectorContentPort.postMessage({ what: 'startInspector' });
|
||||||
|
}, { once: true });
|
@ -53,18 +53,15 @@ const NoPaths = 'M0 0';
|
|||||||
|
|
||||||
const reCosmeticAnchor = /^#(\$|\?|\$\?)?#/;
|
const reCosmeticAnchor = /^#(\$|\?|\$\?)?#/;
|
||||||
|
|
||||||
const epickerId = (( ) => {
|
{
|
||||||
const url = new URL(self.location.href);
|
const url = new URL(self.location.href);
|
||||||
if ( url.searchParams.has('zap') ) {
|
if ( url.searchParams.has('zap') ) {
|
||||||
pickerRoot.classList.add('zap');
|
pickerRoot.classList.add('zap');
|
||||||
}
|
}
|
||||||
return url.searchParams.get('epid');
|
}
|
||||||
})();
|
|
||||||
if ( epickerId === null ) { return; }
|
|
||||||
|
|
||||||
const docURL = new URL(vAPI.getURL(''));
|
const docURL = new URL(vAPI.getURL(''));
|
||||||
|
|
||||||
let epickerConnectionId;
|
|
||||||
let resultsetOpt;
|
let resultsetOpt;
|
||||||
|
|
||||||
let netFilterCandidates = [];
|
let netFilterCandidates = [];
|
||||||
@ -305,7 +302,7 @@ const cosmeticCandidatesFromFilterChoice = function(filterChoice) {
|
|||||||
candidates.push(paths);
|
candidates.push(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'optimizeCandidates',
|
what: 'optimizeCandidates',
|
||||||
candidates,
|
candidates,
|
||||||
slot,
|
slot,
|
||||||
@ -333,7 +330,7 @@ const onSvgClicked = function(ev) {
|
|||||||
// If zap mode, highlight element under mouse, this makes the zapper usable
|
// If zap mode, highlight element under mouse, this makes the zapper usable
|
||||||
// on touch screens.
|
// on touch screens.
|
||||||
if ( pickerRoot.classList.contains('zap') ) {
|
if ( pickerRoot.classList.contains('zap') ) {
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'zapElementAtPoint',
|
what: 'zapElementAtPoint',
|
||||||
mx: ev.clientX,
|
mx: ev.clientX,
|
||||||
my: ev.clientY,
|
my: ev.clientY,
|
||||||
@ -358,7 +355,7 @@ const onSvgClicked = function(ev) {
|
|||||||
if ( ev.type === 'touch' ) {
|
if ( ev.type === 'touch' ) {
|
||||||
pickerRoot.classList.add('show');
|
pickerRoot.classList.add('show');
|
||||||
}
|
}
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'filterElementAtPoint',
|
what: 'filterElementAtPoint',
|
||||||
mx: ev.clientX,
|
mx: ev.clientX,
|
||||||
my: ev.clientY,
|
my: ev.clientY,
|
||||||
@ -432,7 +429,7 @@ const onSvgTouch = (( ) => {
|
|||||||
pickerRoot.classList.contains('zap') &&
|
pickerRoot.classList.contains('zap') &&
|
||||||
svgIslands.getAttribute('d') !== NoPaths
|
svgIslands.getAttribute('d') !== NoPaths
|
||||||
) {
|
) {
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'unhighlight'
|
what: 'unhighlight'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -463,7 +460,7 @@ const onCandidateChanged = function() {
|
|||||||
$id('resultsetModifiers').classList.toggle(
|
$id('resultsetModifiers').classList.toggle(
|
||||||
'hide', text === '' || text !== computedCandidate
|
'hide', text === '' || text !== computedCandidate
|
||||||
);
|
);
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'dialogSetFilter',
|
what: 'dialogSetFilter',
|
||||||
filter,
|
filter,
|
||||||
compiled: reCosmeticAnchor.test(filter)
|
compiled: reCosmeticAnchor.test(filter)
|
||||||
@ -476,7 +473,7 @@ const onCandidateChanged = function() {
|
|||||||
|
|
||||||
const onPreviewClicked = function() {
|
const onPreviewClicked = function() {
|
||||||
const state = pickerRoot.classList.toggle('preview');
|
const state = pickerRoot.classList.toggle('preview');
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'togglePreview',
|
what: 'togglePreview',
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
@ -496,7 +493,7 @@ const onCreateClicked = function() {
|
|||||||
killCache: reCosmeticAnchor.test(candidate) === false,
|
killCache: reCosmeticAnchor.test(candidate) === false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'dialogCreate',
|
what: 'dialogCreate',
|
||||||
filter: candidate,
|
filter: candidate,
|
||||||
compiled: reCosmeticAnchor.test(candidate)
|
compiled: reCosmeticAnchor.test(candidate)
|
||||||
@ -578,7 +575,7 @@ const onKeyPressed = function(ev) {
|
|||||||
(ev.key === 'Delete' || ev.key === 'Backspace') &&
|
(ev.key === 'Delete' || ev.key === 'Backspace') &&
|
||||||
pickerRoot.classList.contains('zap')
|
pickerRoot.classList.contains('zap')
|
||||||
) {
|
) {
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'zapElementAtPoint',
|
what: 'zapElementAtPoint',
|
||||||
options: { stay: true },
|
options: { stay: true },
|
||||||
});
|
});
|
||||||
@ -678,7 +675,7 @@ const svgListening = (( ) => {
|
|||||||
|
|
||||||
const onTimer = ( ) => {
|
const onTimer = ( ) => {
|
||||||
timer = undefined;
|
timer = undefined;
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'highlightElementAtPoint',
|
what: 'highlightElementAtPoint',
|
||||||
mx,
|
mx,
|
||||||
my,
|
my,
|
||||||
@ -798,7 +795,7 @@ const pausePicker = function() {
|
|||||||
|
|
||||||
const unpausePicker = function() {
|
const unpausePicker = function() {
|
||||||
pickerRoot.classList.remove('paused', 'preview');
|
pickerRoot.classList.remove('paused', 'preview');
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerContentPort.postMessage({
|
||||||
what: 'togglePreview',
|
what: 'togglePreview',
|
||||||
state: false,
|
state: false,
|
||||||
});
|
});
|
||||||
@ -838,8 +835,9 @@ const startPicker = function() {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const quitPicker = function() {
|
const quitPicker = function() {
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, { what: 'quitPicker' });
|
pickerContentPort.postMessage({ what: 'quitPicker' });
|
||||||
vAPI.MessagingConnection.disconnectFrom(epickerConnectionId);
|
pickerContentPort.close();
|
||||||
|
pickerContentPort = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -876,49 +874,27 @@ const onPickerMessage = function(msg) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const onConnectionMessage = function(msg) {
|
// Wait for the content script to establish communication
|
||||||
switch ( msg.what ) {
|
|
||||||
case 'connectionBroken':
|
|
||||||
break;
|
|
||||||
case 'connectionMessage':
|
|
||||||
onPickerMessage(msg.payload);
|
|
||||||
break;
|
|
||||||
case 'connectionAccepted':
|
|
||||||
epickerConnectionId = msg.id;
|
|
||||||
startPicker();
|
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
|
||||||
what: 'start',
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vAPI.MessagingConnection.connectTo(
|
let pickerContentPort;
|
||||||
`epickerDialog-${epickerId}`,
|
|
||||||
`epicker-${epickerId}`,
|
globalThis.addEventListener('message', ev => {
|
||||||
onConnectionMessage
|
const msg = ev.data || {};
|
||||||
);
|
if ( msg.what !== 'epickerStart' ) { return; }
|
||||||
|
if ( Array.isArray(ev.ports) === false ) { return; }
|
||||||
|
if ( ev.ports.length === 0 ) { return; }
|
||||||
|
pickerContentPort = ev.ports[0];
|
||||||
|
pickerContentPort.onmessage = ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
onPickerMessage(msg);
|
||||||
|
};
|
||||||
|
pickerContentPort.onmessageerror = ( ) => {
|
||||||
|
quitPicker();
|
||||||
|
};
|
||||||
|
startPicker();
|
||||||
|
pickerContentPort.postMessage({ what: 'start' });
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
DO NOT:
|
|
||||||
- Remove the following code
|
|
||||||
- Add code beyond the following code
|
|
||||||
Reason:
|
|
||||||
- https://github.com/gorhill/uBlock/pull/3721
|
|
||||||
- uBO never uses the return value from injected content scripts
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
void 0;
|
|
||||||
|
@ -44,64 +44,44 @@ if (
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const logger = self.logger;
|
const logger = self.logger;
|
||||||
var inspectorConnectionId;
|
const inspector = qs$('#domInspector');
|
||||||
var inspectedTabId = 0;
|
const domTree = qs$('#domTree');
|
||||||
var inspectedURL = '';
|
const filterToIdMap = new Map();
|
||||||
var inspectedHostname = '';
|
|
||||||
var inspector = qs$('#domInspector');
|
let inspectedTabId = 0;
|
||||||
var domTree = qs$('#domTree');
|
let inspectedURL = '';
|
||||||
var uidGenerator = 1;
|
let inspectedHostname = '';
|
||||||
var filterToIdMap = new Map();
|
let uidGenerator = 1;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const messaging = vAPI.messaging;
|
const inspectorFramePort = new globalThis.BroadcastChannel('loggerInspector');
|
||||||
|
inspectorFramePort.onmessage = ev => {
|
||||||
vAPI.MessagingConnection.addListener(function(msg) {
|
const msg = ev.data || {};
|
||||||
if ( msg.from !== 'domInspector' || msg.to !== 'loggerUI' ) { return; }
|
if ( msg.what === 'domLayoutFull' ) {
|
||||||
switch ( msg.what ) {
|
inspectedURL = msg.url;
|
||||||
case 'connectionBroken':
|
inspectedHostname = msg.hostname;
|
||||||
if ( inspectorConnectionId === msg.id ) {
|
renderDOMFull(msg);
|
||||||
filterToIdMap.clear();
|
} else if ( msg.what === 'domLayoutIncremental' ) {
|
||||||
logger.removeAllChildren(domTree);
|
renderDOMIncremental(msg);
|
||||||
inspectorConnectionId = undefined;
|
|
||||||
}
|
}
|
||||||
injectInspector();
|
};
|
||||||
break;
|
inspectorFramePort.onmessageerror = ( ) => {
|
||||||
case 'connectionMessage':
|
};
|
||||||
if ( msg.payload.what === 'domLayoutFull' ) {
|
|
||||||
inspectedURL = msg.payload.url;
|
|
||||||
inspectedHostname = msg.payload.hostname;
|
|
||||||
renderDOMFull(msg.payload);
|
|
||||||
} else if ( msg.payload.what === 'domLayoutIncremental' ) {
|
|
||||||
renderDOMIncremental(msg.payload);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'connectionRequested':
|
|
||||||
if ( msg.tabId === undefined || msg.tabId !== inspectedTabId ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
filterToIdMap.clear();
|
|
||||||
logger.removeAllChildren(domTree);
|
|
||||||
inspectorConnectionId = msg.id;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const nodeFromDomEntry = function(entry) {
|
const nodeFromDomEntry = function(entry) {
|
||||||
var node, value;
|
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
dom.attr(li, 'id', entry.nid);
|
dom.attr(li, 'id', entry.nid);
|
||||||
// expander/collapser
|
// expander/collapser
|
||||||
li.appendChild(document.createElement('span'));
|
li.appendChild(document.createElement('span'));
|
||||||
// selector
|
// selector
|
||||||
node = document.createElement('code');
|
let node = document.createElement('code');
|
||||||
node.textContent = entry.sel;
|
node.textContent = entry.sel;
|
||||||
li.appendChild(node);
|
li.appendChild(node);
|
||||||
// descendant count
|
// descendant count
|
||||||
value = entry.cnt || 0;
|
let value = entry.cnt || 0;
|
||||||
node = document.createElement('span');
|
node = document.createElement('span');
|
||||||
node.textContent = value !== 0 ? value.toLocaleString() : '';
|
node.textContent = value !== 0 ? value.toLocaleString() : '';
|
||||||
dom.attr(node, 'data-cnt', value);
|
dom.attr(node, 'data-cnt', value);
|
||||||
@ -114,7 +94,7 @@ const nodeFromDomEntry = function(entry) {
|
|||||||
dom.cl.add(node, 'filter');
|
dom.cl.add(node, 'filter');
|
||||||
value = filterToIdMap.get(entry.filter);
|
value = filterToIdMap.get(entry.filter);
|
||||||
if ( value === undefined ) {
|
if ( value === undefined ) {
|
||||||
value = uidGenerator.toString();
|
value = `${uidGenerator}`;
|
||||||
filterToIdMap.set(entry.filter, value);
|
filterToIdMap.set(entry.filter, value);
|
||||||
uidGenerator += 1;
|
uidGenerator += 1;
|
||||||
}
|
}
|
||||||
@ -142,18 +122,15 @@ const appendListItem = function(ul, li) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const renderDOMFull = function(response) {
|
const renderDOMFull = function(response) {
|
||||||
var domTreeParent = domTree.parentElement;
|
const domTreeParent = domTree.parentElement;
|
||||||
var ul = domTreeParent.removeChild(domTree);
|
let ul = domTreeParent.removeChild(domTree);
|
||||||
logger.removeAllChildren(domTree);
|
logger.removeAllChildren(domTree);
|
||||||
|
|
||||||
filterToIdMap.clear();
|
filterToIdMap.clear();
|
||||||
|
|
||||||
var lvl = 0;
|
let lvl = 0;
|
||||||
var entries = response.layout;
|
let li;
|
||||||
var n = entries.length;
|
for ( const entry of response.layout ) {
|
||||||
var li, entry;
|
|
||||||
for ( var i = 0; i < n; i++ ) {
|
|
||||||
entry = entries[i];
|
|
||||||
if ( entry.lvl === lvl ) {
|
if ( entry.lvl === lvl ) {
|
||||||
li = nodeFromDomEntry(entry);
|
li = nodeFromDomEntry(entry);
|
||||||
appendListItem(ul, li);
|
appendListItem(ul, li);
|
||||||
@ -186,14 +163,11 @@ const renderDOMFull = function(response) {
|
|||||||
domTreeParent.appendChild(domTree);
|
domTreeParent.appendChild(domTree);
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://www.youtube.com/watch?v=IDGNA83mxDo
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const patchIncremental = function(from, delta) {
|
const patchIncremental = function(from, delta) {
|
||||||
var span, cnt;
|
let li = from.parentElement.parentElement;
|
||||||
var li = from.parentElement.parentElement;
|
const patchCosmeticHide = delta >= 0 &&
|
||||||
var patchCosmeticHide = delta >= 0 &&
|
|
||||||
dom.cl.has(from, 'isCosmeticHide') &&
|
dom.cl.has(from, 'isCosmeticHide') &&
|
||||||
dom.cl.has(li, 'hasCosmeticHide') === false;
|
dom.cl.has(li, 'hasCosmeticHide') === false;
|
||||||
// Include descendants count when removing a node
|
// Include descendants count when removing a node
|
||||||
@ -201,9 +175,9 @@ const patchIncremental = function(from, delta) {
|
|||||||
delta -= countFromNode(from);
|
delta -= countFromNode(from);
|
||||||
}
|
}
|
||||||
for ( ; li.localName === 'li'; li = li.parentElement.parentElement ) {
|
for ( ; li.localName === 'li'; li = li.parentElement.parentElement ) {
|
||||||
span = li.children[2];
|
const span = li.children[2];
|
||||||
if ( delta !== 0 ) {
|
if ( delta !== 0 ) {
|
||||||
cnt = countFromNode(li) + delta;
|
const cnt = countFromNode(li) + delta;
|
||||||
span.textContent = cnt !== 0 ? cnt.toLocaleString() : '';
|
span.textContent = cnt !== 0 ? cnt.toLocaleString() : '';
|
||||||
dom.attr(span, 'data-cnt', cnt);
|
dom.attr(span, 'data-cnt', cnt);
|
||||||
}
|
}
|
||||||
@ -219,11 +193,10 @@ const renderDOMIncremental = function(response) {
|
|||||||
// Process each journal entry:
|
// Process each journal entry:
|
||||||
// 1 = node added
|
// 1 = node added
|
||||||
// -1 = node removed
|
// -1 = node removed
|
||||||
var journal = response.journal;
|
const nodes = new Map(response.nodes);
|
||||||
var nodes = new Map(response.nodes);
|
let li = null;
|
||||||
var entry, previous, li, ul;
|
let ul = null;
|
||||||
for ( var i = 0, n = journal.length; i < n; i++ ) {
|
for ( const entry of response.journal ) {
|
||||||
entry = journal[i];
|
|
||||||
// Remove node
|
// Remove node
|
||||||
if ( entry.what === -1 ) {
|
if ( entry.what === -1 ) {
|
||||||
li = qs$(`#${entry.nid}`);
|
li = qs$(`#${entry.nid}`);
|
||||||
@ -239,7 +212,7 @@ const renderDOMIncremental = function(response) {
|
|||||||
}
|
}
|
||||||
// Add node as sibling
|
// Add node as sibling
|
||||||
if ( entry.what === 1 && entry.l ) {
|
if ( entry.what === 1 && entry.l ) {
|
||||||
previous = qs$(`#${entry.l}`);
|
const previous = qs$(`#${entry.l}`);
|
||||||
// This should not happen
|
// This should not happen
|
||||||
if ( previous === null ) {
|
if ( previous === null ) {
|
||||||
// throw new Error('No left sibling!?');
|
// throw new Error('No left sibling!?');
|
||||||
@ -276,24 +249,21 @@ const renderDOMIncremental = function(response) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const countFromNode = function(li) {
|
const countFromNode = function(li) {
|
||||||
var span = li.children[2];
|
const span = li.children[2];
|
||||||
var cnt = parseInt(dom.attr(span, 'data-cnt'), 10);
|
const cnt = parseInt(dom.attr(span, 'data-cnt'), 10);
|
||||||
return isNaN(cnt) ? 0 : cnt;
|
return isNaN(cnt) ? 0 : cnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const selectorFromNode = function(node) {
|
const selectorFromNode = function(node) {
|
||||||
var selector = '';
|
let selector = '';
|
||||||
var code;
|
|
||||||
while ( node !== null ) {
|
while ( node !== null ) {
|
||||||
if ( node.localName === 'li' ) {
|
if ( node.localName === 'li' ) {
|
||||||
code = qs$(node, 'code');
|
const code = qs$(node, 'code');
|
||||||
if ( code !== null ) {
|
if ( code !== null ) {
|
||||||
selector = code.textContent + ' > ' + selector;
|
selector = `${code.textContent} > ${selector}`;
|
||||||
if ( selector.indexOf('#') !== -1 ) {
|
if ( selector.includes('#') ) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node = node.parentElement;
|
node = node.parentElement;
|
||||||
@ -306,7 +276,7 @@ const selectorFromNode = function(node) {
|
|||||||
const selectorFromFilter = function(node) {
|
const selectorFromFilter = function(node) {
|
||||||
while ( node !== null ) {
|
while ( node !== null ) {
|
||||||
if ( node.localName === 'li' ) {
|
if ( node.localName === 'li' ) {
|
||||||
var code = qs$(node, 'code:nth-of-type(2)');
|
const code = qs$(node, 'code:nth-of-type(2)');
|
||||||
if ( code !== null ) {
|
if ( code !== null ) {
|
||||||
return code.textContent;
|
return code.textContent;
|
||||||
}
|
}
|
||||||
@ -319,7 +289,7 @@ const selectorFromFilter = function(node) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const nidFromNode = function(node) {
|
const nidFromNode = function(node) {
|
||||||
var li = node;
|
let li = node;
|
||||||
while ( li !== null ) {
|
while ( li !== null ) {
|
||||||
if ( li.localName === 'li' ) {
|
if ( li.localName === 'li' ) {
|
||||||
return li.id || '';
|
return li.id || '';
|
||||||
@ -367,17 +337,17 @@ const startDialog = (function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onClicked = function(ev) {
|
const onClicked = function(ev) {
|
||||||
var target = ev.target;
|
const target = ev.target;
|
||||||
|
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
if ( target.id === 'createCosmeticFilters' ) {
|
if ( target.id === 'createCosmeticFilters' ) {
|
||||||
messaging.send('loggerUI', {
|
vAPI.messaging.send('loggerUI', {
|
||||||
what: 'createUserFilter',
|
what: 'createUserFilter',
|
||||||
filters: textarea.value,
|
filters: textarea.value,
|
||||||
});
|
});
|
||||||
// Force a reload for the new cosmetic filter(s) to take effect
|
// Force a reload for the new cosmetic filter(s) to take effect
|
||||||
messaging.send('loggerUI', {
|
vAPI.messaging.send('loggerUI', {
|
||||||
what: 'reloadTab',
|
what: 'reloadTab',
|
||||||
tabId: inspectedTabId,
|
tabId: inspectedTabId,
|
||||||
});
|
});
|
||||||
@ -386,7 +356,7 @@ const startDialog = (function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showCommitted = function() {
|
const showCommitted = function() {
|
||||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'showCommitted',
|
what: 'showCommitted',
|
||||||
hide: hideSelectors.join(',\n'),
|
hide: hideSelectors.join(',\n'),
|
||||||
unhide: unhideSelectors.join(',\n')
|
unhide: unhideSelectors.join(',\n')
|
||||||
@ -394,7 +364,7 @@ const startDialog = (function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showInteractive = function() {
|
const showInteractive = function() {
|
||||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'showInteractive',
|
what: 'showInteractive',
|
||||||
hide: hideSelectors.join(',\n'),
|
hide: hideSelectors.join(',\n'),
|
||||||
unhide: unhideSelectors.join(',\n')
|
unhide: unhideSelectors.join(',\n')
|
||||||
@ -449,8 +419,8 @@ const onClicked = function(ev) {
|
|||||||
|
|
||||||
if ( inspectedTabId === 0 ) { return; }
|
if ( inspectedTabId === 0 ) { return; }
|
||||||
|
|
||||||
var target = ev.target;
|
const target = ev.target;
|
||||||
var parent = target.parentElement;
|
const parent = target.parentElement;
|
||||||
|
|
||||||
// Expand/collapse branch
|
// Expand/collapse branch
|
||||||
if (
|
if (
|
||||||
@ -473,7 +443,7 @@ const onClicked = function(ev) {
|
|||||||
|
|
||||||
// Toggle cosmetic filter
|
// Toggle cosmetic filter
|
||||||
if ( dom.cl.has(target, 'filter') ) {
|
if ( dom.cl.has(target, 'filter') ) {
|
||||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'toggleFilter',
|
what: 'toggleFilter',
|
||||||
original: false,
|
original: false,
|
||||||
target: dom.cl.toggle(target, 'off'),
|
target: dom.cl.toggle(target, 'off'),
|
||||||
@ -489,7 +459,7 @@ const onClicked = function(ev) {
|
|||||||
}
|
}
|
||||||
// Toggle node
|
// Toggle node
|
||||||
else {
|
else {
|
||||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'toggleNodes',
|
what: 'toggleNodes',
|
||||||
original: true,
|
original: true,
|
||||||
target: dom.cl.toggle(target, 'off') === false,
|
target: dom.cl.toggle(target, 'off') === false,
|
||||||
@ -509,7 +479,7 @@ const onMouseOver = (function() {
|
|||||||
let mouseoverTarget = null;
|
let mouseoverTarget = null;
|
||||||
|
|
||||||
const timerHandler = ( ) => {
|
const timerHandler = ( ) => {
|
||||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'highlightOne',
|
what: 'highlightOne',
|
||||||
selector: selectorFromNode(mouseoverTarget),
|
selector: selectorFromNode(mouseoverTarget),
|
||||||
nid: nidFromNode(mouseoverTarget),
|
nid: nidFromNode(mouseoverTarget),
|
||||||
@ -544,7 +514,7 @@ const injectInspector = function() {
|
|||||||
const tabId = currentTabId();
|
const tabId = currentTabId();
|
||||||
if ( tabId <= 0 ) { return; }
|
if ( tabId <= 0 ) { return; }
|
||||||
inspectedTabId = tabId;
|
inspectedTabId = tabId;
|
||||||
messaging.send('loggerUI', {
|
vAPI.messaging.send('loggerUI', {
|
||||||
what: 'scriptlet',
|
what: 'scriptlet',
|
||||||
tabId,
|
tabId,
|
||||||
scriptlet: 'dom-inspector',
|
scriptlet: 'dom-inspector',
|
||||||
@ -554,9 +524,8 @@ const injectInspector = function() {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const shutdownInspector = function() {
|
const shutdownInspector = function() {
|
||||||
if ( inspectorConnectionId !== undefined ) {
|
if ( inspectorFramePort !== undefined ) {
|
||||||
vAPI.MessagingConnection.disconnectFrom(inspectorConnectionId);
|
inspectorFramePort.postMessage({ what: 'quitInspector' });
|
||||||
inspectorConnectionId = undefined;
|
|
||||||
}
|
}
|
||||||
logger.removeAllChildren(domTree);
|
logger.removeAllChildren(domTree);
|
||||||
dom.cl.remove(inspector, 'vExpanded');
|
dom.cl.remove(inspector, 'vExpanded');
|
||||||
@ -594,10 +563,7 @@ const toggleHCompactView = function() {
|
|||||||
|
|
||||||
const revert = function() {
|
const revert = function() {
|
||||||
dom.cl.remove('#domTree .off', 'off');
|
dom.cl.remove('#domTree .off', 'off');
|
||||||
vAPI.MessagingConnection.sendTo(
|
inspectorFramePort.postMessage({ what: 'resetToggledNodes' });
|
||||||
inspectorConnectionId,
|
|
||||||
{ what: 'resetToggledNodes' }
|
|
||||||
);
|
|
||||||
dom.cl.add(qs$(inspector, '.permatoolbar .revert'), 'disabled');
|
dom.cl.add(qs$(inspector, '.permatoolbar .revert'), 'disabled');
|
||||||
dom.cl.add(qs$(inspector, '.permatoolbar .commit'), 'disabled');
|
dom.cl.add(qs$(inspector, '.permatoolbar .commit'), 'disabled');
|
||||||
};
|
};
|
||||||
|
@ -1846,6 +1846,48 @@ vAPI.messaging.listen({
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Channel:
|
||||||
|
// domInspectorContent
|
||||||
|
// unprivileged
|
||||||
|
|
||||||
|
{
|
||||||
|
// >>>>> start of local scope
|
||||||
|
|
||||||
|
const onMessage = (request, sender, callback) => {
|
||||||
|
// Async
|
||||||
|
switch ( request.what ) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Sync
|
||||||
|
let response;
|
||||||
|
switch ( request.what ) {
|
||||||
|
case 'getInspectorArgs':
|
||||||
|
response = {
|
||||||
|
inspectorURL: vAPI.getURL(
|
||||||
|
`/web_accessible_resources/dom-inspector.html?secret=${vAPI.warSecret.short()}`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return vAPI.messaging.UNHANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.messaging.listen({
|
||||||
|
name: 'domInspectorContent',
|
||||||
|
listener: onMessage,
|
||||||
|
privileged: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// <<<<< end of local scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
// Channel:
|
// Channel:
|
||||||
// documentBlocked
|
// documentBlocked
|
||||||
// privileged
|
// privileged
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
uBlock Origin - a browser extension to block requests.
|
uBlock Origin - a browser extension to block requests.
|
||||||
Copyright (C) 2015-2018 Raymond Hill
|
Copyright (C) 2015-present Raymond Hill
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -24,30 +24,21 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
(( ) => {
|
(async ( ) => {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
if ( typeof vAPI !== 'object' || !vAPI.domFilterer ) { return; }
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
|
if ( vAPI.domFilterer instanceof Object === false ) { return; }
|
||||||
/******************************************************************************/
|
if ( document.querySelector(`iframe[${vAPI.sessionId}]`) !== null ) { return; }
|
||||||
|
|
||||||
var sessionId = vAPI.sessionId;
|
|
||||||
|
|
||||||
if ( document.querySelector('iframe.dom-inspector.' + sessionId) !== null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
let loggerConnectionId;
|
|
||||||
|
|
||||||
// Highlighter-related
|
// Highlighter-related
|
||||||
let svgRoot = null;
|
let inspectorRoot = null;
|
||||||
let pickerRoot = null;
|
|
||||||
|
|
||||||
let nodeToIdMap = new WeakMap(); // No need to iterate
|
const nodeToIdMap = new WeakMap(); // No need to iterate
|
||||||
|
|
||||||
let blueNodes = [];
|
let blueNodes = [];
|
||||||
const roRedNodes = new Map(); // node => current cosmetic filter
|
const roRedNodes = new Map(); // node => current cosmetic filter
|
||||||
@ -59,7 +50,11 @@ const reHasCSSCombinators = /[ >+~]/;
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const domLayout = (function() {
|
//const getNodeId = node => nodeToIdMap.get(node) || 0;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const domLayout = (( ) => {
|
||||||
const skipTagNames = new Set([
|
const skipTagNames = new Set([
|
||||||
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
|
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
|
||||||
]);
|
]);
|
||||||
@ -70,87 +65,81 @@ const domLayout = (function() {
|
|||||||
[ 'object', 'data' ]
|
[ 'object', 'data' ]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var idGenerator = 0;
|
let idGenerator = 1;
|
||||||
|
|
||||||
// This will be used to uniquely identify nodes across process.
|
// This will be used to uniquely identify nodes across process.
|
||||||
|
|
||||||
const newNodeId = function(node) {
|
const newNodeId = node => {
|
||||||
var nid = 'n' + (idGenerator++).toString(36);
|
const nid = `n${(idGenerator++).toString(36)}`;
|
||||||
nodeToIdMap.set(node, nid);
|
nodeToIdMap.set(node, nid);
|
||||||
return nid;
|
return nid;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectorFromNode = function(node) {
|
const selectorFromNode = node => {
|
||||||
var str, attr, pos, sw, i;
|
const tag = node.localName;
|
||||||
var tag = node.localName;
|
let selector = CSS.escape(tag);
|
||||||
var selector = CSS.escape(tag);
|
|
||||||
// Id
|
// Id
|
||||||
if ( typeof node.id === 'string' ) {
|
if ( typeof node.id === 'string' ) {
|
||||||
str = node.id.trim();
|
let str = node.id.trim();
|
||||||
if ( str !== '' ) {
|
if ( str !== '' ) {
|
||||||
selector += '#' + CSS.escape(str);
|
selector += `#${CSS.escape(str)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Class
|
// Class
|
||||||
var cl = node.classList;
|
const cl = node.classList;
|
||||||
if ( cl ) {
|
if ( cl ) {
|
||||||
for ( i = 0; i < cl.length; i++ ) {
|
for ( let i = 0; i < cl.length; i++ ) {
|
||||||
selector += '.' + CSS.escape(cl[i]);
|
selector += `.${CSS.escape(cl[i])}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tag-specific attributes
|
// Tag-specific attributes
|
||||||
attr = resourceAttrNames.get(tag);
|
const attr = resourceAttrNames.get(tag);
|
||||||
if ( attr !== undefined ) {
|
if ( attr !== undefined ) {
|
||||||
str = node.getAttribute(attr) || '';
|
let str = node.getAttribute(attr) || '';
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
if ( str.startsWith('data:') ) {
|
const pos = str.startsWith('data:') ? 5 : str.search(/[#?]/);
|
||||||
pos = 5;
|
let sw = '';
|
||||||
} else {
|
|
||||||
pos = str.search(/[#?]/);
|
|
||||||
}
|
|
||||||
if ( pos !== -1 ) {
|
if ( pos !== -1 ) {
|
||||||
str = str.slice(0, pos);
|
str = str.slice(0, pos);
|
||||||
sw = '^';
|
sw = '^';
|
||||||
} else {
|
|
||||||
sw = '';
|
|
||||||
}
|
}
|
||||||
if ( str !== '' ) {
|
if ( str !== '' ) {
|
||||||
selector += '[' + attr + sw + '="' + CSS.escape(str, true) + '"]';
|
selector += `[${attr}${sw}="${CSS.escape(str, true)}"]`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selector;
|
return selector;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DomRoot = function() {
|
function DomRoot() {
|
||||||
this.nid = newNodeId(document.body);
|
this.nid = newNodeId(document.body);
|
||||||
this.lvl = 0;
|
this.lvl = 0;
|
||||||
this.sel = 'body';
|
this.sel = 'body';
|
||||||
this.cnt = 0;
|
this.cnt = 0;
|
||||||
this.filter = roRedNodes.get(document.body);
|
this.filter = roRedNodes.get(document.body);
|
||||||
};
|
}
|
||||||
|
|
||||||
const DomNode = function(node, level) {
|
function DomNode(node, level) {
|
||||||
this.nid = newNodeId(node);
|
this.nid = newNodeId(node);
|
||||||
this.lvl = level;
|
this.lvl = level;
|
||||||
this.sel = selectorFromNode(node);
|
this.sel = selectorFromNode(node);
|
||||||
this.cnt = 0;
|
this.cnt = 0;
|
||||||
this.filter = roRedNodes.get(node);
|
this.filter = roRedNodes.get(node);
|
||||||
};
|
}
|
||||||
|
|
||||||
const domNodeFactory = function(level, node) {
|
const domNodeFactory = (level, node) => {
|
||||||
const localName = node.localName;
|
const localName = node.localName;
|
||||||
if ( skipTagNames.has(localName) ) { return null; }
|
if ( skipTagNames.has(localName) ) { return null; }
|
||||||
// skip uBlock's own nodes
|
// skip uBlock's own nodes
|
||||||
if ( node.classList.contains(sessionId) ) { return null; }
|
if ( node === inspectorRoot ) { return null; }
|
||||||
if ( level === 0 && localName === 'body' ) {
|
if ( level === 0 && localName === 'body' ) {
|
||||||
return new DomRoot();
|
return new DomRoot();
|
||||||
}
|
}
|
||||||
return new DomNode(node, level);
|
return new DomNode(node, level);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Collect layout data.
|
// Collect layout data
|
||||||
|
|
||||||
const getLayoutData = function() {
|
const getLayoutData = ( ) => {
|
||||||
const layout = [];
|
const layout = [];
|
||||||
const stack = [];
|
const stack = [];
|
||||||
let lvl = 0;
|
let lvl = 0;
|
||||||
@ -188,14 +177,14 @@ const domLayout = (function() {
|
|||||||
|
|
||||||
// Descendant count for each node.
|
// Descendant count for each node.
|
||||||
|
|
||||||
const patchLayoutData = function(layout) {
|
const patchLayoutData = layout => {
|
||||||
var stack = [], ptr;
|
const stack = [];
|
||||||
var lvl = 0;
|
let ptr;
|
||||||
var domNode, cnt;
|
let lvl = 0;
|
||||||
var i = layout.length;
|
let i = layout.length;
|
||||||
|
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
domNode = layout[i];
|
const domNode = layout[i];
|
||||||
if ( domNode.lvl === lvl ) {
|
if ( domNode.lvl === lvl ) {
|
||||||
stack[ptr] += 1;
|
stack[ptr] += 1;
|
||||||
continue;
|
continue;
|
||||||
@ -210,7 +199,7 @@ const domLayout = (function() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// domNode.lvl < lvl
|
// domNode.lvl < lvl
|
||||||
cnt = stack.pop();
|
const cnt = stack.pop();
|
||||||
domNode.cnt = cnt;
|
domNode.cnt = cnt;
|
||||||
lvl -= 1;
|
lvl -= 1;
|
||||||
ptr = lvl - 1;
|
ptr = lvl - 1;
|
||||||
@ -221,13 +210,13 @@ const domLayout = (function() {
|
|||||||
|
|
||||||
// Track and report mutations of the DOM
|
// Track and report mutations of the DOM
|
||||||
|
|
||||||
var mutationObserver = null;
|
let mutationObserver = null;
|
||||||
var mutationTimer;
|
let mutationTimer;
|
||||||
var addedNodelists = [];
|
let addedNodelists = [];
|
||||||
var removedNodelist = [];
|
let removedNodelist = [];
|
||||||
|
|
||||||
const previousElementSiblingId = function(node) {
|
const previousElementSiblingId = node => {
|
||||||
var sibling = node;
|
let sibling = node;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
sibling = sibling.previousElementSibling;
|
sibling = sibling.previousElementSibling;
|
||||||
if ( sibling === null ) { return null; }
|
if ( sibling === null ) { return null; }
|
||||||
@ -236,11 +225,10 @@ const domLayout = (function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const journalFromBranch = function(root, newNodes, newNodeToIdMap) {
|
const journalFromBranch = (root, newNodes, newNodeToIdMap) => {
|
||||||
var domNode;
|
let node = root.firstElementChild;
|
||||||
var node = root.firstElementChild;
|
|
||||||
while ( node !== null ) {
|
while ( node !== null ) {
|
||||||
domNode = domNodeFactory(undefined, node);
|
const domNode = domNodeFactory(undefined, node);
|
||||||
if ( domNode !== null ) {
|
if ( domNode !== null ) {
|
||||||
newNodeToIdMap.set(domNode.nid, domNode);
|
newNodeToIdMap.set(domNode.nid, domNode);
|
||||||
newNodes.push(node);
|
newNodes.push(node);
|
||||||
@ -267,22 +255,21 @@ const domLayout = (function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const journalFromMutations = function() {
|
const journalFromMutations = ( ) => {
|
||||||
var nodelist, node, domNode, nid;
|
|
||||||
mutationTimer = undefined;
|
mutationTimer = undefined;
|
||||||
|
|
||||||
// This is used to temporarily hold all added nodes, before resolving
|
// This is used to temporarily hold all added nodes, before resolving
|
||||||
// their node id and relative position.
|
// their node id and relative position.
|
||||||
var newNodes = [];
|
const newNodes = [];
|
||||||
var journalEntries = [];
|
const journalEntries = [];
|
||||||
var newNodeToIdMap = new Map();
|
const newNodeToIdMap = new Map();
|
||||||
|
|
||||||
for ( nodelist of addedNodelists ) {
|
for ( const nodelist of addedNodelists ) {
|
||||||
for ( node of nodelist ) {
|
for ( const node of nodelist ) {
|
||||||
if ( node.nodeType !== 1 ) { continue; }
|
if ( node.nodeType !== 1 ) { continue; }
|
||||||
if ( node.parentElement === null ) { continue; }
|
if ( node.parentElement === null ) { continue; }
|
||||||
cosmeticFilterMapper.incremental(node);
|
cosmeticFilterMapper.incremental(node);
|
||||||
domNode = domNodeFactory(undefined, node);
|
const domNode = domNodeFactory(undefined, node);
|
||||||
if ( domNode !== null ) {
|
if ( domNode !== null ) {
|
||||||
newNodeToIdMap.set(domNode.nid, domNode);
|
newNodeToIdMap.set(domNode.nid, domNode);
|
||||||
newNodes.push(node);
|
newNodes.push(node);
|
||||||
@ -291,19 +278,16 @@ const domLayout = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
addedNodelists = [];
|
addedNodelists = [];
|
||||||
for ( nodelist of removedNodelist ) {
|
for ( const nodelist of removedNodelist ) {
|
||||||
for ( node of nodelist ) {
|
for ( const node of nodelist ) {
|
||||||
if ( node.nodeType !== 1 ) { continue; }
|
if ( node.nodeType !== 1 ) { continue; }
|
||||||
nid = nodeToIdMap.get(node);
|
const nid = nodeToIdMap.get(node);
|
||||||
if ( nid === undefined ) { continue; }
|
if ( nid === undefined ) { continue; }
|
||||||
journalEntries.push({
|
journalEntries.push({ what: -1, nid });
|
||||||
what: -1,
|
|
||||||
nid: nid
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removedNodelist = [];
|
removedNodelist = [];
|
||||||
for ( node of newNodes ) {
|
for ( const node of newNodes ) {
|
||||||
journalEntries.push({
|
journalEntries.push({
|
||||||
what: 1,
|
what: 1,
|
||||||
nid: nodeToIdMap.get(node),
|
nid: nodeToIdMap.get(node),
|
||||||
@ -314,7 +298,7 @@ const domLayout = (function() {
|
|||||||
|
|
||||||
if ( journalEntries.length === 0 ) { return; }
|
if ( journalEntries.length === 0 ) { return; }
|
||||||
|
|
||||||
vAPI.MessagingConnection.sendTo(loggerConnectionId, {
|
inspectorFramePort.postMessage({
|
||||||
what: 'domLayoutIncremental',
|
what: 'domLayoutIncremental',
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
hostname: window.location.hostname,
|
hostname: window.location.hostname,
|
||||||
@ -323,8 +307,8 @@ const domLayout = (function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMutationObserved = function(mutationRecords) {
|
const onMutationObserved = mutationRecords => {
|
||||||
for ( var record of mutationRecords ) {
|
for ( const record of mutationRecords ) {
|
||||||
if ( record.addedNodes.length !== 0 ) {
|
if ( record.addedNodes.length !== 0 ) {
|
||||||
addedNodelists.push(record.addedNodes);
|
addedNodelists.push(record.addedNodes);
|
||||||
}
|
}
|
||||||
@ -339,7 +323,7 @@ const domLayout = (function() {
|
|||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
const getLayout = function() {
|
const getLayout = ( ) => {
|
||||||
cosmeticFilterMapper.reset();
|
cosmeticFilterMapper.reset();
|
||||||
mutationObserver = new MutationObserver(onMutationObserved);
|
mutationObserver = new MutationObserver(onMutationObserved);
|
||||||
mutationObserver.observe(document.body, {
|
mutationObserver.observe(document.body, {
|
||||||
@ -355,11 +339,11 @@ const domLayout = (function() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const reset = function() {
|
const reset = ( ) => {
|
||||||
shutdown();
|
shutdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
const shutdown = function() {
|
const shutdown = ( ) => {
|
||||||
if ( mutationTimer !== undefined ) {
|
if ( mutationTimer !== undefined ) {
|
||||||
clearTimeout(mutationTimer);
|
clearTimeout(mutationTimer);
|
||||||
mutationTimer = undefined;
|
mutationTimer = undefined;
|
||||||
@ -370,35 +354,20 @@ const domLayout = (function() {
|
|||||||
}
|
}
|
||||||
addedNodelists = [];
|
addedNodelists = [];
|
||||||
removedNodelist = [];
|
removedNodelist = [];
|
||||||
nodeToIdMap = new WeakMap();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get: getLayout,
|
get: getLayout,
|
||||||
reset: reset,
|
reset,
|
||||||
shutdown: shutdown
|
shutdown,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// https://www.youtube.com/watch?v=qo8zKhd4Cf0
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// For browsers not supporting `:scope`, it's not the end of the world: the
|
const cosmeticFilterMapper = (( ) => {
|
||||||
// suggested CSS selectors may just end up being more verbose.
|
const nodesFromStyleTag = rootNode => {
|
||||||
|
|
||||||
let cssScope = ':scope > ';
|
|
||||||
try {
|
|
||||||
document.querySelector(':scope *');
|
|
||||||
} catch (e) {
|
|
||||||
cssScope = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
const cosmeticFilterMapper = (function() {
|
|
||||||
const nodesFromStyleTag = function(rootNode) {
|
|
||||||
const filterMap = roRedNodes;
|
const filterMap = roRedNodes;
|
||||||
const details = vAPI.domFilterer.getAllSelectors();
|
const details = vAPI.domFilterer.getAllSelectors();
|
||||||
|
|
||||||
@ -434,25 +403,25 @@ const cosmeticFilterMapper = (function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const incremental = function(rootNode) {
|
const incremental = rootNode => {
|
||||||
nodesFromStyleTag(rootNode);
|
nodesFromStyleTag(rootNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
const reset = function() {
|
const reset = ( ) => {
|
||||||
roRedNodes.clear();
|
roRedNodes.clear();
|
||||||
if ( document.documentElement !== null ) {
|
if ( document.documentElement !== null ) {
|
||||||
incremental(document.documentElement);
|
incremental(document.documentElement);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const shutdown = function() {
|
const shutdown = ( ) => {
|
||||||
vAPI.domFilterer.toggle(true);
|
vAPI.domFilterer.toggle(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
incremental: incremental,
|
incremental,
|
||||||
reset: reset,
|
reset,
|
||||||
shutdown: shutdown
|
shutdown,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -475,22 +444,19 @@ const elementsFromSelector = function(selector, context) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const elementsFromSpecialSelector = function(selector) {
|
const elementsFromSpecialSelector = function(selector) {
|
||||||
var out = [], i;
|
const out = [];
|
||||||
var matches = /^(.+?):has\((.+?)\)$/.exec(selector);
|
let matches = /^(.+?):has\((.+?)\)$/.exec(selector);
|
||||||
if ( matches !== null ) {
|
if ( matches !== null ) {
|
||||||
var nodes;
|
let nodes;
|
||||||
try {
|
try {
|
||||||
nodes = document.querySelectorAll(matches[1]);
|
nodes = document.querySelectorAll(matches[1]);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
nodes = [];
|
nodes = [];
|
||||||
}
|
}
|
||||||
i = nodes.length;
|
for ( const node of nodes ) {
|
||||||
while ( i-- ) {
|
if ( node.querySelector(matches[2]) === null ) { continue; }
|
||||||
var node = nodes[i];
|
|
||||||
if ( node.querySelector(matches[2]) !== null ) {
|
|
||||||
out.push(node);
|
out.push(node);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +469,7 @@ const elementsFromSpecialSelector = function(selector) {
|
|||||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
i = xpr.snapshotLength;
|
let i = xpr.snapshotLength;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
out.push(xpr.snapshotItem(i));
|
out.push(xpr.snapshotItem(i));
|
||||||
}
|
}
|
||||||
@ -512,128 +478,114 @@ const elementsFromSpecialSelector = function(selector) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const getSvgRootChildren = function() {
|
const highlightElements = ( ) => {
|
||||||
if ( svgRoot.children ) {
|
const paths = [];
|
||||||
return svgRoot.children;
|
|
||||||
} else {
|
const path = [];
|
||||||
const childNodes = Array.prototype.slice.apply(svgRoot.childNodes);
|
for ( const elem of rwRedNodes.keys() ) {
|
||||||
return childNodes.filter(function(node) {
|
if ( elem === inspectorRoot ) { continue; }
|
||||||
return node.nodeType === Node.ELEMENT_NODE;
|
if ( rwGreenNodes.has(elem) ) { continue; }
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||||
|
const rect = elem.getBoundingClientRect();
|
||||||
|
const xl = rect.left;
|
||||||
|
const w = rect.width;
|
||||||
|
const yt = rect.top;
|
||||||
|
const h = rect.height;
|
||||||
|
const ws = w.toFixed(1);
|
||||||
|
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
path.push(poly);
|
||||||
|
}
|
||||||
|
paths.push(path.join('') || 'M0 0');
|
||||||
|
|
||||||
|
path.length = 0;
|
||||||
|
for ( const elem of rwGreenNodes ) {
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||||
|
const rect = elem.getBoundingClientRect();
|
||||||
|
const xl = rect.left;
|
||||||
|
const w = rect.width;
|
||||||
|
const yt = rect.top;
|
||||||
|
const h = rect.height;
|
||||||
|
const ws = w.toFixed(1);
|
||||||
|
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
path.push(poly);
|
||||||
|
}
|
||||||
|
paths.push(path.join('') || 'M0 0');
|
||||||
|
|
||||||
|
path.length = 0;
|
||||||
|
for ( const elem of roRedNodes.keys() ) {
|
||||||
|
if ( elem === inspectorRoot ) { continue; }
|
||||||
|
if ( rwGreenNodes.has(elem) ) { continue; }
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||||
|
const rect = elem.getBoundingClientRect();
|
||||||
|
const xl = rect.left;
|
||||||
|
const w = rect.width;
|
||||||
|
const yt = rect.top;
|
||||||
|
const h = rect.height;
|
||||||
|
const ws = w.toFixed(1);
|
||||||
|
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
path.push(poly);
|
||||||
|
}
|
||||||
|
paths.push(path.join('') || 'M0 0');
|
||||||
|
|
||||||
|
path.length = 0;
|
||||||
|
for ( const elem of blueNodes ) {
|
||||||
|
if ( elem === inspectorRoot ) { continue; }
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||||
|
const rect = elem.getBoundingClientRect();
|
||||||
|
const xl = rect.left;
|
||||||
|
const w = rect.width;
|
||||||
|
const yt = rect.top;
|
||||||
|
const h = rect.height;
|
||||||
|
const ws = w.toFixed(1);
|
||||||
|
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
path.push(poly);
|
||||||
|
}
|
||||||
|
paths.push(path.join('') || 'M0 0');
|
||||||
|
|
||||||
|
inspectorFramePort.postMessage({
|
||||||
|
what: 'svgPaths',
|
||||||
|
paths,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const highlightElements = function() {
|
|
||||||
var islands;
|
|
||||||
var elem, rect, poly;
|
|
||||||
var xl, xr, yt, yb, w, h, ws;
|
|
||||||
var svgRootChildren = getSvgRootChildren();
|
|
||||||
|
|
||||||
islands = [];
|
|
||||||
for ( elem of rwRedNodes.keys() ) {
|
|
||||||
if ( elem === pickerRoot ) { continue; }
|
|
||||||
if ( rwGreenNodes.has(elem) ) { continue; }
|
|
||||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
|
||||||
rect = elem.getBoundingClientRect();
|
|
||||||
xl = rect.left;
|
|
||||||
xr = rect.right;
|
|
||||||
w = rect.width;
|
|
||||||
yt = rect.top;
|
|
||||||
yb = rect.bottom;
|
|
||||||
h = rect.height;
|
|
||||||
ws = w.toFixed(1);
|
|
||||||
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
|
||||||
'h' + ws +
|
|
||||||
'v' + h.toFixed(1) +
|
|
||||||
'h-' + ws +
|
|
||||||
'z';
|
|
||||||
islands.push(poly);
|
|
||||||
}
|
|
||||||
svgRootChildren[0].setAttribute('d', islands.join('') || 'M0 0');
|
|
||||||
|
|
||||||
islands = [];
|
|
||||||
for ( elem of rwGreenNodes ) {
|
|
||||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
|
||||||
rect = elem.getBoundingClientRect();
|
|
||||||
xl = rect.left;
|
|
||||||
xr = rect.right;
|
|
||||||
w = rect.width;
|
|
||||||
yt = rect.top;
|
|
||||||
yb = rect.bottom;
|
|
||||||
h = rect.height;
|
|
||||||
ws = w.toFixed(1);
|
|
||||||
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
|
||||||
'h' + ws +
|
|
||||||
'v' + h.toFixed(1) +
|
|
||||||
'h-' + ws +
|
|
||||||
'z';
|
|
||||||
islands.push(poly);
|
|
||||||
}
|
|
||||||
svgRootChildren[1].setAttribute('d', islands.join('') || 'M0 0');
|
|
||||||
|
|
||||||
islands = [];
|
|
||||||
for ( elem of roRedNodes.keys() ) {
|
|
||||||
if ( elem === pickerRoot ) { continue; }
|
|
||||||
if ( rwGreenNodes.has(elem) ) { continue; }
|
|
||||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
|
||||||
rect = elem.getBoundingClientRect();
|
|
||||||
xl = rect.left;
|
|
||||||
xr = rect.right;
|
|
||||||
w = rect.width;
|
|
||||||
yt = rect.top;
|
|
||||||
yb = rect.bottom;
|
|
||||||
h = rect.height;
|
|
||||||
ws = w.toFixed(1);
|
|
||||||
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
|
||||||
'h' + ws +
|
|
||||||
'v' + h.toFixed(1) +
|
|
||||||
'h-' + ws +
|
|
||||||
'z';
|
|
||||||
islands.push(poly);
|
|
||||||
}
|
|
||||||
svgRootChildren[2].setAttribute('d', islands.join('') || 'M0 0');
|
|
||||||
|
|
||||||
islands = [];
|
|
||||||
for ( elem of blueNodes ) {
|
|
||||||
if ( elem === pickerRoot ) { continue; }
|
|
||||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
|
||||||
rect = elem.getBoundingClientRect();
|
|
||||||
xl = rect.left;
|
|
||||||
xr = rect.right;
|
|
||||||
w = rect.width;
|
|
||||||
yt = rect.top;
|
|
||||||
yb = rect.bottom;
|
|
||||||
h = rect.height;
|
|
||||||
ws = w.toFixed(1);
|
|
||||||
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
|
||||||
'h' + ws +
|
|
||||||
'v' + h.toFixed(1) +
|
|
||||||
'h-' + ws +
|
|
||||||
'z';
|
|
||||||
islands.push(poly);
|
|
||||||
}
|
|
||||||
svgRootChildren[3].setAttribute('d', islands.join('') || 'M0 0');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const onScrolled = (function() {
|
const onScrolled = (( ) => {
|
||||||
let buffered = false;
|
let timer;
|
||||||
const timerHandler = function() {
|
return ( ) => {
|
||||||
buffered = false;
|
if ( timer ) { return; }
|
||||||
|
timer = window.requestAnimationFrame(( ) => {
|
||||||
|
timer = undefined;
|
||||||
highlightElements();
|
highlightElements();
|
||||||
};
|
});
|
||||||
return function() {
|
|
||||||
if ( buffered === false ) {
|
|
||||||
window.requestAnimationFrame(timerHandler);
|
|
||||||
buffered = true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const onMouseOver = ( ) => {
|
||||||
|
if ( blueNodes.length === 0 ) { return; }
|
||||||
|
blueNodes = [];
|
||||||
|
highlightElements();
|
||||||
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const selectNodes = function(selector, nid) {
|
const selectNodes = (selector, nid) => {
|
||||||
const nodes = elementsFromSelector(selector);
|
const nodes = elementsFromSelector(selector);
|
||||||
if ( nid === '' ) { return nodes; }
|
if ( nid === '' ) { return nodes; }
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
@ -646,7 +598,7 @@ const selectNodes = function(selector, nid) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const nodesFromFilter = function(selector) {
|
const nodesFromFilter = selector => {
|
||||||
const out = [];
|
const out = [];
|
||||||
for ( const entry of roRedNodes ) {
|
for ( const entry of roRedNodes ) {
|
||||||
if ( entry[1] === selector ) {
|
if ( entry[1] === selector ) {
|
||||||
@ -658,7 +610,7 @@ const nodesFromFilter = function(selector) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const toggleExceptions = function(nodes, targetState) {
|
const toggleExceptions = (nodes, targetState) => {
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
if ( targetState ) {
|
if ( targetState ) {
|
||||||
rwGreenNodes.add(node);
|
rwGreenNodes.add(node);
|
||||||
@ -668,7 +620,7 @@ const toggleExceptions = function(nodes, targetState) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleFilter = function(nodes, targetState) {
|
const toggleFilter = (nodes, targetState) => {
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
if ( targetState ) {
|
if ( targetState ) {
|
||||||
rwRedNodes.delete(node);
|
rwRedNodes.delete(node);
|
||||||
@ -678,23 +630,28 @@ const toggleFilter = function(nodes, targetState) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetToggledNodes = function() {
|
const resetToggledNodes = ( ) => {
|
||||||
rwGreenNodes.clear();
|
rwGreenNodes.clear();
|
||||||
rwRedNodes.clear();
|
rwRedNodes.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const start = function() {
|
const start = ( ) => {
|
||||||
const onReady = function(ev) {
|
const onReady = ( ) => {
|
||||||
if ( ev ) {
|
window.addEventListener('scroll', onScrolled, {
|
||||||
document.removeEventListener(ev.type, onReady);
|
capture: true,
|
||||||
}
|
passive: true,
|
||||||
vAPI.MessagingConnection.sendTo(loggerConnectionId, domLayout.get());
|
});
|
||||||
|
window.addEventListener('mouseover', onMouseOver, {
|
||||||
|
capture: true,
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
inspectorFramePort.postMessage(domLayout.get());
|
||||||
vAPI.domFilterer.toggle(false, highlightElements);
|
vAPI.domFilterer.toggle(false, highlightElements);
|
||||||
};
|
};
|
||||||
if ( document.readyState === 'loading' ) {
|
if ( document.readyState === 'loading' ) {
|
||||||
document.addEventListener('DOMContentLoaded', onReady);
|
document.addEventListener('DOMContentLoaded', onReady, { once: true });
|
||||||
} else {
|
} else {
|
||||||
onReady();
|
onReady();
|
||||||
}
|
}
|
||||||
@ -702,34 +659,49 @@ const start = function() {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const shutdown = function() {
|
const shutdown = ( ) => {
|
||||||
cosmeticFilterMapper.shutdown();
|
cosmeticFilterMapper.shutdown();
|
||||||
domLayout.shutdown();
|
domLayout.shutdown();
|
||||||
vAPI.MessagingConnection.disconnectFrom(loggerConnectionId);
|
window.removeEventListener('scroll', onScrolled, {
|
||||||
window.removeEventListener('scroll', onScrolled, true);
|
capture: true,
|
||||||
pickerRoot.remove();
|
passive: true,
|
||||||
pickerRoot = svgRoot = null;
|
});
|
||||||
|
window.removeEventListener('mouseover', onMouseOver, {
|
||||||
|
capture: true,
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
inspectorFramePort.close();
|
||||||
|
inspectorFramePort = undefined;
|
||||||
|
vAPI.userStylesheet.remove(inspectorCSS);
|
||||||
|
vAPI.userStylesheet.apply();
|
||||||
|
if ( inspectorRoot === null ) { return; }
|
||||||
|
inspectorRoot.remove();
|
||||||
|
inspectorRoot = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const onMessage = function(request) {
|
const onMessage = request => {
|
||||||
var response,
|
|
||||||
nodes;
|
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
|
case 'startInspector':
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'quitInspector':
|
||||||
|
shutdown();
|
||||||
|
break;
|
||||||
|
|
||||||
case 'commitFilters':
|
case 'commitFilters':
|
||||||
highlightElements();
|
highlightElements();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'domLayout':
|
case 'domLayout':
|
||||||
response = domLayout.get();
|
domLayout.get();
|
||||||
highlightElements();
|
highlightElements();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'highlightMode':
|
case 'highlightMode':
|
||||||
//svgRoot.classList.toggle('invert', request.invert);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'highlightOne':
|
case 'highlightOne':
|
||||||
@ -753,110 +725,47 @@ const onMessage = function(request) {
|
|||||||
highlightElements();
|
highlightElements();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'toggleFilter':
|
case 'toggleFilter': {
|
||||||
nodes = selectNodes(request.selector, request.nid);
|
const nodes = selectNodes(request.selector, request.nid);
|
||||||
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
|
if ( nodes.length !== 0 ) {
|
||||||
|
nodes[0].scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
inline: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
toggleExceptions(nodesFromFilter(request.filter), request.target);
|
toggleExceptions(nodesFromFilter(request.filter), request.target);
|
||||||
highlightElements();
|
highlightElements();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'toggleNodes':
|
case 'toggleNodes': {
|
||||||
nodes = selectNodes(request.selector, request.nid);
|
const nodes = selectNodes(request.selector, request.nid);
|
||||||
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
|
if ( nodes.length !== 0 ) {
|
||||||
|
nodes[0].scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
inline: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
toggleFilter(nodes, request.target);
|
toggleFilter(nodes, request.target);
|
||||||
highlightElements();
|
highlightElements();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Install DOM inspector widget
|
// Install DOM inspector widget
|
||||||
|
let inspectorArgs = await vAPI.messaging.send('domInspectorContent', {
|
||||||
const bootstrap = function(ev) {
|
what: 'getInspectorArgs',
|
||||||
if ( ev ) {
|
|
||||||
pickerRoot.removeEventListener(ev.type, bootstrap);
|
|
||||||
}
|
|
||||||
const pickerDoc = ev.target.contentDocument;
|
|
||||||
|
|
||||||
pickerDoc.documentElement.style.setProperty(
|
|
||||||
'color-scheme',
|
|
||||||
'dark light',
|
|
||||||
'important'
|
|
||||||
);
|
|
||||||
|
|
||||||
const style = pickerDoc.createElement('style');
|
|
||||||
style.textContent = [
|
|
||||||
'body {',
|
|
||||||
'background-color: transparent;',
|
|
||||||
'}',
|
|
||||||
'svg {',
|
|
||||||
'height: 100%;',
|
|
||||||
'left: 0;',
|
|
||||||
'position: fixed;',
|
|
||||||
'top: 0;',
|
|
||||||
'width: 100%;',
|
|
||||||
'}',
|
|
||||||
'svg > path:nth-of-type(1) {',
|
|
||||||
'fill: rgba(255,0,0,0.2);',
|
|
||||||
'stroke: #F00;',
|
|
||||||
'}',
|
|
||||||
'svg > path:nth-of-type(2) {',
|
|
||||||
'fill: rgba(0,255,0,0.2);',
|
|
||||||
'stroke: #0F0;',
|
|
||||||
'}',
|
|
||||||
'svg > path:nth-of-type(3) {',
|
|
||||||
'fill: rgba(255,0,0,0.2);',
|
|
||||||
'stroke: #F00;',
|
|
||||||
'}',
|
|
||||||
'svg > path:nth-of-type(4) {',
|
|
||||||
'fill: rgba(0,0,255,0.1);',
|
|
||||||
'stroke: #FFF;',
|
|
||||||
'stroke-width: 0.5px;',
|
|
||||||
'}',
|
|
||||||
''
|
|
||||||
].join('\n');
|
|
||||||
pickerDoc.body.appendChild(style);
|
|
||||||
|
|
||||||
svgRoot = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
||||||
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
||||||
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
||||||
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
||||||
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
||||||
pickerDoc.body.appendChild(svgRoot);
|
|
||||||
|
|
||||||
window.addEventListener('scroll', onScrolled, true);
|
|
||||||
|
|
||||||
// Dynamically add direct connection abilities so that we can establish
|
|
||||||
// a direct, fast messaging connection to the logger.
|
|
||||||
vAPI.messaging.extend().then(extended => {
|
|
||||||
if ( extended !== true ) { return; }
|
|
||||||
vAPI.MessagingConnection.connectTo('domInspector', 'loggerUI', msg => {
|
|
||||||
switch ( msg.what ) {
|
|
||||||
case 'connectionAccepted':
|
|
||||||
loggerConnectionId = msg.id;
|
|
||||||
start();
|
|
||||||
break;
|
|
||||||
case 'connectionBroken':
|
|
||||||
shutdown();
|
|
||||||
break;
|
|
||||||
case 'connectionMessage':
|
|
||||||
onMessage(msg.payload);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
if ( typeof inspectorArgs !== 'object' ) { return; }
|
||||||
};
|
if ( inspectorArgs === null ) { return; }
|
||||||
|
|
||||||
pickerRoot = document.createElement('iframe');
|
const inspectorCSSStyle = [
|
||||||
pickerRoot.classList.add(sessionId);
|
|
||||||
pickerRoot.classList.add('dom-inspector');
|
|
||||||
pickerRoot.style.cssText = [
|
|
||||||
'background: transparent',
|
'background: transparent',
|
||||||
'border: 0',
|
'border: 0',
|
||||||
'border-radius: 0',
|
'border-radius: 0',
|
||||||
@ -878,8 +787,43 @@ pickerRoot.style.cssText = [
|
|||||||
''
|
''
|
||||||
].join(' !important;\n');
|
].join(' !important;\n');
|
||||||
|
|
||||||
pickerRoot.addEventListener('load', ev => { bootstrap(ev); });
|
const inspectorCSS = `
|
||||||
(document.documentElement || document).appendChild(pickerRoot);
|
:root > [${vAPI.sessionId}] {
|
||||||
|
${inspectorCSSStyle}
|
||||||
|
}
|
||||||
|
:root > [${vAPI.sessionId}-loaded] {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
vAPI.userStylesheet.add(inspectorCSS);
|
||||||
|
vAPI.userStylesheet.apply();
|
||||||
|
|
||||||
|
inspectorRoot = document.createElement('iframe');
|
||||||
|
inspectorRoot.setAttribute(vAPI.sessionId, '');
|
||||||
|
document.documentElement.append(inspectorRoot);
|
||||||
|
|
||||||
|
let inspectorFramePort;
|
||||||
|
|
||||||
|
inspectorRoot.addEventListener('load', ( ) => {
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
inspectorFramePort = channel.port1;
|
||||||
|
inspectorFramePort.onmessage = ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
onMessage(msg);
|
||||||
|
};
|
||||||
|
inspectorFramePort.onmessageerror = ( ) => {
|
||||||
|
shutdown();
|
||||||
|
};
|
||||||
|
inspectorRoot.setAttribute(`${vAPI.sessionId}-loaded`, '');
|
||||||
|
inspectorRoot.contentWindow.postMessage(
|
||||||
|
{ what: 'startInspector' },
|
||||||
|
inspectorArgs.inspectorURL,
|
||||||
|
[ channel.port2 ]
|
||||||
|
);
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
inspectorRoot.contentWindow.location = inspectorArgs.inspectorURL;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ if ( typeof vAPI !== 'object' || vAPI === null ) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const epickerId = vAPI.randomToken();
|
const epickerId = vAPI.randomToken();
|
||||||
let epickerConnectionId;
|
|
||||||
|
|
||||||
let pickerRoot = document.querySelector(`[${vAPI.sessionId}]`);
|
let pickerRoot = document.querySelector(`[${vAPI.sessionId}]`);
|
||||||
if ( pickerRoot !== null ) { return; }
|
if ( pickerRoot !== null ) { return; }
|
||||||
@ -144,7 +143,7 @@ const highlightElements = function(elems, force) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerFramePort.postMessage({
|
||||||
what: 'svgPaths',
|
what: 'svgPaths',
|
||||||
ocean: `M0 0h${ow}v${oh}h-${ow}z`,
|
ocean: `M0 0h${ow}v${oh}h-${ow}z`,
|
||||||
islands: islands.join(''),
|
islands: islands.join(''),
|
||||||
@ -900,7 +899,7 @@ const onOptimizeCandidates = function(details) {
|
|||||||
if ( r !== 0 ) { return r; }
|
if ( r !== 0 ) { return r; }
|
||||||
return a.selector.length - b.selector.length;
|
return a.selector.length - b.selector.length;
|
||||||
});
|
});
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerFramePort.postMessage({
|
||||||
what: 'candidatesOptimized',
|
what: 'candidatesOptimized',
|
||||||
candidates: results.map(a => a.selector),
|
candidates: results.map(a => a.selector),
|
||||||
slot: details.slot,
|
slot: details.slot,
|
||||||
@ -910,7 +909,7 @@ const onOptimizeCandidates = function(details) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const showDialog = function(options) {
|
const showDialog = function(options) {
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerFramePort.postMessage({
|
||||||
what: 'showDialog',
|
what: 'showDialog',
|
||||||
url: self.location.href,
|
url: self.location.href,
|
||||||
netFilters: netFilterCandidates,
|
netFilters: netFilterCandidates,
|
||||||
@ -1141,16 +1140,13 @@ const quitPicker = function() {
|
|||||||
self.removeEventListener('resize', onViewportChanged, { passive: true });
|
self.removeEventListener('resize', onViewportChanged, { passive: true });
|
||||||
self.removeEventListener('keydown', onKeyPressed, true);
|
self.removeEventListener('keydown', onKeyPressed, true);
|
||||||
vAPI.shutdown.remove(quitPicker);
|
vAPI.shutdown.remove(quitPicker);
|
||||||
vAPI.MessagingConnection.disconnectFrom(epickerConnectionId);
|
pickerFramePort.close();
|
||||||
vAPI.MessagingConnection.removeListener(onConnectionMessage);
|
pickerFramePort = undefined;
|
||||||
vAPI.userStylesheet.remove(pickerCSS);
|
vAPI.userStylesheet.remove(pickerCSS);
|
||||||
vAPI.userStylesheet.apply();
|
vAPI.userStylesheet.apply();
|
||||||
|
|
||||||
if ( pickerRoot === null ) { return; }
|
if ( pickerRoot === null ) { return; }
|
||||||
|
|
||||||
pickerRoot.remove();
|
pickerRoot.remove();
|
||||||
pickerRoot = null;
|
pickerRoot = null;
|
||||||
|
|
||||||
self.focus();
|
self.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1176,7 +1172,7 @@ const onDialogMessage = function(msg) {
|
|||||||
const resultset = filterToDOMInterface.queryAll(msg) || [];
|
const resultset = filterToDOMInterface.queryAll(msg) || [];
|
||||||
highlightElements(resultset.map(a => a.elem), true);
|
highlightElements(resultset.map(a => a.elem), true);
|
||||||
if ( msg.filter === '!' ) { break; }
|
if ( msg.filter === '!' ) { break; }
|
||||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
pickerFramePort.postMessage({
|
||||||
what: 'resultsetDetails',
|
what: 'resultsetDetails',
|
||||||
count: resultset.length,
|
count: resultset.length,
|
||||||
opt: resultset.length !== 0 ? resultset[0].opt : undefined,
|
opt: resultset.length !== 0 ? resultset[0].opt : undefined,
|
||||||
@ -1215,23 +1211,6 @@ const onDialogMessage = function(msg) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const onConnectionMessage = function(msg) {
|
|
||||||
if ( msg.from !== `epickerDialog-${epickerId}` ) { return; }
|
|
||||||
switch ( msg.what ) {
|
|
||||||
case 'connectionRequested':
|
|
||||||
epickerConnectionId = msg.id;
|
|
||||||
return true;
|
|
||||||
case 'connectionBroken':
|
|
||||||
quitPicker();
|
|
||||||
break;
|
|
||||||
case 'connectionMessage':
|
|
||||||
onDialogMessage(msg.payload);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// epicker-ui.html will be injected in the page through an iframe, and
|
// epicker-ui.html will be injected in the page through an iframe, and
|
||||||
// is a sandboxed so as to prevent the page from interfering with its
|
// is a sandboxed so as to prevent the page from interfering with its
|
||||||
// content and behavior.
|
// content and behavior.
|
||||||
@ -1249,17 +1228,13 @@ const onConnectionMessage = function(msg) {
|
|||||||
// of the iframe, and cannot interfere with its style properties. However the
|
// of the iframe, and cannot interfere with its style properties. However the
|
||||||
// page can remove the iframe.
|
// page can remove the iframe.
|
||||||
|
|
||||||
// We need extra messaging capabilities + fetch/process picker arguments.
|
// fetch/process picker arguments.
|
||||||
{
|
{
|
||||||
const results = await Promise.all([
|
pickerBootArgs = await vAPI.messaging.send('elementPicker', {
|
||||||
vAPI.messaging.extend(),
|
what: 'elementPickerArguments',
|
||||||
vAPI.messaging.send('elementPicker', { what: 'elementPickerArguments' }),
|
});
|
||||||
]);
|
if ( typeof pickerBootArgs !== 'object' ) { return; }
|
||||||
if ( results[0] !== true ) { return; }
|
if ( pickerBootArgs === null ) { return; }
|
||||||
pickerBootArgs = results[1];
|
|
||||||
if ( typeof pickerBootArgs !== 'object' || pickerBootArgs === null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Restore net filter union data if origin is the same.
|
// Restore net filter union data if origin is the same.
|
||||||
const eprom = pickerBootArgs.eprom || null;
|
const eprom = pickerBootArgs.eprom || null;
|
||||||
if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) {
|
if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) {
|
||||||
@ -1302,12 +1277,12 @@ const pickerCSSStyle = [
|
|||||||
'width: 100%',
|
'width: 100%',
|
||||||
'z-index: 2147483647',
|
'z-index: 2147483647',
|
||||||
''
|
''
|
||||||
];
|
].join(' !important;\n');
|
||||||
|
|
||||||
|
|
||||||
const pickerCSS = `
|
const pickerCSS = `
|
||||||
:root > [${vAPI.sessionId}] {
|
:root > [${vAPI.sessionId}] {
|
||||||
${pickerCSSStyle.join(' !important;')}
|
${pickerCSSStyle}
|
||||||
}
|
}
|
||||||
:root > [${vAPI.sessionId}-loaded] {
|
:root > [${vAPI.sessionId}-loaded] {
|
||||||
visibility: visible !important;
|
visibility: visible !important;
|
||||||
@ -1326,7 +1301,7 @@ document.documentElement.append(pickerRoot);
|
|||||||
|
|
||||||
vAPI.shutdown.add(quitPicker);
|
vAPI.shutdown.add(quitPicker);
|
||||||
|
|
||||||
vAPI.MessagingConnection.addListener(onConnectionMessage);
|
let pickerFramePort;
|
||||||
|
|
||||||
{
|
{
|
||||||
const url = new URL(pickerBootArgs.pickerURL);
|
const url = new URL(pickerBootArgs.pickerURL);
|
||||||
@ -1334,10 +1309,24 @@ vAPI.MessagingConnection.addListener(onConnectionMessage);
|
|||||||
if ( pickerBootArgs.zap ) {
|
if ( pickerBootArgs.zap ) {
|
||||||
url.searchParams.set('zap', '1');
|
url.searchParams.set('zap', '1');
|
||||||
}
|
}
|
||||||
pickerRoot.addEventListener("load", function() {
|
pickerRoot.addEventListener('load', ( ) => {
|
||||||
pickerRoot.setAttribute(`${vAPI.sessionId}-loaded`, "");
|
const channel = new MessageChannel();
|
||||||
});
|
pickerFramePort = channel.port1;
|
||||||
pickerRoot.src = url.href;
|
pickerFramePort.onmessage = ev => {
|
||||||
|
const msg = ev.data || {};
|
||||||
|
onDialogMessage(msg);
|
||||||
|
};
|
||||||
|
pickerFramePort.onmessageerror = ( ) => {
|
||||||
|
quitPicker();
|
||||||
|
};
|
||||||
|
pickerRoot.setAttribute(`${vAPI.sessionId}-loaded`, '');
|
||||||
|
pickerRoot.contentWindow.postMessage(
|
||||||
|
{ what: 'epickerStart' },
|
||||||
|
url.href,
|
||||||
|
[ channel.port2 ]
|
||||||
|
);
|
||||||
|
}, { once: true });
|
||||||
|
pickerRoot.contentWindow.location = url.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -222,7 +222,6 @@
|
|||||||
<script src="js/vapi.js"></script>
|
<script src="js/vapi.js"></script>
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
<script src="js/vapi-client-extra.js"></script>
|
|
||||||
<script src="js/theme.js" type="module"></script>
|
<script src="js/theme.js" type="module"></script>
|
||||||
<script src="js/i18n.js" type="module"></script>
|
<script src="js/i18n.js" type="module"></script>
|
||||||
<script src="js/logger-ui.js" type="module"></script>
|
<script src="js/logger-ui.js" type="module"></script>
|
||||||
|
@ -121,7 +121,6 @@
|
|||||||
<script src="js/vapi.js"></script>
|
<script src="js/vapi.js"></script>
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
<script src="js/vapi-client-extra.js"></script>
|
|
||||||
<script src="js/theme.js" type="module"></script>
|
<script src="js/theme.js" type="module"></script>
|
||||||
<script src="js/i18n.js" type="module"></script>
|
<script src="js/i18n.js" type="module"></script>
|
||||||
<script src="js/dashboard-common.js" type="module"></script>
|
<script src="js/dashboard-common.js" type="module"></script>
|
||||||
|
24
src/web_accessible_resources/dom-inspector.html
Normal file
24
src/web_accessible_resources/dom-inspector.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html id="ublock0-inspector">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>uBlock Origin Inspector</title>
|
||||||
|
<link rel="stylesheet" href="../css/dom-inspector.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<svg>
|
||||||
|
<path d></path>
|
||||||
|
<path d></path>
|
||||||
|
<path d></path>
|
||||||
|
<path d></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<script src="../js/vapi.js"></script>
|
||||||
|
<script src="../js/vapi-common.js"></script>
|
||||||
|
<script src="../js/vapi-client.js"></script>
|
||||||
|
<script src="../js/dom-inspector.js" type="module"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -67,7 +67,6 @@
|
|||||||
<script src="../js/vapi.js"></script>
|
<script src="../js/vapi.js"></script>
|
||||||
<script src="../js/vapi-common.js"></script>
|
<script src="../js/vapi-common.js"></script>
|
||||||
<script src="../js/vapi-client.js"></script>
|
<script src="../js/vapi-client.js"></script>
|
||||||
<script src="../js/vapi-client-extra.js"></script>
|
|
||||||
<script src="../js/theme.js" type="module"></script>
|
<script src="../js/theme.js" type="module"></script>
|
||||||
<script src="../js/i18n.js" type="module"></script>
|
<script src="../js/i18n.js" type="module"></script>
|
||||||
<script src="../js/epicker-ui.js" type="module"></script>
|
<script src="../js/epicker-ui.js" type="module"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user