Javascript Chrome扩展:在内容脚本中获取页面变量

Javascript Chrome扩展:在内容脚本中获取页面变量,javascript,google-chrome,google-chrome-extension,Javascript,Google Chrome,Google Chrome Extension,有没有办法从Google Chrome内容脚本中检索页面的javascript变量?没有 内容脚本在称为隔离世界的特殊环境中执行。它们可以访问所注入页面的DOM,但不能访问页面创建的任何JavaScript变量或函数。对于每个内容脚本,它看起来就像在其运行的页面上没有其他JavaScript执行一样。反过来也是如此:页面上运行的JavaScript不能调用任何函数或访问内容脚本定义的任何变量 孤立世界允许每个内容脚本对其JavaScript环境进行更改,而不必担心与页面或其他内容脚本冲突。例如,

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

没有

内容脚本在称为隔离世界的特殊环境中执行。它们可以访问所注入页面的DOM,但不能访问页面创建的任何JavaScript变量或函数。对于每个内容脚本,它看起来就像在其运行的页面上没有其他JavaScript执行一样。反过来也是如此:页面上运行的JavaScript不能调用任何函数或访问内容脚本定义的任何变量

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

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


如果确实需要,可以在页面的DOM中插入
元素;将执行
元素中的代码,该代码将可以访问窗口范围内的JavaScript变量。然后,您可以使用
数据-
属性和触发自定义事件将它们传回内容脚本


听起来很尴尬?为什么是的,是的,而且出于serg引用的文件中的所有原因,故意如此。但是如果你真的,真的需要去做,它是可以做到的。有关更多信息,请参阅和。祝你好运

我创建了一个小助手方法,祝你玩得开心:)

要检索窗口的变量“lannister”、“always”、“pays”、“his”、“debts”, 您可以执行以下操作:

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;
}
函数检索窗口变量(变量){
var ret={};
var scriptContent=“”;
对于(var i=0;i

请注意,我使用了jQuery。。您可以轻松地使用本机js“removeAttribute”和“removeChild”来代替它。

我实际上使用了localStorge API解决了这个问题。 注意:要使用它,我们的contentscript应该能够读取本地存储。 在manifest.json文件中,只需添加“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");
}
这可以被多次调用,所以如果页面更改了元素或内部代码,您可以添加事件侦听器以使用新数据更新扩展


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

使用Liran的解决方案,我正在为
对象添加一些修复,下面是正确的解决方案:

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;
}
函数检索窗口变量(变量){
var ret={};
var scriptContent=“”;
对于(var i=0;i
如果您知道要访问哪些变量,可以制作一个快速自定义内容脚本来检索它们的值

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
        }
    });
});
function returnValues() {
    return document.getElementById("element")[name];
    //return any variables you need to retrieve
}
returnValues();

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

Chrome的文档为您提供了一个很好的起点:

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

解释
  • 此方法创建脚本标记
  • 它将函数
    propagateVariable
    字符串化,并将当前握手和目标变量名传递到字符串中进行保存,因为函数将无法访问我们的内容脚本范围
  • 然后将该脚本标记注入页面
  • 然后,我们在内容脚本中创建一个监听器,等待从页面听到回音,以传回我们正在寻找的变量
  • 到目前为止,注入的脚本已经进入页面
  • 注入的代码被包装在一个包中,因此它自己运行,将数据推送到侦听器
  • 可选:侦听器确保它进行了正确的握手,瞧,我们可以信任数据源(它实际上并不安全,但在这种情况下它有助于创建一个标识符,这给了我们一定程度的信任)function returnValues() { return document.getElementById("element")[name]; //return any variables you need to retrieve } returnValues();
  • /**
     * inject - Inject some javascript in order to expose JS variables to our content JavaScript
     * @param {string} source - the JS source code to execute
     * Example: inject('(' + myFunction.toString() + ')()');
     */
    function inject(source) {
      const j = document.createElement('script'),
        f = document.getElementsByTagName('script')[0];
      j.textContent = source;
      f.parentNode.insertBefore(j, f);
      f.parentNode.removeChild(j);
    }
    
    function getJSvar(whichVar) {
       document.body.setAttribute('data-'+whichVar,whichVar);
    }
    inject('(' + getJSvar.toString() + ')("somePageVariable")');
    
    var pageVar = document.body.getAttribute('data-somePageVariable');
    
        var objNativeGetter = {
    
            divsToTidyup: [],
            DIVID: 'someUniqueDivId',
            _tidyUp: function () {
                console.log(['going to tidy up ', this.divsToTidyup]);
                var el;
                while(el = this.divsToTidyup.shift()) {
                    console.log('removing element with ID : ' + el.getAttribute('id'));
                    el.parentNode.removeChild(el);
                }
            },
    
            // create a div to hold the serialised version of what we want to get at
            _createTheDiv: function () {
                var div = document.createElement('div');
                div.setAttribute('id', this.DIVID);
                div.innerText = '';
                document.body.appendChild(div);
                this.divsToTidyup.push(div);
            },
    
            _getTheValue: function () {
                return JSON.parse(document.getElementById(this.DIVID).innerText);
            },
    
            // find the page variable from the stringified version of what you would normally use to look in the symbol table
            // eg. pbjs.adUnits would be sent as the string: 'pbjs.adUnits'
            _findTheVar: function (strIdentifier) {
                var script = document.createElement('script');
                script.setAttribute('id', 'scrUnique');
                script.textContent = "\nconsole.log(['going to stringify the data into a div...', JSON.stringify(" + strIdentifier + ")]);\ndocument.getElementById('" + this.DIVID + "').innerText = JSON.stringify(" + strIdentifier + ");\n";
                (document.head||document.documentElement).appendChild(script);
                this.divsToTidyup.push(script);
            },
    
            // this is the only call you need to make eg.:
            // var val = objNativeGetter.find('someObject.someValue');
            // sendResponse({theValueYouWant: val});
            find: function(strIdentifier) {
                this._createTheDiv();
                this._findTheVar(strIdentifier);
                var ret = this._getTheValue();
                this._tidyUp();
                return ret;
            }
        };
    
    chrome.runtime.onMessage.addListener(
        function(request, sender, sendResponse) {
    
            var objNativeGetter = {
            .... the object code, above
            }
    
            // do some validation, then carefully call objNativeGetter.find(...) with a known string (don't use any user generated or dynamic string - keep tight control over this)
            var val = objNativeGetter.find('somePageObj.someMethod()');
            sendResponse({theValueYouWant: val});
        }
    );