代码之家  ›  专栏  ›  技术社区  ›  esqew

内容脚本中的页面变量

  •  28
  • esqew  · 技术社区  · 14 年前

    有没有办法从Google Chrome内容脚本中检索页面的javascript变量?

    7 回复  |  直到 5 年前
        1
  •  63
  •   Community noseratio    7 年前

    如果确实需要,可以插入 <script> 元素;页面中的代码 <脚本& GT; 元素将被执行,代码将可以访问窗口范围内的javascript变量。然后您可以使用 data- 属性和触发自定义事件。

    听起来很尴尬?为什么是的,是的,而且出于SERG引用的文档中的所有原因,有意这样做。但如果你真的,真的需要去做,那就可以做到。见 here here 更多信息。祝你好运!

        2
  •  16
  •   Liran Brimer    10 年前

    我创造了一个小帮手方法,玩得开心:)

    检索窗口的变量 “兰尼斯特”,“总是”,“支付”,“他的”,“债务” , 执行以下操作:

    var windowVariables = retrieveWindowVariables(["lannister", "always", "pays", "his", "debts"]);
    console.log(windowVariables.lannister);
    console.log(windowVariables.always);
    

    我的代码:

    function retrieveWindowVariables(variables) {
        var ret = {};
    
        var scriptContent = "";
        for (var i = 0; i < variables.length; i++) {
            var currVariable = variables[i];
            scriptContent += "if (typeof " + currVariable + " !== 'undefined') $('body').attr('tmp_" + currVariable + "', " + currVariable + ");\n"
        }
    
        var script = document.createElement('script');
        script.id = 'tmpScript';
        script.appendChild(document.createTextNode(scriptContent));
        (document.body || document.head || document.documentElement).appendChild(script);
    
        for (var i = 0; i < variables.length; i++) {
            var currVariable = variables[i];
            ret[currVariable] = $("body").attr("tmp_" + currVariable);
            $("body").removeAttr("tmp_" + currVariable);
        }
    
        $("#tmpScript").remove();
    
        return ret;
    }
    

    请注意,我使用了jquery。您可以轻松使用本机JS “删除属性” “移动儿童” 相反。

        3
  •  12
  •   Dovydas Å opa Venkatesh    8 年前

    使用Liran的解决方案,我正在为 Objects ,以下是正确的解决方案:

    function retrieveWindowVariables(variables) {
        var ret = {};
    
        var scriptContent = "";
        for (var i = 0; i < variables.length; i++) {
            var currVariable = variables[i];
            scriptContent += "if (typeof " + currVariable + " !== 'undefined') $('body').attr('tmp_" + currVariable + "', JSON.stringify(" + currVariable + "));\n"
        }
    
        var script = document.createElement('script');
        script.id = 'tmpScript';
        script.appendChild(document.createTextNode(scriptContent));
        (document.body || document.head || document.documentElement).appendChild(script);
    
        for (var i = 0; i < variables.length; i++) {
            var currVariable = variables[i];
            ret[currVariable] = $.parseJSON($("body").attr("tmp_" + currVariable));
            $("body").removeAttr("tmp_" + currVariable);
        }
    
         $("#tmpScript").remove();
    
        return ret;
    }
    
        4
  •  2
  •   CTS_AE    5 年前

    Chrome的文档为您提供了一个良好的起点: https://developer.chrome.com/extensions/content_scripts#host-page-communication

    此方法允许您将全局页变量提取到内容脚本中。它还使用一个想法,只接受您在握手时识别的传入消息。你也可以用 Math.random() 为了握手,但我玩得很开心。

    解释

    1. 此方法创建脚本标记
    2. 它将函数字符串化 propagateVariable 并将当前握手和目标变量名传递到字符串中进行保存,因为函数将无法访问我们的内容脚本范围。
    3. 然后它将该脚本标记注入页面。
    4. 然后,我们在内容脚本中创建一个监听器,等待从页面中听到返回的消息,以返回我们所追求的变量。
    5. 到目前为止,注入的脚本已经到达了页面。
    6. 注入的代码被包装在 IIFE 所以它自己运行,将数据推送到监听器。
    7. 可选:监听器确保它有正确的握手,voila我们可以信任数据源(它实际上并不安全,但在本例中它有助于创建一个标识符,这给了我们一定程度的信任)。

    第1轮

    V1.0

    const globalToExtract = 'someVariableName';
    const array = new Uint32Array(5);
    const handShake = window.crypto.getRandomValues(array).toString();
    
    function propagateVariable(handShake, variableName) {
      const message = { handShake };
      message[variableName] = window[variableName];
      window.postMessage(message, "*");
    }
    
    (function injectPropagator() {
      const script = `( ${propagateVariable.toString()} )('${handShake}', '${globalToExtract}');`
      const scriptTag = document.createElement('script');
      const scriptBody = document.createTextNode(script);
      
      scriptTag.id = 'chromeExtensionDataPropagator';
      scriptTag.appendChild(scriptBody);
      document.body.append(scriptTag);
    })();
    
    window.addEventListener("message", function({data}) {
      console.log("INCOMINGGGG!", data);
      // We only accept messages from ourselves
      if (data.handShake != handShake) return;
    
      console.log("Content script received: ", data);
    }, false);

    1.1承诺!

    function extractGlobal(variableName) {
    
      const array = new Uint32Array(5);
      const handShake = window.crypto.getRandomValues(array).toString();
    
      function propagateVariable(handShake, variableName) {
        const message = { handShake };
        message[variableName] = window[variableName];
        window.postMessage(message, "*");
      }
    
      (function injectPropagator() {
        const script = `( ${propagateVariable.toString()} )('${handShake}', '${variableName}');`
        const scriptTag = document.createElement('script');
        const scriptBody = document.createTextNode(script);
    
        scriptTag.id = 'chromeExtensionDataPropagator';
        scriptTag.appendChild(scriptBody);
        document.body.append(scriptTag);
      })();
    
      return new Promise(resolve => {
        window.addEventListener("message", function({data}) {
          // We only accept messages from ourselves
          if (data.handShake != handShake) return;
          resolve(data);
        }, false);
      });
    }
    
    extractGlobal('someVariableName').then(data => {
      // Do Work Here
    });

    第二轮-等级和承诺

    V2.0

    如果使用ES模块,我建议将类抛出到它自己的文件中,并将其作为默认值导出。然后它就变成:

    ExtractPageVariable('someGlobalPageVariable').data.then(pageVar => {
      // Do work here 💪
    });
    

    class ExtractPageVariable {
      constructor(variableName) {
        this._variableName = variableName;
        this._handShake = this._generateHandshake();
        this._inject();
        this._data = this._listen();
      }
    
      get data() {
        return this._data;
      }
    
      // Private
    
      _generateHandshake() {
        const array = new Uint32Array(5);
        return window.crypto.getRandomValues(array).toString();
      }
    
      _inject() {
        function propagateVariable(handShake, variableName) {
          const message = { handShake };
          message[variableName] = window[variableName];
          window.postMessage(message, "*");
        }
    
        const script = `( ${propagateVariable.toString()} )('${this._handShake}', '${this._variableName}');`
        const scriptTag = document.createElement('script');
        const scriptBody = document.createTextNode(script);
    
        scriptTag.id = 'chromeExtensionDataPropagator';
        scriptTag.appendChild(scriptBody);
        document.body.append(scriptTag);
      }
    
      _listen() {
        return new Promise(resolve => {
          window.addEventListener("message", ({data}) => {
            // We only accept messages from ourselves
            if (data.handShake != this._handShake) return;
            resolve(data);
          }, false);
        })
      }
    }
    
    const windowData = new ExtractPageVariable('somePageVariable').data;
    windowData.then(console.log);
    windowData.then(data => {
       // Do work here
    });
        5
  •  1
  •   Elia Grady    9 年前

    实际上,我使用本地存储API来解决这个问题。 注意:要使用它,ContentScript应该能够读取本地存储。 在manifest.json文件中,只需添加“存储”字符串:

    "permissions": [...,"storage"]
    

    劫持函数存在于内容脚本中:

    function hijack(callback) {
        "use strict";
        var code = function() {
          //We have access to topframe - no longer a contentscript          
          var ourLocalStorageObject = {
            globalVar: window.globalVar,
            globalVar2: window.globalVar2
          };
          var dataString = JSON.stringify(ourLocalStorageObject);
          localStorage.setItem("ourLocalStorageObject", dataString);
        };
        var script = document.createElement('script');
        script.textContent = '(' + code + ')()';
        (document.head||document.documentElement).appendChild(script);
        script.parentNode.removeChild(script);
        callback();
      }
    

    现在我们可以从ContentScript调用

    document.addEventListener("DOMContentLoaded", function(event) { 
        hijack(callback);
    });
    

    或者,如果在ContentScript中使用jQuery,就像我这样:

    $(document).ready(function() { 
        hijack(callback);
    });
    

    提取内容:

    function callback() {
        var localStorageString = localStorage.getItem("ourLocalStorageObject");
        var ourLocalStorageObject= JSON.parse(localStorageString);
    
        console.log("I can see now on content script", ourLocalStorageObject);
        //(optional cleanup):
        localStorage.removeItem("ourLocalStorageObject");
    }
    

    这可以多次调用,因此,如果页面更改了元素或内部代码,则可以添加事件侦听器以使用新数据更新扩展。

    编辑:我添加了回调,因此您可以确保您的数据不会无效(我自己也有此问题)

        6
  •  1
  •   victor    7 年前

    如果您知道要访问哪些变量,可以制作一个快速自定义内容脚本来检索它们的值。

    popup.js :

    chrome.tabs.executeScript(null, {code: 'var name = "property"'}, function() {
        chrome.tabs.executeScript(null, {file: "retrieveValue.js"}, function(ret) {
            for (var i = 0; i < ret.length; i++) {
                console.log(ret[i]); //prints out each returned element in the array
            }
        });
    });
    

    retrieveValue.js :

    function returnValues() {
        return document.getElementById("element")[name];
        //return any variables you need to retrieve
    }
    returnValues();
    

    您可以修改代码以返回数组或其他对象。

        7
  •  0
  •   serg    14 年前

    不。

    内容脚本在称为独立世界的特殊环境中执行。他们可以访问被注入页面的DOM,但不能访问页面创建的任何javascript变量或函数。它查看每个内容脚本,就好像它正在运行的页面上没有其他的javascript执行一样。反过来也是一样:在页面上运行的javascript不能调用任何函数或访问由内容脚本定义的任何变量。

    独立的世界允许每个内容脚本对其JavaScript环境进行更改,而不必担心与页面或其他内容脚本冲突。例如,内容脚本可以包含jquery v1,页面可以包含jquery v2,它们不会相互冲突。

    独立世界的另一个重要好处是它们完全将页面上的javascript与扩展中的javascript分开。这使我们能够为不应该从网页访问的内容脚本提供额外的功能,而不用担心网页访问它。