Class: Dommy::Document

Inherits:
Object
  • Object
show all
Includes:
EventTarget, Node
Defined in:
lib/dommy/document.rb

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

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

Methods included from EventTarget

#__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.



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/dommy/document.rb', line 50

def initialize(host = nil, nokogiri_doc: nil, default_view: nil)
  @host = host
  @default_view = default_view
  @node_wrapper_cache = Internal::NodeWrapperCache.new(self)
  @observer_manager = Internal::ObserverManager.new
  @shadow_registry = Internal::ShadowRootRegistry.new
  @cookie_jar = Internal::CookieJar.new
  @template_content_registry = Internal::TemplateContentRegistry.new(self)
  @mutation_coordinator = Internal::MutationCoordinator.new(self, @observer_manager)
  @nokogiri_doc = nokogiri_doc || Nokogiri::HTML5("<!doctype html><html><head></head><body></body></html>")
  body_node = @nokogiri_doc.at_css("body")
  @body = wrap_node(body_node) if body_node
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



47
48
49
# File 'lib/dommy/document.rb', line 47

def body
  @body
end

#default_viewObject

Returns the value of attribute default_view.



48
49
50
# File 'lib/dommy/document.rb', line 48

def default_view
  @default_view
end

#fullscreen_elementObject (readonly)

Fullscreen API — no actual fullscreen mode, just track which element claimed it. ‘element.requestFullscreen()` sets it; this is the read side.



269
270
271
# File 'lib/dommy/document.rb', line 269

def fullscreen_element
  @fullscreen_element
end

#nokogiri_docObject (readonly)

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



357
358
359
# File 'lib/dommy/document.rb', line 357

def [](key)
  __js_get__(key.to_s)
end

#[]=(key, value) ⇒ Object



361
362
363
# File 'lib/dommy/document.rb', line 361

def []=(key, value)
  __js_set__(key.to_s, value)
end

#__event_parent__Object



524
525
526
# File 'lib/dommy/document.rb', line 524

def __event_parent__
  @default_view
end

#__js_call__(method, args) ⇒ Object



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
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/dommy/document.rb', line 447

def __js_call__(method, args)
  case method
  when "exitFullscreen"
    exit_fullscreen
  when "startViewTransition"
    # View Transitions API stub. Spec: invoke the callback
    # synchronously; return a ViewTransition with already-resolved
    # `finished` / `ready` / `updateCallbackDone` promises.
    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"
    create_comment(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



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/dommy/document.rb', line 379

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 "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_set__(key, value) ⇒ Object



436
437
438
439
440
441
442
443
444
445
# File 'lib/dommy/document.rb', line 436

def __js_set__(key, value)
  case key
  when "title"
    write_title(value.to_s)
  when "cookie"
    self.cookie = value.to_s
  end

  nil
end

#__notify_attribute_changed__(element, name, old_value, new_value) ⇒ Object



579
580
581
# File 'lib/dommy/document.rb', line 579

def __notify_attribute_changed__(element, name, old_value, new_value)
  @mutation_coordinator.notify_attribute_changed(element, name, old_value, new_value)
end

#__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



563
564
565
# File 'lib/dommy/document.rb', line 563

def __notify_connected__(element)
  @mutation_coordinator.notify_connected(element)
end

#__notify_connected_subtree__(nk) ⇒ Object



571
572
573
# File 'lib/dommy/document.rb', line 571

def __notify_connected_subtree__(nk)
  @mutation_coordinator.notify_connected_subtree(nk)
end

#__notify_disconnected__(element) ⇒ Object



567
568
569
# File 'lib/dommy/document.rb', line 567

def __notify_disconnected__(element)
  @mutation_coordinator.notify_disconnected(element)
end

#__notify_disconnected_subtree__(nk) ⇒ Object



575
576
577
# File 'lib/dommy/document.rb', line 575

def __notify_disconnected_subtree__(nk)
  @mutation_coordinator.notify_disconnected_subtree(nk)
end

#__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



546
547
548
# File 'lib/dommy/document.rb', line 546

def __register_shadow_fragment__(fragment_node, shadow_root)
  @shadow_registry.register(fragment_node, shadow_root)
end

#__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.



536
537
538
# File 'lib/dommy/document.rb', line 536

def __reset_wrapper__(nokogiri_node)
  @node_wrapper_cache.reset_wrapper(nokogiri_node)
end

#__set_active_element__(el) ⇒ Object



178
179
180
# File 'lib/dommy/document.rb', line 178

def __set_active_element__(el)
  @active_element = el
end

#__set_fullscreen_element__(element) ⇒ Object



271
272
273
274
275
276
277
# File 'lib/dommy/document.rb', line 271

def __set_fullscreen_element__(element)
  previous = @fullscreen_element
  @fullscreen_element = element
  return if previous == element

  dispatch_event(Event.new("fullscreenchange"))
end

#__shadow_root_containing__(node) ⇒ Object



554
555
556
# File 'lib/dommy/document.rb', line 554

def __shadow_root_containing__(node)
  @shadow_registry.find_enclosing(node)
end

#__shadow_root_for_fragment__(fragment_node) ⇒ Object



550
551
552
# File 'lib/dommy/document.rb', line 550

def __shadow_root_for_fragment__(fragment_node)
  @shadow_registry.find_for_fragment(fragment_node)
end

#active_elementObject

Currently-focused element (or body if none). Updated via ‘el.focus()` / `el.blur()`.



