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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17567624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 08:49:26  来源:igfitidea点击:

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

javascriptgoogle-chrome-extension

提问by user1365732

How can I pass a parameter to the JavaScript in a content script file which is injected using:

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

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

回答by Rob W

There's not such a thing as "pass a parameter to a file".

没有“将参数传递给文件”这样的事情。

What you cando is to either insert a content script beforeexecuting the file, or sending a message afterinserting the file. I will show an example for these distinct methods below.

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

Set parameters before execution of the JS file

JS文件执行前设置参数

If you want to define some variables before inserting the file, just nest chrome.tabs.executeScriptcalls:

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

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

If your variable is not as simple, then I recommend to use JSON.stringifyto turn an object in a string:

如果您的变量不是那么简单,那么我建议使用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'});
});

With the previous method, the variables can be used in content.jsin the following way:

使用前一种方法,可以通过content.js以下方式使用变量:

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

Set parameters after execution of the JS file

JS文件执行后设置参数

The previous method can be used to set parameters after the JS file. Instead of defining variables directly in the global scope, you can use the message passing APIto pass parameters:

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

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

In the content script (content.js), you can listen for these messages using the chrome.runtime.onMessageevent, and handle the message:

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

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

回答by Makyen

There are five general ways to pass data to a content script injected with tabs.executeScript()(MDN):

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

  • Set the data prior toinjecting the script
    1. Use chrome.storage.local(MDN)to pass the data (set prior to injecting your script).
    2. Inject code prior to your script which sets a variable with the data (see detailed discussion for possible security issue).
    3. Set a cookie for the domain in which the content script is being injected. This method can also be used to pass data to manifest.jsoncontent scripts which are injected at document_start, without the need for the content script to perform an asynchronous request.
  • Send/set the data afterinjecting the script
    1. Use message passing(MDN)to pass the data afteryour script is injected.
    2. Use chrome.storage.onChanged(MDN)in your content script to listen for the background script to set a value using chrome.storage.local.set()(MDN).
  • 注入脚本 之前设置数据
    1. 使用( MDN)传递数据(在注入脚本之前设置)。chrome.storage.local
    2. 在您的脚本之前注入代码,该代码使用数据设置变量(有关可能的安全问题,请参阅详细讨论)。
    3. 为正在注入内容脚本的域设置 cookie。此方法还可用于将数据传递给在 处注入的manifest.json内容脚本document_start,而无需内容脚本执行异步请求。
  • 注入脚本 发送/设置数据
    1. 注入脚本后,使用消息传递( MDN)传递数据。
    2. 在您的内容脚本中使用( MDN)来侦听后台脚本以使用( MDN)设置值。chrome.storage.onChangedchrome.storage.local.set()

Use chrome.storage.local(set prior to executing your script)

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

Using this method maintains the execution paradigm you are using of injecting a script that performs a function and then exits. It also does not have the potential security issue of using a dynamic value to build executing code, which is done in the second option below.

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

From your popup script:

从您的弹出脚本:

  1. Store the data using chrome.storage.local.set()(MDN).
  2. In the callback for chrome.storage.local.set(), call tabs.executeScript()(MDN).
  1. 使用( MDN)存储数据。chrome.storage.local.set()
  2. 在回调中chrome.storage.local.set(),调用( MDN)tabs.executeScript()
var updateTextTo = document.getElementById('comments').value;
chrome.storage.local.set({
    updateTextTo: updateTextTo
}, function () {
    chrome.tabs.executeScript({
        file: "content_script3.js"
    });
});

From your content script:

从您的内容脚本:

  1. Read the data from chrome.storage.local.get()(MDN).
  2. Make the changes to the DOM.
  3. Invalidate the data in storage.local(e.g. remove the key with: chrome.storage.local.remove() (MDN)).
  1. ( MDN)读取数据。chrome.storage.local.get()
  2. 对 DOM 进行更改。
  3. 使数据无效storage.local(例如删除密钥:MDN)。chrome.storage.local.remove() 
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;
        });
    }
}

See: Notes 1 & 2.

请参阅:注释 1 和 2。

Inject code prior to your script to set a variable

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

Prior to executing your script, you can inject some code that sets a variable in the content script context which your primary script can then use:

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

Security issue:

安全问题:

