将参数传递给使用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 )的内容脚本的一般方法:
- 在注入脚本之前设置数据
- 使用
chrome.storage.local
( MDN )传递数据(在注入脚本之前设置)。 - 在脚本之前注入代码,用数据设置一个变量(有关可能的安全问题,请参阅详细的讨论)。
- 使用
- 注入脚本后发送/设置数据
- 注入脚本后,使用消息传递 ( MDN )传递数据。
- 在内容脚本中使用
chrome.storage.onChanged
( MDN )来侦听后台脚本,以使用chrome.storage.local.set()
( MDN )设置值。
使用chrome.storage.local
(在执行脚本之前设置)
使用这个方法维护你正在使用的执行范例,注入执行一个函数的脚本然后退出。 它也没有使用动态值构建执行代码的潜在安全问题,这在下面的第二个选项中完成。
从你的弹出脚本:
- 使用
chrome.storage.local.set()
( MDN )存储数据。 - 在
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" }); });
从您的内容脚本中:
- 从
chrome.storage.local.get()
( MDN )读取数据。 - 对DOM进行更改。
- 使
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()
将数据返回到内容脚本中的字符串。 尽管这种编码不是严格要求的,但是您不知道将要发送到内容脚本的值的内容。 这个值很容易就会破坏你注入的代码(例如,用户可能在输入的文本中使用'
和/或'
),如果你没有在某种程度上转义该值,这可能导致任意代码被执行。
从你的弹出脚本:
- 注入一个简单的代码来设置一个变量来包含数据。
- 在
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,"\\'") }
从您的内容脚本中:
- 使用存储在变量中的数据对DOM进行更改
if (typeof newText === 'string') { Array.from(document.querySelectorAll('textarea.comments')).forEach(el => { el.value = newText; }); }
请参阅:注释1,2和3。
使用消息传递 ( MDN ) (注入内容脚本后发送数据)
这需要您的内容脚本代码为弹出窗口或者后台脚本(如果与UI的交互导致弹出窗口关闭)发送的消息安装监听器。 这有点复杂。
从你的弹出脚本:
- 使用
tabs.query()
( MDN )确定活动选项卡。 - 调用
tabs.executeScript()
( MDN ) - 在
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 }); }); });
从您的内容脚本中:
- 使用
chrome.runtime.onMessage.addListener()
( MDN )添加侦听器。 - 退出主代码,使监听器保持活动状态。 如果您选择,您可以返回成功指示器。
- 一旦收到带有数据的消息:
- 对DOM进行更改。
- 删除你的
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:虽然我以前使用过这种方法(在自己的代码中注入了一个带有变量值的脚本),但提醒我应该在阅读这个答案时将其包含在这里。