mirror of https://github.com/gorhill/uBlock.git
Report injected scriptlets in troubleshooting information
This requires to rewrite portions of scriptlet filtering code.
This commit is contained in:
parent
39a8aaa39b
commit
578fc21bd9
|
@ -236,13 +236,13 @@ vAPI.prefetching = (( ) => {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.scriptletsInjector = ((doc, scriptlets) => {
|
vAPI.scriptletsInjector = ((doc, details) => {
|
||||||
let script;
|
let script;
|
||||||
try {
|
try {
|
||||||
script = doc.createElement('script');
|
script = doc.createElement('script');
|
||||||
script.appendChild(doc.createTextNode(scriptlets));
|
script.appendChild(doc.createTextNode(details.scriptlets));
|
||||||
(doc.head || doc.documentElement).appendChild(script);
|
(doc.head || doc.documentElement).appendChild(script);
|
||||||
self.uBO_scriptletsInjected = true;
|
self.uBO_scriptletsInjected = details.filters;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
if ( script ) {
|
if ( script ) {
|
||||||
|
|
|
@ -1391,7 +1391,7 @@ vAPI.Net = class {
|
||||||
// To be defined by platform-specific code.
|
// To be defined by platform-specific code.
|
||||||
|
|
||||||
vAPI.scriptletsInjector = (( ) => {
|
vAPI.scriptletsInjector = (( ) => {
|
||||||
self.uBO_scriptletsInjected = true;
|
self.uBO_scriptletsInjected = '';
|
||||||
}).toString();
|
}).toString();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -304,16 +304,19 @@ vAPI.Net = class extends vAPI.Net {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.scriptletsInjector = ((doc, scriptlets) => {
|
vAPI.scriptletsInjector = ((doc, details) => {
|
||||||
let script, url;
|
let script, url;
|
||||||
try {
|
try {
|
||||||
const blob = new self.Blob([ scriptlets ], { type: 'text/javascript; charset=utf-8' });
|
const blob = new self.Blob(
|
||||||
|
[ details.scriptlets ],
|
||||||
|
{ type: 'text/javascript; charset=utf-8' }
|
||||||
|
);
|
||||||
url = self.URL.createObjectURL(blob);
|
url = self.URL.createObjectURL(blob);
|
||||||
script = doc.createElement('script');
|
script = doc.createElement('script');
|
||||||
script.async = false;
|
script.async = false;
|
||||||
script.src = url;
|
script.src = url;
|
||||||
(doc.head || doc.documentElement).appendChild(script);
|
(doc.head || doc.documentElement).appendChild(script);
|
||||||
self.uBO_scriptletsInjected = true;
|
self.uBO_scriptletsInjected = details.filters;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
if ( url ) {
|
if ( url ) {
|
||||||
|
|
|
@ -1298,7 +1298,7 @@ vAPI.DOMFilterer = class {
|
||||||
const {
|
const {
|
||||||
noSpecificCosmeticFiltering,
|
noSpecificCosmeticFiltering,
|
||||||
noGenericCosmeticFiltering,
|
noGenericCosmeticFiltering,
|
||||||
scriptlets,
|
scriptletDetails,
|
||||||
} = response;
|
} = response;
|
||||||
|
|
||||||
vAPI.noSpecificCosmeticFiltering = noSpecificCosmeticFiltering;
|
vAPI.noSpecificCosmeticFiltering = noSpecificCosmeticFiltering;
|
||||||
|
@ -1322,10 +1322,12 @@ vAPI.DOMFilterer = class {
|
||||||
|
|
||||||
// Library of resources is located at:
|
// Library of resources is located at:
|
||||||
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
|
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
|
||||||
if ( scriptlets && typeof self.uBO_scriptletsInjected !== 'boolean' ) {
|
if ( scriptletDetails && typeof self.uBO_scriptletsInjected !== 'string' ) {
|
||||||
self.uBO_scriptletsInjected = true;
|
self.uBO_scriptletsInjected = scriptletDetails.filters;
|
||||||
vAPI.injectScriptlet(document, scriptlets);
|
if ( scriptletDetails.scriptlets ) {
|
||||||
vAPI.injectedScripts = scriptlets;
|
vAPI.injectScriptlet(document, scriptletDetails.scriptlets);
|
||||||
|
vAPI.injectedScripts = scriptletDetails.scriptlets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( vAPI.domSurveyor ) {
|
if ( vAPI.domSurveyor ) {
|
||||||
|
@ -1346,7 +1348,7 @@ vAPI.DOMFilterer = class {
|
||||||
vAPI.messaging.send('contentscript', {
|
vAPI.messaging.send('contentscript', {
|
||||||
what: 'retrieveContentScriptParameters',
|
what: 'retrieveContentScriptParameters',
|
||||||
url: vAPI.effectiveSelf.location.href,
|
url: vAPI.effectiveSelf.location.href,
|
||||||
needScriptlets: typeof self.uBO_scriptletsInjected !== 'boolean',
|
needScriptlets: typeof self.uBO_scriptletsInjected !== 'string',
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
onResponseReady(response);
|
onResponseReady(response);
|
||||||
});
|
});
|
||||||
|
|
|
@ -623,8 +623,11 @@ const launchReporter = async function(request) {
|
||||||
if ( Array.isArray(v) ) { a.push(...v); }
|
if ( Array.isArray(v) ) { a.push(...v); }
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
|
// Remove duplicate, truncate too long filters.
|
||||||
if ( filters.length !== 0 ) {
|
if ( filters.length !== 0 ) {
|
||||||
request.popupPanel.cosmetic = filters;
|
request.popupPanel.extended = Array.from(
|
||||||
|
new Set(filters.map(s => s.length <= 64 ? s : `${s.slice(0, 64)}…`))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportURL = new URL(vAPI.getURL('support.html'));
|
const supportURL = new URL(vAPI.getURL('support.html'));
|
||||||
|
@ -833,8 +836,20 @@ const retrieveContentScriptParameters = async function(sender, request) {
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-748179731
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-748179731
|
||||||
// For non-network URIs, scriptlet injection is deferred to here. The
|
// For non-network URIs, scriptlet injection is deferred to here. The
|
||||||
// effective URL is available here in `request.url`.
|
// effective URL is available here in `request.url`.
|
||||||
if ( request.needScriptlets ) {
|
if ( logger.enabled || request.needScriptlets ) {
|
||||||
response.scriptlets = scriptletFilteringEngine.injectNow(request);
|
const scriptletDetails = scriptletFilteringEngine.injectNow(request);
|
||||||
|
if ( scriptletDetails !== undefined ) {
|
||||||
|
if ( logger.enabled ) {
|
||||||
|
scriptletFilteringEngine.logFilters(
|
||||||
|
tabId,
|
||||||
|
request.url,
|
||||||
|
scriptletDetails.filters
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( request.needScriptlets ) {
|
||||||
|
response.scriptletDetails = scriptletDetails;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/NanoMeow/QuickReports/issues/6#issuecomment-414516623
|
// https://github.com/NanoMeow/QuickReports/issues/6#issuecomment-414516623
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
import logger from './logger.js';
|
|
||||||
import µb from './background.js';
|
import µb from './background.js';
|
||||||
import { redirectEngine } from './redirect-engine.js';
|
import { redirectEngine } from './redirect-engine.js';
|
||||||
import { sessionFirewall } from './filtering-engines.js';
|
import { sessionFirewall } from './filtering-engines.js';
|
||||||
|
@ -86,31 +85,32 @@ const scriptletFilteringEngine = {
|
||||||
const contentscriptCode = (( ) => {
|
const contentscriptCode = (( ) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
'(',
|
'(',
|
||||||
function(injector, hostname, scriptlets) {
|
function(injector, details) {
|
||||||
const doc = document;
|
const doc = document;
|
||||||
if (
|
if (
|
||||||
doc.location === null ||
|
doc.location === null ||
|
||||||
hostname !== doc.location.hostname ||
|
details.hostname !== doc.location.hostname ||
|
||||||
typeof self.uBO_scriptletsInjected === 'boolean'
|
typeof self.uBO_scriptletsInjected === 'string'
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
injector(doc, decodeURIComponent(scriptlets));
|
injector(doc, details);
|
||||||
if ( typeof self.uBO_scriptletsInjected === 'boolean' ) { return 0; }
|
if ( typeof self.uBO_scriptletsInjected === 'string' ) { return 0; }
|
||||||
}.toString(),
|
}.toString(),
|
||||||
')(',
|
')(',
|
||||||
vAPI.scriptletsInjector, ', ',
|
vAPI.scriptletsInjector, ', ',
|
||||||
'"', 'hostname-slot', '", ',
|
'json-slot',
|
||||||
'"', 'scriptlets-slot', '"',
|
|
||||||
');',
|
');',
|
||||||
];
|
];
|
||||||
return {
|
return {
|
||||||
parts: parts,
|
parts,
|
||||||
hostnameSlot: parts.indexOf('hostname-slot'),
|
jsonSlot: parts.indexOf('json-slot'),
|
||||||
scriptletsSlot: parts.indexOf('scriptlets-slot'),
|
assemble: function(hostname, scriptlets, filters) {
|
||||||
assemble: function(hostname, scriptlets) {
|
this.parts[this.jsonSlot] = JSON.stringify({
|
||||||
this.parts[this.hostnameSlot] = hostname;
|
hostname,
|
||||||
this.parts[this.scriptletsSlot] = encodeURIComponent(scriptlets);
|
scriptlets,
|
||||||
|
filters,
|
||||||
|
});
|
||||||
return this.parts.join('');
|
return this.parts.join('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -221,16 +221,18 @@ const patchScriptlet = function(content, args) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const logOne = function(tabId, url, filter) {
|
scriptletFilteringEngine.logFilters = function(tabId, url, filters) {
|
||||||
µb.filteringContext
|
if ( typeof filters !== 'string' ) { return; }
|
||||||
.duplicate()
|
const fctxt = µb.filteringContext
|
||||||
.fromTabId(tabId)
|
.duplicate()
|
||||||
.setRealm('extended')
|
.fromTabId(tabId)
|
||||||
.setType('scriptlet')
|
.setRealm('extended')
|
||||||
.setURL(url)
|
.setType('scriptlet')
|
||||||
.setDocOriginFromURL(url)
|
.setURL(url)
|
||||||
.setFilter({ source: 'extended', raw: filter })
|
.setDocOriginFromURL(url);
|
||||||
.toLogger();
|
for ( const filter of filters.split('\n') ) {
|
||||||
|
fctxt.setFilter({ source: 'extended', raw: filter }).toLogger();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
scriptletFilteringEngine.reset = function() {
|
scriptletFilteringEngine.reset = function() {
|
||||||
|
@ -308,7 +310,7 @@ const $exceptions = new Set();
|
||||||
const $scriptletMap = new Map();
|
const $scriptletMap = new Map();
|
||||||
const $scriptletDependencyMap = new Map();
|
const $scriptletDependencyMap = new Map();
|
||||||
|
|
||||||
scriptletFilteringEngine.retrieve = function(request, options = {}) {
|
scriptletFilteringEngine.retrieve = function(request) {
|
||||||
if ( scriptletDB.size === 0 ) { return; }
|
if ( scriptletDB.size === 0 ) { return; }
|
||||||
|
|
||||||
const hostname = request.hostname;
|
const hostname = request.hostname;
|
||||||
|
@ -332,14 +334,13 @@ scriptletFilteringEngine.retrieve = function(request, options = {}) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mustLog = Array.isArray(options.logEntries);
|
|
||||||
|
|
||||||
// Wholly disable scriptlet injection?
|
// Wholly disable scriptlet injection?
|
||||||
if ( $exceptions.has('') ) {
|
if ( $exceptions.has('') ) {
|
||||||
if ( mustLog ) {
|
return {
|
||||||
logOne(request.tabId, request.url, '#@#+js()');
|
filters: [
|
||||||
}
|
{ tabId: request.tabId, url: request.url, filter: '#@#+js()' }
|
||||||
return;
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( scriptletCache.resetTime < redirectEngine.modifyTime ) {
|
if ( scriptletCache.resetTime < redirectEngine.modifyTime ) {
|
||||||
|
@ -349,45 +350,38 @@ scriptletFilteringEngine.retrieve = function(request, options = {}) {
|
||||||
let cacheDetails = scriptletCache.lookup(hostname);
|
let cacheDetails = scriptletCache.lookup(hostname);
|
||||||
if ( cacheDetails === undefined ) {
|
if ( cacheDetails === undefined ) {
|
||||||
const fullCode = [];
|
const fullCode = [];
|
||||||
|
for ( const token of $exceptions ) {
|
||||||
|
if ( $scriptlets.has(token) ) {
|
||||||
|
$scriptlets.delete(token);
|
||||||
|
} else {
|
||||||
|
$exceptions.delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
for ( const token of $scriptlets ) {
|
for ( const token of $scriptlets ) {
|
||||||
if ( $exceptions.has(token) ) { continue; }
|
|
||||||
lookupScriptlet(token, $scriptletMap, $scriptletDependencyMap);
|
lookupScriptlet(token, $scriptletMap, $scriptletDependencyMap);
|
||||||
}
|
}
|
||||||
for ( const token of $scriptlets ) {
|
for ( const token of $scriptlets ) {
|
||||||
const isException = $exceptions.has(token);
|
fullCode.push($scriptletMap.get(token));
|
||||||
if ( isException === false ) {
|
|
||||||
fullCode.push($scriptletMap.get(token));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for ( const code of $scriptletDependencyMap.values() ) {
|
for ( const code of $scriptletDependencyMap.values() ) {
|
||||||
fullCode.push(code);
|
fullCode.push(code);
|
||||||
}
|
}
|
||||||
cacheDetails = {
|
cacheDetails = {
|
||||||
code: fullCode.join('\n\n'),
|
code: fullCode.join('\n\n'),
|
||||||
tokens: Array.from($scriptlets),
|
filters: [
|
||||||
exceptions: Array.from($exceptions),
|
...Array.from($scriptlets).map(s => `##+js(${s})`),
|
||||||
|
...Array.from($exceptions).map(s => `#@#+js(${s})`),
|
||||||
|
].join('\n'),
|
||||||
};
|
};
|
||||||
scriptletCache.add(hostname, cacheDetails);
|
scriptletCache.add(hostname, cacheDetails);
|
||||||
$scriptletMap.clear();
|
$scriptletMap.clear();
|
||||||
$scriptletDependencyMap.clear();
|
$scriptletDependencyMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mustLog ) {
|
if ( cacheDetails.code === '' ) {
|
||||||
for ( const token of cacheDetails.tokens ) {
|
return { filters: cacheDetails.filters };
|
||||||
if ( cacheDetails.exceptions.includes(token) ) {
|
|
||||||
logOne(request.tabId, request.url, `#@#+js(${token})`);
|
|
||||||
} else {
|
|
||||||
options.logEntries.push({
|
|
||||||
token: `##+js(${token})`,
|
|
||||||
tabId: request.tabId,
|
|
||||||
url: request.url,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( cacheDetails.code === '' ) { return; }
|
|
||||||
|
|
||||||
const scriptletGlobals = [];
|
const scriptletGlobals = [];
|
||||||
|
|
||||||
if ( isDevBuild === undefined ) {
|
if ( isDevBuild === undefined ) {
|
||||||
|
@ -412,7 +406,7 @@ scriptletFilteringEngine.retrieve = function(request, options = {}) {
|
||||||
'})();',
|
'})();',
|
||||||
];
|
];
|
||||||
|
|
||||||
return out.join('\n');
|
return { scriptlets: out.join('\n'), filters: cacheDetails.filters };
|
||||||
};
|
};
|
||||||
|
|
||||||
scriptletFilteringEngine.injectNow = function(details) {
|
scriptletFilteringEngine.injectNow = function(details) {
|
||||||
|
@ -427,30 +421,21 @@ scriptletFilteringEngine.injectNow = function(details) {
|
||||||
};
|
};
|
||||||
request.domain = domainFromHostname(request.hostname);
|
request.domain = domainFromHostname(request.hostname);
|
||||||
request.entity = entityFromDomain(request.domain);
|
request.entity = entityFromDomain(request.domain);
|
||||||
const logEntries = logger.enabled ? [] : undefined;
|
const scriptletDetails = this.retrieve(request);
|
||||||
const scriptlets = this.retrieve(request, { logEntries });
|
if ( scriptletDetails === undefined ) { return; }
|
||||||
if ( scriptlets === undefined ) { return; }
|
const { scriptlets = '', filters } = scriptletDetails;
|
||||||
let code = contentscriptCode.assemble(request.hostname, scriptlets);
|
if ( scriptlets === '' ) { return scriptletDetails; }
|
||||||
|
let code = contentscriptCode.assemble(request.hostname, scriptlets, filters);
|
||||||
if ( µb.hiddenSettings.debugScriptletInjector ) {
|
if ( µb.hiddenSettings.debugScriptletInjector ) {
|
||||||
code = 'debugger;\n' + code;
|
code = 'debugger;\n' + code;
|
||||||
}
|
}
|
||||||
const promise = vAPI.tabs.executeScript(details.tabId, {
|
vAPI.tabs.executeScript(details.tabId, {
|
||||||
code,
|
code,
|
||||||
frameId: details.frameId,
|
frameId: details.frameId,
|
||||||
matchAboutBlank: true,
|
matchAboutBlank: true,
|
||||||
runAt: 'document_start',
|
runAt: 'document_start',
|
||||||
});
|
});
|
||||||
if ( logEntries !== undefined ) {
|
return scriptletDetails;
|
||||||
promise.then(results => {
|
|
||||||
if ( Array.isArray(results) === false || results[0] !== 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for ( const entry of logEntries ) {
|
|
||||||
logOne(entry.tabId, entry.url, entry.token);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return scriptlets;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
scriptletFilteringEngine.toSelfie = function() {
|
scriptletFilteringEngine.toSelfie = function() {
|
||||||
|
|
|
@ -127,6 +127,10 @@ if ( Array.isArray(allSelectors.exceptions) ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( typeof self.uBO_scriptletsInjected === 'string' ) {
|
||||||
|
matchedSelectors.push(...self.uBO_scriptletsInjected.split('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
if ( matchedSelectors.length === 0 ) { return; }
|
if ( matchedSelectors.length === 0 ) { return; }
|
||||||
|
|
||||||
return matchedSelectors;
|
return matchedSelectors;
|
||||||
|
|
Loading…
Reference in New Issue