1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-17 16:02:33 +01:00
uBlock/src/js/scriptlets/dom-inspector.js
Raymond Hill efa4ff3bcf
Code review re. dynamically loaded vapi-client-extra.js
Related commit:
- 87d0e456f1

Ensure that the code which depends on extending
`vapi-client.js` is ready to deal with
`vapi-client-extra.js` failing to load.
2019-09-26 15:57:55 -04:00

1006 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2015-2018 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';
/******************************************************************************/
/******************************************************************************/
(( ) => {
/******************************************************************************/
if ( typeof vAPI !== 'object' || !vAPI.domFilterer ) { return; }
/******************************************************************************/
var sessionId = vAPI.sessionId;
if ( document.querySelector('iframe.dom-inspector.' + sessionId) !== null ) {
return;
}
/******************************************************************************/
/******************************************************************************/
// Modified to avoid installing as a global shim -- so the scriptlet can be
// flushed from memory once no longer in use.
// Added serializeAsString parameter.
/*! http://mths.be/cssescape v0.2.1 by @mathias | MIT license */
const cssEscape = (function(/*root*/) {
var InvalidCharacterError = function(message) {
this.message = message;
};
InvalidCharacterError.prototype = new Error();
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
// http://dev.w3.org/csswg/cssom/#serialize-an-identifier
return function(value, serializeAsString) {
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then throw an
// `InvalidCharacterError` exception and terminate these steps.
if (codeUnit === 0x0000) {
throw new InvalidCharacterError(
'Invalid character: the input contains U+0000.'
);
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit === 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index === 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit === 0x002D
)
) {
// http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit === 0x002D ||
codeUnit === 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// If "serialize a string":
// If the character is '"' (U+0022) or "\" (U+005C), the escaped
// character. Otherwise, the character itself.
// http://dev.w3.org/csswg/cssom/#serialize-a-string
if ( serializeAsString && codeUnit !== 0x0022 && codeUnit !== 0x005C ) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// http://dev.w3.org/csswg/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
}(self));
/******************************************************************************/
/******************************************************************************/
let loggerConnectionId;
// Highlighter-related
let svgRoot = null;
let pickerRoot = null;
let nodeToIdMap = new WeakMap(); // No need to iterate
let blueNodes = [];
const roRedNodes = new Map(); // node => current cosmetic filter
const rwRedNodes = new Set(); // node => new cosmetic filter (toggle node)
//var roGreenNodes = new Map(); // node => current exception cosmetic filter (can't toggle)
const rwGreenNodes = new Set(); // node => new exception cosmetic filter (toggle filter)
const reHasCSSCombinators = /[ >+~]/;
/******************************************************************************/
const domLayout = (function() {
const skipTagNames = new Set([
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
]);
const resourceAttrNames = new Map([
[ 'a', 'href' ],
[ 'iframe', 'src' ],
[ 'img', 'src' ],
[ 'object', 'data' ]
]);
var idGenerator = 0;
// This will be used to uniquely identify nodes across process.
const newNodeId = function(node) {
var nid = 'n' + (idGenerator++).toString(36);
nodeToIdMap.set(node, nid);
return nid;
};
const selectorFromNode = function(node) {
var str, attr, pos, sw, i;
var tag = node.localName;
var selector = cssEscape(tag);
// Id
if ( typeof node.id === 'string' ) {
str = node.id.trim();
if ( str !== '' ) {
selector += '#' + cssEscape(str);
}
}
// Class
var cl = node.classList;
if ( cl ) {
for ( i = 0; i < cl.length; i++ ) {
selector += '.' + cssEscape(cl[i]);
}
}
// Tag-specific attributes
attr = resourceAttrNames.get(tag);
if ( attr !== undefined ) {
str = node.getAttribute(attr) || '';
str = str.trim();
if ( str.startsWith('data:') ) {
pos = 5;
} else {
pos = str.search(/[#?]/);
}
if ( pos !== -1 ) {
str = str.slice(0, pos);
sw = '^';
} else {
sw = '';
}
if ( str !== '' ) {
selector += '[' + attr + sw + '="' + cssEscape(str, true) + '"]';
}
}
return selector;
};
const DomRoot = function() {
this.nid = newNodeId(document.body);
this.lvl = 0;
this.sel = 'body';
this.cnt = 0;
this.filter = roRedNodes.get(document.body);
};
const DomNode = function(node, level) {
this.nid = newNodeId(node);
this.lvl = level;
this.sel = selectorFromNode(node);
this.cnt = 0;
this.filter = roRedNodes.get(node);
};
const domNodeFactory = function(level, node) {
var localName = node.localName;
if ( skipTagNames.has(localName) ) { return null; }
// skip uBlock's own nodes
if ( node.classList.contains(sessionId) ) { return null; }
if ( level === 0 && localName === 'body' ) {
return new DomRoot();
}
return new DomNode(node, level);
};
// Collect layout data.
const getLayoutData = function() {
var layout = [];
var stack = [];
var node = document.documentElement;
var domNode;
var lvl = 0;
for (;;) {
domNode = domNodeFactory(lvl, node);
if ( domNode !== null ) {
layout.push(domNode);
}
// children
if ( node.firstElementChild !== null ) {
stack.push(node);
lvl += 1;
node = node.firstElementChild;
continue;
}
// sibling
if ( node.nextElementSibling === null ) {
do {
node = stack.pop();
if ( !node ) { break; }
lvl -= 1;
} while ( node.nextElementSibling === null );
if ( !node ) { break; }
}
node = node.nextElementSibling;
}
return layout;
};
// Descendant count for each node.
const patchLayoutData = function(layout) {
var stack = [], ptr;
var lvl = 0;
var domNode, cnt;
var i = layout.length;
while ( i-- ) {
domNode = layout[i];
if ( domNode.lvl === lvl ) {
stack[ptr] += 1;
continue;
}
if ( domNode.lvl > lvl ) {
while ( lvl < domNode.lvl ) {
stack.push(0);
lvl += 1;
}
ptr = lvl - 1;
stack[ptr] += 1;
continue;
}
// domNode.lvl < lvl
cnt = stack.pop();
domNode.cnt = cnt;
lvl -= 1;
ptr = lvl - 1;
stack[ptr] += cnt + 1;
}
return layout;
};
// Track and report mutations of the DOM
var mutationObserver = null;
var mutationTimer;
var addedNodelists = [];
var removedNodelist = [];
const previousElementSiblingId = function(node) {
var sibling = node;
for (;;) {
sibling = sibling.previousElementSibling;
if ( sibling === null ) { return null; }
if ( skipTagNames.has(sibling.localName) ) { continue; }
return nodeToIdMap.get(sibling);
}
};
const journalFromBranch = function(root, newNodes, newNodeToIdMap) {
var domNode;
var node = root.firstElementChild;
while ( node !== null ) {
domNode = domNodeFactory(undefined, node);
if ( domNode !== null ) {
newNodeToIdMap.set(domNode.nid, domNode);
newNodes.push(node);
}
// down
if ( node.firstElementChild !== null ) {
node = node.firstElementChild;
continue;
}
// right
if ( node.nextElementSibling !== null ) {
node = node.nextElementSibling;
continue;
}
// up then right
for (;;) {
if ( node.parentElement === root ) { return; }
node = node.parentElement;
if ( node.nextElementSibling !== null ) {
node = node.nextElementSibling;
break;
}
}
}
};
const journalFromMutations = function() {
var nodelist, node, domNode, nid;
mutationTimer = undefined;
// This is used to temporarily hold all added nodes, before resolving
// their node id and relative position.
var newNodes = [];
var journalEntries = [];
var newNodeToIdMap = new Map();
for ( nodelist of addedNodelists ) {
for ( node of nodelist ) {
if ( node.nodeType !== 1 ) { continue; }
if ( node.parentElement === null ) { continue; }
cosmeticFilterMapper.incremental(node);
domNode = domNodeFactory(undefined, node);
if ( domNode !== null ) {
newNodeToIdMap.set(domNode.nid, domNode);
newNodes.push(node);
}
journalFromBranch(node, newNodes, newNodeToIdMap);
}
}
addedNodelists = [];
for ( nodelist of removedNodelist ) {
for ( node of nodelist ) {
if ( node.nodeType !== 1 ) { continue; }
nid = nodeToIdMap.get(node);
if ( nid === undefined ) { continue; }
journalEntries.push({
what: -1,
nid: nid
});
}
}
removedNodelist = [];
for ( node of newNodes ) {
journalEntries.push({
what: 1,
nid: nodeToIdMap.get(node),
u: nodeToIdMap.get(node.parentElement),
l: previousElementSiblingId(node)
});
}
if ( journalEntries.length === 0 ) { return; }
vAPI.MessagingConnection.sendTo(loggerConnectionId, {
what: 'domLayoutIncremental',
url: window.location.href,
hostname: window.location.hostname,
journal: journalEntries,
nodes: Array.from(newNodeToIdMap)
});
};
const onMutationObserved = function(mutationRecords) {
for ( var record of mutationRecords ) {
if ( record.addedNodes.length !== 0 ) {
addedNodelists.push(record.addedNodes);
}
if ( record.removedNodes.length !== 0 ) {
removedNodelist.push(record.removedNodes);
}
}
if ( mutationTimer === undefined ) {
mutationTimer = vAPI.setTimeout(journalFromMutations, 1000);
}
};
// API
const getLayout = function() {
cosmeticFilterMapper.reset();
mutationObserver = new MutationObserver(onMutationObserved);
mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
return {
what: 'domLayoutFull',
url: window.location.href,
hostname: window.location.hostname,
layout: patchLayoutData(getLayoutData())
};
};
const reset = function() {
shutdown();
};
const shutdown = function() {
if ( mutationTimer !== undefined ) {
clearTimeout(mutationTimer);
mutationTimer = undefined;
}
if ( mutationObserver !== null ) {
mutationObserver.disconnect();
mutationObserver = null;
}
addedNodelists = [];
removedNodelist = [];
nodeToIdMap = new WeakMap();
};
return {
get: getLayout,
reset: reset,
shutdown: shutdown
};
})();
// https://www.youtube.com/watch?v=qo8zKhd4Cf0
/******************************************************************************/
/******************************************************************************/
// For browsers not supporting `:scope`, it's not the end of the world: the
// suggested CSS selectors may just end up being more verbose.
let cssScope = ':scope > ';
try {
document.querySelector(':scope *');
} catch (e) {
cssScope = '';
}
/******************************************************************************/
const cosmeticFilterMapper = (function() {
// https://github.com/gorhill/uBlock/issues/546
var matchesFnName;
if ( typeof document.body.matches === 'function' ) {
matchesFnName = 'matches';
} else if ( typeof document.body.mozMatchesSelector === 'function' ) {
matchesFnName = 'mozMatchesSelector';
} else if ( typeof document.body.webkitMatchesSelector === 'function' ) {
matchesFnName = 'webkitMatchesSelector';
}
const nodesFromStyleTag = function(rootNode) {
var filterMap = roRedNodes,
entry, selector, canonical, nodes, node;
var details = vAPI.domFilterer.getAllSelectors();
// Declarative selectors.
for ( entry of (details.declarative || []) ) {
for ( selector of entry[0].split(',\n') ) {
canonical = selector;
if ( entry[1] !== 'display:none!important;' ) {
canonical += ':style(' + entry[1] + ')';
}
if ( reHasCSSCombinators.test(selector) ) {
nodes = document.querySelectorAll(selector);
} else {
if (
filterMap.has(rootNode) === false &&
rootNode[matchesFnName](selector)
) {
filterMap.set(rootNode, canonical);
}
nodes = rootNode.querySelectorAll(selector);
}
for ( node of nodes ) {
if ( filterMap.has(node) === false ) {
filterMap.set(node, canonical);
}
}
}
}
// Procedural selectors.
for ( entry of (details.procedural || []) ) {
nodes = entry.exec();
for ( node of nodes ) {
// Upgrade declarative selector to procedural one
filterMap.set(node, entry.raw);
}
}
};
const incremental = function(rootNode) {
nodesFromStyleTag(rootNode);
};
const reset = function() {
roRedNodes.clear();
incremental(document.documentElement);
};
const shutdown = function() {
vAPI.domFilterer.toggle(true);
};
return {
incremental: incremental,
reset: reset,
shutdown: shutdown
};
})();
/******************************************************************************/
const elementsFromSelector = function(selector, context) {
if ( !context ) {
context = document;
}
if ( selector.indexOf(':') !== -1 ) {
const out = elementsFromSpecialSelector(selector);
if ( out !== undefined ) { return out; }
}
// plain CSS selector
try {
return context.querySelectorAll(selector);
} catch (ex) {
}
return [];
};
const elementsFromSpecialSelector = function(selector) {
var out = [], i;
var matches = /^(.+?):has\((.+?)\)$/.exec(selector);
if ( matches !== null ) {
var nodes;
try {
nodes = document.querySelectorAll(matches[1]);
} catch(ex) {
nodes = [];
}
i = nodes.length;
while ( i-- ) {
var node = nodes[i];
if ( node.querySelector(matches[2]) !== null ) {
out.push(node);
}
}
return out;
}
matches = /^:xpath\((.+?)\)$/.exec(selector);
if ( matches === null ) { return; }
const xpr = document.evaluate(
matches[1],
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
i = xpr.snapshotLength;
while ( i-- ) {
out.push(xpr.snapshotItem(i));
}
return out;
};
/******************************************************************************/
const getSvgRootChildren = function() {
if ( svgRoot.children ) {
return svgRoot.children;
} else {
const childNodes = Array.prototype.slice.apply(svgRoot.childNodes);
return childNodes.filter(function(node) {
return node.nodeType === Node.ELEMENT_NODE;
});
}
};
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() {
let buffered = false;
const timerHandler = function() {
buffered = false;
highlightElements();
};
return function() {
if ( buffered === false ) {
window.requestAnimationFrame(timerHandler);
buffered = true;
}
};
})();
/******************************************************************************/
const selectNodes = function(selector, nid) {
const nodes = elementsFromSelector(selector);
if ( nid === '' ) { return nodes; }
for ( const node of nodes ) {
if ( nodeToIdMap.get(node) === nid ) {
return [ node ];
}
}
return [];
};
/******************************************************************************/
const nodesFromFilter = function(selector) {
const out = [];
for ( const entry of roRedNodes ) {
if ( entry[1] === selector ) {
out.push(entry[0]);
}
}
return out;
};
/******************************************************************************/
const toggleExceptions = function(nodes, targetState) {
for ( const node of nodes ) {
if ( targetState ) {
rwGreenNodes.add(node);
} else {
rwGreenNodes.delete(node);
}
}
};
const toggleFilter = function(nodes, targetState) {
for ( const node of nodes ) {
if ( targetState ) {
rwRedNodes.delete(node);
} else {
rwRedNodes.add(node);
}
}
};
const resetToggledNodes = function() {
rwGreenNodes.clear();
rwRedNodes.clear();
};
/******************************************************************************/
const start = function() {
const onReady = function(ev) {
if ( ev ) {
document.removeEventListener(ev.type, onReady);
}
vAPI.MessagingConnection.sendTo(loggerConnectionId, domLayout.get());
vAPI.domFilterer.toggle(false, highlightElements);
};
if ( document.readyState === 'loading' ) {
document.addEventListener('DOMContentLoaded', onReady);
} else {
onReady();
}
};
/******************************************************************************/
const shutdown = function() {
cosmeticFilterMapper.shutdown();
domLayout.shutdown();
vAPI.MessagingConnection.disconnectFrom(loggerConnectionId);
window.removeEventListener('scroll', onScrolled, true);
document.documentElement.removeChild(pickerRoot);
pickerRoot = svgRoot = null;
};
/******************************************************************************/
/******************************************************************************/
const onMessage = function(request) {
var response,
nodes;
switch ( request.what ) {
case 'commitFilters':
highlightElements();
break;
case 'domLayout':
response = domLayout.get();
highlightElements();
break;
case 'highlightMode':
//svgRoot.classList.toggle('invert', request.invert);
break;
case 'highlightOne':
blueNodes = selectNodes(request.selector, request.nid);
highlightElements();
break;
case 'resetToggledNodes':
resetToggledNodes();
highlightElements();
break;
case 'showCommitted':
blueNodes = [];
// TODO: show only the new filters and exceptions.
highlightElements();
break;
case 'showInteractive':
blueNodes = [];
highlightElements();
break;
case 'toggleFilter':
nodes = selectNodes(request.selector, request.nid);
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
toggleExceptions(nodesFromFilter(request.filter), request.target);
highlightElements();
break;
case 'toggleNodes':
nodes = selectNodes(request.selector, request.nid);
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
toggleFilter(nodes, request.target);
highlightElements();
break;
default:
break;
}
return response;
};
/******************************************************************************/
// Install DOM inspector widget
const bootstrap = function(ev) {
if ( ev ) {
pickerRoot.removeEventListener(ev.type, bootstrap);
}
const pickerDoc = ev.target.contentDocument;
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;
}
});
});
};
pickerRoot = document.createElement('iframe');
pickerRoot.classList.add(sessionId);
pickerRoot.classList.add('dom-inspector');
pickerRoot.style.cssText = [
'background: transparent',
'border: 0',
'border-radius: 0',
'box-shadow: none',
'display: block',
'height: 100%',
'left: 0',
'margin: 0',
'opacity: 1',
'position: fixed',
'outline: 0',
'padding: 0',
'pointer-events:none;',
'top: 0',
'visibility: visible',
'width: 100%',
'z-index: 2147483647',
''
].join(' !important;\n');
pickerRoot.addEventListener('load', ev => { bootstrap(ev); });
document.documentElement.appendChild(pickerRoot);
/******************************************************************************/
})();
/*******************************************************************************
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;