※やりたいこと
Webページで範囲選択時にクロームアドオンのページアクションを活性化させる。

※大事なこと
content_scriptsについて。
コンテントスクリプトはマッチしたWebページにscriptファイルを追加読込させる。
そのためそのスクリプト内ではもとのWebページのスクリプトのスコープ(windowとかも)を参照可能。
コンテントスクリプト内でchromeオブジェクトは以下の処理が可能(chrome.runtime.idが取得できるよ。)
・i18n
・storage
・runtime:
connect
getManifest
getURL
id
onConnect
onMessage
sendMessage
ここのruntimeにgetBackgorundが無いのがすべての戦犯。(並列に呼ばれる場合があるから取れないんだろうね)
chrome変数の中身はアドオン毎にフォーカスがあるもよう。※別物になるよう制御されてるよ。
※なのでDevelConsoleのTopからchromeを直接参照すると最後に呼び出されたアドオンのchrome変数が参照されるので注意(1敗した)
※フォーカスをExtensionに変えること!
コンテントスクリプトからアドオン処理を呼び出すにはCHROME開発ドキュメントを参照した。
ケース1.メッセージの送信
コンテントページからバックグラウンドに対して単一的に呼び出す場合はメッセージ送信を利用。
コンテント側メソッド、chrome.runtime.sendMessage
第一引数、アドオンID
第二引数、送信するメッセージ(オブジェクトまたは変数)
第三引数、コールバック(レスポンスを受け取れる)
document.addEventListener("click", function (){
//クリックイベントからセレクションが対応されるまでラグが有るので
//トリガー式にして回避
window.setTimeout(function (){
var str = document.getSelection().toString();
var flag = str.length > 0;
chrome.runtime.sendMessage(chrome.runtime.id,
{pageAction: flag},
function(response) {
//console.log(response);
});
});
});
バックグラウンド側メソッド、chrome.runtime.onMessage.addListener
第一引数、リスナー関数
リスナー関数の第一引数、受信メッセージ(sendMessageの第二引数)
リスナー関数の第二引数、送信元の情報(コンテントページの情報)
リスナー関数の第三引数、返答関数(呼び出すことでsendMessageのresuponseに渡される)
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
//console.log(request, sender, sendResponse);
if(request.pageAction == true)
chrome.pageAction.show(sender.tab.id/*,CallBack*/);
else if(request.pageAction == false)
chrome.pageAction.hide(sender.tab.id/*,CallBack*/);
}
);
※ページアクションを活性化させたいのでsender変数から呼び出し元のTabIDを割り出しページアクションメソッドを実行することで解決。
■補足
メッセージリスナーは複数登録していくとかなり厄介になる。
今回はrequest.pageActionの判定をTrueとFalseにし、undefinedの場合は何もしないようにして回避。
content_scripts側のonMessageのキックの仕方が不明。できないんじゃないか疑惑。
backgroudからcontent_scriptsをキックする場合はケース2のコネクション確立を用いる。
ブラウザアクションの場合はchrome.browserAction.disableを利用。
ケース2.コネクションの確立
コンテントページからバックグラウンドに対して複数回に渡り呼び出したり、
バックグラウンドからコンテントページを呼び出す場合(双方向通信)はコネクションポートを利用。
■2.1 バックグラウンド側からポート確立
//バックグラウンド側からポート作成
chrome.runtime.onConnect.addListener(function(port) {
//受信イベントに処理を追加
port.onMessage.addListener(function(msg) {
//受信したメッセージを表示
console.debug(this, msg);
alert(msg);
});
});
var ports = {};
//ページ表示時、ページアクションを活性化(開発)
chrome.tabs.onUpdated.addListener(function(tabId) {
chrome.pageAction.show(tabId);
var port = chrome.tabs.connect(tabId, {name : "connect1"});
});
//ページアクション押下イベントに処理を追加
chrome.pageAction.onClicked.addListener(function (tab){
var port = chrome.tabs.connect(tab.id, {name : "connect1"});
port.postMessage("Push PageAction");
});
※バックグラウンド側からであれば同名ポートをコネクトすれば同一のポートが開ける(利点)
■2.2 コンテント側からポート確立(できないこともない おすすめしない)
//コンテント側からポート作成
var port1 = chrome.runtime.connect(
chrome.runtime.id
,{name : "test"});
//受信イベントに処理を追加
port1.onMessage.addListener(function(msg) {
//受信したメッセージを表示
console.debug(this, msg);
alert(msg);
});
//メッセージを送信
//port1.postMessage("test");
var ports = {};
//ページ表示時、ページアクションを活性化(開発)
chrome.tabs.onUpdated.addListener(function(tabId) {
chrome.pageAction.show(tabId);
});
//コネクトイベントに処理を追加
chrome.runtime.onConnect.addListener(function(port) {
ports[port.sender.tab.id] = port;
//コネクション破棄イベントに処理を追加
port.onDisconnect.addListener(function (port){
//コネクション破棄時処理
//ページ遷移やタブ削除でも呼ばれる
delete ports[port.sender.tab.id];
console.debug("コネクションが破棄されました")
});
//メッセージ受信イベントに処理を追加
port.onMessage.addListener(function(msg) {
//コネクション受信時処理
console.debug("メッセージを受信しました", msg);
});
});
//ページアクション押下イベントに処理を追加
chrome.pageAction.onClicked.addListener(function (tab){
if(ports[tab.id]){
var port = ports[tab.id];
port.postMessage("Push PageAction")
}
});
ポートの中身のpostMessage関数で送信、onMessageイベントで受信制御で相互通信ができる。
問題はbackground側でポートをどうやって保存しておくか。
※Exsampleには結局 コネクション開始時に返答するしかサンプルがない。戦犯!
とりあえずports変数で制御。(今回はコネクションが1種だが、複数の場合はポート名も制御すること)
どうせバックグラウンドからキックするならバックグラウンド側からポート作成したほうが楽そう。(2.1)
※注意 コンテンツ側からポート確立する場合、同名であっても別ポートが開かれる(かも)
(Runtimeがポートを管理するためだと思う)