IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

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