【发布时间】:2013-07-08 04:54:50
【问题描述】:
如何将参数传递给使用以下方式注入的内容脚本文件中的 JavaScript:
chrome.tabs.executeScript(tab.id, {file: "content.js"});
【问题讨论】:
标签: javascript google-chrome-extension
如何将参数传递给使用以下方式注入的内容脚本文件中的 JavaScript:
chrome.tabs.executeScript(tab.id, {file: "content.js"});
【问题讨论】:
标签: javascript google-chrome-extension
没有“将参数传递给文件”之类的东西。
您可以做的是在执行文件之前插入内容脚本,或者在插入文件之后发送消息。我将在下面展示这些不同方法的示例。
如果要在插入文件之前定义一些变量,只需嵌套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文件后面设置参数。不用直接在全局范围内定义变量,可以使用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'
});
【讨论】:
config.somebigobject。请注意,“somebigobject”可以是任何东西,我只是想展示一般如何做到这一点,并且通过选择像“somebigobject”这样的不太可能的名称,我想传达一个信息,即您可以在该对象上放置任何您想要的东西。
有五种通用方法可以将数据传递给注入了tabs.executeScript()的内容脚本(MDN):
chrome.storage.local(MDN) 传递数据(在注入脚本之前设置)。document_start 处注入,而无需内容脚本执行异步请求。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)。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(),只要它们具有相同的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,"\\'")
}
if (typeof newText === 'string') {
Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
el.value = newText;
});
}
请参阅:注释 1、2 和 3。
这需要您的内容脚本代码为弹出窗口或后台脚本(如果与 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)。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 时应该将其包含在此处。
【讨论】:
runAt)脚本是按照调用tabs.executeScript() 的顺序注入的(Chrome 和 Firefox)。我不记得我是否查看了两者的源代码以进行验证。这方面的一个重要方面是注入的脚本来自您的扩展程序,而不是网络资源(大多数异步延迟所在的地方)。你说得对,文档中没有保证,这就是为什么我这样写它确实等待tabs.executeScript() 回调注入第二个非code: 脚本。
tabs.executeScript() 的执行顺序进行,但不太可能不会按那个顺序。例如:tabs.executeScript() 可能会输入一条消息,指示消息队列中的注入被发送到处理内容的正确进程。一旦在该过程中收到,它将进入一个注入队列(每个runAt 的单独队列,或至少由runAt 组织),或立即处理(例如,对于code:,即立即 可用)。等
code: 脚本立即可用(甚至不需要从您的扩展程序(即磁盘或 RAM 缓存)中获取)。这意味着它更有可能在其他脚本之前被注入。但是,我所做的测试表明,即使对于从扩展中获取的多个(相同的runAt)脚本,脚本也是按照调用tabs.executeScript() 的顺序注入的。同样,它不能保证,但它至少在 99%+ 的时间内有效。但是,这并不意味着当您有硬依赖时,您不应该等待回调。
您可以使用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],
},
() => { ... });
编辑:我的错误 - 这仅适用于注入函数,而不是问题指定的文件。
【讨论】: