Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce to use content scripts #5

Open
azu opened this issue Aug 20, 2021 · 3 comments
Open

Reduce to use content scripts #5

azu opened this issue Aug 20, 2021 · 3 comments
Labels
help wanted Extra attention is needed Status: Proposal Request for comments

Comments

@azu
Copy link
Member

azu commented Aug 20, 2021

Currently, we have used content scripts for "Console Integration"

"scripts/contentScript.js"

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.

tabs.executeScript() does not support arguments.
(Also we need to inject again when move pages.)

📝 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

@azu azu added help wanted Extra attention is needed Status: Proposal Request for comments labels Aug 20, 2021
@asamuzaK
Copy link

asamuzaK commented Aug 20, 2021

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']
};

@azu
Copy link
Member Author

azu commented Aug 20, 2021

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.

@asamuzaK
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed Status: Proposal Request for comments
Projects
None yet
Development

No branches or pull requests

2 participants