dommy 0.5.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -13
  3. data/lib/dommy/animation.rb +288 -0
  4. data/lib/dommy/attr.rb +23 -11
  5. data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
  6. data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
  7. data/lib/dommy/backend.rb +129 -0
  8. data/lib/dommy/blob.rb +2 -2
  9. data/lib/dommy/compression_streams.rb +147 -0
  10. data/lib/dommy/cookie_store.rb +128 -0
  11. data/lib/dommy/crypto.rb +396 -0
  12. data/lib/dommy/css.rb +7 -7
  13. data/lib/dommy/custom_elements.rb +6 -6
  14. data/lib/dommy/document.rb +190 -32
  15. data/lib/dommy/dom_parser.rb +5 -4
  16. data/lib/dommy/element.rb +356 -53
  17. data/lib/dommy/event.rb +431 -25
  18. data/lib/dommy/event_source.rb +131 -0
  19. data/lib/dommy/fetch.rb +76 -6
  20. data/lib/dommy/file_reader.rb +176 -0
  21. data/lib/dommy/form_data.rb +1 -3
  22. data/lib/dommy/history.rb +82 -0
  23. data/lib/dommy/html_collection.rb +4 -4
  24. data/lib/dommy/html_elements.rb +130 -67
  25. data/lib/dommy/internal/cookie_jar.rb +2 -0
  26. data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
  27. data/lib/dommy/internal/dom_matching.rb +4 -4
  28. data/lib/dommy/internal/idna.rb +443 -0
  29. data/lib/dommy/internal/idna_data.rb +10379 -0
  30. data/lib/dommy/internal/ipv4_parser.rb +78 -0
  31. data/lib/dommy/internal/node_traversal.rb +1 -1
  32. data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
  33. data/lib/dommy/internal/observable_callback.rb +25 -0
  34. data/lib/dommy/internal/punycode.rb +202 -0
  35. data/lib/dommy/internal/range_text_serializer.rb +72 -0
  36. data/lib/dommy/internal/reflected_attributes.rb +45 -0
  37. data/lib/dommy/internal/template_content_registry.rb +6 -6
  38. data/lib/dommy/intersection_observer.rb +82 -0
  39. data/lib/dommy/{router.rb → location.rb} +8 -142
  40. data/lib/dommy/media_query_list.rb +118 -0
  41. data/lib/dommy/message_channel.rb +249 -0
  42. data/lib/dommy/{observer.rb → mutation_observer.rb} +21 -11
  43. data/lib/dommy/navigator.rb +365 -5
  44. data/lib/dommy/node.rb +12 -0
  45. data/lib/dommy/notification.rb +89 -0
  46. data/lib/dommy/parser.rb +13 -13
  47. data/lib/dommy/performance.rb +146 -0
  48. data/lib/dommy/performance_observer.rb +55 -0
  49. data/lib/dommy/range.rb +597 -0
  50. data/lib/dommy/resize_observer.rb +53 -0
  51. data/lib/dommy/shadow_root.rb +10 -8
  52. data/lib/dommy/streams.rb +386 -0
  53. data/lib/dommy/svg_elements.rb +3863 -0
  54. data/lib/dommy/text_codec.rb +175 -0
  55. data/lib/dommy/tree_walker.rb +21 -21
  56. data/lib/dommy/url.rb +274 -29
  57. data/lib/dommy/url_pattern.rb +144 -0
  58. data/lib/dommy/version.rb +1 -1
  59. data/lib/dommy/web_socket.rb +209 -0
  60. data/lib/dommy/window.rb +369 -0
  61. data/lib/dommy/worker.rb +143 -0
  62. data/lib/dommy/xml_http_request.rb +438 -0
  63. data/lib/dommy.rb +43 -5
  64. metadata +44 -29
  65. data/lib/dommy/world.rb +0 -209
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dommy
4
+ # `TextEncoder` — encodes a String into UTF-8 bytes.
5
+ # Per spec, only "utf-8" encoding is supported.
6
+ #
7
+ # Spec: https://encoding.spec.whatwg.org/#textencoder
8
+ class TextEncoder
9
+ def encoding
10
+ "utf-8"
11
+ end
12
+
13
+ # encode(string) → Array<Integer> (bytes).
14
+ # Browsers return a Uint8Array; Dommy returns the equivalent Ruby
15
+ # byte array since there is no typed-array layer.
16
+ def encode(input = "")
17
+ input.to_s.encode(Encoding::UTF_8).bytes
18
+ end
19
+
20
+ def __js_get__(key)
21
+ key == "encoding" ? encoding : nil
22
+ end
23
+
24
+ def __js_call__(method, args)
25
+ case method
26
+ when "encode"
27
+ encode(args[0])
28
+ end
29
+ end
30
+ end
31
+
32
+ # `TextDecoder` — decodes bytes into a String. Supports utf-8,
33
+ # utf-16, utf-16le, utf-16be, iso-8859-1 (best-effort).
34
+ #
35
+ # Spec: https://encoding.spec.whatwg.org/#textdecoder
36
+ class TextDecoder
37
+ def initialize(label = "utf-8", _options = nil)
38
+ @encoding = normalize_encoding(label.to_s)
39
+ end
40
+
41
+ def encoding
42
+ @encoding
43
+ end
44
+
45
+ # decode(bytes) → String. Accepts Array<Integer> (byte values),
46
+ # a binary String, or anything responding to to_a/bytes.
47
+ def decode(input = nil, _options = nil)
48
+ return "" if input.nil?
49
+
50
+ bytes = case input
51
+ when String
52
+ input.b
53
+ when Array
54
+ input.pack("C*")
55
+ else
56
+ input.respond_to?(:to_a) ? input.to_a.pack("C*") : input.to_s
57
+ end
58
+
59
+ bytes.force_encoding(ruby_encoding).encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
60
+ end
61
+
62
+ def __js_get__(key)
63
+ key == "encoding" ? encoding : nil
64
+ end
65
+
66
+ def __js_call__(method, args)
67
+ case method
68
+ when "decode"
69
+ decode(args[0], args[1])
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def normalize_encoding(label)
76
+ l = label.downcase.strip
77
+ case l
78
+ when "utf-8", "utf8"
79
+ "utf-8"
80
+ when "utf-16", "utf-16le"
81
+ "utf-16le"
82
+ when "utf-16be"
83
+ "utf-16be"
84
+ when "iso-8859-1", "latin1"
85
+ "iso-8859-1"
86
+ else
87
+ "utf-8"
88
+ end
89
+ end
90
+
91
+ def ruby_encoding
92
+ case @encoding
93
+ when "utf-8"
94
+ Encoding::UTF_8
95
+ when "utf-16le"
96
+ Encoding::UTF_16LE
97
+ when "utf-16be"
98
+ Encoding::UTF_16BE
99
+ when "iso-8859-1"
100
+ Encoding::ISO_8859_1
101
+ else
102
+ Encoding::UTF_8
103
+ end
104
+ end
105
+ end
106
+
107
+ # `TextEncoderStream` — Stream-shaped wrapper over `TextEncoder`.
108
+ # `write(string)` flushes UTF-8 bytes downstream.
109
+ class TextEncoderStream
110
+ attr_reader :readable, :writable
111
+
112
+ def initialize(window)
113
+ encoder = TextEncoder.new
114
+ @readable = ReadableStream.new(window)
115
+ controller = TransformStreamDefaultController.new(@readable)
116
+
117
+ @writable = WritableStream.new(
118
+ window,
119
+ {
120
+ "write" => proc { |chunk| controller.enqueue(encoder.encode(chunk)) },
121
+ "close" => proc { @readable.__internal_close__ },
122
+ "abort" => proc { |r| @readable.__internal_error__(r) }
123
+ }
124
+ )
125
+ end
126
+
127
+ def encoding
128
+ "utf-8"
129
+ end
130
+
131
+ def __js_get__(key)
132
+ case key
133
+ when "readable"
134
+ @readable
135
+ when "writable"
136
+ @writable
137
+ when "encoding"
138
+ encoding
139
+ end
140
+ end
141
+ end
142
+
143
+ # `TextDecoderStream` — Stream-shaped wrapper over `TextDecoder`.
144
+ # `write(bytes)` flushes decoded strings downstream.
145
+ class TextDecoderStream
146
+ attr_reader :readable, :writable, :encoding
147
+
148
+ def initialize(window, label = "utf-8", _options = nil)
149
+ decoder = TextDecoder.new(label)
150
+ @encoding = decoder.encoding
151
+ @readable = ReadableStream.new(window)
152
+ controller = TransformStreamDefaultController.new(@readable)
153
+
154
+ @writable = WritableStream.new(
155
+ window,
156
+ {
157
+ "write" => proc { |chunk| controller.enqueue(decoder.decode(chunk)) },
158
+ "close" => proc { @readable.__internal_close__ },
159
+ "abort" => proc { |r| @readable.__internal_error__(r) }
160
+ }
161
+ )
162
+ end
163
+
164
+ def __js_get__(key)
165
+ case key
166
+ when "readable"
167
+ @readable
168
+ when "writable"
169
+ @writable
170
+ when "encoding"
171
+ @encoding
172
+ end
173
+ end
174
+ end
175
+ end
@@ -45,9 +45,11 @@ module Dommy
45
45
  # tree rooted at `root` and filter by `whatToShow` + an optional
