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

1006 lines
30 KiB
JavaScript
Raw Normal View History

/*******************************************************************************
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
2017-10-21 19:43:46 +02:00
let blueNodes = [];
const roRedNodes = new Map(); // node => current cosmetic filter
const rwRedNodes = new Set(); // node => new cosmetic filter (toggle node)
2017-10-21 19:43:46 +02:00
//var roGreenNodes = new Map(); // node => current exception cosmetic filter (can't toggle)
const rwGreenNodes = new Set(); // node => new exception cosmetic filter (toggle filter)
2017-10-21 19:43:46 +02:00
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;
2017-10-21 19:43:46 +02:00
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;
2017-10-21 19:43:46 +02:00
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',
2015-07-01 00:02:29 +02:00
url: window.location.href,
hostname: window.location.hostname,
layout: patchLayoutData(getLayoutData())
};
};
const reset = function() {
2015-07-01 00:02:29 +02:00
shutdown();
};
const shutdown = function() {
if ( mutationTimer !== undefined ) {
clearTimeout(mutationTimer);
mutationTimer = undefined;
}
if ( mutationObserver !== null ) {
mutationObserver.disconnect();
mutationObserver = null;
}
2015-07-01 00:02:29 +02:00
addedNodelists = [];
removedNodelist = [];
nodeToIdMap = new WeakMap();
};
return {
get: getLayout,
2015-07-01 00:02:29 +02:00
reset: reset,
shutdown: shutdown
};
})();
// https://www.youtube.com/watch?v=qo8zKhd4Cf0
2015-07-01 00:02:29 +02:00
/******************************************************************************/
/******************************************************************************/
// 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 > ';
2015-07-01 00:02:29 +02:00
try {
document.querySelector(':scope *');
} catch (e) {
cssScope = '';
}
/******************************************************************************/
const cosmeticFilterMapper = (function() {
2015-07-30 18:29:37 +02:00
// 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) {
2017-10-21 19:43:46 +02:00
var filterMap = roRedNodes,
2017-11-14 21:03:20 +01:00
entry, selector, canonical, nodes, node;
var details = vAPI.domFilterer.getAllSelectors();
2017-10-21 19:43:46 +02:00
// Declarative selectors.
2017-11-14 21:03:20 +01:00
for ( entry of (details.declarative || []) ) {
for ( selector of entry[0].split(',\n') ) {
canonical = selector;
if ( entry[1] !== 'display:none!important;' ) {
canonical += ':style(' + entry[1] + ')';
2017-10-21 19:43:46 +02:00
}
2017-11-14 21:03:20 +01:00
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);
}
}
}
}
2017-10-21 19:43:46 +02:00
// Procedural selectors.
2017-11-14 21:03:20 +01:00
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() {
2017-10-21 19:43:46 +02:00
vAPI.domFilterer.toggle(true);
};
return {
incremental: incremental,
reset: reset,
shutdown: shutdown
};
})();
2015-07-06 19:53:39 +02:00
/******************************************************************************/
const elementsFromSelector = function(selector, context) {
2015-07-01 00:02:29 +02:00
if ( !context ) {
context = document;
}
if ( selector.indexOf(':') !== -1 ) {
const out = elementsFromSpecialSelector(selector);
if ( out !== undefined ) { return out; }
}
// plain CSS selector
2015-07-01 00:02:29 +02:00
try {
return context.querySelectorAll(selector);
2015-07-01 00:02:29 +02:00
} catch (ex) {
}
return [];
};
const elementsFromSpecialSelector = function(selector) {
var out = [], i;
var matches = /^(.+?):has\((.+?)\)$/.exec(selector);
if ( matches !== null ) {
2017-12-31 22:05:23 +01:00
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;
2015-07-01 00:02:29 +02:00
};
/******************************************************************************/
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() {
2017-10-21 19:43:46 +02:00
var islands;
var elem, rect, poly;
var xl, xr, yt, yb, w, h, ws;
var svgRootChildren = getSvgRootChildren();
2015-07-01 00:02:29 +02:00
2017-10-21 19:43:46 +02:00
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);
}
2017-10-21 19:43:46 +02:00
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;
}
};
})();
2015-07-01 00:02:29 +02:00
/******************************************************************************/
const selectNodes = function(selector, nid) {
const nodes = elementsFromSelector(selector);
2017-10-21 19:43:46 +02:00
if ( nid === '' ) { return nodes; }
for ( const node of nodes ) {
2017-10-21 19:43:46 +02:00
if ( nodeToIdMap.get(node) === nid ) {
return [ node ];
2015-06-28 23:42:08 +02:00
}
}
return [];
};
/******************************************************************************/
const nodesFromFilter = function(selector) {
const out = [];
for ( const entry of roRedNodes ) {
2017-11-14 21:03:20 +01:00
if ( entry[1] === selector ) {
out.push(entry[0]);
}
}
return out;
};
/******************************************************************************/
const toggleExceptions = function(nodes, targetState) {
for ( const node of nodes ) {
2017-10-21 19:43:46 +02:00
if ( targetState ) {
rwGreenNodes.add(node);
} else {
rwGreenNodes.delete(node);
}
}
};
const toggleFilter = function(nodes, targetState) {
for ( const node of nodes ) {
2017-10-21 19:43:46 +02:00
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 ) {
2015-07-01 00:02:29 +02:00
case 'commitFilters':
highlightElements();
break;
case 'domLayout':
response = domLayout.get();
highlightElements();
break;
2015-06-28 23:42:08 +02:00
case 'highlightMode':
2017-10-21 19:43:46 +02:00
//svgRoot.classList.toggle('invert', request.invert);
2015-06-28 23:42:08 +02:00
break;
case 'highlightOne':
2017-10-21 19:43:46 +02:00
blueNodes = selectNodes(request.selector, request.nid);
highlightElements();
break;
case 'resetToggledNodes':
resetToggledNodes();
highlightElements();
2015-06-28 23:42:08 +02:00
break;
2015-07-01 00:02:29 +02:00
case 'showCommitted':
2017-10-21 19:43:46 +02:00
blueNodes = [];
// TODO: show only the new filters and exceptions.
highlightElements();
2015-07-01 00:02:29 +02:00
break;
case 'showInteractive':
2017-10-21 19:43:46 +02:00
blueNodes = [];
2015-07-01 00:02:29 +02:00
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 = async 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%;',
'}',
2017-10-21 19:43:46 +02:00
'svg > path:nth-of-type(1) {',
'fill: rgba(255,0,0,0.2);',
'stroke: #F00;',
'}',
2015-07-01 00:02:29 +02:00
'svg > path:nth-of-type(2) {',
2017-10-21 19:43:46 +02:00
'fill: rgba(0,255,0,0.2);',
'stroke: #0F0;',
'}',
2015-07-01 00:02:29 +02:00
'svg > path:nth-of-type(3) {',
'fill: rgba(255,0,0,0.2);',
'stroke: #F00;',
2015-06-28 23:42:08 +02:00
'}',
2015-07-01 00:02:29 +02:00
'svg > path:nth-of-type(4) {',
2017-10-21 19:43:46 +02:00
'fill: rgba(0,0,255,0.1);',
'stroke: #FFF;',
'stroke-width: 0.5px;',
2015-06-28 23:42:08 +02:00
'}',
''
].join('\n');
pickerDoc.body.appendChild(style);
svgRoot = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
2015-07-01 00:02:29 +02:00
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.
if ( vAPI.MessagingConnection instanceof Function === false ) {
await vAPI.messaging.send('vapi', { what: 'extendClient' });
}
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;