将弹出式数据与内容脚本之间的数据通信,使用executeScript() [英] Communicate data from popup to content script injected by popup with executeScript()

查看:105
本文介绍了将弹出式数据与内容脚本之间的数据通信,使用executeScript()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我的问题是,如何从我的弹出框中的< textarea> 获取文本.html 并将它从 popup.js 传递给内容脚本?



这就是我目前所拥有的: p>

popup.html

 <!doctype html> 
< html>
< head>< title>活动< / title>< / head>
< body>
< button id =clickactivity3> N / A< / button>
< textarea rows =4cols =10id =commentsplaceholder =N / A Reason ...>< / textarea>
< script src =popup.js>< / script>
< / body>
< / html>

popup.js

 函数injectTheScript3(){
chrome.tabs.query({active:true,currentWindow:true},function(tabs) {
//查询活动标签,它将只有一个标签
//并在其中注入脚本
chrome.tabs.executeScript(tabs [0] .id,{file: content_script3.js});
});
}

document.getElementById('clickactivity3')。addEventListener('click',injectTheScript3);

content_script3

//返回一个很好的节点列表
var objSelectComments = document.querySelectorAll('。comments');

//想要的用户输入如何?
函数setCommentsValue(objSelectComments,){

//这将用于循环遍历页面上的所有文本字段,并将
//文本应用于每个实例。
for(var i = 0; i< objSelectComments.length; i ++){
objSelectComments [i] = //用户想要的文本
}

解决方案

有三种一般方法可以做到这一点: ol>

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

  • 使用消息传递(< a href =https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Communicating_with_background_scripts =nofollow nore



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



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



    从您的弹出脚本:


    1. 使用 chrome.storage.local.set() MDN

    2. chrome.storage.local.set()的回调中,调用 tabs.executeScript() MDN



      var updateTex tTo = 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. 使 storage.local 中的数据无效(例如,删除键: chrome.storage.local.remove() MDN ))。



      chrome.storage.local.get('updateTextTo',functi (items){
    assignTextToTextareas(items.updateTextTo);
    chrome.storage.local.remove('updateTextTo');
    });
    函数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,然后将其放入代码字符串中。 A)需要使用 .replace()方法作为代码时,将文本正确解释为字符串,并且B)引用任何'存在于数据中。然后它使用 JSON .parse() 将数据返回到内容脚本中的字符串。尽管这种编码不是严格要求的,但是您不知道要发送到内容脚本的值的内容是一个好主意。这个值很容易就会破坏你注入的代码(例如,用户可能使用'和/或在他们输入的文本中)如果你没有,在某种程度上,逃避价值,有一个安全漏洞可能导致任意代码被执行。



    从弹出脚本中:


    1. 注入一段简单的代码,设置一个变量来包含数据。

    2. chrome.tabs.executeScript() tabs.executeScript( =nofollow noreferrer> MDN ),调用 tabs.executeScript() ()将按照您调用 tabs.executeScript()的顺序执行脚本,只要它们的值 RUNAT 。因此,不需要等待小代码的回调)。



    < pre class =lang-js prettyprint-override> var updateTextTo = document.getElementById('comments')。value;
    chrome.tabs.executeScript({
    code:var newText = JSON.parse('
    + JSON.stringify(updateTextTo).replace(/ \\ / g,'替换(/'/ g,\\')+';
    },function(){
    chrome.tabs.executeScript ({
    file:content_script3.js
    });
    });





    1. 使用存储的数据对DOM进行更改在变量中



      if(typeof newText ==='字符串'){
    Array.from(document.querySelectorAll('textarea.comments'))。forEach(el => {
    el.value = newText;
    });

    请参阅:注释1,2和&3。 b

    使用消息传递 MDN )(在注入内容脚本后发送数据



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


    $ b 从您的弹出脚本中:


    1. 使用 tabs.query确定活动选项卡() MDN )。

    2. 调用 tabs.executeScript() MDN

    3. tabs.executeScript() code>,使用 tabs.sendMessage() code> 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. 使用 <$ c添加侦听器$ c> chrome.runtime.onMessage.addListener() MDN
    2. 退出主代码,使监听器处于活动状态。您可以返回成功指标,如果您选择。

    3. 收到包含以下数据的消息:


      1. 更改为DOM

      2. 删除 runtime.onMessage 侦听器


    #3.2是可选的。您可以保持代码处于活动状态,等待另一条消息,但这会改变您正在使用的范例,以便在代码中加载代码,并保持驻留等待消息发起操作。

      chrome.runtime.onMessage.addListener(assignTextToTextareas); 
    函数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); //可选
    }

    请参阅:备注1& 2。




    注1:使用 Array.from() 很好,如果你不是多次使用并使用浏览器版本(Chrome> =版本45,Firefox> = 32)。在Chrome和Firefox中, Array.from()是与其他从NodeList获取数组的方法相比,速度较慢。为了更快速,更兼容地转换为数组,您可以在 asArray()代码3773011>这个答案。该答案中提供的第二个版本 asArray()也更加健壮。

    注意2:如果你是愿意将您的代码限制为Chrome版本> = 51或Firefox版本> = 50 ,对于 forEach()方法Web / API / NodeListrel =nofollow noreferrer> NodeLists 截至v51。因此,你不需要转换成数组。显然,如果使用不同类型的循环,则不需要转换为数组。注意3:虽然我以前使用过这种方法(注入脚本与变量值)在我自己的代码中,提醒我应该在这里阅读这个答案时将它包括在内。


    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.

    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?

    This is what I have currently:

    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 
        }
    

    解决方案

    There are three general ways to do this:

    1. Use chrome.storage.local (MDN) to pass the data (set prior to injecting your script).
    2. Inject code prior to your script which sets a variable with the data (see detailed discussion for possible security issue).
    3. Use message passing (MDN) to pass the data after your script is injected.

    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.

    From your popup script:

    1. Store the data using chrome.storage.local.set() (MDN)
    2. In the callback for chrome.storage.local.set(), call tabs.executeScript() (MDN)

    var updateTextTo = document.getElementById('comments').value;
    chrome.storage.local.set({
        updateTextTo: updateTextTo
    }, function () {
        chrome.tabs.executeScript({
            file: "content_script3.js"
        });
    });
    

    From your content script:

    1. Read the data from chrome.storage.local.get() (MDN)
    2. Make the changes to the DOM
    3. 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;
            });
        }
    }
    

    See: Notes 1 & 2.

    Inject code prior to your script to set a variable

    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:

    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.

    From your popup script:

    1. Inject a simple piece of code that sets a variable to contain the data.
    2. In the callback for chrome.tabs.executeScript() (MDN), call tabs.executeScript() to inject your script (Note: tabs.executeScript() will execute scripts in the order in which you call tabs.executeScript(), as long as they have the same value for runAt. Thus, waiting for the callback of the small code 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"
        });
    });
    

    From your content script:

    1. Make the changes to the DOM using the data stored in the variable

    if (typeof newText === 'string') {
        Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
            el.value = newText;
        });
    }
    

    See: Notes 1, 2, & 3.

    Use message passing (MDN) (send data after content script is injected)

    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.

    From your popup script:

    1. Determine the active tab using tabs.query() (MDN).
    2. Call tabs.executeScript() (MDN)
    3. In the callback for tabs.executeScript(), use tabs.sendMessage() (MDN) (which requires knowing the tabId), 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
            });
        });
    });
    

    From your content script:

    1. Add a listener using chrome.runtime.onMessage.addListener() (MDN)
    2. Exit your primary code, leaving the listener active. You could return a success indicator, if you choose.
    3. Upon receiving a message with the data:

      1. Make the changes to the DOM
      2. Remove your runtime.onMessage listener

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

    See: Notes 1 & 2.


    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.

    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.

    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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