46
46
  # filter callable (or object with `acceptNode`).
47
47
  module TreeTraversalCore
48
+ private
49
+
48
50
  # Returns FILTER_ACCEPT / FILTER_REJECT / FILTER_SKIP for the
49
51
  # given wrapped node.
50
- def __accept__(node)
52
+ def accept(node)
51
53
  return NodeFilter::FILTER_REJECT unless node
52
54
  return NodeFilter::FILTER_SKIP if (NodeFilter.bitmask_for(node) & @what_to_show) == 0
53
55
 
@@ -55,8 +57,6 @@ module Dommy
55
57
  result || NodeFilter::FILTER_ACCEPT
56
58
  end
57
59
 
58
- private
59
-
60
60
  def invoke_filter(node)
61
61
  return NodeFilter::FILTER_ACCEPT if @filter.nil?
62
62
 
@@ -92,7 +92,7 @@ module Dommy
92
92
  def next_node
93
93
  node = first_descendant_or_following(@current_node)
94
94
  while node
95
- verdict = __accept__(node)
95
+ verdict = accept(node)
96
96
  if verdict == NodeFilter::FILTER_ACCEPT
97
97
  @current_node = node
98
98
  return node
@@ -109,7 +109,7 @@ module Dommy
109
109
  def previous_node
