Module: Dommy::Js::DomInterfaces
- Defined in:
- lib/dommy/js/dom_interfaces.rb
Overview
Derives WebIDL interface metadata for a Dommy DOM object: the most-derived interface name and the single-inheritance chain up to the root (EventTarget for nodes). This mirrors the JS prototype chain the bridge builds so ‘instanceof` / Object.prototype.toString resolve correctly.
Engine-agnostic, and the single home for DOM interface hierarchy knowledge: BASE_CHAINS (seeded eagerly on the JS side) must stay consistent with what #chain_for derives from real objects.
Constant Summary collapse
- NAME_OVERRIDES =
Dommy class basename -> WebIDL interface name, where they diverge. Anything not listed uses the class basename verbatim (HTMLDivElement, …).
{ "TextNode" => "Text", "CommentNode" => "Comment", "CharacterDataNode" => "CharacterData", "Fragment" => "DocumentFragment", "ClassList" => "DOMTokenList", "DatasetMap" => "DOMStringMap", "StyleDeclaration" => "CSSStyleDeclaration", "LiveNodeList" => "NodeList", "StandaloneEventTarget" => "EventTarget" }.freeze
- HTML_LEAF_INTERFACES =
Concrete HTML element interfaces. A browser exposes every one of these as a global constructor whether or not an instance exists, so a framework’s bare ‘instanceof HTMLInputElement` feature check resolves regardless of page content (idiomorph, Turbo’s morph engine, probes ‘instanceof HTMLInputElement`/`HTMLTextAreaElement` during focus restoration even when the page has no such element). Each is a direct HTMLElement subclass except the two media leaves, appended with their chains below. Mirrors the `class HTMLxxxElement < HTMLElement` set in the dommy gem’s html_elements.
%w[ HTMLAnchorElement HTMLAreaElement HTMLBaseElement HTMLBodyElement HTMLBRElement HTMLButtonElement HTMLDataElement HTMLDetailsElement HTMLDialogElement HTMLDivElement HTMLEmbedElement HTMLFieldsetElement HTMLFormElement HTMLHeadElement HTMLHeadingElement HTMLHRElement HTMLHtmlElement HTMLIFrameElement HTMLImageElement HTMLInputElement HTMLLabelElement HTMLLegendElement HTMLLIElement HTMLLinkElement HTMLMapElement HTMLMetaElement HTMLMeterElement HTMLModElement HTMLObjectElement HTMLOListElement HTMLOptGroupElement HTMLOptionElement HTMLOutputElement HTMLParagraphElement HTMLPictureElement HTMLPreElement HTMLProgressElement HTMLQuoteElement HTMLScriptElement HTMLSelectElement HTMLSlotElement HTMLSourceElement HTMLSpanElement HTMLStyleElement HTMLTableCaptionElement HTMLTableCellElement HTMLTableElement HTMLTableRowElement HTMLTableSectionElement HTMLTemplateElement HTMLTextAreaElement HTMLTimeElement HTMLTitleElement HTMLTrackElement HTMLUListElement ].freeze
- BASE_CHAINS =
Base interface chains seeded eagerly on the JS side so ‘instanceof Node` / `typeof HTMLElement` resolve before an instance of that exact type has crossed. Concrete leaves (HTMLButtonElement, …) are built lazily from #chain_for when an instance crosses. Keep consistent with #chain_for.
[ %w[Node EventTarget], %w[Element Node EventTarget], %w[HTMLElement Element Node EventTarget], %w[SVGElement Element Node EventTarget], %w[CharacterData Node EventTarget], %w[Text CharacterData Node EventTarget], %w[Comment CharacterData Node EventTarget], %w[Document Node EventTarget], %w[DocumentFragment Node EventTarget], %w[DocumentType Node EventTarget], %w[Attr Node EventTarget], %w[Event], %w[CustomEvent Event], %w[MessageEvent Event], %w[PopStateEvent Event], %w[CloseEvent Event], %w[MouseEvent Event], %w[KeyboardEvent Event], %w[DOMException], # Window-exposed constructors that frameworks call bare (new X(...)). # Seeding them creates the global; construction routes to the window. %w[MutationObserver], %w[IntersectionObserver], %w[ResizeObserver], %w[PerformanceObserver], %w[AbortController], %w[AbortSignal EventTarget], %w[FormData], %w[URL], %w[URLSearchParams], %w[Headers], %w[Request], %w[Response], %w[Blob], %w[File], %w[FileList], %w[FileReader], %w[XMLHttpRequest], %w[TextEncoder], %w[TextDecoder], %w[DOMParser], %w[XMLSerializer], %w[MessageChannel], %w[BroadcastChannel], %w[WebSocket], %w[EventSource], %w[Notification], %w[Worker], %w[DataTransfer], %w[ReadableStream], %w[WritableStream], %w[TransformStream], %w[Range], # Collection interfaces, seeded so `result instanceof NodeList` / # `instanceof HTMLCollection` resolve (querySelectorAll, children, …). %w[NodeList], %w[HTMLCollection], # Traversal: NodeFilter exposes only [Constant]s (NodeFilter.SHOW_ELEMENT, # .FILTER_ACCEPT, …); TreeWalker/NodeIterator are instances. %w[NodeFilter], %w[TreeWalker], %w[NodeIterator], # Concrete HTML element interfaces (see HTML_LEAF_INTERFACES) + the media # subtree, so bare `instanceof HTMLInputElement` always resolves. *HTML_LEAF_INTERFACES.map { |n| [n, "HTMLElement", "Element", "Node", "EventTarget"] }, %w[HTMLMediaElement HTMLElement Element Node EventTarget], %w[HTMLAudioElement HTMLMediaElement HTMLElement Element Node EventTarget], %w[HTMLVideoElement HTMLMediaElement HTMLElement Element Node EventTarget] ].freeze
Class Method Summary collapse
-
.chain_for(obj) ⇒ Object
Walk the Dommy class superclass chain (HTMLDivElement < HTMLElement < Element), then append the module-provided base interfaces (Node -> EventTarget) for nodes, since Dommy models Node as a mixin rather than a superclass.
-
.info(obj) ⇒ Object
{ “name” => most-derived interface, “chain” => […] } for a host object.
- .name_for(klass) ⇒ Object
Class Method Details
.chain_for(obj) ⇒ Object
Walk the Dommy class superclass chain (HTMLDivElement < HTMLElement < Element), then append the module-provided base interfaces (Node -> EventTarget) for nodes, since Dommy models Node as a mixin rather than a superclass. Stops at the first foreign superclass (Object, or StandardError for DOMException) so non-DOM ancestors stay out.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/dommy/js/dom_interfaces.rb', line 116 def chain_for(obj) names = [] klass = obj.class while klass && klass.name&.start_with?("Dommy::") name = name_for(klass) names << name if name && !names.include?(name) klass = klass.superclass end if defined?(Dommy::Node) && obj.is_a?(Dommy::Node) names << "Node" unless names.include?("Node") names << "EventTarget" unless names.include?("EventTarget") end names end |
.info(obj) ⇒ Object
{ “name” => most-derived interface, “chain” => […] } for a host object.
106 107 108 109 |
# File 'lib/dommy/js/dom_interfaces.rb', line 106 def info(obj) chain = chain_for(obj) {"name" => chain.first, "chain" => chain} end |
.name_for(klass) ⇒ Object
131 132 133 134 135 136 |
# File 'lib/dommy/js/dom_interfaces.rb', line 131 def name_for(klass) base = klass.name&.split("::")&.last return nil unless base NAME_OVERRIDES.fetch(base, base) end |