= DOM Bindings, Event Loop & More ''by Ryosuke Niwa'' - C++ and JS Objects - C++ objects define behavior - JS wrappers expose them to JavaScript - Injected scripts have their own DOM wrapper “world” - Converting Objects - toJS(node) to get a JS wrapper - toWrapped(node) / jsNode.wrapped() to get C++ object - Cache main world’s wrapper via ScriptWrappable - When should we use it? - Use it when performance matters - Cost is one extra object; memory concern - Wrapped object is always fast - Lifecycle of DOM Objects - JS wrapper keeps C++ object alive - Ref<> in JSDOMWrapper - Two ways to keep JS wrappers alive - Visit children - Reachable from Opaque Roots - Visit children? Implement visitChildren function and do something? - Yes - Inspector GC heap, you can look at it in Web Inspector - Common misconception - C++ objects do NOT keep their JS wrappers alive by default
class Some : RefCounted {
 …
 Ref m_other; // <<— JS other will still go away
}
class Other : RefCounted { } - Lifecycle: Visit Children - JSCustomMarkFunction in IDL - Add JS*::visitAdditionalChildren in JS*Custom.cpp - Visit JS object kept by WebCore - Lifecycle: Opaque Roots - GeneratelsReachable=Impl* or CustomlsReachable in IDL - addOpaqueRoot in visitAdditionalChildren - JS*::isReachableFromOpaqueRoots - Lifecycle: Concurrency - Visiting & opaque root checks happen in non-main threads - Can’t make createWeakPtr or ref / deref RefCounted objects - Cant lookup HashMap - What ones run with the main thread stopped vs what runs when main thread is not stopped? - VisitChildren runs when the main thread is running, runs on background threads when the main thread is not running - IsOpaqueRoots stops the main thread and runs on background threads - Lifecycle: Common Cases - Keeping JS object alive → Visit Children - Store JSC::Weak - ActiveDOMCallback for callbacks - C++ object relationship → Opaque Roots - Agree on opaque root; typically root Node - Write thread safe code to get opaque root - Lifecycle: NodeLists - Diagram - Lifecycle: DOM Nodes - Node is alive if it has refCount > 0 or has parent node - Node increments Document’s m_referencingNodeCount - Document is alive if refCount > 0 or m_referencingNodeCount > 0 - Node::removedLastRef on Element - ContainerNode::removeDetachedChildren in ~ContainerNode - Turn into flat linked list in deletion queue - Document::removedLastRef() must clear any Ref / RefPtr to Node - Not safe to traverse DOM tree during destruction - Node Insertion & Removal - Node::insertedIntoAncestor / removedFromAncestor - Called whenever node’s ancestor changes - Either “this” or its ancestor got inserted or removed - Don’t assume tree scope or document change - No script execution in insertedIntoAncestor or removedFromAncestor - Will hit release assertion - Use didFinishInsertingNode instead - Node Insertion Order - insertedIntoAncestor called in tree order - Only talk to nodes earlier in tree order - Node Removal Order - removedFromAncestor called in tree order - Only talk to nodes earlier in tree order - Run layout tests with --world-leaks - Lifecycle: Delayed Use - Asynchronous use of “this” - XHR, media, … - Make “this” ActiveDOMObject - Asynchronous use of Node - MutationObserver, ResizeObserver, … - GCReachableRef ← This is a leak! - Lifecycle: ActiveDOMObject - Async work → dispatchEvent on this - Reachable if hasPendingActivity is true - Suspendable for back-forward cache - HTML5 Event Loop - WindowEventLoop has been added - WorkerEventLoop is coming - WindowEventLoop is shared across documents of similar origins - Event Loop: In New Code - Do NOT USE - Timer / SuspendableTimer - GenericEventQueue / GenericTaskQueue ''(rniwa is a genius. officially.)''