110
110
  node = preceding(@current_node)
111
111
  while node && node != @root
112
- verdict = __accept__(node)
112
+ verdict = accept(node)
113
113
  if verdict == NodeFilter::FILTER_ACCEPT
114
114
  @current_node = node
115
115
  return node
@@ -124,7 +124,7 @@ module Dommy
124
124
  def parent_node
125
125
  node = wrapped_parent(@current_node)
126
126
  while node && reachable_from_root?(node)
127
- return @current_node = node if __accept__(node) == NodeFilter::FILTER_ACCEPT
127
+ return @current_node = node if accept(node) == NodeFilter::FILTER_ACCEPT
128
128
 
129
129
  node = wrapped_parent(node)
130
130
  end
@@ -192,7 +192,7 @@ module Dommy
192
192
  def walk_siblings(start, direction)
193
193
  node = start
194
194
  while node
195
- v = __accept__(node)
195
+ v = accept(node)
196
196
  return @current_node = node if v == NodeFilter::FILTER_ACCEPT
197
197
 
198
198
  node = (v == NodeFilter::FILTER_REJECT) ? nil : send(direction, node)
@@ -246,30 +246,30 @@ module Dommy
246
246
  end
247
247
 
248
248
  def wrapped_parent(node)
249
- parent_nk = node.respond_to?(:__node__) ? node.__node__.parent : nil
250
- return nil unless parent_nk && !parent_nk.is_a?(Nokogiri::XML::Document)
249
+ parent_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.parent : nil
250
+ return nil unless parent_nk && !parent_nk.is_a?(Backend.document_class)
251
251
 