The following uses "'" + JSON.stringify().replace(/\\/g,'\\\\').replace(/'/g,"\\'") + "'"to encode the data into text which will be proper JSON when interpreted as code, prior to putting it in the codestring. The .replace()methods are needed to A) have the text correctly interpreted as a string when used as code, and B) quote any 'which exist in the data. It then uses JSON.parse()to return the data to a string in your content script. While this encoding is not strictly required, it is a good idea as you don't know the content of the value which you are going to send to the content script. This value could easily be something that would corrupt the code you are injecting (i.e. The user may be using 'and/or "in the text they entered). If you do not, in some way, escape the value, there is a security hole which could result in arbitrary code being executed.

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

From your popup script:

从您的弹出脚本:

  1. Inject a simple piece of code that sets a variable to contain the data.
  2. In the callback for chrome.tabs.executeScript()(MDN), call tabs.executeScript()to inject your script (Note: tabs.executeScript()will execute scripts in the order in which you call tabs.executeScript(), as long as they have the same value for runAt. Thus, waiting for the callback of the small codeis not strictly required).
  1. 注入一段简单的代码,设置一个包含数据的变量。
  2. ( MDN)的回调中,调用注入你的脚本(注意:将按照你调用的顺序执行脚本,只要它们具有相同的值。因此,等待 small 的回调不是严格要求的)。chrome.tabs.executeScript()tabs.executeScript()tabs.executeScript()tabs.executeScript()runAtcode
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,"\'")
}

From your content script:

从您的内容脚本:

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

See: Notes 1, 2, & 3.

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

Use message passing(MDN)(send data aftercontent script is injected)

使用消息传递MDN内容脚本注入发送数据)

This requires your content script code to install a listener for a message sent by the popup, or perhaps the background script (if the interaction with the UI causes the popup to close). It is a bit more complex.

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

From your popup script:

从您的弹出脚本:

  1. Determine the active tab using tabs.query()(MDN).
  2. Call tabs.executeScript()(MDN)
  3. In the callback for tabs.executeScript(), use tabs.sendMessage()(MDN)(which requires knowing the tabId), to send the data as a message.
  1. 使用( MDN)确定活动选项卡。tabs.query()
  2. 呼叫( MDN)tabs.executeScript()
  3. 在 的回调中tabs.executeScript(),使用( MDN)(需要知道)将数据作为消息发送。tabs.sendMessage()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
        });
    });
});

From your content script:

从您的内容脚本:

  1. Add a listener using chrome.runtime.onMessage.addListener()(MDN).
  2. Exit your primary code, leaving the listener active. You could return a success indicator, if you choose.
  3. Upon receiving a message with the data:
    1. Make the changes to the DOM.
    2. Remove your runtime.onMessagelistener
  1. 使用( MDN)添加侦听器。chrome.runtime.onMessage.addListener()
  2. 退出您的主要代码,让侦听器保持活动状态。如果您选择,您可以返回一个成功指示器。
  3. 收到带有数据的消息后:
    1. 对 DOM 进行更改。
    2. 删除你的runtime.onMessage听众

#3.2 is optional. You could keep your code active waiting for another message, but that would change the paradigm you are using to one where you load your code and it stays resident waiting for messages to initiate actions.

#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
}

See: Notes 1 & 2.

请参阅:注释 1 和 2。



Note 1: Using Array.from()is fine if you are not doing it many times and are using a browser version which has it(Chrome >= version 45, Firefox >= 32). In Chrome and Firefox, Array.from()is slow compared to other methods of getting an array from a NodeList. For a faster, more compatible conversion to an Array, you could use the asArray()code in this answer. The second version of asArray()provided in that answer is also more robust.

注意 1:Array.from()如果您没有多次使用并且使用具有它浏览器版本(Chrome >= 版本 45,Firefox >= 32),则可以使用。在 Chrome 和 Firefox 中,Array.from()与从 NodeList 获取数组的其他方法相比速度较慢。为了更快、更兼容地转换为数组,您可以使用此答案中asArray()代码。该答案中提供的第二个版本也更强大。asArray()

Note 2: If you are willing to limit your code to Chrome version >= 51 or Firefox version >= 50, Chrome has a forEach()method for NodeListsas of v51. Thus, you don't need to convert to an array. Obviously, you don't need to convert to an Array if you use a different type of loop.

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

Note 3: While I have previously used this method (injecting a script with the variable value) in my own code, I was reminded that I should have included it here when reading this answer.

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