如何访问网页DOM而不是扩展页面DOM?

Ste*_*ven 63 google-chrome google-chrome-extension

我正在编写Chrome扩展程序,并<div>在popup.html文件中单击按钮后尝试覆盖当前网页.

当我document.body.insertBefore从popup.html中访问该方法时,它会覆盖<div>弹出窗口而不是当前网页.

我是否必须在background.html和popup.html之间使用消息传递才能访问网页的DOM?我想在popup.html中做所有事情,并且如果可能的话也使用jQuery.

Moh*_*our 105

Chrome扩展程序概述中所述:体系结构(必读):

如果您的扩展需要与网页交互,那么它需要一个内容脚本.内容脚本是一些JavaScript,它在已加载到浏览器中的页面的上下文中执行.将内容脚本视为该加载页面的一部分,而不是作为其打包的扩展(其父扩展)的一部分.

browserAction弹出窗口是您单击浏览器工具栏中的图标时看到的页面,它是带有chrome-extension://URL 的HTML页面,因此当您访问其DOM时,您正在影响弹出窗口.这同样适用于扩展的背景/选项页面.

要访问/操作网页DOM,您有两种方法:

  1. manifest.json中声明内容脚本并使用消息传递:

    chrome.tabs.sendMessage()从你的后台/弹出页面到注入的内容脚本的chrome.runtime.onMessage监听器,它将sendResponse根据文档在网页上执行操作并通过回调传输结果(注意:只支持JSON可用的对象,如数字,字符串,数组,简单对象,这意味着不是DOM元素,不是类,不是函数).如果内容脚本需要启动与扩展页面的通信,它应该使用chrome.runtime.sendMessage().

  2. 或者使用Tabs API注入内容脚本:
    chrome.tabs.executeScript(tabId, details, callback)

    • 所需的权限:"tabs","https://www.example.com/*"
      (或"<all_urls>"及其变种类似"*://*/*","http://*/*","https://*/*")

    • 在显式用户激活的情况下,更好的选择是使用"activeTab"权限而不是"tabs"并且"<all_urls>"因为它可以作为许多用途的替代"<all_urls>",但在安装期间不显示警告消息.

    • .executeScript()可以与回调函数一起使用,该函数接收注入的内容脚本中的最后一个计算表达式的数组,每个帧中的一个元素在注册表中注入.Chrome 在内部使用JSON.parse()JSON.stringify()结果,从而将支持的类型限制为普通对象和简单的可字符串值,如数字/字符串或其数组.
      因此它不适用于DOM元素,函数,自定义属性,getter/setter:您需要手动映射/提取所需数据并将其传递到一个简单的数组/对象中.

内容脚本在称为孤立世界的特殊环境中执行.他们可以访问注入页面的DOM,但不能访问页面创建的任何JavaScript变量或函数.它将每个内容脚本视为在其运行的页面上没有执行其他JavaScript.反过来也是如此:页面上运行的JavaScript无法调用任何函数或访问内容脚本定义的任何变量.

仍然可以更深入地访问网页JavaScript变量/函数.



作为第二种方法的示例,让我们在单击浏览器操作时显示div.
我们将chrome.tabs.executeScript()browserAction单击处理程序中使用内容脚本文件(或文字代码字符串,如果它很小,请参阅方法的文档)到该页面的DOM.

var someVar = {text: 'test', foo: 1, bar: false};
chrome.tabs.executeScript({
    code: '(' + function(params) {
        document.body.insertAdjacentHTML('beforeend',
            '<div style="all:unset; position:fixed; left:0; top:0; right:0; bottom:0;' +
                'background-color:rgba(0,255,0,0.3)">' + params.text + '</div>'
        );
        return {success: true, html: document.body.innerHTML};
    } + ')(' + JSON.stringify(someVar) + ');'
}, function(results) {
    console.log(results[0]);
});
Run Code Online (Sandbox Code Playgroud)

如您所见,我们使用了函数代码的自动字符串转换,以便能够将注入的代码编写为具有语法高亮和linting的普通JavaScript.显而易见的缺点是浏览器浪费时间来解析代码,但通常它不到1毫秒,因此可以忽略不计.

  • 这非常有用,而且正是我需要知道的.谢谢! (4认同)
  • 你自己创造它。 (2认同)

wOx*_*xOm 13

使用编程注入来添加该 div 的扩展弹出脚本的一些示例。

清单V3

不要忘记在 manifest.json 中添加权限,请参阅其他答案以获取更多信息。

  • 简单的调用:

    (async () => {
      const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
      const [{result}] = await chrome.scripting.executeScript({
        target: {tabId: tab.id},
        func: () => document.querySelector('foo')?.textContent,
      });
      document.body.textContent = result;
    })();
    
    Run Code Online (Sandbox Code Playgroud)
  • 简单调用2:

    (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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    注意:在 Chrome 91 或更早版本中func:应该是function:.

  • 带参数调用并接收结果

    需要 Chrome 92 来实现args

    示例1:

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

    示例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,
      };
    }
    
    Run Code Online (Sandbox Code Playgroud)

清单V2

  • 简单的调用:

    // uses inContent1 from ManifestV3 example above
    chrome.tabs.executeScript({ code: `(${ inContent1 })()` });
    
    Run Code Online (Sandbox Code Playgroud)
  • 带参数调用并接收结果:

    // 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
      }
    });
    
    Run Code Online (Sandbox Code Playgroud)

    此示例使用inContent函数代码自动转换为字符串,这样做的好处是 IDE 可以应用语法高亮和 linting。明显的缺点是浏览器会浪费时间来解析代码,但通常不到 1 毫秒,因此可以忽略不计。