search

Home  >  Q&A  >  body text

Access a webpage's `window`/DOM/HTML from an extension

I'm writing a Chrome extension and trying to overlay

on the current webpage immediately after clicking a button in a popup.html file.

When I access the document.body.insertBefore method from popup.html, it overwrites the

on the popup instead of the current webpage.

Do I have to use messaging between background.html and popup.html to access the DOM of the web page? I'd like to do it all in popup.html and also use jQuery if possible.

P粉046878197P粉046878197404 days ago636

reply all(2)I'll reply

  • P粉296080076

    P粉2960800762023-10-19 08:17:05

    Some examples of using programmatic injection to add an expanding popup script to this div.

    List V3

    Don't forget to add permissions in manifest.json, see other answers for more information.

    • Simple call:

      (async () => {
        const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
        await chrome.scripting.executeScript({
          target: {tabId: tab.id},
          func: inContent1,
        });
      })();
      
      // executeScript runs this code inside the tab
      function inContent1() {
        const el = document.createElement('div');
        el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
        el.textContent = 'DIV';
        document.body.appendChild(el);
      }
      

      Note: In Chrome 91 or earlier, func: should be function:.

    • Call with parameters and receive results

      Requires Chrome 92 as it implements args.

      Example 1:

      res = await chrome.scripting.executeScript({
        target: {tabId: tab.id},
        func: (a, b) => { return [window[a], window[b]]; },
        args: ['foo', 'bar'],
      });
      

      Example 2:

      (async () => {
        const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
        let res;
        try {
          res = await chrome.scripting.executeScript({
            target: {tabId: tab.id},
            func: inContent2,
            args: [{ foo: 'bar' }], // arguments must be JSON-serializable
          });
        } catch (e) {
          console.warn(e.message || e);
          return;
        }
        // res[0] contains results for the main page of the tab 
        document.body.textContent = JSON.stringify(res[0].result);
      })();
      
      // executeScript runs this code inside the tab
      function inContent2(params) {
        const el = document.createElement('div');
        el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
        el.textContent = params.foo;
        document.body.appendChild(el);
        return {
          success: true,
          html: document.body.innerHTML,
        };
      }
      

    List V2

    • Simple call:

      // uses inContent1 from ManifestV3 example above
      chrome.tabs.executeScript({ code: `(${ inContent1 })()` });
      
    • Call with parameters and receive the result:

      // uses inContent2 from ManifestV3 example above
      chrome.tabs.executeScript({
        code: `(${ inContent2 })(${ JSON.stringify({ foo: 'bar' }) })`
      }, ([result] = []) => {
        if (!chrome.runtime.lastError) {
          console.log(result); // shown in devtools of the popup window
        }
      });
      

      This example uses the inContent function to automatically convert the code to a string, the benefit of this is that the IDE can apply syntax highlighting and linting. The obvious disadvantage is that the browser wastes time parsing the code, but it's usually less than 1 millisecond so it's negligible.

    reply
    0
  • P粉600402085

    P粉6004020852023-10-19 00:39:30

    Problem: Extension pages (pop-ups, options, background pages in MV2, etc.) are separated from the web page, they have their own DOM, document, < code>window and chrome-extension:// URLs.

    Solution: Use content scripts to access web pages or interact with their content.

    • Content scripts are executed in the web page, not in the extension.
    • Content scripts are isolated by default, see How to run code in the page context (also known as the main world).
    • Do not load content scripts in extension pages.

    Method 1. Declarative

    manifest.json:

    "content_scripts": [{
      "matches": ["*://*.example.com/*"],
      "js": ["contentScript.js"]
    }],
    

    It will run once when the page loads. After that, use messaging.

    warn! It cannot send DOM elements, Maps, Sets, ArrayBuffers, classes, functions, etc. It can only send JSON-compatible simple objects and types, so you need to manually extract the required data and pass it as a simple array or object.

    Method 2. Programming

    • ManifestV3

      In extension scripts (such as pop-up windows), content scripts/functions can be injected into tabs as needed.

      The result of this method is the last expression in the content script and therefore can be used to extract data. Data must be JSON compatible, see warning above.

      Required

      Permissions in manifest.json:

      • "script" - mandatory;
      • "activeTab" - Ideal scenario, suitable for responding to user actions (usually clicking an extension icon in the toolbar). Do not show any permission warnings when installing extensions.

      If the ideal situation is not possible, add allowed sites to host_permissions in manifest.json:

      • "*://*.example.com/" and any other website you want.

      • "" or "*://*/" These will place your extension in the very slow review queue in the Chrome Web Store Due to extensive host permissions.

    • Differences between ManifestV2 and the above:

    reply
    0
  • Cancelreply