Google Chrome Content ScriptからページのJavaScript変数を取得する方法はありますか?
私は少しヘルパーメソッドを作成しました、楽しんでください:)
ウィンドウの変数"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;
}
jQueryを使用したことに注意してください。代わりに、ネイティブjs "removeAttribute"および"removeChild"を簡単に使用できます。
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;
}
Chromeのドキュメントが出発点として役立ちます: https://developer.chrome.com/extensions/content_scripts#Host-page-communication
このメソッドを使用すると、コンテンツスクリプトにグローバルページ変数を抽出できます。また、ハンドシェイクで認識された着信メッセージのみを受け入れるというアイデアも使用します。ハンドシェイクにMath.random()
を使用することもできますが、楽しんでいました。
propagateVariable
を文字列化し、現在のhandShakeとターゲット変数名を保持するために文字列に渡します。関数はコンテンツスクリプトスコープにアクセスできないためです。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);
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
});
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
});
アクセスする変数がわかっている場合は、カスタムコンテンツスクリプトをすばやく作成して、それらの値を取得できます。
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();
コードを変更して、配列またはその他のオブジェクトを返すことができます。
私は実際にlocalStorge APIを使用してそれを回避しました。注:これを使用するには、コンテンツスクリプトがlocalStorageを読み取ることができる必要があります。 manifest.jsonファイルで、「ストレージ」文字列を追加するだけです。
"permissions": [...,"storage"]
Hijack関数は、コンテンツスクリプトに含まれています。
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);
});
または、私と同じようにコンテンツスクリプトで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");
}
これは複数回呼び出すことができるため、ページで要素や内部コードが変更された場合は、イベントリスナーを追加して、拡張機能を新しいデータで更新できます。
編集:データが無効にならないことを確認できるようにコールバックを追加しました(この問題は自分で発生しました)
番号。
コンテンツスクリプトは、孤立した世界と呼ばれる特別な環境で実行されます。挿入されたページのDOMにアクセスできますが、ページによって作成されたJavaScript変数や関数にはアクセスできません。実行されているページで他のJavaScriptが実行されていないかのように、各コンテンツスクリプトを調べます。同じことが逆にも言えます。ページ上で実行されているJavaScriptは、関数を呼び出したり、コンテンツスクリプトで定義された変数にアクセスしたりすることはできません。
分離された世界では、ページや他のコンテンツスクリプトとの競合を心配することなく、各コンテンツスクリプトがJavaScript環境を変更できます。たとえば、コンテンツスクリプトにJQuery v1を含め、ページにJQuery v2を含めることができます。これらは互いに競合しません。
孤立した世界のもう1つの重要な利点は、ページ上のJavaScriptを拡張機能のJavaScriptから完全に分離することです。これにより、Webページにアクセスすることを心配せずに、Webページからアクセスできないコンテンツスクリプトに追加の機能を提供できます。
他の回答で部分的に説明したように、ページのJS変数は、Chrome拡張コンテンツスクリプトから分離されています。通常、それらにアクセスする方法はありません。
ただし、ページにJavaScriptタグを挿入すると、そこで定義されている変数にアクセスできます。
ユーティリティ関数を使用して、ページにスクリプトを挿入します。
/**
* 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');
変数が複合データ型(オブジェクト、配列...)の場合、値をJSON文字列としてgetJSvar()に保存し、JSON.parseでコンテンツスクリプトに戻す必要があることに注意してください。