使用 executeScript() 将弹出窗口中的数据传递给弹出窗口注入的内容脚本

Communicate data from popup to content script injected by popup with executeScript()

我的 Chrome 扩展弹出窗口中有一个文本区域和一个按钮。我希望用户在文本区域中输入所需的文本。然后,一旦他们单击该按钮,它将注入一个内容脚本,以将当前页面上具有 <textarea class="comments"> 的字段的文本更改为用户在 [=43] 中的 <textarea> 中输入的文本=] 扩展弹出窗口。

我的问题是,如何从 popup.html 中的 <textarea> 获取文本并将其从 popup.js到内容脚本?

这是我目前拥有的:

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 
    }

一般有以下三种方法:

  1. 使用 chrome.storage.local (MDN) 传递数据(在注入脚本之前设置)。
  2. 在您的脚本之前注入代码,用数据设置一个变量(请参阅有关可能的安全问题的详细讨论)。
  3. 使用message passing (MDN) 在 注入脚本后传递数据。

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

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

来自您的弹出脚本:

  1. 使用 chrome.storage.local.set() (MDN)
  2. 存储数据
  3. 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. 进行更改
  4. 使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,"\'") + "'" 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() 到 return 数据到内容脚本中的字符串。虽然这种编码不是严格要求的,但这是一个好主意,因为您不知道要发送到内容脚本的值的内容。这个值很容易成为会破坏你正在注入的代码的东西(即用户可能在他们输入的文本中使用 ' and/or ")。如果您不以某种方式转义该值,则存在安全漏洞,可能导致执行任意代码。

来自您的弹出脚本:

  1. 注入一段设置变量以包含数据的简单代码。
  2. 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('"
          + JSON.stringify(updateTextTo).replace(/\/g,'\\').replace(/'/g,"\'") + "';"
}, function () {
    chrome.tabs.executeScript({
        file: "content_script3.js"
    });
});

来自您的内容脚本:

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

参见:注释 1、2 和 3。

使用message passing (MDN)(在注入内容脚本后发送数据

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

来自您的弹出脚本:

  1. 使用 tabs.query() (MDN).
  2. 确定活动标签
  3. 呼叫tabs.executeScript() (MDN)
  4. 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. 退出您的主要代码,让侦听器保持活动状态。如果您愿意,您可以 return 一个成功指标。
  4. 收到包含数据的消息后:
    1. 对 DOM
    2. 进行更改
    3. 删除您的 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() 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。该答案中提供的第二个版本 asArray() 也更强大。

注 2:如果您愿意 limit your code to Chrome version >= 51 or Firefox version >= 50, Chrome has a forEach() method for NodeLists 从 v51 开始。因此,您不需要转换为数组。显然,如果您使用不同类型的循环,则不需要转换为数组。

注意 3:虽然我以前在自己的代码中使用过这种方法(注入带有变量值的脚本),但在阅读 this answer.[=65 时提醒我应该将其包含在这里=]