夜になるとエアコン無しに過ごせる涼しい季節になって来ました。皆さん如何お過ごしでしょうか。
最近Google Chromeを使っているのですが、AutoPagerizeのGoogle Chrome拡張を入れてみて感動し、被はてなブックマーク数を画面下に表示する拡張が欲しくなったので、つい勢いで作ってしまいました。
はいはい。病気病気
スクリーンショットはこんな感じ
os0xさんの記事をふんだんに参考にしながらなんとか作り上げました(os0x++)。
以下その作成手順。
まず、manifest.jsonを用意しました。
manifest.json
{
"name": "hatena bookmark counter",
"description": "hatena bookmark counter for google chrome. hatena bookmark is social bookmark web service. see 'http://b.hatena.ne.jp/'. this extension show image of numbers which how many users bookmarked the URL.",
"version": "0.0.1",
"icons": { "normal": "hbcount.gif" },
"permissions": [ "tabs" ],
"toolstrips": [ "hbcount.html" ],
"content_scripts": [
{
"js": [ "hbcount.js" ],
"matches": [ "http://*/*" ]
}
]
}
今回の拡張は、ページがロードされた瞬間に発動するjavascriptと、Google Chrome起動中に画面下で常駐するためのtoolstripsと呼ばれるHTMLコンテンツを使いました。まずユーザスクリプト側。
hbcount.js
if (window == top) {
var port = chrome.extension.connect();
port.onMessage.addListener(function(data) {
location.href = data.url;
});
port.postMessage({url: location.href});
}
ユーザスクリプト側からはtoolstripのコントロールに対してアクセス出来ませんのでpostMessageでメッセージを送信し被はてなブックマーク数アイコンを更新します。なおtoolstripにあるアイコンをクリックすると、はてなブックマークエントリページを開く様になっているのですが、toolstrip側から現在開いているタブのコンテンツにはアクセスする事も出来たのですが、少し危険を感じたのでpostMessageを使いtoolstrip側からユーザスクリプト側にURLを返信してlocation.hrefでの画面遷移をさせています。真ん中のイベント待ちはその為の物です。なお、topかどうかを確認しているのは現在のURL以外でもこの拡張が走ってしまうのを防止している小細工です。例えばiframeなんかでアフィが表示されているとそれに対してもこの拡張が実行されてしまいます。
次に本体であるtoolstripのソースは以下の通り。
hbcount.html
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<script type="text/javascript">
window.onload = function() {
var hbcount = document.getElementById("hbcount");
chrome.extension.onConnect.addListener(function(port) {
function update(url) {
hbcount.src = 'hbcount.gif';
hbcount.onclick = null;
if (url.match(/^chrome:\/\//)) return;
url = url.replace(/#/g, '%23');
hbcount.src = 'http://b.hatena.ne.jp/entry/image/' + url;
hbcount.onclick = function() { port.postMessage({'url': 'http://b.hatena.ne.jp/entry/' + url}); }
}
port.onMessage.addListener(function(data) { update(data.url); });
chrome.tabs.onSelectionChanged.addListener(function(id, props) {
chrome.tabs.get(id, function(tab) { update(tab.url); });
});
});
};
</script>
<img src="hbcount.gif" id="hbcount" style="cursor: pointer;" title="はてなブックマーク"/>
ユーザスクリプトからメッセージを受け取りupdate関数にて更新します。アイコンクリック時のメッセージ返信を行う処理もここにあります。その下にある処理ではタブが切り替わったタイミングで現在の被ブックマーク数を更新する処理になります。この場合、いくらmanifest.jsonでhttp://のみと宣言していてもタブに対するイベント登録を行うのでchrome://といったURLも対象になってしまいます。update関数でURLを避けているのはその為です。
さて、ここから話がそれ始めます。
Google Chromeの拡張はchromeブラウザ自身でパッキングする事が出来ます。ただchromeへのパスをいちいち書いてられないのでMakefileを書きました。
Makefile
ifndef CHROME
ifneq ($(windir),)
# Windows
DEST = "$(shell pwd)\chrome-hbcount"
CHROME = "$(USERPROFILE)/Local Settings/Application Data/Google/Chrome/Application/chrome.exe"
else
# Other Platform: Linux? Mac?
DEST = $(shell pwd)/chrome-hbcount
CHROME = $(shell which crxmake)
ifeq ($(CHROME),)
CHROME = $(shell which google-chrome)
endif
ifeq ($(CHROME),)
CHROME = $(shell which chromium-browser)
endif
ifeq ($(CHROME),)
CHROME = chrome
endif
endif
endif
SRCS = hbcount.gif hbcount.html hbcount.js manifest.json
all : chrome-hbcount.crx
first : $(SRCS)
@-rm -r $(DEST)
@mkdir $(DEST)
@cp $(SRCS) $(DEST)/.
$(CHROME) --pack-extension=$(DEST)
chrome-hbcount.crx : $(SRCS)
@-rm -r $(DEST)
@mkdir $(DEST)
@cp $(SRCS) $(DEST)/.
$(CHROME) --pack-extension=$(DEST) --pack-extension-key=chrome-hbcount.pem
clean:
@-rm *.crx
@-rm -r $(DEST)
初回だけmake first、以降はmakeでビルドです。WindowsでもLinuxでもMacでも使える様に、かつgoogle-chromeが入っていない場合にはchromium-browserを使う様になっています。ちなみにcrxmakeというのはConstellationさんが書いたchrome拡張のパッケージングツールです(Constellation++)。chromeを起動する事なくパッケージング出来ます。なおchrome-hbcountという部分を書き換えれば、他の拡張でも使えるかもしれません。
さらに拡張を書いていると拡張のバージョンがちょくちょくあがります。
Google Chrome(chromium-browser)の拡張にはupdate_urlという設定項目があり、決まった形のXMLファイルを用意しておけば自動アップデート出来る様になる予定があるそうなのですが、拡張を書き換える度にいちいちupdate.xmlを書き換えてアップロードするも面倒くさそうだったので、手順を自動化出来る様にしてみました。簡単には
- manifest.jsonからバージョンを抜き出す
- 抜き出したバージョンでupdate.xmlを更新する
- githubのダウンロードページにあるファイルを一旦削除する
- 拡張モジュール(crx)とupdate.xmlをアップロードする
dist-upload.pl
#!/usr/bin/perl
use strict;
use warnings;
use JSON;
use Perl6::Slurp;
use WWW::Mechanize;
use Net::GitHub::Upload;
use Config::Pit;
my $config = pit_get('github-upload', require => {
'login' => 'your login id on github.com',
'password' => 'your password on github.com',
});
my $manifest = from_json(slurp 'manifest.json');
my $id = $manifest->{id};
my $version = $manifest->{version};
open my $fh, '>update.xml';
print $fh <<EOF;
<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
<app appid="$id">
<updatecheck codebase="http://cloud.github.com/downloads/mattn/chrome-hbcount/chrome-hbcount.crx" version="$version" />
</app>
</gupdate>
EOF
close $fh;
my $mech = WWW::Mechanize->new;
$mech->get('https://github.com/login');
$mech->submit_form(
form_number => 2,
fields => {
login => $config->{login},
password => $config->{password},
});
$mech->get('http://github.com/mattn/chrome-hbcount/downloads');
for my $form (@{$mech->forms}) {
if ($form->action =~ /^http:\/\/github.com\/mattn\/chrome-hbcount\/downloads\//) {
print "deleting ".$form->action."\n";
$mech->request($form->click);
}
}
chomp(my $user = `git config github.user`);
chomp(my $token = `git config github.token`);
my $gh = Net::GitHub::Upload->new(
login => $user,
token => $token,
);
print "uploading chrome-hbcount.crx\n";
$gh->upload( repos => 'mattn/chrome-hbcount', file => 'chrome-hbcount.crx' );
print "uploading update.xml\n";
$gh->upload( repos => 'mattn/chrome-hbcount', file => 'update.xml' );
こちらもmattnとかchrome-hbcountという部分を書き換えれば、他の拡張で使えるかもしれません。ちなみにNet::GitHub::Uploadはtypesterさんのモジュールです(typester++)。これで、あとは
- 拡張を書き換える
- manifest.jsonのバージョンを上げる
- make
- dist-upload.pl
さて、はてなブックマークカウンタ拡張に話を戻して...
コードおよび拡張ファイルはgithubに置いてあります。Downloadのページからでもダウンロード出来ますが、以下のリンクでインストール出来る様になっています。
hatena bookmark counter for google chromeリポジトリは以下のリンク先にあります。
mattn's chrome-hbcount at master - GitHuライセンスはBSDにしましたので、どうぞお好きに使って下さい。流用してdelicious拡張なんてのもいいですね。
hatena bookmark counter for google chrome
http://github.com/mattn/chrome-hbcount/tree/master