Reduce to use content scripts
Currently, we have used content scripts for "Console Integration"
https://github.com/secretlint/webextension/blob/7d1ded14cfbafd24f20b0cfc2e89cf5e3fb34920/app/manifest.json#L21 https://github.com/secretlint/webextension/blob/main/app/scripts/contentScript.ts
This approach always inject content scripts to any website.
Instead of it, we want to use chrome.scripting.executeScript, however it does not exists on Firefox.
- https://developer.chrome.com/docs/extensions/reference/scripting/#injection-targets
tabs.executeScript() does not support arguments.
(Also we need to inject again when move pages.)
- https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/Tabs/executeScript
📝 Details of context.
tabs.executeScript({ code }) use dynamic eval, This is another reason why i avoid.
We need to treat it with caution and it will make complex.
tabs.executeScript() support file but it is not dynamic(no arguments).
tabs.executeScript() support code but it just use eval.
chrome.scripting.executeScript support freezed function and arguments, but Firefox does not support yet.
chrome.scripting.executeScript(
{
target: {tabId: tabId},
func: changeBackgroundColor,
args: [color],
},
() => { ... });
Related #4
FYI, here is a simplified version of what I actually use for the Copy URL To Clipboard extension.
contentScript.js:
// NOTE: Write content script in IIFE to avoid redeclaration of const error.
// If you want to use async functions, see Note in https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript#return_value
'use strict';
(() => {
const myFunc = () => {
const { foo } = window;
let res;
if (foo) {
const { bar, baz } = foo;
res = `${bar} ${baz}`;
}
return res;
};
return myFunc();
})();
executeContentScript.js (import it to the background script):
/* api */
const { permissions, tabs } = browser;
/**
* execute content script to active tab
*
* @param {object} opt - options
* @returns {Array|undfined} - result
*/
const execScriptToActiveTab = async opt => {
// NOTE: Requires 'activeTab' permission.
const isGranted = await permissions.contains({
permissions: ['activeTab']
});
let res;
if (isGranted && opt) {
res = await tabs.executeScript(opt);
}
return res;
};
/**
* execute scripts to active tab in order
*
* @param {Array} arr - array of objects
* @returns {Array|boolean|undefined} - result of the last executed script
*/
export const execScriptsToTabInOrder = async (arr = []) => {
const func = [];
let res;
for (const item of arr) {
func.push(execScriptToActiveTab(item));
}
if (func.length) {
// NOTE: If resolved it contains `value`, if rejected it contains `reason`.
const { reason, value } = await Promise.allSettled(func).then(a => a.pop());
if (value) {
res = value;
} else {
console.error(reason);
res = false;
}
}
return res;
};
background.js:
import { execScriptsToTabInOrder } from 'path/to/executeContentScript.js';
/**
* execute content script with data
*
* @returns {Array|boolean} - array containing result
* if any error occurs, return value is false
*/
const executeContentScriptWithData = async () => {
// Data you want to use in the content script
const foo = {
bar: 'bar',
baz: 1
};
const res = await execScriptsToTabInOrder([
// NOTE: window.foo won't be exposed to the real web.
{ code: `window.foo = ${JSON.stringify(foo)};` },
{ file: 'path/to/contentScript.js' }
]);
return res; // ['bar 1']
};
Thanks to suggest!
Is window.foo exposed into website(page context)?
(Does Website can read window.foo?
I want to prevent to read it from page context.
No, as I noted in the sample, web page cannot see window.foo.
See DOM access
- Content scripts cannot see JavaScript variables defined by page scripts.
The same is true in reverse; page scripts cannot see JavaScript properties added by content scripts.