2015/06/26

chrome.runtime.sendMessageでcontent_scriptsとbackground間で通信する方法

photo by gfpeck

Chrome Extensionsをつくっていたところ、same-origin policy(同一生成元ポリシー)に阻まれてしまった。
具体的にいうと、content_scriptsでとあるサイトの情報をIndexedDBに登録して、他のページでその情報を表示するということがしたかった。

いろいろググってみたところ、chrome.runtime.sendMessage, onMessageというものを使えば実現できるかもしれないと気づいた。
※ Chrome Extensions以外ならpostMessageやJSONPがヒントになるかも


前置きが長くなってしまったが、「chrome.runtime.sendMessage」「chrome.runtime.onMessage」を使ってcontent_scripts - background間で通信する方法をまとめる。




manifest.json をつくる


Chrome Extensionsをつくるうえで、最初に必要となるのが「manifest.json」

今回は「Black Everyday Company」を訪れたときに「content_script.js」を実行し、「background.js」と通信するという簡単な拡張機能にする。
backgroundは実行するjsファイルがひとつの場合でも「”scripts”: [“./hoge.js”]」と書かないとうまく動作しなかった。



content_scripts をつくる


ブログ記事のタイトルを取得して、backgroundでデータベースに登録するというような内容。
コールバックは、アラートで表示させるだけ。
var title = document.getElementsByClassName('post-title entry-title')[0].innerText;

chrome.runtime.sendMessage(
    {
        type: "add",
        value: title
    },
    function (response) {
        if (response) {
            alert(response);
        }
    }
    );

chrome.runtime.sendMessage(
    {
        type: "search",
        value: title
    },
    function (response) {
        if (response) {
            alert(response);
        }
    }
    );

今回は、例として2回sendMessageでメッセージを送信している。
別に1回でも良いし、条件に合わせて実行する方を決めても良いし、、、


追記:2015/07/03 11:30
sendMessageでメッセージを送信するとき、コールバックを指定しないと「Error in event handler for runtime.onMessage: Error: Attempting to use a disconnected port object」というエラーが出てしまう。

onMessageはsendMessageで指定されたコールバックを返すためのものだから、コールバックがないとポートを切断してしまう(らしい)

※ 正式な回答ではなく、個人の見解(以下のリンク参照)
▶ sendMessageにcallbackを指定せずにonMessageのリスナーで非同期レスポンスをするとエラーが発生する - Google グループ


background をつくる


content_script.jsからのリクエストを受信するため、onMessageにイベントリスナーを登録する。
chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        switch (request.type) {
            case 'add':
                add(request.value, sendResponse);
                break;
            case 'search':
                search(request.value, sendResponse);
                break;
            default:
                console.log('typeがわかんないよ');
                console.log(request);
                break;
            }
        }
    );

function add(value, callback) {
    // ここで登録処理
    console.log('background:add_' + value);
    callback("add:" + value);
}

function search(value, callback) {
    // ここで検索処理
    console.log('background:search_' + value);
    callback("search:" + value);
}

request.typeには 'add' や 'search' が入ってくるので、それを条件に実行するメソッドを変えている。

           request : sendMessageの第一引数が渡ってくる(今回は {type, value}ってやつ)
            sender : idやurl、tab情報やframeIdが渡ってくる
sendResponse : sendMessageの第二引数のコールバックが渡ってくる



実行してみる


ここまでにつくった「manifest.json」「content_script.js」「background.js」をひとつのフォルダにまとめて、Chromeの拡張機能から「パッケージ化されていない拡張機能を読み込む」で読み込ませる。

そして、「ビューを検証」のバックグラウンドページのリンクをクリックして、開発者ツールを立ちあげておく。


「Black Everyday Company」のサイトに移動すると、以下のようなアラートが2回表示される。
このアラートは、content_script.jsで指定したコールバックによるもの。



バックグラウンドページのコンソールを確認すると、以下のようなログが表示される。
このログは、background.jsで実行された関数によるもの。


こんなかんじで、content_scriptsとbackground間で通信することができる。

本来の目的である「とあるサイトの情報をIndexedDBに登録して、他のページでデータを参照できるかどうか」は、投稿するかわからないが、実装できたら紹介する予定。


参考サイト






以上

written by @bc_rikko

0 件のコメント :

コメントを投稿