Mozilla 台湾の audio 要素のデモが超カッコいい
百聞は一見にしかず
Firefox 3.5 (現状 Beta 4)で見てみてください
http://moztw.org/demo/audioplayer/
Firefox 拡張を jQuery で書く! Jetpack を使ってみた。
はじめに
JavaScript が書ければ、誰でも簡単に Firefox の拡張が書けてしまう。しかも、もれなく jQuery が付いて来る!
というものを Mozilla Labs がリリースしたみたいですね。
https://jetpack.mozillalabs.com/
というわけで
少し触ってみました
Jetpack Feature の書き方
Jetpack で書く Firefox 拡張を「Jetpack Feature」といいます。
これは、以下の 2 つのものを用意すれば誰でも簡単に公開することが出来ます。
- JavaScript ファイル
- 公開用 HTML ファイル
JavaScript ファイル
JavaScript ファイルには、 Jetpack Feature のアプリケーションコードを書きます。
(function() { Jetpack.statusBar.append({ onReady: function(doc) { for (var i = 0; i < 10; i++) Jetpack.notifications.show('Hello, world!'); // Hello, world を 10 回表示 } }); })();
詳細は、公式ページの Tutorial や Reference を見たほうが早いです。
公開用 HTML ファイル
以下のような link 要素を含んだ HTML ファイルを書きます。
<!DOCTYPE html> <html> <head> <link rel="jetpack" href="hello.js" /> <title>Hello, Jetpack!</title> </head> <body> <h1>Hello, Jetpack!</h1> </body> </html>
こうすることで、この hello.js で書かれた JavaScript を Jetpack Feature として公開することができます。
具体的には、 Jetpack をインストールした Firefox でこの HTML を見ると「この Jetpack Feature をインストールしますか?」的なメッセージが表示されます。
更新チェッカーを書いてみた
Hello, world だけじゃあまりにもあまりなので、
(function() { Jetpack.statusBar.append({ onReady: function(doc) { // データの永続化は Jetpack.sessionStorage で if (!Jetpack.sessionStorage.feedUrl) { // グローバルに prompt がなかったのでコンテンツのを使う Jetpack.sessionStorage.feedUrl = Jetpack.tabs.focused.contentWindow.prompt('input feed url'); } var url = Jetpack.sessionStorage.feedUrl; // setInterval はある setInterval(function() { // jQuery の get 関数を使う $.get(url, function(content) { // MD5 を取る var md5 = CybozuLabs.MD5.calc(content); // 更新をチェック if (Jetpack.sessionStorage.feedMd5 != md5) { // 更新をお知らせ Jetpack.notifications.show('Contents updated! ' + Jetpack.sessionStorage.feedMd5 + ' to ' + md5); // MD5 を取る Jetpack.sessionStorage.feedMd5 = md5; } }) }, 10000); } }); })(); /* 以下に CybozuLabs.MD5 ライブラリのコードをはり付ける */
「プライバシー情報の消去」をする人は、 Firefox 3.1 で「Pathtraq の定番ランキング」をライブブックマークしておくと便利
Firefox の「プライバシー情報の消去機能」
何かと、恥ずかしいページを見てしまったときなどに便利な機能ですね。
ちなみにWindows の場合 Ctrl+Shift+Del、 Mac の場合 Command+Shift+Del で出来ます。知ってました?
この機能便利なのですが、、
ロケーションバーの補完に頼ってる人にはちょっと辛い面もあります><
補完機能が使い物にならなくなってしまうのです。
たとえば、普段は以下のように補完が効くので、 b → tab → enter で目的のページにたどり着けます。
しかし、プライバシー情報の消去を行った後だと、以下のように、何も候補が出なくなってしまうのです。
この問題が Firefox 3.1 から少し改善されます
Firefox 3.1 では、「プライバシー情報の消去」(Firefox 3.1 では「最近の履歴を消去」)したときに、ライブブックマークしているフィードが配信している URL が補完候補に追加されるようになったのです。
なので、
よくいくサイトを配信しているフィードを、ライブブックマークしておくと、便利です!
よくいくサイトを配信しているフィードといえば
Pathtraq の定番ランキングですね!
マウント アンマウント | パソコン豆知識
ライブブックマークしておくと
Firefox メモ
alert の位置を知る
- dom/src/base/nsGlobalWindow.cpp 4035 行目
- nsGlobalWindow::Alert
JavaScript を書く
alert(0); var img = document.createElement('img'); img.onload = function() { alert(1) }; alert(2); img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; alert(3); document.body.appendChild(img); alert(4);
Firefox で開く
0 -> 2 -> 3 -> 1 -> 4
gdb 起動(Firefox のビルドが必要)
$ gdb MinefieldDebug.app/Contents/MacOS/firefox-bin (gdb) b nsGlobalWindow::Alert (gdb) r
alert(1) のところだけ見る
var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; document.body.appendChild(img);
バックトレース
#0 nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x18cc38d0) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 : : #16 0x1314fcf1 in nsImageLoadingContent::Event::Run (this=0x17f93040) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:833 : :
nsImageLoadingContent::Event::Run のコードを見てみる
(gdb) b nsImageLoadingContent::Event::Run (gdb) c Breakpoint 2, nsImageLoadingContent::Event::Run (this=0x17e55150) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:823 823 if (mMessage.EqualsLiteral("load")) { (gdb) l 818 NS_IMETHODIMP 819 nsImageLoadingContent::Event::Run() 820 { 821 PRUint32 eventMsg; 822 823 if (mMessage.EqualsLiteral("load")) { 824 eventMsg = NS_LOAD; 825 } else { 826 eventMsg = NS_LOAD_ERROR; 827 } (gdb) l 828 829 nsCOMPtr<nsIContent> ourContent = do_QueryInterface(mContent); 830 831 nsEvent event(PR_TRUE, eventMsg); 832 event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; 833 nsEventDispatcher::Dispatch(ourContent, mPresContext, &event); 834 835 return NS_OK; 836 } 837 (gdb)
うーん。送信してるところが見たい
(gdb) reverse-search "load" 823 if (mMessage.EqualsLiteral("load")) { (gdb) reverse-search "load" 249 FireEvent(NS_LITERAL_STRING("load"));
あった!
249 行目で止めてみる
(gdb) b nsImageLoadingContent.cpp:249 (gdb) c Breakpoint 4, nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 249 FireEvent(NS_LITERAL_STRING("load")); (gdb)
バックトレース
Breakpoint 4, nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 249 FireEvent(NS_LITERAL_STRING("load")); (gdb) bt #0 nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 : : #10 0x13227d41 in nsHTMLImageElement::SetSrc (this=0x1c6dd380, aValue=@0xbfff99a4) at /Users/amachang/mozilla/content/html/content/src/nsHTMLImageElement.cpp:219 #11 0x11223f98 in nsIDOMHTMLImageElement_SetSrc (cx=0xb67000, obj=0x1cc65340, id=441507292, vp=0xbfffa5f8) at dom_quickstubs.cpp:6846 : : #64 0x000026e3 in main (argc=1, argv=0xbffff8e8) at /Users/amachang/mozilla/browser/app/nsBrowserApp.cpp:156
ながいなー
よーく見ると
nsHTMLImageElement::SetSrc から load イベントが発火されていることが分かる
でも
alert(0); var img = document.createElement('img'); img.onload = function() { alert(1) }; alert(2); img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; // ここで発火されるのに alert(3); // ここが alert(1) より先に起こるのは何故? document.body.appendChild(img); alert(4);
alert(3) で止めてバックトレースを見る
Breakpoint 1, nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x1e75d860) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 4035 FORWARD_TO_OUTER(Alert, (aString), NS_ERROR_NOT_INITIALIZED); (gdb) p aString $7 = (const nsAString_internal &) @0x1e75d860: { mData = 0x1e66a898, mLength = 1, mFlags = 5 } (gdb) p aString->mData $8 = (PRUnichar *) 0x1e66a898 (gdb) p (char*)aString->mData $9 = 0x1e66a898 "3" (gdb) bt #0 nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x1e75d860) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 #1 0x0049e1c1 in NS_InvokeByIndex_P (that=0x1c97f420, methodIndex=65, paramCount=1, params=0xbfff84e4) at /Users/amachang/mozilla/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_unixish_x86.cpp:179 : :
うーん。
別スレッドなのかな?
全スレッドバックトレース
(gdb) thread apply all bt
でも、 alert(1) も alert(2) も main から繋がってたから。たぶん同じスレッドだと思うんだけどなー
あそっか
イベントは発火されてもすぐには実行されなくて、しかるべきタイミングでイベントのキューを見に行くのか。
そのタイミングってどこよ
もっかい nsImageLoadingContent::Event::Run で止めてみる
Event::Run のバックとレースを見てみると alert(3) の nsGlobalWindow::Alert から呼ばれていることが分かった
#0 nsImageLoadingContent::Event::Run (this=0x1d67fe50) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:823 #1 0x004833c0 in nsThread::ProcessNextEvent (this=0x719030, mayWait=1, result=0xbfffadfc) at /Users/amachang/mozilla/xpcom/threads/nsThread.cpp:510 #2 0x0040d198 in NS_ProcessNextEvent_P (thread=0x719030, mayWait=1) at nsThreadUtils.cpp:227 #3 0x1298c2da in nsXULWindow::ShowModal (this=0x16991bc0) at /Users/amachang/mozilla/xpfe/appshell/src/nsXULWindow.cpp:396 : : #10 0x133b6904 in nsGlobalWindow::Alert (this=0x1c53aa10, aString=@0x1d69f080) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4064
でも、なんで XULWindow::showModal が NS_ProcessNextEvent_P を呼び出すかが分からない
NS_ProcessNextEvent_P (マクロ名 NS_ProcessNextEvent)を呼び出してるところを grep してみるとたくさんあることに気がつく。
これらのタイミングでイベントは処理されるのか。
- alert されたとき
- window.open したとき
- HTML のパースが終わったとき
などなど
つまり、
var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; // この時点で画像のロードが終わっていて alert(3); // この alert に誘発されて onload イベントハンドラが起動した document.body.appendChild(img);
ってことか
じゃあ、 Firefox の画像って非同期にロードされてる訳じゃないのかな?
試しに、以下のような CGI を作ってみる
#! /usr/bin/perl print "Content-Type: image/gif\n\n"; # 10 秒停止 sleep(10); open IMG, "hogehogehogehoge.gif"; binmode IMG; binmode STDIO; while (my $bin = <IMG>) { print STDOUT $bin; } close IMG;
で、以下のような JavaScript からこの image.cgi を起動
var start = new Date; var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'image.cgi'; document.body.appendChild(img); alert(4);
お。ちゃんと非同期になってるなー
とりあえず
今日はこの辺で、結局なんで Firefox の img.src の設定が遅いかは分からなかった
Firefox の nightly で LDR が動かない
原因
Firefox nightly がまだバグってて
alert(window.Function == Function); // false; alert(window.Function == function(){}.constructor); // false; alert(Function == function(){}.constructor); // true;
てな感じになる
対処
ページの先頭で
window.Function = Function;
とやれば動く