contentscriptにおけるiframeの扱い
Chromeのcontentscriptを書いていると、なぜかiframeを操作できないことに気付いた
http://d.hatena.ne.jp/KYudy/20100716/1279266322
セキュリティーに配慮した仕様なんだろうか?
ざっと探したところそれらしい記述も見つからないし、代替手段も思いつかない・・・
どうしたらいいだろうか?
全てのフレームにContentScriptを適用
ところでmanifestに
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "file://*/*", "ftp://*/*" ],
"all_frames":true,
"js": ["foreground.js"
],
"run_at": "document_start"
}
],
これはかなり大きな意味を持っている。なぜならもしどこかのframeにフォーカスが移っていて
かつ上の記述がない場合はextensionが全く働かなくなるからである
しかしこれによって一つ問題が生じる
Webページ上に存在するフレームの数だけ対応するContentScriptが実行されるということは
一つのWebページとしてそれらが協力して一つの仕事をするように調整しなくちゃいけないという問題である
そこでbackgroundページが登場するわけだ
一つのWebページに存在するContentScriptをグループとして管理するために
Contentscriptがロードされた際にbackgroundページにメッセージを投げるようにして、そしてそのContentscriptが実行されているFrameが属しているWindow
を識別子とするグループを作成し、以後同じWindowに所属するフレームで実行されているContentScriptからのメッセージをそのグループに登録していく、
そしてグループ全体に必要な操作はそのグループのメンバー全員にメッセージを投げることで行われるようにした
まずContentscript内の記述
//contentscript function onLoadListener(e){ if(window.parent==window){ alert("I'm parentWindow"); chrome.extension.sendRequest("join",function nop(){}); }else{ alert("I'm childWindow"); chrome.extension.sendRequest("join",function nop(){}); } //...... }
chrome.extension.sendRequest()を使ってJoinメッセージを投げている。
function nopは何もしないCallBack関数だ
一番上のフレームはParentとしているが、今のところChildと同じ処理をしている
//background chrome.extension.onRequest.addListener( function(request, sender,sendResponse) { callfunc(request,sender); }); function callfunc(funcName,sender){ var func=eval(funcName); if(func){//valid? func(sender); }else{ console.log("callfunc() invalid fuction name"); } }
Backgourndページではchrome.extension.onRequest.addListener()がメッセージを受け取った時に呼び出されるイベントを登録する
そしてcallfuncをつかってメッセージの内容である"join"からjoin(sender)を呼び出す
//background function join(sender){ currentTab=chrome.tabs.getSelected(null,function(tab){ if(!groups[""+tab.id]){ while(wait){;} wait=1; alert("alloc"); groups[""+tab.id]=Array(); wait=0; } groups[""+tab.id].push(sender); //window.alert("JOIN to group"+tab.id+" num of memeber "+groups[""+tab.id].length); }); }
joinではグループの作成およびグループにsenderを登録するという作業をしている(chrome.tabs.getSelected)
まずグループの識別子は現在選択されているタブ(すなわちメッセージを投げたContentscriptが属するタブ)のIdである。
これによって同じタブに所属するContentscriptを識別する
まず一番初めにグループにJoinする場合はgourpsという連想配列にまだグループを表す配列を作成していないので
groups["タブのID"]に配列を作成する。そしてsenderをその配列にPUSHする。2回目以降は確保済みの配列に対して
senderをPUSHする
一つだけ注意点があるが、それはwaitという変数である
getSelected関数は逐次的に実行されないイベントなので場合によっては2つのContentscriptが同時に配列を確保する
可能性がある(実際に配列の確保が重複するのを確認した)
そこで排他制御(完全に排他性を保証できないと思われるが・・・)をするためにwaitをつかっている
もしかしたらgetSelectedの内部でやらないほうがいいのかもしれない(すくなくともそれ以外の場所は逐次的に実行されると思われる)