2015-06-26 21:45:54 +02:00
|
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-05-03 15:55:36 +02:00
|
|
|
|
Copyright (C) 2015-2018 Raymond Hill
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
'use strict';
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
(( ) => {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
if ( typeof vAPI !== 'object' || !vAPI.domFilterer ) { return; }
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2015-07-06 13:48:56 +02:00
|
|
|
|
var sessionId = vAPI.sessionId;
|
|
|
|
|
|
|
|
|
|
if ( document.querySelector('iframe.dom-inspector.' + sessionId) !== null ) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
// Modified to avoid installing as a global shim -- so the scriptlet can be
|
|
|
|
|
// flushed from memory once no longer in use.
|
|
|
|
|
|
2015-07-03 12:34:18 +02:00
|
|
|
|
// Added serializeAsString parameter.
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
/*! http://mths.be/cssescape v0.2.1 by @mathias | MIT license */
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const cssEscape = (function(/*root*/) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
var InvalidCharacterError = function(message) {
|
|
|
|
|
this.message = message;
|
|
|
|
|
};
|
|
|
|
|
InvalidCharacterError.prototype = new Error();
|
|
|
|
|
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
|
|
|
|
|
|
|
|
|
|
// http://dev.w3.org/csswg/cssom/#serialize-an-identifier
|
2015-07-03 12:34:18 +02:00
|
|
|
|
return function(value, serializeAsString) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
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: there’s 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, […]
|
2015-07-03 12:34:18 +02:00
|
|
|
|
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit === 0x007F ||
|
2015-06-26 21:45:54 +02:00
|
|
|
|
// 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), […]
|
|
|
|
|
(
|
2015-07-03 12:34:18 +02:00
|
|
|
|
index === 1 &&
|
2015-06-26 21:45:54 +02:00
|
|
|
|
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
|
2015-07-03 12:34:18 +02:00
|
|
|
|
firstCodeUnit === 0x002D
|
2015-06-26 21:45:54 +02:00
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
// 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 ||
|
2015-07-03 12:34:18 +02:00
|
|
|
|
codeUnit === 0x002D ||
|
|
|
|
|
codeUnit === 0x005F ||
|
2015-06-26 21:45:54 +02:00
|
|
|
|
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
|
|
|
|
|
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
|
|
|
|
|
codeUnit >= 0x0061 && codeUnit <= 0x007A
|
|
|
|
|
) {
|
|
|
|
|
// the character itself
|
|
|
|
|
result += string.charAt(index);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-03 12:34:18 +02:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
// Otherwise, the escaped character.
|
|
|
|
|
// http://dev.w3.org/csswg/cssom/#escape-a-character
|
|
|
|
|
result += '\\' + string.charAt(index);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
}(self));
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
let loggerConnectionId;
|
2017-11-20 14:42:32 +01:00
|
|
|
|
|
2015-06-27 19:32:10 +02:00
|
|
|
|
// Highlighter-related
|
2019-09-19 14:31:38 +02:00
|
|
|
|
let svgRoot = null;
|
|
|
|
|
let pickerRoot = null;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
let nodeToIdMap = new WeakMap(); // No need to iterate
|
2017-10-21 19:43:46 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +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)
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const rwGreenNodes = new Set(); // node => new exception cosmetic filter (toggle filter)
|
2017-10-21 19:43:46 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const reHasCSSCombinators = /[ >+~]/;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const domLayout = (function() {
|
|
|
|
|
const skipTagNames = new Set([
|
2017-11-20 14:42:32 +01:00
|
|
|
|
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
|
|
|
|
|
]);
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const resourceAttrNames = new Map([
|
2017-11-20 14:42:32 +01:00
|
|
|
|
[ 'a', 'href' ],
|
|
|
|
|
[ 'iframe', 'src' ],
|
|
|
|
|
[ 'img', 'src' ],
|
|
|
|
|
[ 'object', 'data' ]
|
|
|
|
|
]);
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var idGenerator = 0;
|
|
|
|
|
|
|
|
|
|
// This will be used to uniquely identify nodes across process.
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const newNodeId = function(node) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var nid = 'n' + (idGenerator++).toString(36);
|
|
|
|
|
nodeToIdMap.set(node, nid);
|
|
|
|
|
return nid;
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const selectorFromNode = function(node) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
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
|
2017-11-20 14:42:32 +01:00
|
|
|
|
attr = resourceAttrNames.get(tag);
|
|
|
|
|
if ( attr !== undefined ) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
str = node.getAttribute(attr) || '';
|
|
|
|
|
str = str.trim();
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( str.startsWith('data:') ) {
|
|
|
|
|
pos = 5;
|
|
|
|
|
} else {
|
|
|
|
|
pos = str.search(/[#?]/);
|
|
|
|
|
}
|
2015-06-26 21:45:54 +02:00
|
|
|
|
if ( pos !== -1 ) {
|
|
|
|
|
str = str.slice(0, pos);
|
|
|
|
|
sw = '^';
|
|
|
|
|
} else {
|
|
|
|
|
sw = '';
|
|
|
|
|
}
|
|
|
|
|
if ( str !== '' ) {
|
2015-07-03 12:34:18 +02:00
|
|
|
|
selector += '[' + attr + sw + '="' + cssEscape(str, true) + '"]';
|
2015-06-26 21:45:54 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return selector;
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const DomRoot = function() {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
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);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const DomNode = function(node, level) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
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);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const domNodeFactory = function(level, node) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
var localName = node.localName;
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( skipTagNames.has(localName) ) { return null; }
|
2015-06-26 21:45:54 +02:00
|
|
|
|
// skip uBlock's own nodes
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( node.classList.contains(sessionId) ) { return null; }
|
2015-06-26 21:45:54 +02:00
|
|
|
|
if ( level === 0 && localName === 'body' ) {
|
|
|
|
|
return new DomRoot();
|
|
|
|
|
}
|
2015-06-27 19:32:10 +02:00
|
|
|
|
return new DomNode(node, level);
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Collect layout data.
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const getLayoutData = function() {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var layout = [];
|
2015-06-26 21:45:54 +02:00
|
|
|
|
var stack = [];
|
2017-11-20 14:42:32 +01:00
|
|
|
|
var node = document.documentElement;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
var domNode;
|
|
|
|
|
var lvl = 0;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
domNode = domNodeFactory(lvl, node);
|
|
|
|
|
if ( domNode !== null ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
layout.push(domNode);
|
2015-06-26 21:45:54 +02:00
|
|
|
|
}
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
2015-06-27 19:32:10 +02:00
|
|
|
|
|
|
|
|
|
return layout;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Descendant count for each node.
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const patchLayoutData = function(layout) {
|
2015-06-26 21:45:54 +02:00
|
|
|
|
var stack = [], ptr;
|
|
|
|
|
var lvl = 0;
|
|
|
|
|
var domNode, cnt;
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var i = layout.length;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
|
|
|
|
while ( i-- ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
domNode = layout[i];
|
2015-06-26 21:45:54 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2015-06-27 19:32:10 +02:00
|
|
|
|
return layout;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
2017-11-20 14:42:32 +01:00
|
|
|
|
// Track and report mutations of the DOM
|
2015-06-27 19:32:10 +02:00
|
|
|
|
|
|
|
|
|
var mutationObserver = null;
|
2017-11-20 14:42:32 +01:00
|
|
|
|
var mutationTimer;
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var addedNodelists = [];
|
|
|
|
|
var removedNodelist = [];
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const previousElementSiblingId = function(node) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var sibling = node;
|
|
|
|
|
for (;;) {
|
|
|
|
|
sibling = sibling.previousElementSibling;
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( sibling === null ) { return null; }
|
|
|
|
|
if ( skipTagNames.has(sibling.localName) ) { continue; }
|
2015-06-27 19:32:10 +02:00
|
|
|
|
return nodeToIdMap.get(sibling);
|
|
|
|
|
}
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const journalFromBranch = function(root, newNodes, newNodeToIdMap) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
var domNode;
|
|
|
|
|
var node = root.firstElementChild;
|
|
|
|
|
while ( node !== null ) {
|
|
|
|
|
domNode = domNodeFactory(undefined, node);
|
|
|
|
|
if ( domNode !== null ) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
newNodeToIdMap.set(domNode.nid, domNode);
|
|
|
|
|
newNodes.push(node);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
}
|
|
|
|
|
// down
|
|
|
|
|
if ( node.firstElementChild !== null ) {
|
|
|
|
|
node = node.firstElementChild;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// right
|
|
|
|
|
if ( node.nextElementSibling !== null ) {
|
|
|
|
|
node = node.nextElementSibling;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// up then right
|
|
|
|
|
for (;;) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( node.parentElement === root ) { return; }
|
2015-06-27 19:32:10 +02:00
|
|
|
|
node = node.parentElement;
|
|
|
|
|
if ( node.nextElementSibling !== null ) {
|
|
|
|
|
node = node.nextElementSibling;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const journalFromMutations = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
var nodelist, node, domNode, nid;
|
2017-11-22 00:08:32 +01:00
|
|
|
|
mutationTimer = undefined;
|
2015-06-27 19:32:10 +02:00
|
|
|
|
|
|
|
|
|
// This is used to temporarily hold all added nodes, before resolving
|
|
|
|
|
// their node id and relative position.
|
2017-11-20 14:42:32 +01:00
|
|
|
|
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; }
|
2015-07-11 13:38:03 +02:00
|
|
|
|
cosmeticFilterMapper.incremental(node);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
domNode = domNodeFactory(undefined, node);
|
|
|
|
|
if ( domNode !== null ) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
newNodeToIdMap.set(domNode.nid, domNode);
|
|
|
|
|
newNodes.push(node);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
}
|
2017-11-20 14:42:32 +01:00
|
|
|
|
journalFromBranch(node, newNodes, newNodeToIdMap);
|
2015-06-27 19:32:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addedNodelists = [];
|
2017-11-20 14:42:32 +01:00
|
|
|
|
for ( nodelist of removedNodelist ) {
|
|
|
|
|
for ( node of nodelist ) {
|
|
|
|
|
if ( node.nodeType !== 1 ) { continue; }
|
2015-06-27 19:32:10 +02:00
|
|
|
|
nid = nodeToIdMap.get(node);
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( nid === undefined ) { continue; }
|
2015-06-27 19:32:10 +02:00
|
|
|
|
journalEntries.push({
|
|
|
|
|
what: -1,
|
|
|
|
|
nid: nid
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
removedNodelist = [];
|
2017-11-20 14:42:32 +01:00
|
|
|
|
for ( node of newNodes ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
journalEntries.push({
|
|
|
|
|
what: 1,
|
|
|
|
|
nid: nodeToIdMap.get(node),
|
|
|
|
|
u: nodeToIdMap.get(node.parentElement),
|
|
|
|
|
l: previousElementSiblingId(node)
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-11-20 14:42:32 +01:00
|
|
|
|
|
|
|
|
|
if ( journalEntries.length === 0 ) { return; }
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
vAPI.MessagingConnection.sendTo(loggerConnectionId, {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
what: 'domLayoutIncremental',
|
|
|
|
|
url: window.location.href,
|
|
|
|
|
hostname: window.location.hostname,
|
|
|
|
|
journal: journalEntries,
|
|
|
|
|
nodes: Array.from(newNodeToIdMap)
|
|
|
|
|
});
|
2015-06-27 19:32:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const onMutationObserved = function(mutationRecords) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
for ( var record of mutationRecords ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
if ( record.addedNodes.length !== 0 ) {
|
|
|
|
|
addedNodelists.push(record.addedNodes);
|
|
|
|
|
}
|
|
|
|
|
if ( record.removedNodes.length !== 0 ) {
|
|
|
|
|
removedNodelist.push(record.removedNodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( mutationTimer === undefined ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
mutationTimer = vAPI.setTimeout(journalFromMutations, 1000);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// API
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const getLayout = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
cosmeticFilterMapper.reset();
|
|
|
|
|
mutationObserver = new MutationObserver(onMutationObserved);
|
|
|
|
|
mutationObserver.observe(document.body, {
|
|
|
|
|
childList: true,
|
|
|
|
|
subtree: true
|
|
|
|
|
});
|
2015-06-27 19:32:10 +02:00
|
|
|
|
|
2017-11-20 14:42:32 +01:00
|
|
|
|
return {
|
|
|
|
|
what: 'domLayoutFull',
|
2015-07-01 00:02:29 +02:00
|
|
|
|
url: window.location.href,
|
2017-11-20 14:42:32 +01:00
|
|
|
|
hostname: window.location.hostname,
|
|
|
|
|
layout: patchLayoutData(getLayoutData())
|
2015-06-27 19:32:10 +02:00
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const reset = function() {
|
2015-07-01 00:02:29 +02:00
|
|
|
|
shutdown();
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const shutdown = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( mutationTimer !== undefined ) {
|
2015-06-27 19:32:10 +02:00
|
|
|
|
clearTimeout(mutationTimer);
|
2017-11-20 14:42:32 +01:00
|
|
|
|
mutationTimer = undefined;
|
2015-06-27 19:32:10 +02:00
|
|
|
|
}
|
|
|
|
|
if ( mutationObserver !== null ) {
|
|
|
|
|
mutationObserver.disconnect();
|
|
|
|
|
mutationObserver = null;
|
|
|
|
|
}
|
2015-07-01 00:02:29 +02:00
|
|
|
|
addedNodelists = [];
|
|
|
|
|
removedNodelist = [];
|
|
|
|
|
nodeToIdMap = new WeakMap();
|
2015-06-27 19:32:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
get: getLayout,
|
2015-07-01 00:02:29 +02:00
|
|
|
|
reset: reset,
|
2015-06-27 19:32:10 +02:00
|
|
|
|
shutdown: shutdown
|
|
|
|
|
};
|
|
|
|
|
})();
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2015-06-29 16:46:20 +02:00
|
|
|
|
// 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.
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
let cssScope = ':scope > ';
|
2015-07-01 00:02:29 +02:00
|
|
|
|
try {
|
|
|
|
|
document.querySelector(':scope *');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
cssScope = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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';
|
|
|
|
|
}
|
2015-07-11 13:38:03 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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();
|
2016-06-28 01:09:04 +02:00
|
|
|
|
|
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);
|
|
|
|
|
}
|
2016-06-28 01:09:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-25 22:56:39 +01:00
|
|
|
|
|
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 ) {
|
2017-11-22 00:08:32 +01:00
|
|
|
|
// Upgrade declarative selector to procedural one
|
|
|
|
|
filterMap.set(node, entry.raw);
|
2016-06-28 01:09:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-11 13:38:03 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const incremental = function(rootNode) {
|
2016-06-28 01:09:04 +02:00
|
|
|
|
nodesFromStyleTag(rootNode);
|
2015-07-11 13:38:03 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const reset = function() {
|
|
|
|
|
roRedNodes.clear();
|
2015-07-11 13:38:03 +02:00
|
|
|
|
incremental(document.documentElement);
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const shutdown = function() {
|
2017-10-21 19:43:46 +02:00
|
|
|
|
vAPI.domFilterer.toggle(true);
|
2015-07-11 13:38:03 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
incremental: incremental,
|
|
|
|
|
reset: reset,
|
|
|
|
|
shutdown: shutdown
|
|
|
|
|
};
|
|
|
|
|
})();
|
2015-07-06 19:53:39 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const elementsFromSelector = function(selector, context) {
|
2015-07-01 00:02:29 +02:00
|
|
|
|
if ( !context ) {
|
|
|
|
|
context = document;
|
|
|
|
|
}
|
2016-06-28 01:09:04 +02:00
|
|
|
|
if ( selector.indexOf(':') !== -1 ) {
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const out = elementsFromSpecialSelector(selector);
|
|
|
|
|
if ( out !== undefined ) { return out; }
|
2016-06-28 01:09:04 +02:00
|
|
|
|
}
|
|
|
|
|
// plain CSS selector
|
2015-07-01 00:02:29 +02:00
|
|
|
|
try {
|
2019-09-19 14:31:38 +02:00
|
|
|
|
return context.querySelectorAll(selector);
|
2015-07-01 00:02:29 +02:00
|
|
|
|
} catch (ex) {
|
|
|
|
|
}
|
2019-09-19 14:31:38 +02:00
|
|
|
|
return [];
|
2016-06-28 01:09:04 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const elementsFromSpecialSelector = function(selector) {
|
2016-06-28 01:09:04 +02:00
|
|
|
|
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 = [];
|
|
|
|
|
}
|
2016-06-28 01:09:04 +02:00
|
|
|
|
i = nodes.length;
|
|
|
|
|
while ( i-- ) {
|
|
|
|
|
var node = nodes[i];
|
|
|
|
|
if ( node.querySelector(matches[2]) !== null ) {
|
|
|
|
|
out.push(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matches = /^:xpath\((.+?)\)$/.exec(selector);
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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));
|
2016-06-28 01:09:04 +02:00
|
|
|
|
}
|
2019-09-19 14:31:38 +02:00
|
|
|
|
return out;
|
2015-07-01 00:02:29 +02:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const getSvgRootChildren = function() {
|
2017-02-28 07:19:00 +01:00
|
|
|
|
if ( svgRoot.children ) {
|
|
|
|
|
return svgRoot.children;
|
|
|
|
|
} else {
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const childNodes = Array.prototype.slice.apply(svgRoot.childNodes);
|
2017-02-28 07:19:00 +01:00
|
|
|
|
return childNodes.filter(function(node) {
|
|
|
|
|
return node.nodeType === Node.ELEMENT_NODE;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const highlightElements = function() {
|
2017-10-21 19:43:46 +02:00
|
|
|
|
var islands;
|
|
|
|
|
var elem, rect, poly;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
var xl, xr, yt, yb, w, h, ws;
|
2017-02-28 07:19:00 +01:00
|
|
|
|
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);
|
2015-06-26 21:45:54 +02:00
|
|
|
|
}
|
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');
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const onScrolled = (function() {
|
|
|
|
|
let buffered = false;
|
|
|
|
|
const timerHandler = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
buffered = false;
|
|
|
|
|
highlightElements();
|
|
|
|
|
};
|
|
|
|
|
return function() {
|
|
|
|
|
if ( buffered === false ) {
|
|
|
|
|
window.requestAnimationFrame(timerHandler);
|
|
|
|
|
buffered = true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
})();
|
2015-07-01 00:02:29 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const selectNodes = function(selector, nid) {
|
|
|
|
|
const nodes = elementsFromSelector(selector);
|
2017-10-21 19:43:46 +02:00
|
|
|
|
if ( nid === '' ) { return nodes; }
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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 [];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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);
|
2015-06-26 21:45:54 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
2015-07-06 13:48:56 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const resetToggledNodes = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
rwGreenNodes.clear();
|
|
|
|
|
rwRedNodes.clear();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const start = function() {
|
|
|
|
|
const onReady = function(ev) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( ev ) {
|
|
|
|
|
document.removeEventListener(ev.type, onReady);
|
|
|
|
|
}
|
2019-09-19 14:31:38 +02:00
|
|
|
|
vAPI.MessagingConnection.sendTo(loggerConnectionId, domLayout.get());
|
2017-11-22 00:08:32 +01:00
|
|
|
|
vAPI.domFilterer.toggle(false, highlightElements);
|
2017-11-20 14:42:32 +01:00
|
|
|
|
};
|
|
|
|
|
if ( document.readyState === 'loading' ) {
|
|
|
|
|
document.addEventListener('DOMContentLoaded', onReady);
|
|
|
|
|
} else {
|
|
|
|
|
onReady();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const shutdown = function() {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
cosmeticFilterMapper.shutdown();
|
|
|
|
|
domLayout.shutdown();
|
2019-09-19 14:31:38 +02:00
|
|
|
|
vAPI.MessagingConnection.disconnectFrom(loggerConnectionId);
|
2017-11-20 14:42:32 +01:00
|
|
|
|
window.removeEventListener('scroll', onScrolled, true);
|
|
|
|
|
document.documentElement.removeChild(pickerRoot);
|
|
|
|
|
pickerRoot = svgRoot = null;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const onMessage = function(request) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
var response,
|
|
|
|
|
nodes;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2015-06-29 16:46:20 +02:00
|
|
|
|
switch ( request.what ) {
|
2015-07-01 00:02:29 +02:00
|
|
|
|
case 'commitFilters':
|
|
|
|
|
highlightElements();
|
|
|
|
|
break;
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
case 'domLayout':
|
2017-11-20 14:42:32 +01:00
|
|
|
|
response = domLayout.get();
|
|
|
|
|
highlightElements();
|
2015-06-26 21:45:54 +02:00
|
|
|
|
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);
|
2017-11-20 14:42:32 +01:00
|
|
|
|
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.
|
2017-11-20 14:42:32 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2016-06-28 01:09:04 +02:00
|
|
|
|
case 'toggleFilter':
|
2017-11-20 14:42:32 +01:00
|
|
|
|
nodes = selectNodes(request.selector, request.nid);
|
|
|
|
|
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
|
|
|
|
|
toggleExceptions(nodesFromFilter(request.filter), request.target);
|
|
|
|
|
highlightElements();
|
2016-06-28 01:09:04 +02:00
|
|
|
|
break;
|
|
|
|
|
|
2015-06-26 21:45:54 +02:00
|
|
|
|
case 'toggleNodes':
|
2017-11-20 14:42:32 +01:00
|
|
|
|
nodes = selectNodes(request.selector, request.nid);
|
|
|
|
|
if ( nodes.length !== 0 ) { nodes[0].scrollIntoView(); }
|
|
|
|
|
toggleFilter(nodes, request.target);
|
|
|
|
|
highlightElements();
|
2015-06-26 21:45:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-29 16:46:20 +02:00
|
|
|
|
return response;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
// Install DOM inspector widget
|
|
|
|
|
|
2019-09-26 21:57:55 +02:00
|
|
|
|
const bootstrap = function(ev) {
|
2017-11-20 14:42:32 +01:00
|
|
|
|
if ( ev ) {
|
|
|
|
|
pickerRoot.removeEventListener(ev.type, bootstrap);
|
|
|
|
|
}
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const pickerDoc = ev.target.contentDocument;
|
2015-06-26 21:45:54 +02:00
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
const style = pickerDoc.createElement('style');
|
2015-06-26 21:45:54 +02:00
|
|
|
|
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-06-26 21:45:54 +02:00
|
|
|
|
'}',
|
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-06-26 21:45:54 +02:00
|
|
|
|
'}',
|
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
|
|
|
|
'}',
|
2015-06-26 21:45:54 +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'));
|
2015-06-26 21:45:54 +02:00
|
|
|
|
pickerDoc.body.appendChild(svgRoot);
|
|
|
|
|
|
|
|
|
|
window.addEventListener('scroll', onScrolled, true);
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
// Dynamically add direct connection abilities so that we can establish
|
|
|
|
|
// a direct, fast messaging connection to the logger.
|
2019-09-26 21:57:55 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-09-19 14:31:38 +02:00
|
|
|
|
});
|
2015-06-26 21:45:54 +02:00
|
|
|
|
};
|
|
|
|
|
|
2017-11-20 14:42:32 +01:00
|
|
|
|
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');
|
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
|
pickerRoot.addEventListener('load', ev => { bootstrap(ev); });
|
2015-06-26 21:45:54 +02:00
|
|
|
|
document.documentElement.appendChild(pickerRoot);
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
2018-05-03 15:55:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
|
|
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;
|