【问题标题】:Pass a parameter to a content script injected using chrome.tabs.executeScript()将参数传递给使用 chrome.tabs.executeScript() 注入的内容脚本
【发布时间】:2013-07-08 04:54:50
【问题描述】:

如何将参数传递给使用以下方式注入的内容脚本文件中的 JavaScript:

chrome.tabs.executeScript(tab.id, {file: "content.js"});

【问题讨论】:

    标签: javascript google-chrome-extension


    【解决方案1】:

    没有“将参数传递给文件”之类的东西。

    可以做的是在执行文件之前插入内容脚本,或者在插入文件之后发送消息。我将在下面展示这些不同方法的示例。

    执行JS文件前设置参数

    如果要在插入文件之前定义一些变量,只需嵌套chrome.tabs.executeScript调用即可:

    chrome.tabs.executeScript(tab.id, {
        code: 'var config = 1;'
    }, function() {
        chrome.tabs.executeScript(tab.id, {file: 'content.js'});
    });
    

    如果你的变量不是那么简单,那么我推荐使用JSON.stringify将对象转成字符串:

    var config = {somebigobject: 'complicated value'};
    chrome.tabs.executeScript(tab.id, {
        code: 'var config = ' + JSON.stringify(config)
    }, function() {
        chrome.tabs.executeScript(tab.id, {file: 'content.js'});
    });
    

    使用前面的方法,变量可以在content.js中使用如下方式:

    // content.js
    alert('Example:' + config);
    

    JS文件执行后设置参数

    前面的方法可以在JS文件后面设置参数。不用直接在全局范围内定义变量,可以使用message passing API来传递参数:

    chrome.tabs.executeScript(tab.id, {file: 'content.js'}, function() {
        chrome.tabs.sendMessage(tab.id, 'whatever value; String, object, whatever');
    });
    

    在内容脚本(content.js)中,您可以使用chrome.runtime.onMessage事件监听这些消息,并处理这些消息:

    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
        // Handle message.
        // In this example, message === 'whatever value; String, object, whatever'
    });
    

    【讨论】:

    • 我只得到一个消息对象,没有文本。
    • chrome.extension.onMessage.addListener(function (response, sender) { //选中文本 var text = document.getElementById('text'); text.innerHTML = response.data; //需要在 js 文件中获取此数据 它已在任何选项卡 chrome.tabs.executeScript(tab.id, { code: 'var config = 788888888 ;' }, function() { chrome.tabs.executeScript(tab.id, {file: 'content.js'}); }); });
    • @user1365732 我已经展示了两种不同的方法来实现相同的目标。
    • 我已经尝试过这些方法,第一个对我有用,我如何将自己的数据放入 var config 中,只接受整数,在第二种方法消息对象中,我如何获得文本值。
    • @user1365732 是的,因为您必须读取该对象的属性。在此示例中,config.somebigobject。请注意,“somebigobject”可以是任何东西,我只是想展示一般如何做到这一点,并且通过选择像“somebigobject”这样的不太可能的名称,我想传达一个信息,即您可以在该对象上放置任何您想要的东西。
    【解决方案2】:

    有五种通用方法可以将数据传递给注入了tabs.executeScript()的内容脚本(MDN)

    • 注入脚本之前设置数据
      1. 使用chrome.storage.local(MDN) 传递数据(在注入脚本之前设置)。
      2. 在设置数据变量的脚本之前注入代码(有关可能的安全问题,请参阅详细讨论)。
      3. 为要注入内容脚本的域设置 cookie。此方法还可用于将数据传递给 manifest.json 内容脚本,这些脚本在 document_start 处注入,而无需内容脚本执行异步请求。
    • 注入脚本后发送/设置数据
      1. 使用message passing(MDN) 在您的脚本被注入后传递数据。
      2. 在您的内容脚本中使用chrome.storage.onChanged(MDN) 来监听后台脚本以使用chrome.storage.local.set()设置值 (MDN).

    使用chrome.storage.local(在执行脚本之前设置)

    使用此方法可维护您正在使用的执行范例,即注入执行功能然后退出的脚本。它也没有使用动态值来构建执行代码的潜在安全问题,这是在下面的第二个选项中完成的。

    从您的弹出脚本中:

    1. 使用chrome.storage.local.set()存储数据(MDN)
    2. chrome.storage.local.set() 的回调中,调用tabs.executeScript()(MDN)
    var updateTextTo = document.getElementById('comments').value;
    chrome.storage.local.set({
        updateTextTo: updateTextTo
    }, function () {
        chrome.tabs.executeScript({
            file: "content_script3.js"
        });
    });
    

    来自您的内容脚本:

    1. chrome.storage.local.get()读取数据(MDN)
    2. 对 DOM 进行更改。
    3. 使storage.local 中的数据无效(例如,删除密钥:chrome.storage.local.remove()(MDN))。
    chrome.storage.local.get('updateTextTo', function (items) {
        assignTextToTextareas(items.updateTextTo);
        chrome.storage.local.remove('updateTextTo');
    });
    function assignTextToTextareas(newText){
        if (typeof newText === 'string') {
            Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
                el.value = newText;
            });
        }
    }
    

    请参阅:注释 1 和 2。

    在脚本之前注入代码以设置变量

    在执行脚本之前,您可以注入一些代码,在内容脚本上下文中设置变量,然后您的主脚本可以使用该变量:

    安全问题:

    以下使用"'" + JSON.stringify().replace(/\\/g,'\\\\').replace(/'/g,"\\'") + "'" 将数据编码为文本,当将其解释为代码时将是正确的JSON,然后将其放入code 字符串中。 .replace() 方法需要 A) 在用作代码时将文本正确解释为字符串,并且 B) 引用数据中存在的任何 '。然后它使用JSON.parse() 将数据返回到内容脚本中的字符串。虽然这种编码不是严格要求的,但这是一个好主意,因为您不知道要发送到内容脚本的值的内容。这个值很容易破坏你正在注入的代码(即用户可能在他们输入的文本中使用' 和/或")。如果您不以某种方式转义该值,则存在可能导致执行任意代码的安全漏洞。

    从您的弹出脚本中:

    1. 注入一段简单的代码,设置一个变量来包含数据。
    2. chrome.tabs.executeScript()的回调中(MDN),调用tabs.executeScript()注入你的脚本(注意:tabs.executeScript()会按顺序执行脚本在其中调用tabs.executeScript(),只要它们具有相同的runAt 值即可。因此,不严格要求等待小code 的回调)。
    var updateTextTo = document.getElementById('comments').value;
    chrome.tabs.executeScript({
        code: "var newText = JSON.parse('" + encodeToPassToContentScript(updateTextTo) + "');"
    }, function () {
        chrome.tabs.executeScript({
            file: "content_script3.js"
        });
    });
    
    function encodeToPassToContentScript(obj){
        //Encodes into JSON and quotes \ characters so they will not break
        //  when re-interpreted as a string literal. Failing to do so could
        //  result in the injection of arbitrary code and/or JSON.parse() failing.
        return JSON.stringify(obj).replace(/\\/g,'\\\\').replace(/'/g,"\\'")
    }
    

    来自您的内容脚本:

    1. 使用存储在变量中的数据对 DOM 进行更改
    if (typeof newText === 'string') {
        Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
            el.value = newText;
        });
    }
    

    请参阅:注释 1、2 和 3。

    使用message passing(MDN)(在注入内容脚本后发送数据)

    这需要您的内容脚本代码为弹出窗口或后台脚本(如果与 UI 的交互导致弹出窗口关闭)发送的消息安装侦听器。它有点复杂。

    从您的弹出脚本中:

    1. 使用tabs.query()确定活动标签(MDN)
    2. 致电tabs.executeScript()(MDN)
    3. tabs.executeScript() 的回调中,使用tabs.sendMessage()(MDN)(这需要知道tabId),将数据作为消息。
    var updateTextTo = document.getElementById('comments').value;
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        chrome.tabs.executeScript(tabs[0].id, {
            file: "content_script3.js"
        }, function(){
            chrome.tabs.sendMessage(tabs[0].id,{
                updateTextTo: updateTextTo
            });
        });
    });
    

    来自您的内容脚本:

    1. 使用chrome.runtime.onMessage.addListener()添加侦听器(MDN)
    2. 退出主代码,让监听器保持活动状态。如果您愿意,您可以返回一个成功指示器。
    3. 收到包含数据的消息后:
      1. 对 DOM 进行更改。
      2. 删除您的 runtime.onMessage 监听器

    #3.2 是可选的。您可以让您的代码保持活动状态以等待另一条消息,但这会将您使用的范例更改为您加载代码并保持驻留等待消息启动操作的范例。

    chrome.runtime.onMessage.addListener(assignTextToTextareas);
    function assignTextToTextareas(message){
        newText = message.updateTextTo;
        if (typeof newText === 'string') {
            Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
                el.value = newText;
            });
        }
        chrome.runtime.onMessage.removeListener(assignTextToTextareas);  //optional
    }
    

    请参阅:注释 1 和 2。


    注意 1:如果您没有多次使用 browser version which has it(Chrome >= 版本 45,Firefox >= 32),则使用 Array.from() 很好。在 Chrome 和 Firefox 中,Array.from() is slow compared to other methods of getting an array from a NodeList。为了更快、更兼容地转换为数组,您可以使用this answer 中的asArray() 代码。该答案中提供的asArray() 的第二个版本也更强大。

    注意 2:如果您愿意 limit your code to Chrome version >= 51 or Firefox version >= 50,Chrome 从 v51 开始提供 forEach() 方法用于 NodeLists。因此,您不需要转换为数组。显然,如果使用不同类型的循环,则无需转换为 Array。

    注意 3:虽然我之前在自己的代码中使用过这种方法(注入带有变量值的脚本),但提醒我在阅读 this answer 时应该将其包含在此处。

    【讨论】:

    • 您确定“将按照您调用的顺序执行脚本..”?订单不是未定义吗?
    • @Pacerier,我的所有测试(其他项目,不仅仅是这个答案)都显示(相同的runAt)脚本是按照调用tabs.executeScript() 的顺序注入的(Chrome 和 Firefox)。我不记得我是否查看了两者的源代码以进行验证。这方面的一个重要方面是注入的脚本来自您的扩展程序,而不是网络资源(大多数异步延迟所在的地方)。你说得对,文档中没有保证,这就是为什么我这样写它确实等待tabs.executeScript() 回调注入第二个非code: 脚本。
    • @Pacerier,实现此类代码的最合理方法可能无法保证注入按照tabs.executeScript() 的执行顺序进行,但不太可能不会按那个顺序。例如:tabs.executeScript() 可能会输入一条消息,指示消息队列中的注入被发送到处理内容的正确进程。一旦在该过程中收到,它将进入一个注入队列(每个runAt 的单独队列,或至少由runAt 组织),或立即处理(例如,对于code:,即立即 可用)。等
    • @Pacerier,在这种情况下,code: 脚本立即可用(甚至不需要从您的扩展程序(即磁盘或 RAM 缓存)中获取)。这意味着它更有可能在其他脚本之前被注入。但是,我所做的测试表明,即使对于从扩展中获取的多个(相同的runAt)脚本,脚本也是按照调用tabs.executeScript() 的顺序注入的。同样,它不能保证,但它至少在 99%+ 的时间内有效。但是,这并不意味着当您有硬依赖时,您不应该等待回调。
    【解决方案3】:

    您可以使用args 属性,请参阅this documentation

    const color = getUserColor();
    function changeBackgroundColor(backgroundColor) {
      document.body.style.backgroundColor = backgroundColor;
    }
    const tabId = getTabId();
    chrome.scripting.executeScript(
        {
          target: {tabId: tabId},
          func: changeBackgroundColor,
          args: [color],
        },
        () => { ... });
    

    编辑:我的错误 - 这仅适用于注入函数,而不是问题指定的文件。

    【讨论】:

      猜你喜欢
      • 2012-10-24
      • 1970-01-01
      • 2015-05-13
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多