2018年3月28日水曜日

[ChromeExtension]コンテキスト間メッセージ通信

この文書作成時のChromeバージョンは以下です。

  • 63.0.3239.132 (64bit)

関連記事

成果物

やりたいこと

Twitterタイムライン上の画像で右クリックコンテキストメニューしたときに以下を取得したい。

  • 画像投稿者のユーザー名
  • ツイートID
  • 複数画像の何枚目か(twitterは1ツイートに4枚まで画像を添付できる)

右クリックしたコンテキストのソースURLぐらいしか手掛かりがないので、表示中のTwitterのDOMを解析して解決する。
もちろん、TwitterのDOM構成が変わると動かなくなる。

Content Scripts を使ってtwitterにスクリプトを埋め込む

Content Scriptsは特定のページに埋め込まれ、ページのDOMにアクセス可能だ。今回はtwitter.comの中ならどこでも埋め込むようにする。jQueryも使うので埋め込んでおく。

manifest.json
{
  ...
  "content_scripts":[
     {
          "matches" : [
              "*://twitter.com/*"
          ],
          "js":[
              "scripts/getImageInfo.js",
              "scripts/jquery-3.3.1.min.js"
          ]
      }
  ],
  ...
}

content_scriptsが配列であることからわかるように、複数のページにスクリプトを埋め込むことができる。

コンテキスト間メッセージ通信

Browser Action (Page Action)によるポップアップ、Event Page、Content Scriptが埋め込まれたページ、これらは別コンテキストである。
他のコンテキストの処理を呼び出す場合には、Chrome経由でメッセージ通信を行う必要がある。

今回はコンテキストメニュークリック時にEvent PageからContent Scriptにメッセージを送信する。
また、Content ScriptでDOM解析した結果をEventPage側で同期的に処理する。
image.png (18.4 kB)

送信側

Content Script を呼び出す場合はchrome.tabs.sendMessage()を使う。
それ以外を呼び出す場合はchome.runtime.sendMessage()を使う。
注意点は以下

  • Content Scriptにメッセージを送信する場合とそれ以外に送信する場合で、APIが異なる
  • 一度も起動していないBrowser Action (Page Action)ポップアップは存在しないので、メッセージを送信できない
background.js
...
function downloadImage(srcUrl, response){
    ...
}

function messageToGetTweetInfo(info, tab){
    console.log(info);
    chrome.tabs.sendMessage(
        tab.id,
        {name: 'twitterImageDL', srcUrl: info.srcUrl},
        function(response) {downloadImage(info.srcUrl, response);}
    );
}

chrome.contextMenus.onClicked.addListener(messageToGetTweetInfo);

...

コンテキストメニューを開いたページにメッセージを送る場合は、contextMenus.onClicked.addListener()で登録した関数の
第2引数(tab)のidプロパティをtabs.sendMessage()の第1引数に入れるとよい。
tabs.sendMessageの第2引数には、受信側で行う処理を一意に特定できる識別子を入れるとよい。
同期メッセージの結果はコールバックの引数として取得できる。

ポップアップ起動時など、tabのidが不明な場合は、chrome.tabs.queryを用いてタブを取得する。

popup.js

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, {name:'message_name'}, function(response){
        ...
    });
});

受信側

chrome.runtime.onMessageイベントリスナーを登録する。
結果の返送は第3引数のハンドラを利用する。

getImageInfo.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
    console.log(request);
    if(request.name === 'twitterImageDL'){
        ...
        sendResponse({
            result : true,
            username : username,
            tweetId : tweetId,
            imageIndex : imageIndex
        });
    }
    return true;    //  sync
});

注意点は以下

  • trueを返却すると同期実行、trueを返却しないと非同期実行
  • 同じExtension内のメッセージイベントのみが発火する(他のアプリからのメッセージを受ける場合はonMessageExternal

DOMを読む

jQueryで泥臭く読む。twimg.comのURLをsrcに持つimgタグを検索するところからスタート。特に説明は割愛。

これでユーザー名、ツイートID、画像が何枚目かを取得できた。
chrome.downloads.downloadの引数にファイル名を指定してダウンロードできるようにすれば、一旦は完成!

0 コメント:

コメントを投稿