k4200’s notes and thoughts

Programmer side of k4200

Chromeのブラウザ拡張(Extension)を作る

最近仕事関連でChrome Extensionを作ったので、ポイントとかを簡単にまとめてみる。

前提

環境とか
  • 古いChromeには対応しない(ChromeのバージョンによってAPIが変わってたりする)
  • manifestのバージョンはv2
作るもの

Extensionにはいくつかのタイプがあるが、今回は以下の様なものを作る。

  1. ブラウザに表示されている内容を取得
  2. 1で取得した内容をExtensionのJavaScriptがパース
  3. Extensionのポップアップを表示し、2の内容を表示
  4. ポップアップ上のボタンを押すと、内容がサーバーに送信される

イメージとしては、はてブとかEvernoteのブラウザ拡張を思い浮かべてもらえれば良いかと。

後述する専門用語で言うと、browser action (あるいは page action) + content script という構成。

アーキテクチャ

主な構成

まずは仕組みを理解しとくと後々楽だと思うので、簡単に説明。詳しくは、この辺をじっくり読めば大体わかるはず。

大雑把な構成要素は以下の通り。

  • background page or background script: バックグラウンドでの処理とかを行う
  • UI page: ポップアップとか
  • content script: ブラウザに表示されているWebページの内容を扱うスクリプト

content scriptはちょっと特殊(?)なので少し説明。例えば、はてブのトップページにアクセスした時に、タイトルとはてブ数だけを一覧表示するブラウザ拡張を考える。Chromehttp://b.hatena.ne.jp/ のページを表示して、ブラウザ拡張からそのページのDOMにアクセスする必要があるが、それを行えるのはcontent scriptだけで、UI pageとbackground pageからはアクセス出来ない。

content scriptとその他のコンポーネントはメッセージをやりとりすることでデータの受け渡しを行う。

UI要素

色んなのがあるので、簡単に説明。

  • Page Action: 特定のページ・サイトを開いた時だけボタンが表示されるタイプのもの。楽天サイトでのみ使えるブラウザ拡張とかはこれを使う事が多い。
  • Browser Action: 基本的にどんなページでも、常にボタンが表示されるタイプ。Evernoteのブラウザ拡張みたいなのを使いたい場合はこれ。
  • Context Menu: 右クリックした時に、独自のメニュー項目を追加する。

UIは色んなパターンがあるので、詳細はdeveloper guideを参照のこと。

実際の作り方

設計

上の方で書いたけど、上に書いたような専門用語を使って、もう一度簡単にまとめる。

  • 特定のサイト・ページ (http://example.com/) を開いた時に、Page Action(のボタン)を有効にする。
  • ページの読み込みが終わったら、Content Scriptを実行し、http://example.com/ のページの内容を読み込んでContent ScriptのJavaScriptで処理する。
  • Content Script から Background Script にデータをメッセージとして送る
  • Page Actionのボタンが押されたら、Popupを表示し、Background Script が保持しているデータを表示
  • Popup内のボタンが押されたら、サーバーへデータを送信する
manifest.json
{
  "manifest_version": 2,
  "name": "Example extension",
  "description": "",
  "version": "1.0",

  "permissions": [
  ],

  "background": {
    "scripts": ["jquery-2.0.3.min.js", "background.js"]
  },

  "page_action": {
    "default_icon": "icon-s.png",
    "default_popup": "popup.html"
  },

  "content_scripts": [
    {
      "matches": ["http://example.com/*"],
      "js": ["jquery-2.0.3.min.js", "contentscript.js"],
      "run_at": "document_end"
    }
  ]
}
contentscript.js

ページの内容を取得して、background.jsにメッセージとしてデータを送る。

console.log('contentscript.js started');

var items = $.map($('li.some-item'),
                  function(elem, i) {
                      return elem.text;
                  });

chrome.runtime.sendMessage({"items": items,},
                           function(response) {
                                console.log('message sent');
                           });
background.js

メッセージを受け取って、データを保持するだけ。

function checkForValidUrl(tabId, changeInfo, tab) {
  if (tab.url.match(/http:\/\/example.com\/.*/)) {
    // ... show the page action.
    chrome.pageAction.show(tabId);
  }
};

// URLをチェック
chrome.tabs.onUpdated.addListener(checkForValidUrl);


var parsedItems = []; // ここにデータを保持
function parseItem(item) {
    // ....
    parsedItems.push({'foo': bar, 'hobe': fuga});
}

// メッセージを受け取った時に呼び出されるメソッドを登録
chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
	parsedItems = [];
	for (var i in items) {
	    parseItem(request.items[i]);
	}
	var res = 'finish';
	sendResponse(res);
    });
popup.js

ここは、background.js からデータを受け取る以外は通常通りのプログラムでOK。

// background.js のデータを参照
var parsedItems = chrome.extension.getBackgroundPage().parsedItems;

$(document).ready(function() {
   //...
});

まとめ

HTML + JavaScriptに慣れていれば、Chrome Extensionは結構簡単に作れる。仕組みを理解することが重要。

そのうちFirefoxについても書くが、Firefoxの方が色々面倒だった