将参数传递给使用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. 在脚本之前注入代码,用数据设置一个变量(有关可能的安全问题,请参阅详细的讨论)。
  • 注入脚本发送/设置数据
    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,"\\'") + "'"来编码数据在解释为代码之前将其放入正确的JSON中,然后将其放入code字符串中。 .replace()方法需要A)在作为代码使用时正确解释为字符串,B)引用数据中存在的任何' 。 然后使用JSON.parse()将数据返回到内容脚本中的字符串。 尽管这种编码不是严格要求的,但是您不知道将要发送到内容脚本的值的内容。 这个值很容易就会破坏你注入的代码(例如,用户可能在输入的文本中使用'和/或' ),如果你没有在某种程度上转义该值,这可能导致任意代码被执行。

从你的弹出脚本:

  1. 注入一个简单的代码来设置一个变量来包含数据。
  2. chrome.tabs.executeScript() ( MDN )的回调tabs.executeScript() ,调用tabs.executeScript()注入脚本(注意: tabs.executeScript()将按照您调用tabs.executeScript()只要它们具有相同的runAt值,就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 ,则从v51起,Chrome对NodeLists具有forEach()方法。 因此,你不需要转换成数组。 显然,如果使用不同类型的循环,则不需要转换为数组。

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