dommy 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +30 -38
- data/lib/dommy/animation.rb +1 -1
- data/lib/dommy/attr.rb +23 -11
- data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
- data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
- data/lib/dommy/backend.rb +129 -0
- data/lib/dommy/blob.rb +2 -2
- data/lib/dommy/compression_streams.rb +4 -4
- data/lib/dommy/cookie_store.rb +1 -1
- data/lib/dommy/crypto.rb +9 -8
- data/lib/dommy/css.rb +7 -7
- data/lib/dommy/custom_elements.rb +6 -6
- data/lib/dommy/document.rb +98 -32
- data/lib/dommy/dom_parser.rb +5 -4
- data/lib/dommy/element.rb +231 -50
- data/lib/dommy/event.rb +61 -25
- data/lib/dommy/event_source.rb +8 -8
- data/lib/dommy/fetch.rb +14 -6
- data/lib/dommy/file_reader.rb +3 -3
- data/lib/dommy/form_data.rb +1 -3
- data/lib/dommy/history.rb +7 -4
- data/lib/dommy/html_collection.rb +4 -4
- data/lib/dommy/html_elements.rb +110 -42
- data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
- data/lib/dommy/internal/dom_matching.rb +3 -3
- data/lib/dommy/internal/node_traversal.rb +1 -1
- data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
- data/lib/dommy/internal/template_content_registry.rb +6 -6
- data/lib/dommy/intersection_observer.rb +2 -2
- data/lib/dommy/location.rb +8 -4
- data/lib/dommy/media_query_list.rb +3 -3
- data/lib/dommy/message_channel.rb +9 -9
- data/lib/dommy/mutation_observer.rb +21 -11
- data/lib/dommy/navigator.rb +12 -12
- data/lib/dommy/node.rb +12 -0
- data/lib/dommy/notification.rb +3 -3
- data/lib/dommy/parser.rb +13 -13
- data/lib/dommy/performance_observer.rb +2 -2
- data/lib/dommy/range.rb +2 -2
- data/lib/dommy/resize_observer.rb +2 -2
- data/lib/dommy/shadow_root.rb +10 -8
- data/lib/dommy/streams.rb +22 -22
- data/lib/dommy/text_codec.rb +4 -4
- data/lib/dommy/tree_walker.rb +21 -21
- data/lib/dommy/url.rb +25 -8
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +13 -13
- data/lib/dommy/window.rb +14 -1
- data/lib/dommy/worker.rb +5 -5
- data/lib/dommy/xml_http_request.rb +19 -4
- data/lib/dommy.rb +12 -2
- metadata +12 -26
data/lib/dommy/element.rb
CHANGED
|
@@ -9,7 +9,9 @@ module Dommy
|
|
|
9
9
|
include EventTarget
|
|
10
10
|
include Node
|
|
11
11
|
|
|
12
|
-
attr_reader :
|
|
12
|
+
attr_reader :document
|
|
13
|
+
|
|
14
|
+
def __dommy_backend_node__ = @__node__
|
|
13
15
|
|
|
14
16
|
def initialize(document, nokogiri_node)
|
|
15
17
|
@document = document
|
|
@@ -98,6 +100,12 @@ module Dommy
|
|
|
98
100
|
end
|
|
99
101
|
end
|
|
100
102
|
|
|
103
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
104
|
+
JS_METHOD_NAMES = %w[cloneNode querySelector querySelectorAll getElementById appendChild].freeze
|
|
105
|
+
def __js_method_names__
|
|
106
|
+
JS_METHOD_NAMES
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
def __js_call__(method, args)
|
|
102
110
|
case method
|
|
103
111
|
when "cloneNode"
|
|
@@ -128,9 +136,9 @@ module Dommy
|
|
|
128
136
|
def detach_dom_nodes(value)
|
|
129
137
|
case value
|
|
130
138
|
when String
|
|
131
|
-
[@document.create_text_node(value).
|
|
139
|
+
[@document.create_text_node(value).__dommy_backend_node__]
|
|
132
140
|
else
|
|
133
|
-
node = value.respond_to?(:
|
|
141
|
+
node = value.respond_to?(:__dommy_backend_node__) ? value.__dommy_backend_node__ : nil
|
|
134
142
|
return [] unless node
|
|
135
143
|
|
|
136
144
|
node.unlink if node.parent
|
|
@@ -147,7 +155,7 @@ module Dommy
|
|
|
147
155
|
|
|
148
156
|
# Fragments aren't part of the bubble chain; nil terminates
|
|
149
157
|
# bubbling at the boundary (shadow root, detached fragment, etc.).
|
|
150
|
-
def
|
|
158
|
+
def __internal_event_parent__
|
|
151
159
|
nil
|
|
152
160
|
end
|
|
153
161
|
end
|
|
@@ -157,7 +165,7 @@ module Dommy
|
|
|
157
165
|
class CharacterDataNode
|
|
158
166
|
include Node
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
def __dommy_backend_node__ = @__node__
|
|
161
169
|
|
|
162
170
|
def initialize(document, nokogiri_node)
|
|
163
171
|
@document = document
|
|
@@ -191,7 +199,19 @@ module Dommy
|
|
|
191
199
|
end
|
|
192
200
|
|
|
193
201
|
def remove
|
|
202
|
+
parent = @__node__.parent
|
|
203
|
+
removed = @__node__
|
|
194
204
|
@__node__.unlink
|
|
205
|
+
# Mirror Element#remove_child: notify with the Nokogiri::Node
|
|
206
|
+
# (not the Dommy wrapper) so MutationCoordinator's wrap_node
|
|
207
|
+
# cache keys consistently.
|
|
208
|
+
if parent
|
|
209
|
+
@document.notify_child_list_mutation(
|
|
210
|
+
target_node: parent,
|
|
211
|
+
added_nodes: [],
|
|
212
|
+
removed_nodes: [removed]
|
|
213
|
+
)
|
|
214
|
+
end
|
|
195
215
|
nil
|
|
196
216
|
end
|
|
197
217
|
|
|
@@ -243,16 +263,105 @@ module Dommy
|
|
|
243
263
|
nil
|
|
244
264
|
end
|
|
245
265
|
|
|
246
|
-
|
|
266
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
267
|
+
JS_METHOD_NAMES = %w[remove before after replaceWith].freeze
|
|
268
|
+
def __js_method_names__
|
|
269
|
+
JS_METHOD_NAMES
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def __js_call__(method, args)
|
|
247
273
|
case method
|
|
248
274
|
when "remove"
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
remove
|
|
276
|
+
when "before"
|
|
277
|
+
before(*args)
|
|
278
|
+
when "after"
|
|
279
|
+
after(*args)
|
|
280
|
+
when "replaceWith"
|
|
281
|
+
replace_with(*args)
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# ChildNode mixin: WHATWG DOM defines `before`, `after`,
|
|
286
|
+
# `replaceWith` on all child nodes, including Text and Comment.
|
|
287
|
+
# Implementations operate on the Nokogiri layer and notify the
|
|
288
|
+
# MutationObserver with the underlying nodes (mirroring
|
|
289
|
+
# Element#remove_child / replace_child).
|
|
290
|
+
|
|
291
|
+
def before(*args)
|
|
292
|
+
parent = @__node__.parent
|
|
293
|
+
return nil unless parent
|
|
294
|
+
|
|
295
|
+
added = args.map { |arg| coerce_node(arg) }.compact
|
|
296
|
+
added.reverse_each { |node| @__node__.add_previous_sibling(node) }
|
|
297
|
+
notify_child_list_added(parent, added)
|
|
298
|
+
nil
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def after(*args)
|
|
302
|
+
parent = @__node__.parent
|
|
303
|
+
return nil unless parent
|
|
304
|
+
|
|
305
|
+
added = args.map { |arg| coerce_node(arg) }.compact
|
|
306
|
+
anchor = @__node__.next_sibling
|
|
307
|
+
if anchor
|
|
308
|
+
added.reverse_each { |node| anchor.add_previous_sibling(node) }
|
|
309
|
+
else
|
|
310
|
+
added.each { |node| parent.add_child(node) }
|
|
311
|
+
end
|
|
312
|
+
notify_child_list_added(parent, added)
|
|
313
|
+
nil
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def replace_with(*args)
|
|
317
|
+
parent = @__node__.parent
|
|
318
|
+
return nil unless parent
|
|
319
|
+
|
|
320
|
+
added = args.map { |arg| coerce_node(arg) }.compact
|
|
321
|
+
removed = @__node__
|
|
322
|
+
anchor = @__node__.next_sibling
|
|
323
|
+
@__node__.unlink
|
|
324
|
+
if anchor
|
|
325
|
+
added.reverse_each { |node| anchor.add_previous_sibling(node) }
|
|
326
|
+
else
|
|
327
|
+
added.each { |node| parent.add_child(node) }
|
|
251
328
|
end
|
|
329
|
+
@document.notify_child_list_mutation(
|
|
330
|
+
target_node: parent,
|
|
331
|
+
added_nodes: added,
|
|
332
|
+
removed_nodes: [removed]
|
|
333
|
+
)
|
|
334
|
+
nil
|
|
252
335
|
end
|
|
253
336
|
|
|
254
337
|
private
|
|
255
338
|
|
|
339
|
+
# Coerce a `before` / `after` / `replaceWith` argument into a raw
|
|
340
|
+
# Nokogiri node, ready to be linked into a parent. Strings become
|
|
341
|
+
# fresh text nodes; existing nodes are detached from their current
|
|
342
|
+
# parent first (matching Element#detach_dom_nodes minus the
|
|
343
|
+
# Fragment branch which is rarely needed off a text/comment node).
|
|
344
|
+
def coerce_node(arg)
|
|
345
|
+
case arg
|
|
346
|
+
when String
|
|
347
|
+
@document.create_text_node(arg).__dommy_backend_node__
|
|
348
|
+
else
|
|
349
|
+
node = arg.respond_to?(:__dommy_backend_node__) ? arg.__dommy_backend_node__ : nil
|
|
350
|
+
node.unlink if node && node.parent
|
|
351
|
+
node
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def notify_child_list_added(parent, added)
|
|
356
|
+
return if added.empty?
|
|
357
|
+
|
|
358
|
+
@document.notify_child_list_mutation(
|
|
359
|
+
target_node: parent,
|
|
360
|
+
added_nodes: added,
|
|
361
|
+
removed_nodes: []
|
|
362
|
+
)
|
|
363
|
+
end
|
|
364
|
+
|
|
256
365
|
def write_data(value)
|
|
257
366
|
old = @__node__.content
|
|
258
367
|
@__node__.content = value.to_s
|
|
@@ -265,6 +374,12 @@ module Dommy
|
|
|
265
374
|
3
|
|
266
375
|
end
|
|
267
376
|
|
|
377
|
+
# Own __js_call__ methods, on top of CharacterDataNode's.
|
|
378
|
+
JS_METHOD_NAMES = %w[cloneNode].freeze
|
|
379
|
+
def __js_method_names__
|
|
380
|
+
super + JS_METHOD_NAMES
|
|
381
|
+
end
|
|
382
|
+
|
|
268
383
|
def __js_call__(method, args)
|
|
269
384
|
case method
|
|
270
385
|
when "cloneNode"
|
|
@@ -280,6 +395,12 @@ module Dommy
|
|
|
280
395
|
8
|
|
281
396
|
end
|
|
282
397
|
|
|
398
|
+
# Own __js_call__ methods, on top of CharacterDataNode's.
|
|
399
|
+
JS_METHOD_NAMES = %w[cloneNode].freeze
|
|
400
|
+
def __js_method_names__
|
|
401
|
+
super + JS_METHOD_NAMES
|
|
402
|
+
end
|
|
403
|
+
|
|
283
404
|
def __js_call__(method, args)
|
|
284
405
|
case method
|
|
285
406
|
when "cloneNode"
|
|
@@ -311,7 +432,7 @@ module Dommy
|
|
|
311
432
|
end
|
|
312
433
|
|
|
313
434
|
def value
|
|
314
|
-
@element.
|
|
435
|
+
@element.__dommy_backend_node__["class"].to_s
|
|
315
436
|
end
|
|
316
437
|
|
|
317
438
|
def value=(new_value)
|
|
@@ -383,6 +504,12 @@ module Dommy
|
|
|
383
504
|
nil
|
|
384
505
|
end
|
|
385
506
|
|
|
507
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
508
|
+
JS_METHOD_NAMES = %w[add remove contains toggle replace item].freeze
|
|
509
|
+
def __js_method_names__
|
|
510
|
+
JS_METHOD_NAMES
|
|
511
|
+
end
|
|
512
|
+
|
|
386
513
|
def __js_call__(method, args)
|
|
387
514
|
case method
|
|
388
515
|
when "add"
|
|
@@ -444,14 +571,14 @@ module Dommy
|
|
|
444
571
|
end
|
|
445
572
|
|
|
446
573
|
def class_tokens
|
|
447
|
-
raw = @element.
|
|
574
|
+
raw = @element.__dommy_backend_node__["class"].to_s
|
|
448
575
|
raw.split(/\s+/).reject(&:empty?)
|
|
449
576
|
end
|
|
450
577
|
|
|
451
578
|
def update_tokens
|
|
452
579
|
tokens = yield(class_tokens)
|
|
453
580
|
if tokens.empty?
|
|
454
|
-
@element.remove_attribute("class") if @element.
|
|
581
|
+
@element.remove_attribute("class") if @element.__dommy_backend_node__.key?("class")
|
|
455
582
|
else
|
|
456
583
|
@element.set_attribute("class", tokens.join(" "))
|
|
457
584
|
end
|
|
@@ -467,7 +594,7 @@ module Dommy
|
|
|
467
594
|
end
|
|
468
595
|
|
|
469
596
|
def __js_get__(key)
|
|
470
|
-
@element.
|
|
597
|
+
@element.__dommy_backend_node__[attr_name(key)]
|
|
471
598
|
end
|
|
472
599
|
|
|
473
600
|
def __js_set__(key, value)
|
|
@@ -629,6 +756,12 @@ module Dommy
|
|
|
629
756
|
nil
|
|
630
757
|
end
|
|
631
758
|
|
|
759
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
760
|
+
JS_METHOD_NAMES = %w[setProperty removeProperty getPropertyValue item].freeze
|
|
761
|
+
def __js_method_names__
|
|
762
|
+
JS_METHOD_NAMES
|
|
763
|
+
end
|
|
764
|
+
|
|
632
765
|
def __js_call__(method, args)
|
|
633
766
|
case method
|
|
634
767
|
when "setProperty"
|
|
@@ -674,7 +807,7 @@ module Dommy
|
|
|
674
807
|
end
|
|
675
808
|
|
|
676
809
|
def properties
|
|
677
|
-
raw = @element.
|
|
810
|
+
raw = @element.__dommy_backend_node__["style"].to_s
|
|
678
811
|
raw.split(";").each_with_object({}) do |entry, out|
|
|
679
812
|
key, value = entry.split(":", 2)
|
|
680
813
|
next unless key && value
|
|
@@ -685,7 +818,7 @@ module Dommy
|
|
|
685
818
|
|
|
686
819
|
def write_properties(props)
|
|
687
820
|
if props.empty?
|
|
688
|
-
@element.remove_attribute("style") if @element.
|
|
821
|
+
@element.remove_attribute("style") if @element.__dommy_backend_node__.key?("style")
|
|
689
822
|
else
|
|
690
823
|
@element.set_attribute("style", props.map { |k, v| "#{k}:#{v}" }.join(";"))
|
|
691
824
|
end
|
|
@@ -696,7 +829,9 @@ module Dommy
|
|
|
696
829
|
include EventTarget
|
|
697
830
|
include Node
|
|
698
831
|
|
|
699
|
-
attr_reader :
|
|
832
|
+
attr_reader :document
|
|
833
|
+
|
|
834
|
+
def __dommy_backend_node__ = @__node__
|
|
700
835
|
|
|
701
836
|
def initialize(document, nokogiri_node)
|
|
702
837
|
@document = document
|
|
@@ -860,7 +995,7 @@ module Dommy
|
|
|
860
995
|
parent = @__node__.parent
|
|
861
996
|
return unless parent
|
|
862
997
|
|
|
863
|
-
if parent.is_a?(
|
|
998
|
+
if parent.is_a?(Backend.document_class)
|
|
864
999
|
raise(
|
|
865
1000
|
DOMException::NoModificationAllowedError,
|
|
866
1001
|
"outerHTML setter not allowed on the document element"
|
|
@@ -884,9 +1019,9 @@ module Dommy
|
|
|
884
1019
|
# `el.contains(other)` — true if `other` is `el` itself or any
|
|
885
1020
|
# descendant. Per spec, returns false for null/non-Node.
|
|
886
1021
|
def contains?(other)
|
|
887
|
-
return false unless other.respond_to?(:
|
|
1022
|
+
return false unless other.respond_to?(:__dommy_backend_node__)
|
|
888
1023
|
|
|
889
|
-
other_node = other.
|
|
1024
|
+
other_node = other.__dommy_backend_node__
|
|
890
1025
|
return true if other_node == @__node__
|
|
891
1026
|
|
|
892
1027
|
Internal::NodeTraversal.ancestor_of?(@__node__, other_node)
|
|
@@ -897,7 +1032,7 @@ module Dommy
|
|
|
897
1032
|
# inside a shadow tree, returns that ShadowRoot. Otherwise walks
|
|
898
1033
|
# until we hit the Nokogiri Document (then returns the Document).
|
|
899
1034
|
def root_node
|
|
900
|
-
sr = @document.
|
|
1035
|
+
sr = @document.__internal_shadow_root_containing__(@__node__)
|
|
901
1036
|
return sr if sr
|
|
902
1037
|
|
|
903
1038
|
current = @__node__
|
|
@@ -905,7 +1040,7 @@ module Dommy
|
|
|
905
1040
|
loop do
|
|
906
1041
|
parent = current.respond_to?(:parent) ? current.parent : nil
|
|
907
1042
|
break unless parent
|
|
908
|
-
if parent.is_a?(
|
|
1043
|
+
if parent.is_a?(Backend.document_class)
|
|
909
1044
|
attached = true
|
|
910
1045
|
break
|
|
911
1046
|
end
|
|
@@ -1054,14 +1189,14 @@ module Dommy
|
|
|
1054
1189
|
|
|
1055
1190
|
parent = current.respond_to?(:parent) ? current.parent : nil
|
|
1056
1191
|
return false unless parent
|
|
1057
|
-
return true if parent.is_a?(
|
|
1192
|
+
return true if parent.is_a?(Backend.document_class)
|
|
1058
1193
|
|
|
1059
|
-
sr = @document.
|
|
1194
|
+
sr = @document.__internal_shadow_root_for_fragment__(parent)
|
|
1060
1195
|
if sr
|
|
1061
1196
|
host = sr.host
|
|
1062
1197
|
return false unless host
|
|
1063
1198
|
|
|
1064
|
-
current = host.
|
|
1199
|
+
current = host.__dommy_backend_node__
|
|
1065
1200
|
else
|
|
1066
1201
|
current = parent
|
|
1067
1202
|
end
|
|
@@ -1074,12 +1209,12 @@ module Dommy
|
|
|
1074
1209
|
# tests rely on `document.activeElement` updating. Track the most
|
|
1075
1210
|
# recently focused element on the document.
|
|
1076
1211
|
def focus
|
|
1077
|
-
@document.
|
|
1212
|
+
@document.__internal_set_active_element__(self)
|
|
1078
1213
|
nil
|
|
1079
1214
|
end
|
|
1080
1215
|
|
|
1081
1216
|
def blur
|
|
1082
|
-
@document.
|
|
1217
|
+
@document.__internal_set_active_element__(nil)
|
|
1083
1218
|
nil
|
|
1084
1219
|
end
|
|
1085
1220
|
|
|
@@ -1149,7 +1284,7 @@ module Dommy
|
|
|
1149
1284
|
|
|
1150
1285
|
# Internal — gives access to the shadow root regardless of mode.
|
|
1151
1286
|
# Used by event composition / `composedPath()`.
|
|
1152
|
-
def
|
|
1287
|
+
def __internal_shadow_root__
|
|
1153
1288
|
@__shadow_root
|
|
1154
1289
|
end
|
|
1155
1290
|
|
|
@@ -1157,7 +1292,7 @@ module Dommy
|
|
|
1157
1292
|
# "beforebegin", "afterbegin", "beforeend", "afterend". Returns the
|
|
1158
1293
|
# inserted element or nil if position has no anchor (root cases).
|
|
1159
1294
|
def insert_adjacent_element(position, element)
|
|
1160
|
-
return nil unless element.respond_to?(:
|
|
1295
|
+
return nil unless element.respond_to?(:__dommy_backend_node__)
|
|
1161
1296
|
|
|
1162
1297
|
case position.to_s
|
|
1163
1298
|
when "beforebegin"
|
|
@@ -1256,10 +1391,10 @@ module Dommy
|
|
|
1256
1391
|
# nodes).
|
|
1257
1392
|
def compare_document_position(other)
|
|
1258
1393
|
return 0 if equal?(other)
|
|
1259
|
-
return DOCUMENT_POSITION_DISCONNECTED unless other.respond_to?(:
|
|
1394
|
+
return DOCUMENT_POSITION_DISCONNECTED unless other.respond_to?(:__dommy_backend_node__)
|
|
1260
1395
|
|
|
1261
1396
|
self_node = @__node__
|
|
1262
|
-
other_node = other.
|
|
1397
|
+
other_node = other.__dommy_backend_node__
|
|
1263
1398
|
|
|
1264
1399
|
self_ancestors = ancestor_chain(self_node)
|
|
1265
1400
|
other_ancestors = ancestor_chain(other_node)
|
|
@@ -1309,11 +1444,11 @@ module Dommy
|
|
|
1309
1444
|
# suite and standard DOM Node.isEqualNode.
|
|
1310
1445
|
def equal_node?(other)
|
|
1311
1446
|
return false unless other.is_a?(Element)
|
|
1312
|
-
return false unless @__node__.name == other.
|
|
1447
|
+
return false unless @__node__.name == other.__dommy_backend_node__.name
|
|
1313
1448
|
return false unless attribute_signature == other.send(:attribute_signature)
|
|
1314
|
-
return false unless @__node__.children.size == other.
|
|
1449
|
+
return false unless @__node__.children.size == other.__dommy_backend_node__.children.size
|
|
1315
1450
|
|
|
1316
|
-
@__node__.children.zip(other.
|
|
1451
|
+
@__node__.children.zip(other.__dommy_backend_node__.children).all? do |a, b|
|
|
1317
1452
|
wa = @document.wrap_node(a)
|
|
1318
1453
|
wb = @document.wrap_node(b)
|
|
1319
1454
|
wa.respond_to?(:equal_node?) ? wa.equal_node?(wb) : a.content == b.content
|
|
@@ -1541,7 +1676,20 @@ module Dommy
|
|
|
1541
1676
|
def __js_set__(key, value)
|
|
1542
1677
|
case key
|
|
1543
1678
|
when "textContent"
|
|
1679
|
+
# `node.content =` removes all existing children and (if
|
|
1680
|
+
# value is non-empty) appends a single text node. Capture
|
|
1681
|
+
# before/after to feed MutationObserver — mirrors the
|
|
1682
|
+
# innerHTML branch below.
|
|
1683
|
+
removed = @__node__.children.to_a
|
|
1544
1684
|
@__node__.content = value.to_s
|
|
1685
|
+
added = @__node__.children.to_a
|
|
1686
|
+
if removed.any? || added.any?
|
|
1687
|
+
@document.notify_child_list_mutation(
|
|
1688
|
+
target_node: @__node__,
|
|
1689
|
+
added_nodes: added,
|
|
1690
|
+
removed_nodes: removed
|
|
1691
|
+
)
|
|
1692
|
+
end
|
|
1545
1693
|
when "innerHTML"
|
|
1546
1694
|
removed = @__node__.children.to_a
|
|
1547
1695
|
if @__node__.name == "template"
|
|
@@ -1611,6 +1759,21 @@ module Dommy
|
|
|
1611
1759
|
|
|
1612
1760
|
public
|
|
1613
1761
|
|
|
1762
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
1763
|
+
JS_METHOD_NAMES = %w[
|
|
1764
|
+
getAttribute setAttribute hasAttribute removeAttribute getAttributeNames closest
|
|
1765
|
+
querySelector querySelectorAll getElementsByClassName getElementsByTagName
|
|
1766
|
+
insertAdjacentElement insertAdjacentHTML insertAdjacentText toggleAttribute matches
|
|
1767
|
+
toString getAttributeNode setAttributeNode removeAttributeNode focus blur attachShadow
|
|
1768
|
+
addEventListener removeEventListener dispatchEvent appendChild insertBefore removeChild
|
|
1769
|
+
replaceChild cloneNode append prepend replaceChildren before after getInnerHTML getHTML
|
|
1770
|
+
remove replaceWith click getBoundingClientRect getClientRects scrollIntoView scroll
|
|
1771
|
+
scrollTo scrollBy requestFullscreen showPopover hidePopover togglePopover
|
|
1772
|
+
].freeze
|
|
1773
|
+
def __js_method_names__
|
|
1774
|
+
JS_METHOD_NAMES
|
|
1775
|
+
end
|
|
1776
|
+
|
|
1614
1777
|
def __js_call__(method, args)
|
|
1615
1778
|
case method
|
|
1616
1779
|
when "getAttribute"
|
|
@@ -1700,11 +1863,11 @@ module Dommy
|
|
|
1700
1863
|
[]
|
|
1701
1864
|
when "scrollIntoView", "scroll", "scrollTo", "scrollBy"
|
|
1702
1865
|
# No layout — record the request for tests to assert against.
|
|
1703
|
-
@
|
|
1704
|
-
@
|
|
1866
|
+
@scroll_log ||= []
|
|
1867
|
+
@scroll_log << [method, args]
|
|
1705
1868
|
nil
|
|
1706
1869
|
when "requestFullscreen"
|
|
1707
|
-
@document.
|
|
1870
|
+
@document.__internal_set_fullscreen_element__(self)
|
|
1708
1871
|
PromiseValue.resolve(@document.default_view, nil)
|
|
1709
1872
|
when "showPopover"
|
|
1710
1873
|
toggle_popover_state(true)
|
|
@@ -1739,14 +1902,14 @@ module Dommy
|
|
|
1739
1902
|
@document.wrap_node(node)
|
|
1740
1903
|
end
|
|
1741
1904
|
|
|
1742
|
-
def
|
|
1905
|
+
def __internal_event_parent__
|
|
1743
1906
|
parent_node = @__node__.parent
|
|
1744
1907
|
# If our Nokogiri parent is a shadow tree's backing fragment,
|
|
1745
1908
|
# the bubble path's next stop is the ShadowRoot itself — not
|
|
1746
|
-
# the bare Fragment wrapper. The ShadowRoot's
|
|
1909
|
+
# the bare Fragment wrapper. The ShadowRoot's __internal_event_parent__
|
|
1747
1910
|
# will return nil (composed events route to host explicitly).
|
|
1748
|
-
if parent_node.is_a?(
|
|
1749
|
-
sr = @document.
|
|
1911
|
+
if parent_node.is_a?(Backend.document_fragment_class)
|
|
1912
|
+
sr = @document.__internal_shadow_root_for_fragment__(parent_node)
|
|
1750
1913
|
return sr if sr
|
|
1751
1914
|
end
|
|
1752
1915
|
|
|
@@ -1841,13 +2004,28 @@ module Dommy
|
|
|
1841
2004
|
def query_selector(selector)
|
|
1842
2005
|
return nil if selector.nil? || selector.to_s.empty?
|
|
1843
2006
|
|
|
1844
|
-
@document.wrap_node(@__node__.at_css(selector.to_s))
|
|
2007
|
+
@document.wrap_node(@__node__.at_css(selector.to_s, Internal::CSS_PSEUDO_HANDLERS))
|
|
1845
2008
|
end
|
|
1846
2009
|
|
|
1847
2010
|
def query_selector_all(selector)
|
|
1848
2011
|
return NodeList.new if selector.nil? || selector.to_s.empty?
|
|
1849
2012
|
|
|
1850
|
-
NodeList.new(@__node__.css(selector.to_s).map { |node| @document.wrap_node(node) }.compact)
|
|
2013
|
+
NodeList.new(@__node__.css(selector.to_s, Internal::CSS_PSEUDO_HANDLERS).map { |node| @document.wrap_node(node) }.compact)
|
|
2014
|
+
end
|
|
2015
|
+
|
|
2016
|
+
# XPath queries scoped to this element, returning wrapped nodes.
|
|
2017
|
+
def at_xpath(expression)
|
|
2018
|
+
node = @__node__.at_xpath(expression)
|
|
2019
|
+
node && @document.wrap_node(node)
|
|
2020
|
+
end
|
|
2021
|
+
|
|
2022
|
+
def xpath(expression)
|
|
2023
|
+
@__node__.xpath(expression).map { |node| @document.wrap_node(node) }
|
|
2024
|
+
end
|
|
2025
|
+
|
|
2026
|
+
# The XPath string locating this element in its document.
|
|
2027
|
+
def path
|
|
2028
|
+
@__node__.path
|
|
1851
2029
|
end
|
|
1852
2030
|
|
|
1853
2031
|
def append_child(child)
|
|
@@ -1993,10 +2171,10 @@ module Dommy
|
|
|
1993
2171
|
# produce a cycle (inserting an ancestor as a descendant of
|
|
1994
2172
|
# itself). Strings and Fragments are always safe.
|
|
1995
2173
|
def check_hierarchy!(child)
|
|
1996
|
-
return unless child.respond_to?(:
|
|
2174
|
+
return unless child.respond_to?(:__dommy_backend_node__)
|
|
1997
2175
|
|
|
1998
|
-
node = child.
|
|
1999
|
-
return unless node.is_a?(
|
|
2176
|
+
node = child.__dommy_backend_node__
|
|
2177
|
+
return unless node.is_a?(Backend.node_class)
|
|
2000
2178
|
|
|
2001
2179
|
if node == @__node__ || @__node__.ancestors.any? { |a| a == node }
|
|
2002
2180
|
raise(
|
|
@@ -2013,13 +2191,13 @@ module Dommy
|
|
|
2013
2191
|
def detach_dom_nodes(value)
|
|
2014
2192
|
case value
|
|
2015
2193
|
when Element, TextNode, CommentNode
|
|
2016
|
-
node = value.
|
|
2194
|
+
node = value.__dommy_backend_node__
|
|
2017
2195
|
node.unlink if node.parent
|
|
2018
2196
|
[node]
|
|
2019
2197
|
when Fragment
|
|
2020
2198
|
value.extract_children
|
|
2021
2199
|
when String
|
|
2022
|
-
[@document.create_text_node(value).
|
|
2200
|
+
[@document.create_text_node(value).__dommy_backend_node__]
|
|
2023
2201
|
else
|
|
2024
2202
|
node = unwrap_dom_node(value)
|
|
2025
2203
|
return [] unless node
|
|
@@ -2030,7 +2208,7 @@ module Dommy
|
|
|
2030
2208
|
end
|
|
2031
2209
|
|
|
2032
2210
|
def unwrap_dom_node(value)
|
|
2033
|
-
return value.
|
|
2211
|
+
return value.__dommy_backend_node__ if value.respond_to?(:__dommy_backend_node__)
|
|
2034
2212
|
|
|
2035
2213
|
nil
|
|
2036
2214
|
end
|
|
@@ -2066,11 +2244,11 @@ module Dommy
|
|
|
2066
2244
|
end
|
|
2067
2245
|
|
|
2068
2246
|
# Test inspector for scroll calls (no real layout to scroll).
|
|
2069
|
-
def
|
|
2070
|
-
@
|
|
2247
|
+
def __test_scroll_log__
|
|
2248
|
+
@scroll_log ||= []
|
|
2071
2249
|
end
|
|
2072
2250
|
|
|
2073
|
-
public :
|
|
2251
|
+
public :__test_scroll_log__
|
|
2074
2252
|
|
|
2075
2253
|
# Re-expose snake_case methods that the JS bridge dispatch routes
|
|
2076
2254
|
# to. Defined as private originally so internal helpers (element_children,
|
|
@@ -2088,6 +2266,9 @@ module Dommy
|
|
|
2088
2266
|
:clone_node,
|
|
2089
2267
|
:query_selector,
|
|
2090
2268
|
:query_selector_all,
|
|
2269
|
+
:at_xpath,
|
|
2270
|
+
:xpath,
|
|
2271
|
+
:path,
|
|
2091
2272
|
:closest,
|
|
2092
2273
|
:animate,
|
|
2093
2274
|
:get_animations,
|