Is there any way to retrieve a page's javascript variables from a Google Chrome Content Script?
问题:
回答1:
If you really need to, you can insert a <script>
element into the page's DOM; the code inside your <script>
element will be executed and that code will have access to JavaScript variables at the scope of the window. You can then communicate them back to the content script using data-
attributes and firing custom events.
Sound awkward? Why yes, it is, and intentionally so for all the reasons in the documentation that serg has cited. But if you really, really need to do it, it can be done. See here and here for more info. And good luck!
回答2:
I created a little helper method, have fun :)
to retrieve the window's variables "lannister", "always", "pays", "his", "debts", you execute the following:
var windowVariables = retrieveWindowVariables(["lannister", "always", "pays", "his", "debts"]);
console.log(windowVariables.lannister);
console.log(windowVariables.always);
my code:
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;
}
please note that i used jQuery.. you can easily use the native js "removeAttribute" and "removeChild" instead.
回答3:
Using Liran's solution, I'm adding some fix for Objects
, here's correct solution:
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:
No.
Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.
Isolated worlds allow each content script to make changes to its JavaScript environment without worrying about conflicting with the page or with other content scripts. For example, a content script could include JQuery v1 and the page could include JQuery v2, and they wouldn't conflict with each other.
Another important benefit of isolated worlds is that they completely separate the JavaScript on the page from the JavaScript in extensions. This allows us to offer extra functionality to content scripts that should not be accessible from web pages without worrying about web pages accessing it.
回答5:
I actually worked around it using the localStorge API. Note: to use this, our contentscript should be able to read the localStorage. In the manifest.json file, just add the "storage" string:
"permissions": [...,"storage"]
The hijack function lives in the content script:
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();
}
Now we can call from the contentscript
document.addEventListener("DOMContentLoaded", function(event) {
hijack(callback);
});
or if you use jQuery in your contentscript, like I do:
$(document).ready(function() {
hijack(callback);
});
to extract the content:
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");
}
This can be called multiple times, so if your page changes elements or internal code, you can add event listeners to update your extension with the new data.
Edit: I've added callbacks so you can be sure your data won't be invalid (had this issue myself)
回答6:
If you know which variables you want to access, you can make a quick custom content-script to retrieve their values.
In 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
}
});
});
In retrieveValue.js
:
function returnValues() {
return document.getElementById("element")[name];
//return any variables you need to retrieve
}
returnValues();
You can modify the code to return arrays or other objects.