使用executeScript()将弹出窗口中弹出的数据与popup注入的内容脚本进行通信

时间:2016-11-17 02:03:34

标签: javascript html google-chrome-extension firefox-webextensions

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

我的问题是,如何从 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 传递到内容脚本?

这就是我目前的情况:

popup.html

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);

popup.js

//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 
    }

content_script3

{{1}}

1 个答案:

答案 0 :(得分:12)

通常有三种方法可以做到这一点:

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

    使用此方法维护您正在使用的执行范例,即注入执行函数的脚本然后退出。它也没有使用动态值来构建执行代码的潜在安全问题,这在下面的第二个选项中完成。

    从您的弹出式脚本:

    1. 使用chrome.storage.local.set()MDN
    2. 存储数据
    3. chrome.storage.local.set()的回调中,请致电tabs.executeScript()MDN
    4. 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. 中读取数据
      3. 对DOM进行更改
      4. 使storage.local中的数据无效(例如,移除密钥:chrome.storage.local.remove()MDN))。
      5. 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;
                });
            }
        }
        

        参见:Notes 1&amp; 2。

        在脚本之前注入代码以设置变量

        在执行脚本之前,您可以注入一些代码,在内容脚本上下文中设置一个变量,然后主脚本可以使用该代码:

        安全问题:
        以下使用"'" + JSON.stringify().replace(/\\/g,'\\\\').replace(/'/g,"\\'") + "'"将数据编码为文本,在将其作为代码解释为适当的JSON之前,将其放入code字符串中。 A {需要.replace()方法需要将文本正确解释为用作代码时的字符串,B)引用数据中存在的任何'。然后使用JSON.parse()将数据返回到内容脚本中的字符串。虽然此编码并非严格要求,但最好不要知道要发送到内容脚本的值的内容。此值很容易会破坏您正在注入的代码(即用户可能在他们输入的文本中使用'和/或"。如果你不以某种方式逃避该值,则会出现一个安全漏洞,可能导致执行任意代码。

        从您的弹出式脚本:

        1. 注入一段设置变量以包含数据的简单代码。
        2. chrome.tabs.executeScript()MDN)的回调中,请调用tabs.executeScript()注入您的脚本(注意:tabs.executeScript()将按您调用{tabs.executeScript()的顺序执行脚本1}},只要它们具有runAt的相同值。因此,等待小code的回调并非严格要求。)
        3. 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和&amp; 3。

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

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

            从您的弹出式脚本:

            1. 使用tabs.query()MDN)确定有效标签。
            2. 致电tabs.executeScript()MDN
            3. tabs.executeScript()的回调中,使用tabs.sendMessage()MDN)(需要知道tabId),将数据作为消息发送。
            4. 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. 退出主代码,让侦听器处于活动状态。如果您愿意,可以返回成功指标。
              4. 收到包含数据的消息后:
                1. 对DOM进行更改
                2. 删除您的runtime.onMessage听众
              5. #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
                }
                

                参见:Notes 1&amp; 2。

                注意1:如果您没有多次使用Array.from()并使用browser version which has it(Chrome&gt; =版本45,Firefox&gt; = 32),则可以使用Array.from() is slow compared to other methods of getting an array from a NodeList。在Chrome和Firefox中,this answer。要更快,更兼容地转换为数组,您可以使用limit your code to Chrome version >= 51 or Firefox version >= 50中的asArray()代码。该答案中提供的asArray()的第二个版本也更加健壮。

                注意2:如果您愿意NodeLists,则自v51起,Chrome对this answer采用forEach()方法。因此,您不需要转换为数组。显然,如果使用不同类型的循环,则不需要转换为数组。

                注3:虽然我以前在我自己的代码中使用过这种方法(在变量值中注入一个脚本),但我还是提醒我在阅读{{3}}时应该把它包含在这里。