Class: Dommy::Document
Overview
‘document` — the entry point for DOM construction and querying. Wrapper caching keeps DOM identity stable across repeated traversals (`body.children.parentElement`).
Constant Summary
collapse
- JS_METHOD_NAMES =
Methods routed through js_call (keep in sync with its when-arms).
%w[
exitFullscreen startViewTransition createElement createElementNS createTextNode
createComment createDocumentFragment querySelector querySelectorAll getElementById
getElementsByClassName getElementsByTagName getElementsByName createAttribute
createAttributeNS createTreeWalker createNodeIterator createEvent importNode adoptNode
hasFocus getSelection elementFromPoint queryCommandSupported addEventListener
removeEventListener dispatchEvent write open close
].freeze
- NAME_RE =
Spec-permitted name pattern (XML “Name” production restricted to ASCII for practicality). Used by ‘createElement` and `createAttribute` to validate the argument.
/\A[A-Za-z_][\w\-.:]*\z/.freeze
Constants included
from Node
Node::ATTRIBUTE_NODE, Node::CDATA_SECTION_NODE, Node::COMMENT_NODE, Node::DOCUMENT_FRAGMENT_NODE, Node::DOCUMENT_NODE, Node::DOCUMENT_POSITION_CONTAINED_BY, Node::DOCUMENT_POSITION_CONTAINS, Node::DOCUMENT_POSITION_DISCONNECTED, Node::DOCUMENT_POSITION_FOLLOWING, Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, Node::DOCUMENT_POSITION_PRECEDING, Node::DOCUMENT_TYPE_NODE, Node::ELEMENT_NODE, Node::PROCESSING_INSTRUCTION_NODE, Node::TEXT_NODE
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#[](key) ⇒ Object
-
#[]=(key, value) ⇒ Object
-
#__internal_event_parent__ ⇒ Object
-
#__internal_notify_attribute_changed__(element, name, old_value, new_value) ⇒ Object
-
#__internal_notify_connected__(element) ⇒ Object
Lifecycle callback dispatchers.
-
#__internal_notify_connected_subtree__(nk) ⇒ Object
-
#__internal_notify_disconnected__(element) ⇒ Object
-
#__internal_notify_disconnected_subtree__(nk) ⇒ Object
-
#__internal_register_shadow_fragment__(fragment_node, shadow_root) ⇒ Object
ShadowRoot identity registry: map a Nokogiri DocumentFragment (the shadow tree’s backing node) to the wrapping ShadowRoot so slot assignment and event composition can walk from any inner node back to its shadow boundary.
-
#__internal_reset_wrapper__(nokogiri_node) ⇒ Object
Clear the cached wrapper so the next ‘wrap_node` creates a new one.
-
#__internal_set_active_element__(el) ⇒ Object
-
#__internal_set_fullscreen_element__(element) ⇒ Object
-
#__internal_shadow_root_containing__(node) ⇒ Object
-
#__internal_shadow_root_for_fragment__(fragment_node) ⇒ Object
-
#__js_call__(method, args) ⇒ Object
-
#__js_get__(key) ⇒ Object
-
#__js_method_names__ ⇒ Object
-
#__js_set__(key, value) ⇒ Object
-
#active_element ⇒ Object
Currently-focused element (or body if none).
-
#adopt_node(node) ⇒ Object
Move a node from another document into this one.
-
#at_xpath(expression) ⇒ Object
XPath queries returning wrapped nodes (Element / TextNode / etc).
-
#attach_template_content(template_element, html) ⇒ Object
—– template content helpers (called from Element) —–.
-
#base_uri ⇒ Object
‘document.baseURI` — resolves the first `<base href>` (if any) relative to the document URL; otherwise just the document URL.
-
#child_element_count ⇒ Object
-
#children ⇒ Object
ParentNode mixin (operates on the document’s element children — in practice the ‘<html>` root).
-
#close ⇒ Object
-
#cookie ⇒ Object
-
#cookie=(value) ⇒ Object
-
#create_attribute(name) ⇒ Object
-
#create_attribute_ns(namespace_uri, qualified_name) ⇒ Object
-
#create_comment(text) ⇒ Object
-
#create_document_fragment ⇒ Object
-
#create_element(name) ⇒ Object
Delegate factory methods to NodeWrapperCache.
-
#create_element_ns(namespace_uri, qualified_name) ⇒ Object
-
#create_event(type_name) ⇒ Object
Legacy ‘document.createEvent(“EventName”)` factory.
-
#create_node_iterator(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil) ⇒ Object
‘document.createNodeIterator(root, whatToShow?, filter?)` — flat depth-first iteration.
-
#create_range ⇒ Object
-
#create_text_node(text) ⇒ Object
-
#create_tree_walker(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil) ⇒ Object
‘document.createTreeWalker(root, whatToShow?, filter?)` — stateful tree traversal with sibling/parent navigation.
-
#doctype ⇒ Object
Minimal DocumentType — represents the ‘<!doctype html>` line.
-
#document_element ⇒ Object
-
#domain ⇒ Object
‘document.domain` — host portion of the URL.
-
#element_from_point(_x, _y) ⇒ Object
-
#exit_fullscreen ⇒ Object
(also: #exitFullscreen)
-
#first_element_child ⇒ Object
-
#forms ⇒ Object
-
#get_element_by_id(id) ⇒ Object
-
#get_elements_by_class_name(name) ⇒ Object
-
#get_elements_by_name(name) ⇒ Object
-
#get_elements_by_tag_name(name) ⇒ Object
-
#get_selection ⇒ Object
-
#has_focus? ⇒ Boolean
(also: #has_focus)
Stubs for layout / focus / selection / execCommand APIs that don’t apply to a layout-less DOM.
-
#has_template_content?(nokogiri_node) ⇒ Boolean
-
#head ⇒ Object
-
#images ⇒ Object
-
#import_node(node, deep = false) ⇒ Object
Copy a node from another document into this one.
-
#initialize(host = nil, nokogiri_doc: nil, default_view: nil) ⇒ Document
constructor
A new instance of Document.
-
#last_element_child ⇒ Object
-
#links ⇒ Object
Live HTMLCollection helpers — each call re-queries the document so post-mutation reads reflect the current state.
-
#migrate_template_descendants(root) ⇒ Object
-
#notify_attribute_mutation(target_node:, attribute_name:, old_value:) ⇒ Object
-
#notify_character_data_mutation(target_node:, old_value:) ⇒ Object
-
#notify_child_list_mutation(target_node:, added_nodes:, removed_nodes:, previous_sibling: nil, next_sibling: nil) ⇒ Object
-
#open ⇒ Object
No-ops — real browsers reset the DOM on ‘open()` and flush pending writes on `close()`.
-
#origin ⇒ Object
‘document.origin` — serialized origin of the document URL, mirroring `window.location.origin`.
-
#query_command_supported(_command) ⇒ Object
-
#query_selector(selector) ⇒ Object
-
#query_selector_all(selector) ⇒ Object
-
#referrer ⇒ Object
‘document.referrer` — Dommy never has a referring page, so this is always empty.
-
#register_observer(observer) ⇒ Object
-
#scripts ⇒ Object
-
#template_content_fragment(template_element) ⇒ Object
-
#template_content_inner_html(template_element) ⇒ Object
-
#title ⇒ Object
—– Public Ruby API (snake_case) —–.
-
#title=(value) ⇒ Object
-
#to_html ⇒ Object
Serialize the whole document to HTML (including the doctype).
-
#unregister_observer(observer) ⇒ Object
-
#url ⇒ Object
(also: #document_uri)
‘document.URL` / `documentURI` — both return location.href in real browsers (legacy aliases of the same field).
-
#wrap_node(node) ⇒ Object
Delegate node wrapping to NodeWrapperCache.
-
#write(*args) ⇒ Object
‘document.write(html)` — legacy API.
-
#xpath(expression) ⇒ Object
#__internal_deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener
Constructor Details
#initialize(host = nil, nokogiri_doc: nil, default_view: nil) ⇒ Document
Returns a new instance of Document.
Instance Attribute Details
#body ⇒ Object
Returns the value of attribute body.
47
48
49
|
# File 'lib/dommy/document.rb', line 47
def body
@body
end
|
#content_type ⇒ Object
content_type defaults to “text/html”; settable so an integration layer can reflect the response Content-Type. Read-only over the JS bridge.
51
52
53
|
# File 'lib/dommy/document.rb', line 51
def content_type
@content_type
end
|
#default_view ⇒ Object
Returns the value of attribute default_view.
48
49
50
|
# File 'lib/dommy/document.rb', line 48
def default_view
@default_view
end
|
#fullscreen_element ⇒ Object
Fullscreen API — no actual fullscreen mode, just track which element claimed it. ‘element.requestFullscreen()` sets it; this is the read side.
312
313
314
|
# File 'lib/dommy/document.rb', line 312
def fullscreen_element
@fullscreen_element
end
|
#nokogiri_doc ⇒ Object
Returns the value of attribute nokogiri_doc.
47
48
49
|
# File 'lib/dommy/document.rb', line 47
def nokogiri_doc
@nokogiri_doc
end
|
Instance Method Details
#[](key) ⇒ Object
400
401
402
|
# File 'lib/dommy/document.rb', line 400
def [](key)
__js_get__(key.to_s)
end
|
#[]=(key, value) ⇒ Object
404
405
406
|
# File 'lib/dommy/document.rb', line 404
def []=(key, value)
__js_set__(key.to_s, value)
end
|
#__internal_event_parent__ ⇒ Object
584
585
586
|
# File 'lib/dommy/document.rb', line 584
def __internal_event_parent__
@default_view
end
|
#__internal_notify_attribute_changed__(element, name, old_value, new_value) ⇒ Object
639
640
641
|
# File 'lib/dommy/document.rb', line 639
def __internal_notify_attribute_changed__(element, name, old_value, new_value)
@mutation_coordinator.notify_attribute_changed(element, name, old_value, new_value)
end
|
#__internal_notify_connected__(element) ⇒ Object
Lifecycle callback dispatchers. Errors raised inside user callbacks are swallowed so a single buggy custom element can’t break the whole mutation pipeline. Delegate to MutationCoordinator
623
624
625
|
# File 'lib/dommy/document.rb', line 623
def __internal_notify_connected__(element)
@mutation_coordinator.notify_connected(element)
end
|
#__internal_notify_connected_subtree__(nk) ⇒ Object
631
632
633
|
# File 'lib/dommy/document.rb', line 631
def __internal_notify_connected_subtree__(nk)
@mutation_coordinator.notify_connected_subtree(nk)
end
|
#__internal_notify_disconnected__(element) ⇒ Object
627
628
629
|
# File 'lib/dommy/document.rb', line 627
def __internal_notify_disconnected__(element)
@mutation_coordinator.notify_disconnected(element)
end
|
#__internal_notify_disconnected_subtree__(nk) ⇒ Object
635
636
637
|
# File 'lib/dommy/document.rb', line 635
def __internal_notify_disconnected_subtree__(nk)
@mutation_coordinator.notify_disconnected_subtree(nk)
end
|
#__internal_register_shadow_fragment__(fragment_node, shadow_root) ⇒ Object
ShadowRoot identity registry: map a Nokogiri DocumentFragment (the shadow tree’s backing node) to the wrapping ShadowRoot so slot assignment and event composition can walk from any inner node back to its shadow boundary. Delegate to ShadowRootRegistry
606
607
608
|
# File 'lib/dommy/document.rb', line 606
def __internal_register_shadow_fragment__(fragment_node, shadow_root)
@shadow_registry.register(fragment_node, shadow_root)
end
|
#__internal_reset_wrapper__(nokogiri_node) ⇒ Object
Clear the cached wrapper so the next ‘wrap_node` creates a new one. Used by `customElements.define` to upgrade nodes that were constructed before the registration landed.
596
597
598
|
# File 'lib/dommy/document.rb', line 596
def __internal_reset_wrapper__(nokogiri_node)
@node_wrapper_cache.reset_wrapper(nokogiri_node)
end
|
#__internal_set_active_element__(el) ⇒ Object
206
207
208
|
# File 'lib/dommy/document.rb', line 206
def __internal_set_active_element__(el)
@active_element = el
end
|
#__internal_set_fullscreen_element__(element) ⇒ Object
314
315
316
317
318
319
320
|
# File 'lib/dommy/document.rb', line 314
def __internal_set_fullscreen_element__(element)
previous = @fullscreen_element
@fullscreen_element = element
return if previous == element
dispatch_event(Event.new("fullscreenchange"))
end
|
#__internal_shadow_root_containing__(node) ⇒ Object
614
615
616
|
# File 'lib/dommy/document.rb', line 614
def __internal_shadow_root_containing__(node)
@shadow_registry.find_enclosing(node)
end
|
#__internal_shadow_root_for_fragment__(fragment_node) ⇒ Object
610
611
612
|
# File 'lib/dommy/document.rb', line 610
def __internal_shadow_root_for_fragment__(fragment_node)
@shadow_registry.find_for_fragment(fragment_node)
end
|
#__js_call__(method, args) ⇒ Object
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
|
# File 'lib/dommy/document.rb', line 507
def __js_call__(method, args)
case method
when "exitFullscreen"
exit_fullscreen
when "startViewTransition"
callback = args[0]
if callback.respond_to?(:__js_call__)
callback.__js_call__("call", [])
elsif callback.respond_to?(:call)
callback.call
end
ViewTransition.new(@default_view)
when "createElement"
create_element(args[0])
when "createElementNS"
create_element_ns(args[0], args[1])
when "createTextNode"
create_text_node(args[0])
when "createComment"
(args[0])
when "createDocumentFragment"
create_document_fragment
when "querySelector"
query_selector(args[0])
when "querySelectorAll"
query_selector_all(args[0])
when "getElementById"
get_element_by_id(args[0])
when "getElementsByClassName"
get_elements_by_class_name(args[0])
when "getElementsByTagName"
get_elements_by_tag_name(args[0])
when "getElementsByName"
get_elements_by_name(args[0])
when "createAttribute"
create_attribute(args[0])
when "createAttributeNS"
create_attribute_ns(args[0], args[1])
when "createTreeWalker"
create_tree_walker(args[0], args[1] || NodeFilter::SHOW_ALL, args[2])
when "createNodeIterator"
create_node_iterator(args[0], args[1] || NodeFilter::SHOW_ALL, args[2])
when "createEvent"
create_event(args[0])
when "importNode"
import_node(args[0], args[1])
when "adoptNode"
adopt_node(args[0])
when "hasFocus"
has_focus?
when "getSelection"
get_selection
when "elementFromPoint"
element_from_point(args[0], args[1])
when "queryCommandSupported"
query_command_supported(args[0])
when "addEventListener"
add_event_listener(args[0], args[1], args[2])
when "removeEventListener"
remove_event_listener(args[0], args[1])
when "dispatchEvent"
dispatch_event(args[0])
when "write"
write(*args)
when "open"
open
when "close"
close
else
nil
end
end
|
#__js_get__(key) ⇒ Object
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
|
# File 'lib/dommy/document.rb', line 422
def __js_get__(key)
case key
when "body"
@body
when "head"
head
when "doctype"
doctype
when "defaultView"
@default_view
when "fullscreenElement"
@fullscreen_element
when "fullscreenEnabled"
true
when "scrollingElement"
wrap_node(@nokogiri_doc.at_css("html"))
when "documentElement"
wrap_node(@nokogiri_doc.at_css("html"))
when "title"
read_title
when "cookie"
cookie
when "nodeType"
9
when "activeElement"
active_element
when "URL", "documentURI"
url
when "baseURI"
base_uri
when "domain"
domain
when "origin"
origin
when "contentType"
content_type
when "referrer"
referrer
when "links"
links
when "forms"
forms
when "scripts"
scripts
when "images"
images
when "children"
children
when "childElementCount"
child_element_count
when "firstElementChild"
first_element_child
when "lastElementChild"
last_element_child
when "nodeName"
"#document"
else
nil
end
end
|
#__js_method_names__ ⇒ Object
503
504
505
|
# File 'lib/dommy/document.rb', line 503
def __js_method_names__
JS_METHOD_NAMES
end
|
#__js_set__(key, value) ⇒ Object
483
484
485
486
487
488
489
490
491
492
|
# File 'lib/dommy/document.rb', line 483
def __js_set__(key, value)
case key
when "title"
write_title(value.to_s)
when "cookie"
self.cookie = value.to_s
end
nil
end
|
#active_element ⇒ Object
Currently-focused element (or body if none). Updated via ‘el.focus()` / `el.blur()`.
202
203
204
|
# File 'lib/dommy/document.rb', line 202
def active_element
@active_element || @body
end
|
#adopt_node(node) ⇒ Object
Move a node from another document into this one. The source node is detached from its previous owner and its ownerDocument becomes this. Returns the (possibly re-wrapped) node.
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
# File 'lib/dommy/document.rb', line 242
def adopt_node(node)
return nil unless node.respond_to?(:__dommy_backend_node__)
src = node.__dommy_backend_node__
src.unlink if src.parent
return wrap_node(src) if src.document == @nokogiri_doc
src_doc_wrapper = node.instance_variable_get(:@document)
@nokogiri_doc.root.add_child(src)
src.unlink
node.instance_variable_set(:@document, self)
if src_doc_wrapper.respond_to?(:__internal_reset_wrapper__)
src_doc_wrapper.__internal_reset_wrapper__(src)
end
@node_wrapper_cache.register(src, node)
node
end
|
#at_xpath(expression) ⇒ Object
XPath queries returning wrapped nodes (Element / TextNode / etc).
92
93
94
95
|
# File 'lib/dommy/document.rb', line 92
def at_xpath(expression)
node = @nokogiri_doc.at_xpath(expression)
node && wrap_node(node)
end
|
#attach_template_content(template_element, html) ⇒ Object
—– template content helpers (called from Element) —–
711
712
713
|
# File 'lib/dommy/document.rb', line 711
def attach_template_content(template_element, html)
@template_content_registry.attach(template_element, html)
end
|
#base_uri ⇒ Object
‘document.baseURI` — resolves the first `<base href>` (if any) relative to the document URL; otherwise just the document URL. When `<base href>` is itself absolute, that wins. Browsers also ignore subsequent <base> elements; we mirror that.
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# File 'lib/dommy/document.rb', line 114
def base_uri
doc_url = url
base_el = @nokogiri_doc.at_css("base[href]")
return doc_url unless base_el
href = base_el["href"].to_s
return doc_url if href.empty?
begin
URI.join(doc_url.to_s.empty? ? "about:blank" : doc_url, href).to_s
rescue URI::InvalidURIError
doc_url
end
end
|
#child_element_count ⇒ Object
188
189
190
|
# File 'lib/dommy/document.rb', line 188
def child_element_count
children.size
end
|
#children ⇒ Object
ParentNode mixin (operates on the document’s element children —in practice the ‘<html>` root).
181
182
183
184
185
186
|
# File 'lib/dommy/document.rb', line 181
def children
HTMLCollection.new do
root = @nokogiri_doc.root
root ? [wrap_node(root)].compact : []
end
end
|
#close ⇒ Object
396
397
398
|
# File 'lib/dommy/document.rb', line 396
def close
nil
end
|
#cookie ⇒ Object
356
357
358
|
# File 'lib/dommy/document.rb', line 356
def cookie
@cookie_jar.to_cookie_string
end
|
#cookie=(value) ⇒ Object
360
361
362
363
|
# File 'lib/dommy/document.rb', line 360
def cookie=(value)
@cookie_jar.set_cookie(value)
nil
end
|
#create_attribute(name) ⇒ Object
Create a detached Attr. ‘setAttributeNode` attaches it to an element. Per spec, name must match the XML Name production —invalid names throw InvalidCharacterError.
213
214
215
|
# File 'lib/dommy/document.rb', line 213
def create_attribute(name)
@node_wrapper_cache.create_attribute(name)
end
|
#create_attribute_ns(namespace_uri, qualified_name) ⇒ Object
217
218
219
|
# File 'lib/dommy/document.rb', line 217
def create_attribute_ns(namespace_uri, qualified_name)
@node_wrapper_cache.create_attribute_ns(namespace_uri, qualified_name)
end
|
Create a Comment node. Wraps the Nokogiri comment so it flows through the same wrap_node identity machinery as Element / TextNode.
410
411
412
|
# File 'lib/dommy/document.rb', line 410
def (text)
@node_wrapper_cache.(text)
end
|
#create_document_fragment ⇒ Object
414
415
416
|
# File 'lib/dommy/document.rb', line 414
def create_document_fragment
@node_wrapper_cache.create_document_fragment
end
|
#create_element(name) ⇒ Object
Delegate factory methods to NodeWrapperCache
689
690
691
|
# File 'lib/dommy/document.rb', line 689
def create_element(name)
@node_wrapper_cache.create_element(name)
end
|
#create_element_ns(namespace_uri, qualified_name) ⇒ Object
365
366
367
|
# File 'lib/dommy/document.rb', line 365
def create_element_ns(namespace_uri, qualified_name)
@node_wrapper_cache.create_element_ns(namespace_uri, qualified_name)
end
|
#create_event(type_name) ⇒ Object
Legacy ‘document.createEvent(“EventName”)` factory. Returns an Event subclass instance whose init still has to be called (`event.initEvent(type, bubbles, cancelable)`). Matches the mapping happy-dom and linkedom use.
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
|
# File 'lib/dommy/document.rb', line 275
def create_event(type_name)
name = type_name.to_s
case name
when "Event", "Events", "HTMLEvents"
Event.new("")
when "CustomEvent"
CustomEvent.new("")
when "MouseEvent", "MouseEvents"
MouseEvent.new("")
when "KeyboardEvent", "KeyboardEvents"
KeyboardEvent.new("")
else
Event.new("")
end
end
|
#create_node_iterator(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil) ⇒ Object
‘document.createNodeIterator(root, whatToShow?, filter?)` —flat depth-first iteration.
#create_range ⇒ Object
305
306
307
|
# File 'lib/dommy/document.rb', line 305
def create_range
Range.new(self)
end
|
#create_text_node(text) ⇒ Object
693
694
695
|
# File 'lib/dommy/document.rb', line 693
def create_text_node(text)
@node_wrapper_cache.create_text_node(text)
end
|
#create_tree_walker(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil) ⇒ Object
‘document.createTreeWalker(root, whatToShow?, filter?)` — stateful tree traversal with sibling/parent navigation. `filter` may be a Ruby Proc, a JS-bridge callable, or an object with `accept_node` / `acceptNode`.
#doctype ⇒ Object
Minimal DocumentType — represents the ‘<!doctype html>` line. Always present in HTML5 documents we parse, so we synthesize a stub object whose only useful field is `name`. Tests just need `nodeType == 10`.
350
351
352
|
# File 'lib/dommy/document.rb', line 350
def doctype
@doctype ||= DocumentType.new("html")
end
|
#document_element ⇒ Object
78
79
80
|
# File 'lib/dommy/document.rb', line 78
def document_element
wrap_node(@nokogiri_doc.at_css("html"))
end
|
#domain ⇒ Object
‘document.domain` — host portion of the URL. Real browsers restrict cross-origin reads of this; we just return the bare host.
131
132
133
134
135
136
|
# File 'lib/dommy/document.rb', line 131
def domain
view = @default_view
return "" unless view&.location
view.location.__js_get__("hostname").to_s
end
|
#element_from_point(_x, _y) ⇒ Object
332
333
334
|
# File 'lib/dommy/document.rb', line 332
def element_from_point(_x, _y)
nil
end
|
#exit_fullscreen ⇒ Object
Also known as:
exitFullscreen
322
323
324
325
326
327
328
|
# File 'lib/dommy/document.rb', line 322
def exit_fullscreen
return PromiseValue.resolve(@default_view, nil) if @fullscreen_element.nil?
@fullscreen_element = nil
dispatch_event(Event.new("fullscreenchange"))
PromiseValue.resolve(@default_view, nil)
end
|
#first_element_child ⇒ Object
192
193
194
|
# File 'lib/dommy/document.rb', line 192
def first_element_child
wrap_node(@nokogiri_doc.root)
end
|
161
162
163
164
165
|
# File 'lib/dommy/document.rb', line 161
def forms
HTMLCollection.new do
@nokogiri_doc.css("form").map { |n| wrap_node(n) }.compact
end
end
|
#get_element_by_id(id) ⇒ Object
705
706
707
|
# File 'lib/dommy/document.rb', line 705
def get_element_by_id(id)
@node_wrapper_cache.get_element_by_id(id)
end
|
#get_elements_by_class_name(name) ⇒ Object
418
419
420
|
# File 'lib/dommy/document.rb', line 418
def get_elements_by_class_name(name)
@node_wrapper_cache.get_elements_by_class_name(name)
end
|
#get_elements_by_name(name) ⇒ Object
373
374
375
|
# File 'lib/dommy/document.rb', line 373
def get_elements_by_name(name)
@node_wrapper_cache.get_elements_by_name(name)
end
|
#get_elements_by_tag_name(name) ⇒ Object
369
370
371
|
# File 'lib/dommy/document.rb', line 369
def get_elements_by_tag_name(name)
@node_wrapper_cache.get_elements_by_tag_name(name)
end
|
#get_selection ⇒ Object
301
302
303
|
# File 'lib/dommy/document.rb', line 301
def get_selection
@__selection ||= Selection.new(self)
end
|
#has_focus? ⇒ Boolean
Also known as:
has_focus
Stubs for layout / focus / selection / execCommand APIs that don’t apply to a layout-less DOM. They exist so callers don’t hit NoMethodError; semantics are documented as no-op.
295
296
297
|
# File 'lib/dommy/document.rb', line 295
def has_focus?
true
end
|
#has_template_content?(nokogiri_node) ⇒ Boolean
727
728
729
|
# File 'lib/dommy/document.rb', line 727
def has_template_content?(nokogiri_node)
@template_content_registry.has_content?(nokogiri_node)
end
|
#head ⇒ Object
82
83
84
|
# File 'lib/dommy/document.rb', line 82
def head
wrap_node(@nokogiri_doc.at_css("head"))
end
|
#images ⇒ Object
173
174
175
176
177
|
# File 'lib/dommy/document.rb', line 173
def images
HTMLCollection.new do
@nokogiri_doc.css("img").map { |n| wrap_node(n) }.compact
end
end
|
#import_node(node, deep = false) ⇒ Object
Copy a node from another document into this one. The returned wrapper is owned by ‘this`. Per spec, the source node is left in place. `deep: true` copies the entire subtree.
232
233
234
235
236
237
|
# File 'lib/dommy/document.rb', line 232
def import_node(node, deep = false)
return nil unless node.respond_to?(:__dommy_backend_node__)
copy = clone_into_doc(node.__dommy_backend_node__, deep)
wrap_node(copy)
end
|
#last_element_child ⇒ Object
196
197
198
|
# File 'lib/dommy/document.rb', line 196
def last_element_child
wrap_node(@nokogiri_doc.root)
end
|
#links ⇒ Object
Live HTMLCollection helpers — each call re-queries the document so post-mutation reads reflect the current state.
155
156
157
158
159
|
# File 'lib/dommy/document.rb', line 155
def links
HTMLCollection.new do
@nokogiri_doc.css("a[href], area[href]").map { |n| wrap_node(n) }.compact
end
end
|
#migrate_template_descendants(root) ⇒ Object
723
724
725
|
# File 'lib/dommy/document.rb', line 723
def migrate_template_descendants(root)
@template_content_registry.migrate_descendants(root)
end
|
#notify_attribute_mutation(target_node:, attribute_name:, old_value:) ⇒ Object
667
668
669
670
671
672
673
|
# File 'lib/dommy/document.rb', line 667
def notify_attribute_mutation(target_node:, attribute_name:, old_value:)
@mutation_coordinator.notify_attribute_mutation(
target_node: target_node,
attribute_name: attribute_name,
old_value: old_value
)
end
|
#notify_character_data_mutation(target_node:, old_value:) ⇒ Object
675
676
677
678
679
680
|
# File 'lib/dommy/document.rb', line 675
def notify_character_data_mutation(target_node:, old_value:)
@mutation_coordinator.notify_character_data_mutation(
target_node: target_node,
old_value: old_value
)
end
|
#notify_child_list_mutation(target_node:, added_nodes:, removed_nodes:, previous_sibling: nil, next_sibling: nil) ⇒ Object
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
|
# File 'lib/dommy/document.rb', line 651
def notify_child_list_mutation(
target_node:,
added_nodes:,
removed_nodes:,
previous_sibling: nil,
next_sibling: nil
)
@mutation_coordinator.notify_child_list_mutation(
target_node: target_node,
added_nodes: added_nodes,
removed_nodes: removed_nodes,
previous_sibling: previous_sibling,
next_sibling: next_sibling
)
end
|
#open ⇒ Object
No-ops — real browsers reset the DOM on ‘open()` and flush pending writes on `close()`. We don’t model the parse pipeline.
392
393
394
|
# File 'lib/dommy/document.rb', line 392
def open
nil
end
|
#origin ⇒ Object
‘document.origin` — serialized origin of the document URL, mirroring `window.location.origin`. Empty when there is no associated window.
140
141
142
143
144
145
|
# File 'lib/dommy/document.rb', line 140
def origin
view = @default_view
return "" unless view&.location
view.location.__js_get__("origin").to_s
end
|
#query_command_supported(_command) ⇒ Object
336
337
338
|
# File 'lib/dommy/document.rb', line 336
def query_command_supported(_command)
false
end
|
#query_selector(selector) ⇒ Object
697
698
699
|
# File 'lib/dommy/document.rb', line 697
def query_selector(selector)
@node_wrapper_cache.query_selector(selector)
end
|
#query_selector_all(selector) ⇒ Object
701
702
703
|
# File 'lib/dommy/document.rb', line 701
def query_selector_all(selector)
@node_wrapper_cache.query_selector_all(selector)
end
|
#referrer ⇒ Object
‘document.referrer` — Dommy never has a referring page, so this is always empty.
149
150
151
|
# File 'lib/dommy/document.rb', line 149
def referrer
""
end
|
#register_observer(observer) ⇒ Object
643
644
645
|
# File 'lib/dommy/document.rb', line 643
def register_observer(observer)
@mutation_coordinator.register_observer(observer)
end
|
#scripts ⇒ Object
167
168
169
170
171
|
# File 'lib/dommy/document.rb', line 167
def scripts
HTMLCollection.new do
@nokogiri_doc.css("script").map { |n| wrap_node(n) }.compact
end
end
|
#template_content_fragment(template_element) ⇒ Object
715
716
717
|
# File 'lib/dommy/document.rb', line 715
def template_content_fragment(template_element)
@template_content_registry.fragment_for(template_element)
end
|
#template_content_inner_html(template_element) ⇒ Object
719
720
721
|
# File 'lib/dommy/document.rb', line 719
def template_content_inner_html(template_element)
@template_content_registry.inner_html_of(template_element)
end
|
#title ⇒ Object
—– Public Ruby API (snake_case) —–
70
71
72
|
# File 'lib/dommy/document.rb', line 70
def title
read_title
end
|
#title=(value) ⇒ Object
74
75
76
|
# File 'lib/dommy/document.rb', line 74
def title=(value)
write_title(value.to_s)
end
|
#to_html ⇒ Object
Serialize the whole document to HTML (including the doctype).
87
88
89
|
# File 'lib/dommy/document.rb', line 87
def to_html
@nokogiri_doc.to_html
end
|
#unregister_observer(observer) ⇒ Object
647
648
649
|
# File 'lib/dommy/document.rb', line 647
def unregister_observer(observer)
@mutation_coordinator.unregister_observer(observer)
end
|
#url ⇒ Object
Also known as:
document_uri
‘document.URL` / `documentURI` — both return location.href in real browsers (legacy aliases of the same field).
103
104
105
106
|
# File 'lib/dommy/document.rb', line 103
def url
view = @default_view
view&.location ? view.location.href : ""
end
|
#wrap_node(node) ⇒ Object
Delegate node wrapping to NodeWrapperCache
589
590
591
|
# File 'lib/dommy/document.rb', line 589
def wrap_node(node)
@node_wrapper_cache.wrap(node)
end
|
#write(*args) ⇒ Object
‘document.write(html)` — legacy API. Appends parsed nodes to the body. Real browsers only re-stream the DOM during initial parse; this stub is enough for tests that fire write() during teardown.
380
381
382
383
384
385
386
387
388
|
# File 'lib/dommy/document.rb', line 380
def write(*args)
html = args.join
fragment = Parser.fragment(html, owner_doc: @nokogiri_doc)
removed = []
added = fragment.children.to_a
added.each { |node| @body.__dommy_backend_node__.add_child(node) }
notify_child_list_mutation(target_node: @body.__dommy_backend_node__, added_nodes: added, removed_nodes: removed)
nil
end
|
#xpath(expression) ⇒ Object
97
98
99
|
# File 'lib/dommy/document.rb', line 97
def xpath(expression)
@nokogiri_doc.xpath(expression).map { |node| wrap_node(node) }
end
|