174
175
176
# File 'lib/dommy/document.rb', line 174

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.



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/dommy/document.rb', line 214

def adopt_node(node)
  return nil unless node.respond_to?(:__node__)

  src = node.__node__
  src.unlink if src.parent
  moved = if src.document == @nokogiri_doc
    src
  else
    clone_into_doc(src, true)
  end

  wrap_node(moved)
end

#attach_template_content(template_element, html) ⇒ Object

—– template content helpers (called from Element) —–



651
652
653
# File 'lib/dommy/document.rb', line 651

def attach_template_content(template_element, html)
  @template_content_registry.attach(template_element, html)
end

#base_uriObject

‘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.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/dommy/document.rb', line 95

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_countObject



160
161
162
# File 'lib/dommy/document.rb', line 160

def child_element_count
  children.size
end

#childrenObject

ParentNode mixin (operates on the document’s element children —in practice the ‘<html>` root).



153
154
155
156
157
158
# File 'lib/dommy/document.rb', line 153

def children
  HTMLCollection.new do
    root = @nokogiri_doc.root
    root ? [wrap_node(root)].compact : []
  end
end

#closeObject



353
354
355
# File 'lib/dommy/document.rb', line 353

def close
  nil
end

Delegate to CookieJar



313
314
315
# File 'lib/dommy/document.rb', line 313

def cookie
  @cookie_jar.to_cookie_string
end

#cookie=(value) ⇒ Object



317
318
319
320
# File 'lib/dommy/document.rb', line 317

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.



185
186
187
# File 'lib/dommy/document.rb', line 185

def create_attribute(name)
  @node_wrapper_cache.create_attribute(name)
end

#create_attribute_ns(namespace_uri, qualified_name) ⇒ Object



189
190
191
# File 'lib/dommy/document.rb', line 189

def create_attribute_ns(namespace_uri, qualified_name)
  @node_wrapper_cache.create_attribute_ns(namespace_uri, qualified_name)
end

#create_comment(text) ⇒ Object

Create a Comment node. Wraps the Nokogiri comment so it flows through the same wrap_node identity machinery as Element / TextNode.



367
368
369
# File 'lib/dommy/document.rb', line 367

def create_comment(text)
  @node_wrapper_cache.create_comment(text)
end

#create_document_fragmentObject



371
372
373
# File 'lib/dommy/document.rb', line 371

def create_document_fragment
  @node_wrapper_cache.create_document_fragment
end

#create_element(name) ⇒ Object

Delegate factory methods to NodeWrapperCache



629
630
631
# File 'lib/dommy/document.rb', line 629

def create_element(name)
  @node_wrapper_cache.create_element(name)
end

#create_element_ns(namespace_uri, qualified_name) ⇒ Object



322
323
324
# File 'lib/dommy/document.rb', line 322

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.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/dommy/document.rb', line 232

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.



299
300
301
# File 'lib/dommy/document.rb', line 299

def create_node_iterator(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil)
  NodeIterator.new(root, what_to_show, filter)
end

#create_rangeObject



262
263
264
# File 'lib/dommy/document.rb', line 262

def create_range
  Range.new(self)
end

#create_text_node(text) ⇒ Object



633
634
635
# File 'lib/dommy/document.rb', line 633

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`.



197
198
199
# File 'lib/dommy/document.rb', line 197

def create_tree_walker(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil)
  TreeWalker.new(root, what_to_show, filter)
end

#doctypeObject

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`.



307
308
309
# File 'lib/dommy/document.rb', line 307

def doctype
  @doctype ||= DocumentType.new("html")
end

#document_elementObject



74
75
76
# File 'lib/dommy/document.rb', line 74

def document_element
  wrap_node(@nokogiri_doc.at_css("html"))
end

#domainObject

‘document.domain` — host portion of the URL. Real browsers restrict cross-origin reads of this; we just return the bare host.



112
113
114
115
116
117
# File 'lib/dommy/document.rb', line 112

def domain
  view = @default_view
  return "" unless view&.location

  view.location.__js_get__("hostname").to_s
end

#element_from_point(_x, _y) ⇒ Object



289
290
291
# File 'lib/dommy/document.rb', line 289

def element_from_point(_x, _y)
  nil
end

#exit_fullscreenObject Also known as: exitFullscreen



279
280
281
282
283
284
285
# File 'lib/dommy/document.rb', line 279

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_childObject



164
165
166
# File 'lib/dommy/document.rb', line 164

def first_element_child
  wrap_node(@nokogiri_doc.root)
end

#formsObject



133
134
135
136
137
# File 'lib/dommy/document.rb', line 133

def forms
  HTMLCollection.new do
    @nokogiri_doc.css("form").map { |n| wrap_node(n) }.compact
  end
end

#get_element_by_id(id) ⇒ Object



645
646
647
# File 'lib/dommy/document.rb', line 645

def get_element_by_id(id)
  @node_wrapper_cache.get_element_by_id(id)
end

#get_elements_by_class_name(name) ⇒ Object



375
376
377
# File 'lib/dommy/document.rb', line 375

def get_elements_by_class_name(name)
  @node_wrapper_cache.get_elements_by_class_name(name)
end

#get_elements_by_name(name) ⇒ Object



330
331
332
# File 'lib/dommy/document.rb', line 330

def get_elements_by_name(name)
  @node_wrapper_cache.get_elements_by_name(name)
end

#get_elements_by_tag_name(name) ⇒ Object



326
327
328
# File 'lib/dommy/document.rb', line 326

def get_elements_by_tag_name(name)
  @node_wrapper_cache.get_elements_by_tag_name(name)
end

#get_selectionObject



258
259
260
# File 'lib/dommy/document.rb', line 258

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.

Returns:

  • (Boolean)


252
253
254
# File 'lib/dommy/document.rb', line 252

def has_focus?
  true
end

#has_template_content?(nokogiri_node) ⇒ Boolean

Returns:

  • (Boolean)


667
668
669
# File 'lib/dommy/document.rb', line 667

def has_template_content?(nokogiri_node)
  @template_content_registry.has_content?(nokogiri_node)
end

#headObject



78
79
80
# File 'lib/dommy/document.rb', line 78

def head
  wrap_node(@nokogiri_doc.at_css("head"))
end

#imagesObject



145
146
147
148
149
# File 'lib/dommy/document.rb', line 145

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.



204
205
206
207
208
209
# File 'lib/dommy/document.rb', line 204

def import_node(node, deep = false)
  return nil unless node.respond_to?(:__node__)

  copy = clone_into_doc(node.__node__, deep)
  wrap_node(copy)
end

#last_element_childObject



168
169
170
# File 'lib/dommy/document.rb', line 168

def last_element_child
  wrap_node(@nokogiri_doc.root)
end

Live HTMLCollection helpers — each call re-queries the document so post-mutation reads reflect the current state.



127
128
129
130
131
# File 'lib/dommy/document.rb', line 127

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



663
664
665
# File 'lib/dommy/document.rb', line 663

def migrate_template_descendants(root)
  @template_content_registry.migrate_descendants(root)
end

#notify_attribute_mutation(target_node:, attribute_name:, old_value:) ⇒ Object



607
608
609
610
611
612
613
# File 'lib/dommy/document.rb', line 607

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



615
616
617
618
619
620
# File 'lib/dommy/document.rb', line 615

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



591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/dommy/document.rb', line 591

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

#openObject

No-ops — real browsers reset the DOM on ‘open()` and flush pending writes on `close()`. We don’t model the parse pipeline.



349
350
351
# File 'lib/dommy/document.rb', line 349

def open
  nil
end

#query_command_supported(_command) ⇒ Object



293
294
295
# File 'lib/dommy/document.rb', line 293

def query_command_supported(_command)
  false
end

#query_selector(selector) ⇒ Object



637
638
639
# File 'lib/dommy/document.rb', line 637

def query_selector(selector)
  @node_wrapper_cache.query_selector(selector)
end

#query_selector_all(selector) ⇒ Object



641
642
643
# File 'lib/dommy/document.rb', line 641

def query_selector_all(selector)
  @node_wrapper_cache.query_selector_all(selector)
end

#referrerObject

‘document.referrer` — Dommy never has a referring page, so this is always empty.



121
122
123
# File 'lib/dommy/document.rb', line 121

def referrer
  ""
end

#register_observer(observer) ⇒ Object



583
584
585
# File 'lib/dommy/document.rb', line 583

def register_observer(observer)
  @mutation_coordinator.register_observer(observer)
end

#scriptsObject



139
140
141
142
143
# File 'lib/dommy/document.rb', line 139

def scripts
  HTMLCollection.new do
    @nokogiri_doc.css("script").map { |n| wrap_node(n) }.compact
  end
end

#template_content_fragment(template_element) ⇒ Object



655
656
657
# File 'lib/dommy/document.rb', line 655

def template_content_fragment(template_element)
  @template_content_registry.fragment_for(template_element)
end

#template_content_inner_html(template_element) ⇒ Object



659
660
661
# File 'lib/dommy/document.rb', line 659

def template_content_inner_html(template_element)
  @template_content_registry.inner_html_of(template_element)
end

#titleObject

—– Public Ruby API (snake_case) —–



66
67
68
# File 'lib/dommy/document.rb', line 66

def title
  read_title
end

#title=(value) ⇒ Object



70
71
72
# File 'lib/dommy/document.rb', line 70

def title=(value)
  write_title(value.to_s)
end

#unregister_observer(observer) ⇒ Object



587
588
589
# File 'lib/dommy/document.rb', line 587

def unregister_observer(observer)
  @mutation_coordinator.unregister_observer(observer)
end

#urlObject Also known as: document_uri

‘document.URL` / `documentURI` — both return location.href in real browsers (legacy aliases of the same field).



84
85
86
87
# File 'lib/dommy/document.rb', line 84

def url
  view = @default_view
  view&.location ? view.location.href : ""
end

#wrap_node(node) ⇒ Object

Delegate node wrapping to NodeWrapperCache



529
530
531
# File 'lib/dommy/document.rb', line 529

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.



337
338
339
340
341
342
343
344
345
# File 'lib/dommy/document.rb', line 337

def write(*args)
  html = args.join
  fragment = Parser.fragment(html, owner_doc: @nokogiri_doc)
  removed = []
  added = fragment.children.to_a
  added.each { |node| @body.__node__.add_child(node) }
  notify_child_list_mutation(target_node: @body.__node__, added_nodes: added, removed_nodes: removed)
  nil
end