Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Text 
GeckoのDOMイベント処理の実装
中野雅之 
•肩書き 
•正式: Mozilla Japan 国際化担当マネージャ 
•非公式: Mozilla Japan 大阪支部長 
•大阪の自宅で、自宅警備しながら仕事してます
中野雅之 
•色んなアカウント 
•メールアドレス: masayuki@d-toybox.com 
•Skype: masayuki-nakano 
•Twitter: @d_toybox 
•Blog: 「もずはっく日記」で検索
アジェンダ 
•DOMイベントの基礎知識 
•Geckoのイベント処理の実装 
•パフォーマンスを悪くしないためのヒント
DOM イベントの基礎 
•フェイズと、プロパゲーション 
•イベントを捕まえる 
•イベントの伝播を抑制する 
•ブラウザのイベント処理を妨害する
capturing フェイズ 
•最初は window 
•次に document 
•root から target の親 
•target の決定は、イ ベント依存 
•フォーカス 
•発生位置 
•……等々
target フェイズ 
•target フェイズは、 capturing フェイズの 一部でもあり、 bubbling フェイズの 一部でもある。 
•window や document が target の場合も
bubbling フェイズ 
•target の親から root 
•次に document 
•最後に window 
•Event.bubbles が false の場合、 bubbling は発生しな い
イベントを捕まえる 
•書式: 
•EventTarget.addEventListener( イベント名, ハンドラ, Capture?); 
•EventTarget.removeEventListener( イベント名, ハンドラ, Ca...
イベントを捕まえる 
•単純な例1: 
•全ての要素で発生するkeydownイベントを知りたい 
•イベントターゲットより先に知りたい 
•removeする必要はない 
document.addEventListener("keydown", ...
イベントを捕まえる 
•単純な例2: 
•全ての要素で発生するkeydownイベントを知りたい 
•イベントターゲットより後に知りたい 
•removeする必要がある 
function keydownHandler(aEvent) { /* ...
イベントの伝播を抑制する 
•複数のイベントリスナを登録している場合、あるイベント ハンドラで処理済みのイベントを他のイベントハンドラで は無視したい場合が考えられる 
•Event.stopPropagation() か、 Event.sto...
ブラウザのイベント処理を妨害する 
•ブラウザにデフォルトの動作(default action)を実行して もらいたくない場合が考えられる 
•Event.preventDefault()を呼び出すと、 Event.defaultPrevent...
Geckoのイベント処理の実装 
•OS ネイティブなイベントを DOM イベントとして Web アプリに通知するケースを解説 
•図にすると、次のように……
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
EventListenerManager 
•イベントターゲットで、最初にイベントリスナが登録さ れた時に、インスタンスが生成され、そのノードに保存 される 
•そのノードに登録された全てのイベントリスナを管理 する 
•HandleEvent...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
モジュール間の処理の流れの例 
1.widget/ は、各 OS のネイティブイベントをハンドリ ングするモジュール。ネイティブイベントが発生した ら、内部イベントである、Widget*Event クラスのイ ンスタンスをスタックに作成し、その...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
モジュール間の処理の流れの例 
3.EventStateManager::PreHandleEvent() が前処 理を行う 
•詳しくは後ほど
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
モジュール間の処理の流れの例 
4.PresShell が EventDispatcher::Dispatch() を呼び 出す 
5.EventDispatcher::CreateEvent() で Widget*Event のクラスに対応し...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
モジュール間の処理の流れ 
7.EventTargetChainItem::HandleEventTargetChain() で、 受け取った、各 EventListenerManager の HandleEvent() を実行していく 
8....
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
モジュール間の処理の流れ 
10.EventStateManager::PostHandleEvent() で、デフォルト アクションを実行したり、関連するイベントを生成したりする
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
Widget*Event、Internal*Event 
•WidgetEvent クラスが全てのイベントクラスの基底ク ラス 
•WidgetMouseEvent や、InternalFocusEvent といっ た派生クラスが、イベントごと...
Widget*Event、Internal*Event 
•widget/ 
•widget/BasicEvents.h 
•widget/ContentEvents.h 
•widget/MiscEvents.h 
•widget/Mouse...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
PresShell 
•PresShell は、 Presentation Shell の略で、 nsPresShell.cpp で実装され、 nsIPresShell.h で定 義されている nsIPresShell インターフェースを持つ ...
EventStateManager::PreHandleEvent() 
•PresShell がイベントを発火する直前に呼び出す 
•マウスの操作によって変化する hover や、active とい った要素の状態設定を行う 
•マウスのクリ...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
EventDispatcher::CreateEvent() 
•Widget*Event クラスのインスタンスや、 Internal*Event クラスのインスタンスから、DOM イベ ントの実装クラスを選択し、インスタンスを生成する 
•J...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
dom::Event 
•DOM イベントの実装クラスは、名前空間 “mozilla::dom“ に、標準仕様と同じ名前で定義 
•dom::KeyboardEvent、dom::WheelEvent 等 
•nsIDOMEvent インターフ...
dom::Event 
•古くから実装されている DOM イベントクラスは、固有 の、nsIDOM*Event インターフェースも継承している 
•新規作成時にはほとんど定義されない 
•データにアクセスするだけなら、内部イベントクラス にアク...
dom::Event 
•内部イベントクラスを定義・実装する必要が無い、シン プルなイベントを実装する際は、webidlで定義したイ ベントから、実装コードを自動生成することも可能 
•自動生成の定義は dom/webidl/moz.build...
dom::Event 
•実装例 
•dom/events/ 
•dom/events/Event.cpp 
•dom/events/UIEvent.cpp 
•dom/events/WheelEvent.cpp 
•content/svg/c...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
EventDispatcher::Dispatch() 
•PresShell から呼び出される 
•最初に、 target の PreHandleEvent() を実行する 
•window から target ノードまで辿り、作成されている...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
EventTargetChainItem::HandleEventTargetChain() 
•EventDispatcher::Dispatch() に呼び出される 
•列挙されている、window から target の親ノードまで の ...
EventTargetChainItem::HandleEventTargetChain() 
•次に、target の EventListenerManager の HandleEvent() を呼び出す (target フェーズ) 
•Ev...
EventTargetChainItem::HandleEventTargetChain() 
•次に、列挙された、target の親から、 window までの EventListenerManager の、HandleEvent() を実行...
EventTargetChainItem::HandleEventTargetChain() 
•次に、イベントターゲットが生成した、CSS box を実装 している、nsIFrame::HandleEvent() を呼び出す 
• PresS...
EventTargetChainItem::HandleEventTargetChain() 
•最後に、System Event Group のイベントリスナが登録 されている場合、capturing、target、bubbling フェーズ...
Geckoのイベント処理の実装 
nsDocument 
PresShell 
widget 
EventDispatcher 
EventStateManager 
Widget*Event 
dom::*Event 
PreHandleEv...
EventStateManager::PostHandleEvent() 
•PresShell が最後に呼び出す 
•イベントの後処理や、default action を実行 
•マウスホイールによるスクロール処理やズーム処理 
•mouse...
パフォーマンスを悪くしないためのヒント 
•イベントリスナを必要以上に登録しない 
•登録するノードが増える程、EventListenerManager のインスタンスの数が増える 
•登録するハンドラが増える程、全てのイベントの発生時 に、マ...
パフォーマンスを悪くしないためのヒント 
•発火回数が多いイベントや、発火のコストが高いイベント はリスナを登録しないようにする 
•EventListenerManager と、nsDocument は、一部の イベントリスナが登録されたかど...
パフォーマンスを悪くしないためのヒント 
•mMayHaveCapturingListeners 
•全てのイベントリスナが、bubbling フェーズのイベント のリスナであれば、false 
•これが false なら、capturing ...
パフォーマンスを悪くしないためのヒント 
•mMayHavePaintEventListener 
•“MozAfterPaint” 
•描画する度に発生するので、発生回数が莫大
パフォーマンスを悪くしないためのヒント 
•mMayHaveMutationListeners 
•“DOMAttrModified” 
•“DOMCharacterDataModified” 
•“DOMNodeInserted” 
•“DO...
パフォーマンスを悪くしないためのヒント 
•mMayHavePointerEnterLeaveEventListener 
•“pointerdown” 
•“pointermove” 
•“pointerup” 
•“pointercance...
パフォーマンスを悪くしないためのヒント 
•mMayHaveTouchEventListener 
•“touchstart” 
•“touchend” 
•“touchmove” 
•“touchcancel” 
•タッチデバイスを直接サポー...
パフォーマンスを悪くしないためのヒント 
•mMayHaveMouseEnterLeaveEventListener 
•“mouseenter” 
•“mouseleave” 
•“mouseover” や “mouseout” 発生時に、祖...
パフォーマンスを悪くしないためのヒント 
•他にも細かい、イベント単位での最適化があるので、 EventListenerManager::AddEventListenerInternal()のコー ドを参照
パフォーマンスを悪くしないためのヒント 
•極端な話をすると、 
•全てのイベントリスナを window に登録 
•bubbling フェーズで登録 
•Event.target を自前で確認 
•Event.stopImmediatePro...
パフォーマンスを悪くしないためのヒント 
最適化はほどほどに!
パフォーマンスを悪くしないためのヒント 
読みやすいコードで、 
バグの少ないアプリを!
知っておいた方が良い用語・略語 
•D3E 
•DOM Level 3 Events 
•ESM 
•EventStateManager 
•dispatch、fire 
•(イベントを)発火する 
•consumed 
•preventDef...
その他の資料 
•DOM Level 3 Events 仕様書 (Working Draft) 
•Event developer guide (MDN) 
•Adding a new event (MDN) 
•dom/events (mxr)
GeckoのDOMイベント処理の実装
Upcoming SlideShare
Loading in …5
×

GeckoのDOMイベント処理の実装

6,675 views

Published on

DOMイベントと、その発火のGeckoにおける実装の解説。

Published in: Technology

GeckoのDOMイベント処理の実装

  1. 1. Text GeckoのDOMイベント処理の実装
  2. 2. 中野雅之 •肩書き •正式: Mozilla Japan 国際化担当マネージャ •非公式: Mozilla Japan 大阪支部長 •大阪の自宅で、自宅警備しながら仕事してます
  3. 3. 中野雅之 •色んなアカウント •メールアドレス: masayuki@d-toybox.com •Skype: masayuki-nakano •Twitter: @d_toybox •Blog: 「もずはっく日記」で検索
  4. 4. アジェンダ •DOMイベントの基礎知識 •Geckoのイベント処理の実装 •パフォーマンスを悪くしないためのヒント
  5. 5. DOM イベントの基礎 •フェイズと、プロパゲーション •イベントを捕まえる •イベントの伝播を抑制する •ブラウザのイベント処理を妨害する
  6. 6. capturing フェイズ •最初は window •次に document •root から target の親 •target の決定は、イ ベント依存 •フォーカス •発生位置 •……等々
  7. 7. target フェイズ •target フェイズは、 capturing フェイズの 一部でもあり、 bubbling フェイズの 一部でもある。 •window や document が target の場合も
  8. 8. bubbling フェイズ •target の親から root •次に document •最後に window •Event.bubbles が false の場合、 bubbling は発生しな い
  9. 9. イベントを捕まえる •書式: •EventTarget.addEventListener( イベント名, ハンドラ, Capture?); •EventTarget.removeEventListener( イベント名, ハンドラ, Capture?); •イベント名: イベントの名前 "click" とか、 "keydown" •ハンドラ: function foo(aEvent) { /* something */ } がお手軽で十分 •Capture?: true なら、capturing フェイズと、target フェイズ、false なら、target フェイズと、bubbling フェイズ
  10. 10. イベントを捕まえる •単純な例1: •全ての要素で発生するkeydownイベントを知りたい •イベントターゲットより先に知りたい •removeする必要はない document.addEventListener("keydown", function (aEvent) { /* something */ }, true);
  11. 11. イベントを捕まえる •単純な例2: •全ての要素で発生するkeydownイベントを知りたい •イベントターゲットより後に知りたい •removeする必要がある function keydownHandler(aEvent) { /* something */ } document.addEventListener("keydown", keydownHandler, false); … document.removeEventListener("keydown", keydownHandler, false);
  12. 12. イベントの伝播を抑制する •複数のイベントリスナを登録している場合、あるイベント ハンドラで処理済みのイベントを他のイベントハンドラで は無視したい場合が考えられる •Event.stopPropagation() か、 Event.stopImmediatePropagation() を呼ぶと、それ以 降のイベント発生を中止できる •Event.stopPropagation() では、次の EventTarget 以 降でのイベントハンドラの実行を中止できる •Event.stopImmediatePropagation() では、同じ EventTarget に登録されている未処理のイベントハンド ラも含めて、イベントハンドラの実行を中止できる
  13. 13. ブラウザのイベント処理を妨害する •ブラウザにデフォルトの動作(default action)を実行して もらいたくない場合が考えられる •Event.preventDefault()を呼び出すと、 Event.defaultPrevented属性値がtrueになる •defaultPrevented属性がtrueなら、ブラウザは default action を実行しない(ただし、タブの切替等、 セキュリティ上、無視することはある) •Web アプリは Event.defaultPrevented の値を、処理 前に確認することで、Event.stopPropagation()の代 用としても使える
  14. 14. Geckoのイベント処理の実装 •OS ネイティブなイベントを DOM イベントとして Web アプリに通知するケースを解説 •図にすると、次のように……
  15. 15. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  16. 16. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  17. 17. EventListenerManager •イベントターゲットで、最初にイベントリスナが登録さ れた時に、インスタンスが生成され、そのノードに保存 される •そのノードに登録された全てのイベントリスナを管理 する •HandleEvent() が呼び出されると、イベントの名前と フェーズ、イベントリスナのグループがマッチするもの を探し、マッチした場合に登録されているハンドラを実 行する •stopImmediatePropagation() が呼び出されたら、中 断する
  18. 18. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  19. 19. モジュール間の処理の流れの例 1.widget/ は、各 OS のネイティブイベントをハンドリ ングするモジュール。ネイティブイベントが発生した ら、内部イベントである、Widget*Event クラスのイ ンスタンスをスタックに作成し、その値を設定する 2.PresShell にイベントを送信、PresShell が target を決定する
  20. 20. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  21. 21. モジュール間の処理の流れの例 3.EventStateManager::PreHandleEvent() が前処 理を行う •詳しくは後ほど
  22. 22. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  23. 23. モジュール間の処理の流れの例 4.PresShell が EventDispatcher::Dispatch() を呼び 出す 5.EventDispatcher::CreateEvent() で Widget*Event のクラスに対応した、dom::*Event クラスを選択し、そのインスタンスをヒープに作成す る 6.EventListenerManager を列挙した配列を用意し、 EventTargetChainItem::HandleEventTargetChain()を 呼び出す
  24. 24. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  25. 25. モジュール間の処理の流れ 7.EventTargetChainItem::HandleEventTargetChain() で、 受け取った、各 EventListenerManager の HandleEvent() を実行していく 8.EventListenerManager::HandleEvent() は、その ノードに登録されたイベントリスナを順に実行してい く 9.EventTargetChainItem::HandleEventTargetChain() が自身をもう一度呼び出し、デフォルトア クションの実行のため、System Event Group に登 録されたリスナを実行する。
  26. 26. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  27. 27. モジュール間の処理の流れ 10.EventStateManager::PostHandleEvent() で、デフォルト アクションを実行したり、関連するイベントを生成したりする
  28. 28. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  29. 29. Widget*Event、Internal*Event •WidgetEvent クラスが全てのイベントクラスの基底ク ラス •WidgetMouseEvent や、InternalFocusEvent といっ た派生クラスが、イベントごとに定義・実装されている •各クラスは、ほぼ構造体であり、メンバのカプセル化 はほとんど行われず、メンバにダイレクトにアクセスす る (読み書き問わず) •widget から生成されるイベントは、Widget*Event、そ れ以外の場合は、DOM イベントのデータを保存する ためだけのものは、Internal*Event と命名されている
  30. 30. Widget*Event、Internal*Event •widget/ •widget/BasicEvents.h •widget/ContentEvents.h •widget/MiscEvents.h •widget/MouseEvents.h •widget/TextEvents.h •widget/TouchEvents.h •dom/events/ •dom/events/InternalMutationEvent.h
  31. 31. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  32. 32. PresShell •PresShell は、 Presentation Shell の略で、 nsPresShell.cpp で実装され、 nsIPresShell.h で定 義されている nsIPresShell インターフェースを持つ •ドキュメントに対し、一つ生成される (正確なところは 不明) •イベントの複雑な処理は EventStateManager、表示 に関わる部分は nsPresContext に分離されている •イベント処理では widget で生成されたイベントを受け 取り、target の決定と、発火処理を行う •PresShell::HandleEvent() や、関連メソッドを参照
  33. 33. EventStateManager::PreHandleEvent() •PresShell がイベントを発火する直前に呼び出す •マウスの操作によって変化する hover や、active とい った要素の状態設定を行う •マウスのクリックカウント等、状態の保存や、その値の Widget*Event のメンバへの書き出しを行う •ホイールの速度等、設定でカスタマイズできる項目を、 Widget*Event のメンバの値を書き換えることで実現 してる
  34. 34. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  35. 35. EventDispatcher::CreateEvent() •Widget*Event クラスのインスタンスや、 Internal*Event クラスのインスタンスから、DOM イベ ントの実装クラスを選択し、インスタンスを生成する •Javascript の document.createEvent() の実装でも ある •EventListenerManager::HandleEventInternal() が 最初のイベントリスナを見つけた時点で呼び出される
  36. 36. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  37. 37. dom::Event •DOM イベントの実装クラスは、名前空間 “mozilla::dom“ に、標準仕様と同じ名前で定義 •dom::KeyboardEvent、dom::WheelEvent 等 •nsIDOMEvent インターフェースを必ず継承している •dom::Event は、WidgetEvent へのポインタを持ち、 イベントの属性値はここに保存している •DOM イベントクラスは、Widget*Event や Internal*Event へ、DOM からアクセスするためのラ ッパクラス
  38. 38. dom::Event •古くから実装されている DOM イベントクラスは、固有 の、nsIDOM*Event インターフェースも継承している •新規作成時にはほとんど定義されない •データにアクセスするだけなら、内部イベントクラス にアクセスする方が高速で、実装も単純化される •nsIDOM*Event のメソッドは全て、virtual call にな るためパフォーマンスが悪い
  39. 39. dom::Event •内部イベントクラスを定義・実装する必要が無い、シン プルなイベントを実装する際は、webidlで定義したイ ベントから、実装コードを自動生成することも可能 •自動生成の定義は dom/webidl/moz.build で定義 •dom/bindings/Codegen.py によって、ビルド時に cpp ファイルが自動的に生成される
  40. 40. dom::Event •実装例 •dom/events/ •dom/events/Event.cpp •dom/events/UIEvent.cpp •dom/events/WheelEvent.cpp •content/svg/content/src/ •content/svg/content/src/SVGZoomEvent.cpp
  41. 41. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  42. 42. EventDispatcher::Dispatch() •PresShell から呼び出される •最初に、 target の PreHandleEvent() を実行する •window から target ノードまで辿り、作成されている全 ての EventListenerManager を列挙し、配列に格納 •EventTargetChainItem::HandleEventTargetChain() にその配列を渡す •その後、 target の PostHandleEvent() を実行する •発火が終了しても、DOM イベントが JS の変数等か ら参照されている場合、内部イベントをヒープにコピ ーする •発火処理後、スタックにある内部イベントは破棄さ れるため
  43. 43. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  44. 44. EventTargetChainItem::HandleEventTargetChain() •EventDispatcher::Dispatch() に呼び出される •列挙されている、window から target の親ノードまで の EventListenerManager の、HandleEvent() を実 行していく (capturing フェーズ) •Event.stopPropagation() が HandleEvent() 実行 中に呼び出されていたら、ループを中断する
  45. 45. EventTargetChainItem::HandleEventTargetChain() •次に、target の EventListenerManager の HandleEvent() を呼び出す (target フェーズ) •Event.stopPropagation() か、 Event.stopImmediatePropagation() が既に呼び出 されていた場合、実行しない •Event.stopPropagation()が HandleEvent() 実行中 に呼び出されていたら、ループを中断する
  46. 46. EventTargetChainItem::HandleEventTargetChain() •次に、列挙された、target の親から、 window までの EventListenerManager の、HandleEvent() を実行し ていく (bubbling フェーズ) •Event.bubbles が false なら実行しない •Event.stopPropagation() か、 Event.stopImmediatePropagation() が既に呼び出 されていた場合、実行しない •Event.stopPropagation() が HandleEvent() 実行 中に呼び出されていたら、ループを中断する
  47. 47. EventTargetChainItem::HandleEventTargetChain() •次に、イベントターゲットが生成した、CSS box を実装 している、nsIFrame::HandleEvent() を呼び出す • PresShell が EventDispatcher::Dispatch() に渡し た callback クラス経由で呼び出し •default action の実装に利用可能
  48. 48. EventTargetChainItem::HandleEventTargetChain() •最後に、System Event Group のイベントリスナが登録 されている場合、capturing、target、bubbling フェーズ を再度、処理する •通常の Web コンテンツからは登録不可 •Web コンテンツに Event.stopPropagation() 等を呼 び出されていても実行される •default action の実装に利用可能 •System Event Group 内のリスナで、 stopPropagation() 等を呼ぶと中断される
  49. 49. Geckoのイベント処理の実装 nsDocument PresShell widget EventDispatcher EventStateManager Widget*Event dom::*Event PreHandleEvent() PostHandleEvent() Dispatch() CreateEvent() 2 3 4 5 6 EventTargetChainItem nsINode EventListenerManager HandleEvent() handler2 handler1 nsINode EventListenerManager HandleEvent() handler2 handler1 7 8 10 1 9
  50. 50. EventStateManager::PostHandleEvent() •PresShell が最後に呼び出す •イベントの後処理や、default action を実行 •マウスホイールによるスクロール処理やズーム処理 •mouseup から click イベントや、dblclick イベントの 生成 •mousemove から、 mouseover、mouseout、 mouseenter、mouseleave イベントの生成
  51. 51. パフォーマンスを悪くしないためのヒント •イベントリスナを必要以上に登録しない •登録するノードが増える程、EventListenerManager のインスタンスの数が増える •登録するハンドラが増える程、全てのイベントの発生時 に、マッチするイベントを探すループの処理回数が増え る •stopPropagation()や、stopImmediatePropagation() を効果的に利用するとループを中断させることができる
  52. 52. パフォーマンスを悪くしないためのヒント •発火回数が多いイベントや、発火のコストが高いイベント はリスナを登録しないようにする •EventListenerManager と、nsDocument は、一部の イベントリスナが登録されたかどうかをフラグで保存し ており、これらが false の場合、イベントそのものを発 火しないようにしている •各フラグは、RemoveEventListener() でリセットされな い。これは、RemoveEventListener() のパフォーマン スを悪くしないため •発生コストが高いイベントは、一度登録したが最後、 document が unload されるまで、その document では発生し続ける
  53. 53. パフォーマンスを悪くしないためのヒント •mMayHaveCapturingListeners •全てのイベントリスナが、bubbling フェーズのイベント のリスナであれば、false •これが false なら、capturing フェーズのループは丸ご とスキップされる •target フェーズのイベントは、bubbling フェーズのイベ ントリスナで取得できるので、target フェーズでイベント を処理したい場合は、bubbling フェーズとして登録す る方が良い
  54. 54. パフォーマンスを悪くしないためのヒント •mMayHavePaintEventListener •“MozAfterPaint” •描画する度に発生するので、発生回数が莫大
  55. 55. パフォーマンスを悪くしないためのヒント •mMayHaveMutationListeners •“DOMAttrModified” •“DOMCharacterDataModified” •“DOMNodeInserted” •“DOMNodeInserted” •“DOMNodeInsertedIntoDocument” •“DOMNodeRemovedFromDocument” •“DOMSubtreeModified” •MutationObserver を利用するべき
  56. 56. パフォーマンスを悪くしないためのヒント •mMayHavePointerEnterLeaveEventListener •“pointerdown” •“pointermove” •“pointerup” •“pointercancel” •“pointerover” •“pointerout” •“pointerenter” •“pointerleave” •“gotpointercapture” •“lostpointercapture”
  57. 57. パフォーマンスを悪くしないためのヒント •mMayHaveTouchEventListener •“touchstart” •“touchend” •“touchmove” •“touchcancel” •タッチデバイスを直接サポートする場合はやむを 得ない •利用しても他ほどパフォーマンスは落ちない(は ず)
  58. 58. パフォーマンスを悪くしないためのヒント •mMayHaveMouseEnterLeaveEventListener •“mouseenter” •“mouseleave” •“mouseover” や “mouseout” 発生時に、祖先要 素全てで発生すべきか確認する必要があり、多 数の要素上で、同時にイベントが発生する可能性 もある •“mouseover” や ”mouseout” で代用可能か、 熟慮した上で判断を
  59. 59. パフォーマンスを悪くしないためのヒント •他にも細かい、イベント単位での最適化があるので、 EventListenerManager::AddEventListenerInternal()のコー ドを参照
  60. 60. パフォーマンスを悪くしないためのヒント •極端な話をすると、 •全てのイベントリスナを window に登録 •bubbling フェーズで登録 •Event.target を自前で確認 •Event.stopImmediatePropagation()で伝播を中断 •こうすると、イベントリスナを呼び出すコストは最小限に 抑えられる •もちろん、綺麗なコードになるわけではないので、やら ない方が良い
  61. 61. パフォーマンスを悪くしないためのヒント 最適化はほどほどに!
  62. 62. パフォーマンスを悪くしないためのヒント 読みやすいコードで、 バグの少ないアプリを!
  63. 63. 知っておいた方が良い用語・略語 •D3E •DOM Level 3 Events •ESM •EventStateManager •dispatch、fire •(イベントを)発火する •consumed •preventDefault() が呼び出され、default action がキ ャンセルされている状態
  64. 64. その他の資料 •DOM Level 3 Events 仕様書 (Working Draft) •Event developer guide (MDN) •Adding a new event (MDN) •dom/events (mxr)

×