繁体   English   中英

将参数传递给使用 chrome.tabs.executeScript() 注入的内容脚本

[英]Pass a parameter to a content script injected using chrome.tabs.executeScript()

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

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

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

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

在执行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 文件后设置参数。 您可以使用消息传递 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'
});

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

  • 在注入脚本之前设置数据
    1. 使用chrome.storage.local ( MDN )传递数据(在注入脚本之前设置)。
    2. 在设置数据变量的脚本之前注入代码(有关可能的安全问题,请参阅详细讨论)。
    3. 为要注入内容脚本的域设置一个 cookie。 此方法还可用于将数据传递给在document_start注入的manifest.json内容脚本,而无需内容脚本执行异步请求。
  • 注入脚本发送/设置数据
    1. 注入脚本,使用消息传递( 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,"\\'") + "'"对数据进行编码在将其放入code字符串之前,将其解释为代码时将是正确的 JSON 文本。 .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。

使用消息传递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:如果您没有多次使用Array.from()并且使用的是具有它的浏览器版本(Chrome >= 版本 45,Firefox >= 32),则使用 Array.from() 很好。 在 Chrome 和 Firefox 中,与其他从 NodeList 获取数组的方法相比, Array.from()速度较慢 为了更快、更兼容地转换为数组,您可以在此答案中使用asArray()代码。 该答案中提供的asArray()的第二个版本也更健壮。

注意 2:如果您愿意将代码限制为 Chrome 版本 >= 51 或 Firefox 版本 >= 50 ,Chrome 从 v51 开始为NodeLists提供了forEach()方法。 因此,您不需要转换为数组。 显然,如果您使用不同类型的循环,则不需要转换为数组。

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

您可以使用args属性,请参阅此文档

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

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

@RobW 的答案是完美的答案。 但是要实现这一点,您需要启动全局变量。

我为此建议一个替代方案,类似于@RobW 的答案。 您无需将变量传递给文件,而是从content.js文件加载 function,然后使用code:并从当前上下文传递变量。

var argString = "abc";
var argInt = 123;

chrome.tabs.executeScript(tabId, { file: "/content.js" }).then(() => {
    chrome.tabs.executeScript(tabId, {
        allFrames: false,
        code: "myFunction('" + argString + "', " + argInt + "); ",
    });
});

这是来自@wOxxOm 的答案here 这种方法对于编写 Manifest v2 & v3 的通用源代码真的很有帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM