そろそろそれっぽいものを作り始められるかな…
とか思ってたら年開けちゃったよ…(・ω・`)
Chromeの拡張機能を試してみた.3
現在の機能: アイコン押したら背景赤くなるよ!
そんだけ。
やりたいのは、「開いてるページ中の要素について調べたい」なので、これじゃ一方的に操作しに行っているだけで意味がない。
そこで、 ContentScriptなるものをページに派遣して、そいつにページの調査をさせ、その結果を拡張が受け取る、とすればいいらしい。
1. ページにスクリプトを派遣する
ContentScriptなるまんまなScriptを派遣する。但しこいつはページ自身に入り込めるわけではなく、Chrome拡張の用意する「隔離空間(Isolated World)」にて実行される。これによりDOMにアクセスはできるが、そのページの他のスクリプトに干渉したりはできない。当たり前か。逆にページのスクリプトがこちらの動きを阻害することも無いらしい。
宣言はmanifest.json
"content_scripts": [ // 表示ページ中に埋め込むなんちゃらの宣言
{
"matches": [ // 埋め込むページのマッチ 必須
"http://*/*",
"https://*/*"
],
//"css": ["css/mycontentstyles.css"], // 埋め込むcss 任意
"js": [ //埋め込むjs 任意
"js/jquery-1.8.2.min.js",
"js/mycontentscript.js"
]
}
],
この項目を追加してやる。階層はpermissionsなんかの兄弟になる位置。jsのほかcssも埋め込めるらしい。どうなっちゃうのやら...
今回はおまけでjqueryも持っていく。無いといろいろめんどいし...
mycontentscript.js(終わったら名前変えよう('A`))には、とりあえず
console.log("I'm here!")
ログ出すだけ。
全部終わったらいつも通りリロードしてから新しくタブでも開いてみる。するとコンソールに先ほどのアホなログが書き込まれているはず。
2. ContentScriptと対話する
埋め込んでログ吐くだけじゃ意味ないわけで。派遣したスクリプトから報告を受けないと派遣した意味がない。対話方法としてまずメッセージングをやってみる。
メッセージングは、簡単に言えば
拡張「ちょいこれやって結果持ってきて(;´Д`) 」
埋め込み「おkわかった。これやな⊂(・ω・`)」
拡張「おk受け取った。あとはこっちでやるわ(;´ω`)」
的な通信。何かを起点にメッセージのやり取りを行うシンプルなもの。
まずはブラウザアクションのデフォルトポップアップにpopup.htmlを用意、適当にmyscript.jsなんてスクリプトを埋め込む。
"browser_action": { // ツールバー右上に表示するのに必要な設定. Chrome4からのAPIで、それ以前のものでは使えない。
"default_title": "titil test", // 説明. 右上にアイコンを出す場合はホバーするとみられる
"default_popup": "htmls/popup.html" // 右上に表示されたアイコンを押した際に出るhtmlの指定.
},
…もうやったのがだいぶ前すぎて忘れてたヨヽ('A`)ノ
適当にpopup.htmlにボタンなりなんなり置いたら、myscript.jsにてclickイベントを乗っける。ちなみにボタンに属性として onclick="" なんて乗っけるとエラー吐かれることがある。ワケワカラン( ゚д゚ )
$("#myBtn").click(function(){
chrome.tabs.getSelected(
null,
function(tab) {
chrome.tabs.sendRequest(
tab.id,
{
greeting: "hello"
},
function(response) {
console.log("send from myscript " + response.farewell);
}
);
}
);
});
参考まんま、だがそれがいい。
sendRequestに必要な情報を載せて送り出す。コールバックにレスポンスが返ってきたときの処理を載せる。
3行目のnullはどのタブを操作するかを考えずに、「今開いてるタブに操作する」ためのnull。idを入れれば指定したタブにのみ操作できるが…やるのかな?('A`)
これ一つで、
拡張「ちょいこれやって結果持ってきて(;´Д`) 」
埋め込み「おkわかった。これやな⊂(・ω・`)」
拡張「おk受け取った。あとはこっちでやるわ(;´ω`)」
の赤字の部分が出来上がる。シンプルにリクエストを送り、返ってきたレスポンスで何かをやる、というもの。ちなみにresponseが無いのに response.farewellとか参照しようとするとエラーを吐くっぽい。nullチェックとか必要か?
今度は受け取り側。
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
else
sendResponse({}); // snub them.
}
);
こちらも参考ま(ry
リクエストをもらったら処理を行い、sendResponseでお返事する。
sendResponseは無くてもいけてしまうが、無いと送信側がいつまでも返信を待って
(;´Д`)マダー?
待ちぼうけを食らうことになる。これはメモリ的によろしくないので、空のsendResponseだけでも返してやるのがよろしいらしい。
先ほど出た response.farewell は、この7行目で送り返されたもの。こんな感じでやり取りができる。
なので、これらを仕込んでから実際にボタンを押すと、コンソールには
send from myscript goodbye
…うわ すっごいfrom間違ってるorz
参考
3. 何が送れるの?
そうすると気になるのはいったい何がメッセージとして送れるのか。
試しに2. で作った送信側に testなるキーを設定("greeting"のままなのは内緒。)
chrome.tabs.getSelected(
null,
function(tab) {
chrome.tabs.sendRequest(
tab.id,
{
greeting: "test"
},
function(response) {
console.log("num is " + response.num);
console.log("string is " + response.string);
console.log("array is " + response.array);
console.log("map is " + response.map);
}
);
}
);
中身を置き換えた感じ。戻ってくるレスポンスにはとりあえず、int、string、配列、マップで実験。
返信側もいじくる
} else if (request.greeting == "test"){
var n = 1;
var s = "test";
var a = [12,13,14];
var m = {x: 100, y: "map y"};
console.log("num is " + n);
console.log("string is " + s);
console.log("array is " + a);
console.log("map is " + m);
sendResponse(
{num: n},
{string: s},
{array: a},
{map: m}
);
とりあえずいろいろ載せてみた。この状態だとどうなるか。
まずは返信側のページのコンソール
num is 1
string is test
array is 12,13,14
map is [object Object]
さすがにmapはそのまま出ないがきちんと出荷されてそう。では送信側に返ってきたログは…
num is 1
string is undefined
array is undefined
map is undefined
…あれ?
2. とかでstringは貰ってるし、実は書いてないけどmapとかも実験できてたはz…
まさかと思い返信側を以下に訂正
} else if (request.greeting == "test"){
var n = 1;
var s = "test";
var a = [12,13,14];
var m = {x: 100, y: "map y"};
console.log("num is " + n);
console.log("string is " + s);
console.log("array is " + a);
console.log("map is " + m);
sendResponse(
{string: s},
{num: n},
{array: a},
{map: m}
);
stringを先頭に返すように変更してみる。すると…
num is undefined
string is test
array is undefined
map is undefined
マジですか Σ(・д・)
というわけで先頭の一個しか読んでくれないらしい。エラー吐いてくれよ…('A`)
ただ、mapはいけるので複数のデータ返したいときはmapで返すか、複数回リクエストするのがいいのかね('A`)