使用 executeScript() 将弹出窗口中的数据通信到弹出窗口注入的内容脚本 [英] Communicate data from popup to content script injected by popup with executeScript()
问题描述
我的 Chrome 扩展弹出窗口中有一个文本区域和一个按钮.我希望用户在文本区域输入所需的文本.然后,一旦他们点击按钮,它就会注入一个内容脚本,将当前页面上具有 <textarea class="comments">
的字段的文本更改为用户输入的文本在 Chrome 扩展弹出窗口的 中.
I have a text area and a button in my Chrome extension popup. I want users to input desired text in the text area. Then, once they click the button, it will inject a content script to change the text of the fields on the current page that have <textarea class="comments">
to the text that user entered in the <textarea>
in the Chrome extension popup.
我的问题是,如何从 popup.html 中的 获取文本并从 popup.js 到内容脚本?
My question is, how can I get the text from the <textarea>
in my popup.html and pass it from the popup.js to the content script?
这是我目前拥有的:
popup.html:
<!doctype html>
<html>
<head><title>activity</title></head>
<body>
<button id="clickactivity3">N/A</button>
<textarea rows="4" cols="10" id="comments" placeholder="N/A Reason..."></textarea>
<script src="popup.js"></script>
</body>
</html>
popup.js:
function injectTheScript3() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// query the active tab, which will be only one tab
//and inject the script in it
chrome.tabs.executeScript(tabs[0].id, {file: "content_script3.js"});
});
}
document.getElementById('clickactivity3').addEventListener('click', injectTheScript3);
content_script3:
//returns a node list which is good
var objSelectComments = document.querySelectorAll('.comments');
//desired user input how?
function setCommentsValue(objSelectComments,){
//This will be for loop to iterate among all the text fields on the page, and apply
// the text to each instance.
for (var i=0; i<objSelectComments.length; i++) {
objSelectComments[i]= //user's desired text
}
推荐答案
一般有以下三种方法:
- 使用
chrome.storage.local
(MDN) 来传递数据(先设置注入你的脚本). - 在您的脚本之前注入代码,用数据设置变量(有关可能的安全问题,请参阅详细讨论).
- 使用消息传递 (MDN) 以在注入您的脚本之后传递数据.
- Use
chrome.storage.local
(MDN) to pass the data (set prior to injecting your script). - Inject code prior to your script which sets a variable with the data (see detailed discussion for possible security issue).
- Use message passing (MDN) to pass the data after your script is injected.
使用 chrome.storage.local
(在执行脚本之前设置)
使用此方法可维护您正在使用的执行范例,即注入执行功能然后退出的脚本.它也不存在使用动态值构建执行代码的潜在安全问题,这在下面的第二个选项中完成.
Use chrome.storage.local
(set prior to executing your script)
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.
来自您的弹出脚本:
- 使用
chrome.storage.local 存储数据.set()
(MDN) - 在
chrome.storage.local.set()
的回调中,调用tabs.executeScript()
(MDN)
- Store the data using
chrome.storage.local.set()
(MDN) - In the callback for
chrome.storage.local.set()
, calltabs.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)).
- Read the data from
chrome.storage.local.get()
(MDN) - Make the changes to the DOM
- Invalidate the data in
storage.local
(e.g. remove the key: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.
See: Notes 1 & 2.
在执行脚本之前,您可以注入一些代码,在内容脚本上下文中设置一个变量,然后您的主脚本可以使用该变量:
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:
安全问题:
以下使用 "'" + JSON.stringify().replace(/\/g,'\\').replace(/'/g,"\'") + "'"
对数据进行编码在将其放入 code
字符串之前,将其转换为在解释为代码时将是正确 JSON 的文本..replace()
方法需要 A) 在用作代码时将文本正确解释为字符串,并且 B) 引用数据中存在的任何 '
.然后它使用 JSON.parse()
将数据返回到内容脚本中的字符串.虽然这种编码不是严格要求的,但这是一个好主意,因为您不知道要发送到内容脚本的值的内容.这个值很容易会破坏你正在注入的代码(即用户可能在他们输入的文本中使用了 '
和/或 "
).如果你这样做了不能以某种方式转义该值,存在可能导致执行任意代码的安全漏洞.
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 code
string. 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.
来自您的弹出脚本:
- 注入一段简单的代码来设置一个包含数据的变量.
- 在
chrome.tabs.executeScript()
的回调中(MDN),调用tabs.executeScript()
注入您的脚本(注意:tabs.executeScript()
将按照您调用tabs.executeScript()<的顺序执行脚本/code>,只要它们的
runAt
值相同即可.因此,等待小code
的回调不是严格要求的.
- Inject a simple piece of code that sets a variable to contain the data.
- In the callback for
chrome.tabs.executeScript()
(MDN), calltabs.executeScript()
to inject your script (Note:tabs.executeScript()
will execute scripts in the order in which you calltabs.executeScript()
, as long as they have the same value forrunAt
. Thus, waiting for the callback of the smallcode
is not strictly required).
var updateTextTo = document.getElementById('comments').value;
chrome.tabs.executeScript({
code: "var newText = JSON.parse('"
+ JSON.stringify(updateTextTo).replace(/\/g,'\\').replace(/'/g,"\'") + "';"
}, function () {
chrome.tabs.executeScript({
file: "content_script3.js"
});
});
来自您的内容脚本:
- 使用存储在变量中的数据对 DOM 进行更改
if (typeof newText === 'string') {
Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
el.value = newText;
});
}
参见:注释 1、2 和3.
See: Notes 1, 2, & 3.
这需要您的内容脚本代码为弹出窗口或后台脚本(如果与 UI 的交互导致弹出窗口关闭)发送的消息安装侦听器.有点复杂.
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.
来自您的弹出脚本:
- 使用
tabs.query()
<确定活动标签/a> (MDN). - 调用
tabs.executeScript()
(MDN) - 在
tabs.executeScript()
的回调中,使用tabs.sendMessage()
(MDN)(需要知道tabId
),将数据作为消息发送.
- Determine the active tab using
tabs.query()
(MDN). - Call
tabs.executeScript()
(MDN) - In the callback for
tabs.executeScript()
, usetabs.sendMessage()
(MDN) (which requires knowing thetabId
), to send the data as a message.
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) - 退出您的主要代码,让侦听器保持活动状态.如果您愿意,您可以返回一个成功指示器.
- 收到包含数据的消息后:
- Add a listener using
chrome.runtime.onMessage.addListener()
(MDN) - Exit your primary code, leaving the listener active. You could return a success indicator, if you choose.
- Upon receiving a message with the data:
- 对 DOM 进行更改
- 移除你的
runtime.onMessage
监听器
#3.2 是可选的.您可以让代码保持活动状态以等待另一条消息,但这会将您使用的范式更改为加载代码并保持驻留等待消息启动操作的范式.
#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.
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.
See: Notes 1 & 2.
注意 1:使用 Array.from()
如果您没有多次这样做并且使用的是 浏览器版本(Chrome >= 版本 45,Firefox >= 32).在 Chrome 和 Firefox 中,Array.from()
与其他获取方法相比速度较慢一个来自 NodeList 的数组.为了更快、更兼容地转换为数组,您可以使用 这个答案中的 asArray()
代码.该答案中提供的 asArray()
的第二个版本也更加健壮.
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.
注意 2:如果您愿意将您的代码限制为Chrome 版本 >= 51 或 Firefox 版本 >= 50,Chrome 有一个 forEach()
方法用于 NodeLists 从 v51 开始.因此,您不需要转换为数组.显然,如果您使用不同类型的循环,则不需要转换为数组.
Note 2: If you are willing to limit your code to Chrome version >= 51 or Firefox version >= 50, Chrome has a forEach()
method for NodeLists as 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.
注意 3:虽然我之前在自己的代码中使用过这种方法(将变量值注入脚本),但我被提醒在阅读 这个答案.
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.
这篇关于使用 executeScript() 将弹出窗口中的数据通信到弹出窗口注入的内容脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!