こないだ箱根合宿で学んだ。

単にchrome拡張を作ってみたかったというのと、chromeではgreasemonkeyが実行できるけど、Firefoxと違ってGM_xmlhttpRequestが無いのでクロスドメイン通信ができないのでなんとなくやってみたかった。
自分の見ているページの履歴を全部自分のサーバーに送って保存したい。

まずos0xさんのChrome拡張入門のスライドが勉強になる。
とくに

拡張コンテキスト、コンテントコンテキスト、ページコンテキストの3つのコンテキストが存在し、それぞれは完全に分かれているので、お互いが干渉してしまうことはない。さらに、拡張同士も独立したコンテキストで実行される。

拡張コンテキストはタブ操作やクロスドメイン通信などの特権を実行でき、コンテントコンテキストと通信したり、スクリプトを実行したりといったことができます。

コンテントコンテキスト(Content Scripts)は特権を持っていませんが、読み込んだページのDOMを操作することができ、拡張コンテキストと相互に通信できます。

ページコンテキストは通常のウェブページで実行されるコンテキストで、コンテントコンテキストとはDOM経由でしかやり取りできませんし、拡張コンテキストとは完全に分断されています

のあたりが今回は重要。


で、とりあえずお勉強として、自分の見ているページのURLを http://localhost:8888/page にpostするchrome extensionを作った。ページロード時に実行される。
github.com/shokai/post_location_href_crxにソースが置いてある。


■chrome拡張のビルドのしかた
rubyを使う人にはcrxmakeというgemが便利だと思う。
chrome拡張をビルドするには、ソースのディレクトリをchromeにドラッグアンドドロップすると.crx形式に固めてくれるが、
crxmakeだとterminalからビルドできる。
post_location_href_crxの中に適当なRakefileを入れておいたので、rake一発でビルドできる。

gem install crxmake
rake -T
rake
で、packageディレクトリができてその中にビルドされた.crxファイルができる。
macだとそのまま
open package/post_location_href.crx
すればchromeで開かれてインストールできるので楽だ。


■chrome拡張に必要なファイル
github.com/shokai/post_location_href_crx/tree/master/src/の中にある物が、クロスドメイン通信するのに最低限必要らしい。
src
|-- background.html
|-- icon.png
|-- icon128.png
|-- icon48.png
|-- main.css
|-- main.js
|-- manifest.json
`-- popup.html


iconを設定しておいたらchromeのツールバーに表示されるようになった
f364e597cb582884a2ba294163072ca5


それぞれのファイル名はmanifest.jsonの中で決めている。
{
"name":"post location.href",
"description":"POST location href to http://localhost:8888/page",
"version": "0.0.2",
"background_page": "background.html",
"permissions": ["tabs" ,"http://*/*", "https://*/*"],
"browser_action": {
"default_icon": "icon.png",
"default_title": "post location.href",
"popup": "popup.html"
},
"icons": {
"128": "icon128.png",
"48": "icon48.png"
},
"content_scripts": [
{
"js": [
"main.js"
],
"css": [
"main.css"
],
"matches": [
"http://*/*",
"https://*/*",
"ftp://*/*"
],
"run_at": "document_start",
"all_frames":true
}]
}


■クロスドメイン通信のしかた
このmanifest.jsonの内容だと、main.jsがコンテントスクリプトとして最初に実行される。
main.js内の最初の方に、バックグラウンドページのコンテキストと通信するコードを書いておけば、新しいページをロードした時にコンテントスクリプト→バックグラウンドページと通信できる。バックグラウンドページではクロスドメインXMLHTTPRequestが使えるので、そこで http://localhost:8888/page に location.href を送れば自分の見た全てのサイトのURLを保存できる。

main.js
var connection = chrome.extension.connect();

connection.onMessage.addListener(function(info, con) {
console.log(info, con);
});

connection.postMessage({url:location.href});

background.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>test app</title>
<script>
var api_endpoint = "http://localhost:8888";

chrome.self.onConnect.addListener(function(port, name) {
port.onMessage.addListener(function(info, con) {
xhr = new XMLHttpRequest();
xhr.open("POST", api_endpoint+"/page", true);
xhr.setRequestHeader("Content-Type" , "application/x-www-form-urlencoded");
xhr.send("url="+encodeURIComponent(info.url));
port.postMessage({url:info.url});

});
});
</script>
</html>



受信は、sinatraとかだと適当にこうして受け取れる
require 'rubygems'
require 'sinatra'
require 'json'

post '/page' do
url = params['url']
unless url
status 403
@mes = {
:error => '"url" required'
}.to_json
else
# 適当に保存するコード
@mes = {
:url => url
}.to_json
end
end