252
252
  doc = node.instance_variable_get(:@document) || (@root.respond_to?(:document) ? @root.document : @root)
253
253
  doc.wrap_node(parent_nk)
254
254
  end
255
255
 
256
256
  def first_wrapped_child(node)
257
- child_nk = node.respond_to?(:__node__) ? node.__node__.children.first : nil
257
+ child_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.first : nil
258
258
  child_nk && document_for(node).wrap_node(child_nk)
259
259
  end
260
260
 
261
261
  def last_wrapped_child(node)
262
- child_nk = node.respond_to?(:__node__) ? node.__node__.children.last : nil
262
+ child_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.last : nil
263
263
  child_nk && document_for(node).wrap_node(child_nk)
264
264
  end
265
265
 
266
266
  def next_sibling_wrapped(node)
267
- n = node.respond_to?(:__node__) ? node.__node__.next : nil
267
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.next : nil
268
268
  n && document_for(node).wrap_node(n)
269
269
  end
270
270
 
271
271
  def previous_sibling_wrapped(node)
272
- n = node.respond_to?(:__node__) ? node.__node__.previous : nil
272
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.previous : nil
273
273
  n && document_for(node).wrap_node(n)
274
274
  end
275
275
 
@@ -306,7 +306,7 @@ module Dommy
306
306
 
307
307
  @reference_node = node
308
308
  @pointer_before_reference = false
309
- return node if __accept__(node) == NodeFilter::FILTER_ACCEPT
309
+ return node if accept(node) == NodeFilter::FILTER_ACCEPT
310
310
  end
311
311
  end
312
312
 
@@ -322,7 +322,7 @@ module Dommy
322
322
 
323
323
  @reference_node = node
324
324
  @pointer_before_reference = true
325
- return node if __accept__(node) == NodeFilter::FILTER_ACCEPT
325
+ return node if accept(node) == NodeFilter::FILTER_ACCEPT
326
326
  end
327
327
  end
328
328
 
@@ -392,28 +392,28 @@ module Dommy
392
392
  end
393
393
 
394
394
  def first_child_node(node)
395
- n = node.respond_to?(:__node__) ? node.__node__.children.first : nil
395
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.first : nil
396
396
  n && document_for(node).wrap_node(n)
397
397
  end
398
398
 
399
399
  def last_child_node(node)
400
- n = node.respond_to?(:__node__) ? node.__node__.children.last : nil
400
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.last : nil
401
401
  n && document_for(node).wrap_node(n)
402
402
  end
403
403
 
404
404
  def next_sibling_node(node)
405
- n = node.respond_to?(:__node__) ? node.__node__.next : nil
405
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.next : nil
406
406
  n && document_for(node).wrap_node(n)
407
407
  end
408
408
 
409
409
  def previous_sibling_node(node)
410
- n = node.respond_to?(:__node__) ? node.__node__.previous : nil
410
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.previous : nil
411
411
  n && document_for(node).wrap_node(n)
412
412
  end
413
413
 
414
414
  def parent_node_of(node)
415
- parent_nk = node.respond_to?(:__node__) ? node.__node__.parent : nil
416
- return nil unless parent_nk && !parent_nk.is_a?(Nokogiri::XML::Document)
415
+ parent_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.parent : nil
416
+ return nil unless parent_nk && !parent_nk.is_a?(Backend.document_class)
417
417
 
418
418
  document_for(node).wrap_node(parent_nk)
419
419
  end