Javascript Chrome扩展:在内容脚本中获取页面变量
有没有办法从Google Chrome内容脚本中检索页面的javascript变量?没有 内容脚本在称为隔离世界的特殊环境中执行。它们可以访问所注入页面的DOM,但不能访问页面创建的任何JavaScript变量或函数。对于每个内容脚本,它看起来就像在其运行的页面上没有其他JavaScript执行一样。反过来也是如此:页面上运行的JavaScript不能调用任何函数或访问内容脚本定义的任何变量 孤立世界允许每个内容脚本对其JavaScript环境进行更改,而不必担心与页面或其他内容脚本冲突。例如,内容脚本可以包含jqueryv1,页面可以包含jqueryv2,它们不会相互冲突 隔离世界的另一个重要好处是,它们将页面上的JavaScript与扩展中的JavaScript完全分离。这使我们能够为不应从网页访问的内容脚本提供额外的功能,而不用担心网页访问它Javascript Chrome扩展:在内容脚本中获取页面变量,javascript,google-chrome,google-chrome-extension,Javascript,Google Chrome,Google Chrome Extension,有没有办法从Google Chrome内容脚本中检索页面的javascript变量?没有 内容脚本在称为隔离世界的特殊环境中执行。它们可以访问所注入页面的DOM,但不能访问页面创建的任何JavaScript变量或函数。对于每个内容脚本,它看起来就像在其运行的页面上没有其他JavaScript执行一样。反过来也是如此:页面上运行的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});
}
);