mirror of https://github.com/gorhill/uBlock.git
Support injecting scriptlet in MAIN or ISOLATED world
This reflects the _world_ of the MV3 scripting API: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/ExecutionWorld MAIN: page's world ISOLATED: extension's content script world Some scriptlets are best executed in either world, so this commit allows to pick in which world a scriptlet should execute (default to MAIN). For instance, the new sed.js scriptlet will now execute in the ISOLATED world.
This commit is contained in:
parent
9f465f503a
commit
1a863a877d
|
@ -2218,6 +2218,7 @@ builtinScriptlets.push({
|
||||||
name: 'sed.js',
|
name: 'sed.js',
|
||||||
requiresTrust: true,
|
requiresTrust: true,
|
||||||
fn: sed,
|
fn: sed,
|
||||||
|
world: 'ISOLATED',
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'pattern-to-regex.fn',
|
'pattern-to-regex.fn',
|
||||||
'run-at.fn',
|
'run-at.fn',
|
||||||
|
@ -2229,7 +2230,6 @@ function sed(
|
||||||
pattern = '',
|
pattern = '',
|
||||||
replacement = ''
|
replacement = ''
|
||||||
) {
|
) {
|
||||||
if ( document.documentElement === null ) { return; }
|
|
||||||
const reNodeName = patternToRegex(nodeName, 'i');
|
const reNodeName = patternToRegex(nodeName, 'i');
|
||||||
const rePattern = patternToRegex(pattern, 'gms');
|
const rePattern = patternToRegex(pattern, 'gms');
|
||||||
const extraArgs = new Map(
|
const extraArgs = new Map(
|
||||||
|
@ -2274,22 +2274,18 @@ function sed(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const observer = new MutationObserver(handleMutations);
|
const observer = new MutationObserver(handleMutations);
|
||||||
observer.observe(document.documentElement, { childList: true, subtree: true });
|
observer.observe(document, { childList: true, subtree: true });
|
||||||
{
|
if ( document.documentElement ) {
|
||||||
const treeWalker = document.createTreeWalker(
|
const treeWalker = document.createTreeWalker(
|
||||||
document.documentElement,
|
document.documentElement,
|
||||||
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
|
||||||
);
|
);
|
||||||
const currentScriptNode = document.currentScript;
|
|
||||||
const currentTextNode = currentScriptNode.firstChild;
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const node = treeWalker.nextNode();
|
const node = treeWalker.nextNode();
|
||||||
count += 1;
|
count += 1;
|
||||||
if ( node === null ) { break; }
|
if ( node === null ) { break; }
|
||||||
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
||||||
if ( node === currentScriptNode ) { continue; }
|
|
||||||
if ( node === currentTextNode ) { continue; }
|
|
||||||
if ( handleNode(node) ) { continue; }
|
if ( handleNode(node) ) { continue; }
|
||||||
stop(); break;
|
stop(); break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,7 +315,7 @@ vAPI.scriptletsInjector = ((doc, details) => {
|
||||||
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.append(script);
|
||||||
self.uBO_scriptletsInjected = details.filters;
|
self.uBO_scriptletsInjected = details.filters;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1324,9 +1324,9 @@ vAPI.DOMFilterer = class {
|
||||||
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
|
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
|
||||||
if ( scriptletDetails && typeof self.uBO_scriptletsInjected !== 'string' ) {
|
if ( scriptletDetails && typeof self.uBO_scriptletsInjected !== 'string' ) {
|
||||||
self.uBO_scriptletsInjected = scriptletDetails.filters;
|
self.uBO_scriptletsInjected = scriptletDetails.filters;
|
||||||
if ( scriptletDetails.scriptlets ) {
|
if ( scriptletDetails.mainWorld ) {
|
||||||
vAPI.injectScriptlet(document, scriptletDetails.scriptlets);
|
vAPI.injectScriptlet(document, scriptletDetails.mainWorld);
|
||||||
vAPI.injectedScripts = scriptletDetails.scriptlets;
|
vAPI.injectedScripts = scriptletDetails.mainWorld;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,7 @@ class RedirectEntry {
|
||||||
this.warURL = undefined;
|
this.warURL = undefined;
|
||||||
this.params = undefined;
|
this.params = undefined;
|
||||||
this.requiresTrust = false;
|
this.requiresTrust = false;
|
||||||
|
this.world = 'MAIN';
|
||||||
this.dependencies = [];
|
this.dependencies = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +158,7 @@ class RedirectEntry {
|
||||||
r.requiresTrust = details.requiresTrust === true;
|
r.requiresTrust = details.requiresTrust === true;
|
||||||
r.warURL = details.warURL !== undefined && details.warURL || undefined;
|
r.warURL = details.warURL !== undefined && details.warURL || undefined;
|
||||||
r.params = details.params !== undefined && details.params || undefined;
|
r.params = details.params !== undefined && details.params || undefined;
|
||||||
|
r.world = details.world || 'MAIN';
|
||||||
if ( Array.isArray(details.dependencies) ) {
|
if ( Array.isArray(details.dependencies) ) {
|
||||||
r.dependencies.push(...details.dependencies);
|
r.dependencies.push(...details.dependencies);
|
||||||
}
|
}
|
||||||
|
@ -227,6 +229,7 @@ class RedirectEngine {
|
||||||
if ( entry.mime.startsWith(mime) === false ) { return; }
|
if ( entry.mime.startsWith(mime) === false ) { return; }
|
||||||
return {
|
return {
|
||||||
js: entry.toContent(),
|
js: entry.toContent(),
|
||||||
|
world: entry.world,
|
||||||
dependencies: entry.dependencies.slice(),
|
dependencies: entry.dependencies.slice(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -320,6 +323,7 @@ class RedirectEngine {
|
||||||
data: fn.toString(),
|
data: fn.toString(),
|
||||||
dependencies: scriptlet.dependencies,
|
dependencies: scriptlet.dependencies,
|
||||||
requiresTrust: scriptlet.requiresTrust === true,
|
requiresTrust: scriptlet.requiresTrust === true,
|
||||||
|
world: scriptlet.world || 'MAIN',
|
||||||
});
|
});
|
||||||
this.resources.set(name, entry);
|
this.resources.set(name, entry);
|
||||||
if ( Array.isArray(aliases) === false ) { continue; }
|
if ( Array.isArray(aliases) === false ) { continue; }
|
||||||
|
|
|
@ -82,7 +82,7 @@ const scriptletFilteringEngine = {
|
||||||
// Consequently, the programmatic-injection code path is taken only with
|
// Consequently, the programmatic-injection code path is taken only with
|
||||||
// Chromium-based browsers.
|
// Chromium-based browsers.
|
||||||
|
|
||||||
const contentscriptCode = (( ) => {
|
const mainWorldInjector = (( ) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
'(',
|
'(',
|
||||||
function(injector, details) {
|
function(injector, details) {
|
||||||
|
@ -95,7 +95,7 @@ const contentscriptCode = (( ) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
injector(doc, details);
|
injector(doc, details);
|
||||||
if ( typeof self.uBO_scriptletsInjected === 'string' ) { return 0; }
|
return 0;
|
||||||
}.toString(),
|
}.toString(),
|
||||||
')(',
|
')(',
|
||||||
vAPI.scriptletsInjector, ', ',
|
vAPI.scriptletsInjector, ', ',
|
||||||
|
@ -112,7 +112,39 @@ const contentscriptCode = (( ) => {
|
||||||
filters,
|
filters,
|
||||||
});
|
});
|
||||||
return this.parts.join('');
|
return this.parts.join('');
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const isolatedWorldInjector = (( ) => {
|
||||||
|
const parts = [
|
||||||
|
'(',
|
||||||
|
function(details) {
|
||||||
|
const doc = document;
|
||||||
|
if (
|
||||||
|
doc.location === null ||
|
||||||
|
details.hostname !== doc.location.hostname ||
|
||||||
|
self.uBO_isolatedScriptlets === 'done'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isolatedScriptlets = function(){};
|
||||||
|
isolatedScriptlets();
|
||||||
|
self.uBO_isolatedScriptlets = 'done';
|
||||||
|
return 0;
|
||||||
|
}.toString(),
|
||||||
|
')(',
|
||||||
|
'json-slot',
|
||||||
|
');',
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
parts,
|
||||||
|
jsonSlot: parts.indexOf('json-slot'),
|
||||||
|
scriptletSlot: parts.indexOf('scriptlet-slot'),
|
||||||
|
assemble: function(hostname, scriptlets) {
|
||||||
|
this.parts[this.jsonSlot] = JSON.stringify({ hostname });
|
||||||
|
return this.parts.join('').replace('function(){}', scriptlets);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -147,8 +179,8 @@ const normalizeRawFilter = function(parser, sourceIsTrusted = false) {
|
||||||
return `+js(${args.join(', ')})`;
|
return `+js(${args.join(', ')})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lookupScriptlet = function(rawToken, scriptletMap, dependencyMap) {
|
const lookupScriptlet = function(rawToken, mainMap, isolatedMap) {
|
||||||
if ( scriptletMap.has(rawToken) ) { return; }
|
if ( mainMap.has(rawToken) || isolatedMap.has(rawToken) ) { return; }
|
||||||
const pos = rawToken.indexOf(',');
|
const pos = rawToken.indexOf(',');
|
||||||
let token, args = '';
|
let token, args = '';
|
||||||
if ( pos === -1 ) {
|
if ( pos === -1 ) {
|
||||||
|
@ -157,8 +189,6 @@ const lookupScriptlet = function(rawToken, scriptletMap, dependencyMap) {
|
||||||
token = rawToken.slice(0, pos).trim();
|
token = rawToken.slice(0, pos).trim();
|
||||||
args = rawToken.slice(pos + 1).trim();
|
args = rawToken.slice(pos + 1).trim();
|
||||||
}
|
}
|
||||||
// TODO: The alias lookup can be removed once scriptlet resources
|
|
||||||
// with obsolete name are converted to their new name.
|
|
||||||
if ( reng.aliases.has(token) ) {
|
if ( reng.aliases.has(token) ) {
|
||||||
token = reng.aliases.get(token);
|
token = reng.aliases.get(token);
|
||||||
} else {
|
} else {
|
||||||
|
@ -166,18 +196,19 @@ const lookupScriptlet = function(rawToken, scriptletMap, dependencyMap) {
|
||||||
}
|
}
|
||||||
const details = reng.contentFromName(token, 'text/javascript');
|
const details = reng.contentFromName(token, 'text/javascript');
|
||||||
if ( details === undefined ) { return; }
|
if ( details === undefined ) { return; }
|
||||||
|
const targetWorldMap = details.world !== 'ISOLATED' ? mainMap : isolatedMap;
|
||||||
const content = patchScriptlet(details.js, args);
|
const content = patchScriptlet(details.js, args);
|
||||||
const dependencies = details.dependencies || [];
|
const dependencies = details.dependencies || [];
|
||||||
while ( dependencies.length !== 0 ) {
|
while ( dependencies.length !== 0 ) {
|
||||||
const token = dependencies.shift();
|
const token = dependencies.shift();
|
||||||
if ( dependencyMap.has(token) ) { continue; }
|
if ( targetWorldMap.has(token) ) { continue; }
|
||||||
const details = reng.contentFromName(token, 'fn/javascript');
|
const details = reng.contentFromName(token, 'fn/javascript');
|
||||||
if ( details === undefined ) { continue; }
|
if ( details === undefined ) { continue; }
|
||||||
dependencyMap.set(token, details.js);
|
targetWorldMap.set(token, details.js);
|
||||||
if ( Array.isArray(details.dependencies) === false ) { continue; }
|
if ( Array.isArray(details.dependencies) === false ) { continue; }
|
||||||
dependencies.push(...details.dependencies);
|
dependencies.push(...details.dependencies);
|
||||||
}
|
}
|
||||||
scriptletMap.set(rawToken, [
|
targetWorldMap.set(rawToken, [
|
||||||
'try {',
|
'try {',
|
||||||
'// >>>> scriptlet start',
|
'// >>>> scriptlet start',
|
||||||
content,
|
content,
|
||||||
|
@ -314,24 +345,14 @@ scriptletFilteringEngine.fromCompiledContent = function(reader) {
|
||||||
|
|
||||||
const $scriptlets = new Set();
|
const $scriptlets = new Set();
|
||||||
const $exceptions = new Set();
|
const $exceptions = new Set();
|
||||||
const $scriptletMap = new Map();
|
const $mainWorldMap = new Map();
|
||||||
const $scriptletDependencyMap = new Map();
|
const $isolatedWorldMap = new Map();
|
||||||
|
|
||||||
scriptletFilteringEngine.retrieve = function(request) {
|
scriptletFilteringEngine.retrieve = function(request) {
|
||||||
if ( scriptletDB.size === 0 ) { return; }
|
if ( scriptletDB.size === 0 ) { return; }
|
||||||
|
|
||||||
const hostname = request.hostname;
|
const hostname = request.hostname;
|
||||||
|
|
||||||
$scriptlets.clear();
|
|
||||||
$exceptions.clear();
|
|
||||||
|
|
||||||
scriptletDB.retrieve(hostname, [ $scriptlets, $exceptions ]);
|
|
||||||
const entity = request.entity !== ''
|
|
||||||
? `${hostname.slice(0, -request.domain.length)}${request.entity}`
|
|
||||||
: '*';
|
|
||||||
scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1);
|
|
||||||
if ( $scriptlets.size === 0 ) { return; }
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/2835
|
// https://github.com/gorhill/uBlock/issues/2835
|
||||||
// Do not inject scriptlets if the site is under an `allow` rule.
|
// Do not inject scriptlets if the site is under an `allow` rule.
|
||||||
if (
|
if (
|
||||||
|
@ -341,22 +362,31 @@ scriptletFilteringEngine.retrieve = function(request) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wholly disable scriptlet injection?
|
|
||||||
if ( $exceptions.has('') ) {
|
|
||||||
return {
|
|
||||||
filters: [
|
|
||||||
{ tabId: request.tabId, url: request.url, filter: '#@#+js()' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( scriptletCache.resetTime < reng.modifyTime ) {
|
if ( scriptletCache.resetTime < reng.modifyTime ) {
|
||||||
scriptletCache.reset();
|
scriptletCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cacheDetails = scriptletCache.lookup(hostname);
|
let cacheDetails = scriptletCache.lookup(hostname);
|
||||||
if ( cacheDetails === undefined ) {
|
if ( cacheDetails === undefined ) {
|
||||||
const fullCode = [];
|
$scriptlets.clear();
|
||||||
|
$exceptions.clear();
|
||||||
|
|
||||||
|
scriptletDB.retrieve(hostname, [ $scriptlets, $exceptions ]);
|
||||||
|
const entity = request.entity !== ''
|
||||||
|
? `${hostname.slice(0, -request.domain.length)}${request.entity}`
|
||||||
|
: '*';
|
||||||
|
scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1);
|
||||||
|
if ( $scriptlets.size === 0 ) { return; }
|
||||||
|
|
||||||
|
// Wholly disable scriptlet injection?
|
||||||
|
if ( $exceptions.has('') ) {
|
||||||
|
return {
|
||||||
|
filters: [
|
||||||
|
{ tabId: request.tabId, url: request.url, filter: '#@#+js()' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for ( const token of $exceptions ) {
|
for ( const token of $exceptions ) {
|
||||||
if ( $scriptlets.has(token) ) {
|
if ( $scriptlets.has(token) ) {
|
||||||
$scriptlets.delete(token);
|
$scriptlets.delete(token);
|
||||||
|
@ -365,27 +395,30 @@ scriptletFilteringEngine.retrieve = function(request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ( const token of $scriptlets ) {
|
for ( const token of $scriptlets ) {
|
||||||
lookupScriptlet(token, $scriptletMap, $scriptletDependencyMap);
|
lookupScriptlet(token, $mainWorldMap, $isolatedWorldMap);
|
||||||
}
|
}
|
||||||
for ( const token of $scriptlets ) {
|
const mainWorldCode = [];
|
||||||
fullCode.push($scriptletMap.get(token));
|
for ( const js of $mainWorldMap.values() ) {
|
||||||
|
mainWorldCode.push(js);
|
||||||
}
|
}
|
||||||
for ( const code of $scriptletDependencyMap.values() ) {
|
const isolatedWorldCode = [];
|
||||||
fullCode.push(code);
|
for ( const js of $isolatedWorldMap.values() ) {
|
||||||
|
isolatedWorldCode.push(js);
|
||||||
}
|
}
|
||||||
cacheDetails = {
|
cacheDetails = {
|
||||||
code: fullCode.join('\n\n'),
|
mainWorld: mainWorldCode.join('\n\n'),
|
||||||
|
isolatedWorld: isolatedWorldCode.join('\n\n'),
|
||||||
filters: [
|
filters: [
|
||||||
...Array.from($scriptlets).map(s => `##+js(${s})`),
|
...Array.from($scriptlets).map(s => `##+js(${s})`),
|
||||||
...Array.from($exceptions).map(s => `#@#+js(${s})`),
|
...Array.from($exceptions).map(s => `#@#+js(${s})`),
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
};
|
};
|
||||||
scriptletCache.add(hostname, cacheDetails);
|
scriptletCache.add(hostname, cacheDetails);
|
||||||
$scriptletMap.clear();
|
$mainWorldMap.clear();
|
||||||
$scriptletDependencyMap.clear();
|
$isolatedWorldMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( cacheDetails.code === '' ) {
|
if ( cacheDetails.mainWorld === '' && cacheDetails.isolatedWorld === '' ) {
|
||||||
return { filters: cacheDetails.filters };
|
return { filters: cacheDetails.filters };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,22 +431,37 @@ scriptletFilteringEngine.retrieve = function(request) {
|
||||||
scriptletGlobals.push([ 'canDebug', true ]);
|
scriptletGlobals.push([ 'canDebug', true ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const out = [
|
return {
|
||||||
'(function() {',
|
mainWorld: cacheDetails.mainWorld === '' ? '' : [
|
||||||
'// >>>> start of private namespace',
|
'(function() {',
|
||||||
'',
|
'// >>>> start of private namespace',
|
||||||
µb.hiddenSettings.debugScriptlets ? 'debugger;' : ';',
|
'',
|
||||||
'',
|
µb.hiddenSettings.debugScriptlets ? 'debugger;' : ';',
|
||||||
// For use by scriptlets to share local data among themselves
|
'',
|
||||||
`const scriptletGlobals = new Map(${JSON.stringify(scriptletGlobals, null, 2)});`,
|
// For use by scriptlets to share local data among themselves
|
||||||
'',
|
`const scriptletGlobals = new Map(${JSON.stringify(scriptletGlobals, null, 2)});`,
|
||||||
cacheDetails.code,
|
'',
|
||||||
'',
|
cacheDetails.mainWorld,
|
||||||
'// <<<< end of private namespace',
|
'',
|
||||||
'})();',
|
'// <<<< end of private namespace',
|
||||||
];
|
'})();',
|
||||||
|
].join('\n'),
|
||||||
return { scriptlets: out.join('\n'), filters: cacheDetails.filters };
|
isolatedWorld: cacheDetails.isolatedWorld === '' ? '' : [
|
||||||
|
'function() {',
|
||||||
|
'// >>>> start of private namespace',
|
||||||
|
'',
|
||||||
|
µb.hiddenSettings.debugScriptlets ? 'debugger;' : ';',
|
||||||
|
'',
|
||||||
|
// For use by scriptlets to share local data among themselves
|
||||||
|
`const scriptletGlobals = new Map(${JSON.stringify(scriptletGlobals, null, 2)});`,
|
||||||
|
'',
|
||||||
|
cacheDetails.isolatedWorld,
|
||||||
|
'',
|
||||||
|
'// <<<< end of private namespace',
|
||||||
|
'}',
|
||||||
|
].join('\n'),
|
||||||
|
filters: cacheDetails.filters,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
scriptletFilteringEngine.injectNow = function(details) {
|
scriptletFilteringEngine.injectNow = function(details) {
|
||||||
|
@ -430,18 +478,31 @@ scriptletFilteringEngine.injectNow = function(details) {
|
||||||
request.entity = entityFromDomain(request.domain);
|
request.entity = entityFromDomain(request.domain);
|
||||||
const scriptletDetails = this.retrieve(request);
|
const scriptletDetails = this.retrieve(request);
|
||||||
if ( scriptletDetails === undefined ) { return; }
|
if ( scriptletDetails === undefined ) { return; }
|
||||||
const { scriptlets = '', filters } = scriptletDetails;
|
const { mainWorld = '', isolatedWorld = '', filters } = scriptletDetails;
|
||||||
if ( scriptlets === '' ) { return scriptletDetails; }
|
if ( mainWorld !== '' ) {
|
||||||
let code = contentscriptCode.assemble(request.hostname, scriptlets, filters);
|
let code = mainWorldInjector.assemble(request.hostname, mainWorld, filters);
|
||||||
if ( µb.hiddenSettings.debugScriptletInjector ) {
|
if ( µb.hiddenSettings.debugScriptletInjector ) {
|
||||||
code = 'debugger;\n' + code;
|
code = 'debugger;\n' + code;
|
||||||
|
}
|
||||||
|
vAPI.tabs.executeScript(details.tabId, {
|
||||||
|
code,
|
||||||
|
frameId: details.frameId,
|
||||||
|
matchAboutBlank: true,
|
||||||
|
runAt: 'document_start',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ( isolatedWorld !== '' ) {
|
||||||
|
let code = isolatedWorldInjector.assemble(request.hostname, isolatedWorld);
|
||||||
|
if ( µb.hiddenSettings.debugScriptletInjector ) {
|
||||||
|
code = 'debugger;\n' + code;
|
||||||
|
}
|
||||||
|
vAPI.tabs.executeScript(details.tabId, {
|
||||||
|
code,
|
||||||
|
frameId: details.frameId,
|
||||||
|
matchAboutBlank: true,
|
||||||
|
runAt: 'document_start',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
vAPI.tabs.executeScript(details.tabId, {
|
|
||||||
code,
|
|
||||||
frameId: details.frameId,
|
|
||||||
matchAboutBlank: true,
|
|
||||||
runAt: 'document_start',
|
|
||||||
});
|
|
||||||
return scriptletDetails;
|
return scriptletDetails;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue