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 の設定が遅いかは分からなかった