dommy 0.9.0 → 0.11.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/lib/dommy/attr.rb +50 -13
- data/lib/dommy/backend/makiri_adapter.rb +56 -0
- data/lib/dommy/backend.rb +47 -0
- data/lib/dommy/blob.rb +48 -6
- data/lib/dommy/browser.rb +246 -36
- data/lib/dommy/callable_invoker.rb +16 -4
- data/lib/dommy/css.rb +33 -10
- data/lib/dommy/custom_elements.rb +42 -2
- data/lib/dommy/document.rb +1299 -152
- data/lib/dommy/dom_parser.rb +12 -1
- data/lib/dommy/element.rb +824 -413
- data/lib/dommy/event.rb +744 -115
- data/lib/dommy/fetch.rb +431 -25
- data/lib/dommy/history.rb +24 -3
- data/lib/dommy/html_collection.rb +148 -21
- data/lib/dommy/html_elements.rb +2307 -317
- data/lib/dommy/interaction/driver.rb +177 -14
- data/lib/dommy/interaction/event_synthesis.rb +111 -6
- data/lib/dommy/interaction/field_interactor.rb +73 -7
- data/lib/dommy/internal/accessible_name.rb +45 -8
- data/lib/dommy/internal/aria_role.rb +5 -3
- data/lib/dommy/internal/child_node.rb +313 -0
- data/lib/dommy/internal/css/cascade.rb +47 -3
- data/lib/dommy/internal/css/parser.rb +97 -14
- data/lib/dommy/internal/css/rule_index.rb +288 -18
- data/lib/dommy/internal/css_priority.rb +33 -0
- data/lib/dommy/internal/dom_matching.rb +1 -1
- data/lib/dommy/internal/global_functions.rb +23 -10
- data/lib/dommy/internal/leaf_node.rb +51 -0
- data/lib/dommy/internal/mutation_coordinator.rb +161 -34
- data/lib/dommy/internal/node_adopter.rb +190 -0
- data/lib/dommy/internal/node_traversal.rb +10 -0
- data/lib/dommy/internal/node_wrapper_cache.rb +109 -26
- data/lib/dommy/internal/observer_manager.rb +24 -0
- data/lib/dommy/internal/observer_matcher.rb +39 -8
- data/lib/dommy/internal/parent_node.rb +345 -82
- data/lib/dommy/internal/range_text_serializer.rb +5 -4
- data/lib/dommy/internal/selector_index.rb +24 -8
- data/lib/dommy/internal/selector_matcher.rb +221 -16
- data/lib/dommy/internal/selector_parser.rb +44 -7
- data/lib/dommy/internal/template_content_registry.rb +23 -7
- data/lib/dommy/internal/utf16.rb +45 -0
- data/lib/dommy/internal/xml_serialization.rb +18 -5
- data/lib/dommy/js/custom_element_bridge.rb +13 -2
- data/lib/dommy/js/dom_interfaces.rb +91 -13
- data/lib/dommy/js/host_bridge.rb +86 -15
- data/lib/dommy/js/host_runtime.js +1444 -98
- data/lib/dommy/js/marshaller.rb +12 -7
- data/lib/dommy/js/script_boot.rb +81 -0
- data/lib/dommy/location.rb +76 -13
- data/lib/dommy/mutation_observer.rb +109 -20
- data/lib/dommy/navigation.rb +263 -0
- data/lib/dommy/node.rb +207 -27
- data/lib/dommy/parser.rb +14 -0
- data/lib/dommy/promise.rb +4 -4
- data/lib/dommy/range.rb +373 -65
- data/lib/dommy/scheduler.rb +16 -0
- data/lib/dommy/shadow_root.rb +65 -17
- data/lib/dommy/storage.rb +61 -10
- data/lib/dommy/tree_walker.rb +25 -39
- data/lib/dommy/url.rb +4 -2
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +45 -2
- data/lib/dommy/window.rb +200 -11
- data/lib/dommy/xml_http_request.rb +30 -4
- data/lib/dommy.rb +5 -0
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47c59ce209a6e63313db2b6685126910463abfc26020ec5f24a7b7494b67b10f
|
|
4
|
+
data.tar.gz: 43980f4f5eb7b2c5bfe76e41bd2786e9c8cad571df1eb882e0e6d5107fbe04c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2627fa8969554f7ecb7a04ed16c331a77e604b44a9ca9e95412a89e48412744a3dd606892255e93eb56988cfda5214000a15821bbc5eb6ed7d18701740b3262
|
|
7
|
+
data.tar.gz: 632bddbea02093111566d46ba9fc44af35ed17879bf9921c4ad477a138d44b83e2c2761337d34f5bdfa31cc150353a4b7afe47b0cca8d817e79eda95d7b556ad
|
data/lib/dommy/attr.rb
CHANGED
|
@@ -22,9 +22,12 @@ module Dommy
|
|
|
22
22
|
nil
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def initialize(name, owner: nil, value: "", namespace_uri: nil, prefix: nil, local_name: nil)
|
|
25
|
+
def initialize(name, owner: nil, value: "", namespace_uri: nil, prefix: nil, local_name: nil, document: nil)
|
|
26
26
|
qname = name.to_s
|
|
27
27
|
@owner = owner
|
|
28
|
+
# The node document (for baseURI/ownerDocument when detached from an
|
|
29
|
+
# element). Owned attrs derive it from their owner instead.
|
|
30
|
+
@document = document
|
|
28
31
|
@detached_value = value.to_s
|
|
29
32
|
if namespace_uri && !namespace_uri.to_s.empty?
|
|
30
33
|
# Namespaced attributes preserve case and carry prefix / localName.
|
|
@@ -50,13 +53,28 @@ module Dommy
|
|
|
50
53
|
@owner
|
|
51
54
|
end
|
|
52
55
|
|
|
56
|
+
# Node.baseURI — the node document's base URL. Derived from the owner
|
|
57
|
+
# element when attached, else the document the attr was created in.
|
|
58
|
+
def base_uri
|
|
59
|
+
return @owner.base_uri if @owner.respond_to?(:base_uri)
|
|
60
|
+
|
|
61
|
+
@document&.base_uri
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Node.ownerDocument — the owner element's current document when attached (so
|
|
65
|
+
# it follows the element across adoptNode), else the creation document.
|
|
66
|
+
def owner_document
|
|
67
|
+
return @owner.document if @owner.respond_to?(:document)
|
|
68
|
+
|
|
69
|
+
@document
|
|
70
|
+
end
|
|
71
|
+
|
|
53
72
|
def value
|
|
54
73
|
if @owner
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
74
|
+
# Both branches go through the namespace-aware read: the backend's
|
|
75
|
+
# `node[name]` indexes by local name, so a null-namespace `b` would read
|
|
76
|
+
# back the value of a prefixed `xml:b` sitting on the same element.
|
|
77
|
+
Backend.get_attribute_ns(@owner.__dommy_backend_node__, @namespace_uri, @local_name).to_s
|
|
60
78
|
else
|
|
61
79
|
@detached_value
|
|
62
80
|
end
|
|
@@ -89,6 +107,8 @@ module Dommy
|
|
|
89
107
|
value
|
|
90
108
|
when "ownerElement"
|
|
91
109
|
@owner
|
|
110
|
+
when "ownerDocument"
|
|
111
|
+
owner_document
|
|
92
112
|
when "localName"
|
|
93
113
|
@local_name
|
|
94
114
|
when "namespaceURI"
|
|
@@ -97,6 +117,8 @@ module Dommy
|
|
|
97
117
|
@prefix
|
|
98
118
|
when "nodeType"
|
|
99
119
|
2
|
|
120
|
+
when "baseURI"
|
|
121
|
+
base_uri
|
|
100
122
|
when "specified"
|
|
101
123
|
# Legacy/useless attribute — always true (WHATWG DOM).
|
|
102
124
|
true
|
|
@@ -119,12 +141,20 @@ module Dommy
|
|
|
119
141
|
include Bridge::Methods
|
|
120
142
|
js_methods %w[cloneNode isSameNode getRootNode hasChildNodes normalize compareDocumentPosition
|
|
121
143
|
appendChild insertBefore removeChild replaceChild
|
|
144
|
+
lookupNamespaceURI lookupPrefix isDefaultNamespace
|
|
122
145
|
addEventListener removeEventListener dispatchEvent]
|
|
123
146
|
def __js_call__(method, args)
|
|
124
147
|
case method
|
|
148
|
+
when "lookupNamespaceURI"
|
|
149
|
+
lookup_namespace_uri(args[0])
|
|
150
|
+
when "lookupPrefix"
|
|
151
|
+
lookup_prefix(args[0])
|
|
152
|
+
when "isDefaultNamespace"
|
|
153
|
+
is_default_namespace(args[0])
|
|
125
154
|
when "cloneNode"
|
|
126
155
|
Attr.new(@name, owner: nil, value: value,
|
|
127
|
-
namespace_uri: @namespace_uri, prefix: @prefix, local_name: @local_name
|
|
156
|
+
namespace_uri: @namespace_uri, prefix: @prefix, local_name: @local_name,
|
|
157
|
+
document: @document || (@owner.respond_to?(:document) ? @owner.document : nil))
|
|
128
158
|
when "isSameNode"
|
|
129
159
|
is_same_node(args[0])
|
|
130
160
|
when "getRootNode"
|
|
@@ -209,7 +239,9 @@ module Dommy
|
|
|
209
239
|
end
|
|
210
240
|
|
|
211
241
|
def get_named_item(name)
|
|
212
|
-
|
|
242
|
+
# getNamedItem / getAttribute lowercase the qualified name only for an HTML
|
|
243
|
+
# element in an HTML document; other elements match case-sensitively.
|
|
244
|
+
key = @element.__internal_normalize_attr_key__(name)
|
|
213
245
|
node = Backend.attribute_nodes(@element.__dommy_backend_node__).find do |a|
|
|
214
246
|
Backend.attribute_ns_info(a)[:qualified_name] == key
|
|
215
247
|
end
|
|
@@ -221,7 +253,7 @@ module Dommy
|
|
|
221
253
|
end
|
|
222
254
|
|
|
223
255
|
def remove_named_item(name)
|
|
224
|
-
key = name
|
|
256
|
+
key = @element.__internal_normalize_attr_key__(name)
|
|
225
257
|
node = Backend.attribute_nodes(@element.__dommy_backend_node__).find do |a|
|
|
226
258
|
Backend.attribute_ns_info(a)[:qualified_name] == key
|
|
227
259
|
end
|
|
@@ -329,12 +361,17 @@ module Dommy
|
|
|
329
361
|
end
|
|
330
362
|
end
|
|
331
363
|
|
|
332
|
-
# WebIDL "supported property names" for NamedNodeMap:
|
|
333
|
-
#
|
|
364
|
+
# WebIDL "supported property names" for NamedNodeMap: each attribute's
|
|
365
|
+
# qualified name, in order with duplicates omitted (the indexed names are
|
|
366
|
+
# reflected separately). For an HTML element in an HTML document, names
|
|
367
|
+
# containing an ASCII upper alpha are excluded (they can't be reached by the
|
|
368
|
+
# case-insensitive named getter).
|
|
334
369
|
def __js_named_props__
|
|
335
|
-
Backend.attribute_nodes(@element.__dommy_backend_node__).map do |a|
|
|
370
|
+
names = Backend.attribute_nodes(@element.__dommy_backend_node__).map do |a|
|
|
336
371
|
Backend.attribute_ns_info(a)[:qualified_name]
|
|
337
|
-
end
|
|
372
|
+
end.uniq
|
|
373
|
+
names.reject! { |n| n.match?(/[A-Z]/) } unless @element.__internal_case_sensitive_attribute_names__?
|
|
374
|
+
names
|
|
338
375
|
end
|
|
339
376
|
|
|
340
377
|
include Bridge::Methods
|
|
@@ -21,6 +21,7 @@ module Dommy
|
|
|
21
21
|
CDATASection = ::Makiri::CDATASection
|
|
22
22
|
ProcessingInstruction = ::Makiri::ProcessingInstruction
|
|
23
23
|
DocumentFragment = ::Makiri::DocumentFragment
|
|
24
|
+
DocumentType = ::Makiri::DocumentType
|
|
24
25
|
Node = ::Makiri::Node
|
|
25
26
|
|
|
26
27
|
# A minimal namespace wrapper exposing the same `href` API that Nokogiri's
|
|
@@ -168,6 +169,39 @@ module Dommy
|
|
|
168
169
|
doc.create_element(name)
|
|
169
170
|
end
|
|
170
171
|
|
|
172
|
+
# An XML-backed document rejects a qualified name that DOM allows (e.g.
|
|
173
|
+
# "f}oo" — an invalid char in the local part), so createElementNS uses
|
|
174
|
+
# Makiri's loose creator, which builds it verbatim (case/prefix preserved).
|
|
175
|
+
# nil for a non-XML backend → the caller uses the strict #create_element.
|
|
176
|
+
def create_element_loose(qualified_name, prefix, local, namespace, doc)
|
|
177
|
+
return nil unless doc.is_a?(::Makiri::XML::Document) && doc.respond_to?(:create_loose_dom_element)
|
|
178
|
+
|
|
179
|
+
doc.create_loose_dom_element(qualified_name, prefix, local, namespace)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# A detached DocumentType node owned by `doc`, for
|
|
183
|
+
# DOMImplementation.createDocumentType. Only the HTML backend ships the
|
|
184
|
+
# factory (`create_document_type`); nil signals the caller to fall back to a
|
|
185
|
+
# synthetic (non-tree) DocumentType. Raises ArgumentError for a name the
|
|
186
|
+
# factory rejects.
|
|
187
|
+
def create_document_type(name, public_id, system_id, doc)
|
|
188
|
+
return nil unless doc.respond_to?(:create_document_type)
|
|
189
|
+
|
|
190
|
+
doc.create_document_type(name.to_s, public_id.to_s, system_id.to_s)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# The parsed document's DocumentType node (`<!DOCTYPE …>`), or nil when the
|
|
194
|
+
# document declares none.
|
|
195
|
+
def internal_subset(doc)
|
|
196
|
+
doc.respond_to?(:internal_subset) ? doc.internal_subset : nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The backend class for a DocumentType node, so the wrapper routes it to
|
|
200
|
+
# Dommy::DocumentType (node-backed).
|
|
201
|
+
def document_type_class
|
|
202
|
+
::Makiri::DocumentType
|
|
203
|
+
end
|
|
204
|
+
|
|
171
205
|
def create_text(content, doc)
|
|
172
206
|
doc.create_text_node(content)
|
|
173
207
|
end
|
|
@@ -242,6 +276,12 @@ module Dommy
|
|
|
242
276
|
|
|
243
277
|
node["xmlns:#{prefix}"] = href.to_s
|
|
244
278
|
nil
|
|
279
|
+
rescue ArgumentError
|
|
280
|
+
# DOM validates a qualified name against the Name production, which
|
|
281
|
+
# admits prefixes an XML backend cannot spell as an `xmlns:` attribute
|
|
282
|
+
# ("0:a", ";:a"). The element is still valid — its prefix and namespace
|
|
283
|
+
# live on the wrapper — so the declaration is simply not written.
|
|
284
|
+
nil
|
|
245
285
|
end
|
|
246
286
|
|
|
247
287
|
def namespace_definitions(_node)
|
|
@@ -296,6 +336,22 @@ module Dommy
|
|
|
296
336
|
node.attribute_nodes
|
|
297
337
|
end
|
|
298
338
|
|
|
339
|
+
# Attribute node matching the qualified name exactly. `node[name]` would
|
|
340
|
+
# also answer for a prefixed attribute with that local name.
|
|
341
|
+
#
|
|
342
|
+
# This sits under `getAttribute` / `hasAttribute` / `setAttribute` /
|
|
343
|
+
# `removeAttribute`, so it runs on the hottest path in the library — every
|
|
344
|
+
# CSS match, every reflected IDL attribute — and Makiri does the scan
|
|
345
|
+
# natively (0.9.0). The value-returning spelling is what the two readers
|
|
346
|
+
# that only want the value ask for: it wraps no Attr at all.
|
|
347
|
+
def attr_by_qualified_name(node, qualified_name)
|
|
348
|
+
node.attribute_by_qualified_name(qualified_name.to_s)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def attr_value_by_qualified_name(node, qualified_name)
|
|
352
|
+
node.attribute_value_by_qualified_name(qualified_name.to_s)
|
|
353
|
+
end
|
|
354
|
+
|
|
299
355
|
# Attribute node matching (namespace, local name) case-sensitively; a
|
|
300
356
|
# null/empty namespace matches a null-namespace attribute.
|
|
301
357
|
def attr_by_ns(node, namespace, local_name)
|
data/lib/dommy/backend.rb
CHANGED
|
@@ -130,6 +130,35 @@ module Dommy
|
|
|
130
130
|
current.create_element(name, doc)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
+
# Create a namespaced element permitting a DOM-valid qualified name that a
|
|
134
|
+
# strict XML backend would reject (an internal invalid char like "f}oo"),
|
|
135
|
+
# preserving case/prefix. Returns nil when the backend has no loose path
|
|
136
|
+
# (fall back to #create_element); raises ArgumentError for a genuinely
|
|
137
|
+
# invalid name (the caller maps it to InvalidCharacterError).
|
|
138
|
+
def create_element_loose(qualified_name, prefix, local, namespace, doc)
|
|
139
|
+
current.create_element_loose(qualified_name, prefix, local, namespace, doc)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# A detached DocumentType node owned by `doc` (for
|
|
143
|
+
# DOMImplementation.createDocumentType). Returns nil when the backend has no
|
|
144
|
+
# doctype factory (the caller falls back to a synthetic DocumentType); raises
|
|
145
|
+
# ArgumentError for a name the factory rejects (the caller then also falls
|
|
146
|
+
# back, since createDocumentType is permissive).
|
|
147
|
+
def create_document_type(name, public_id, system_id, doc)
|
|
148
|
+
current.respond_to?(:create_document_type) ? current.create_document_type(name, public_id, system_id, doc) : nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The parsed document's DocumentType node, or nil when it declares none.
|
|
152
|
+
def internal_subset(doc)
|
|
153
|
+
current.respond_to?(:internal_subset) ? current.internal_subset(doc) : nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The backend class for a DocumentType node (nil if unsupported), so the
|
|
157
|
+
# wrapper cache can route it to Dommy::DocumentType.
|
|
158
|
+
def document_type_class
|
|
159
|
+
current.respond_to?(:document_type_class) ? current.document_type_class : nil
|
|
160
|
+
end
|
|
161
|
+
|
|
133
162
|
def create_text(content, doc)
|
|
134
163
|
current.create_text(content, doc)
|
|
135
164
|
end
|
|
@@ -204,6 +233,24 @@ module Dommy
|
|
|
204
233
|
current.attribute_ns_info(attr_node)
|
|
205
234
|
end
|
|
206
235
|
|
|
236
|
+
# The attribute node whose QUALIFIED name is `qualified_name`, or nil.
|
|
237
|
+
#
|
|
238
|
+
# WHATWG's by-name family ("get an attribute by name", `setAttribute`,
|
|
239
|
+
# `removeAttribute`) matches on the qualified name. A lookup by local name
|
|
240
|
+
# confuses `b` with a prefixed `xml:b`, so those paths must come through
|
|
241
|
+
# here rather than through `node[name]`.
|
|
242
|
+
def attr_by_qualified_name(node, qualified_name)
|
|
243
|
+
current.attr_by_qualified_name(node, qualified_name)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# That attribute's VALUE, or nil when there is no such attribute. The same
|
|
247
|
+
# match without an attribute node in hand, for the two readers that only
|
|
248
|
+
# ever wanted the value — `getAttribute` and `hasAttribute` — which run on
|
|
249
|
+
# every CSS match and every reflected IDL attribute.
|
|
250
|
+
def attr_value_by_qualified_name(node, qualified_name)
|
|
251
|
+
current.attr_value_by_qualified_name(node, qualified_name)
|
|
252
|
+
end
|
|
253
|
+
|
|
207
254
|
# The element's attribute nodes (each readable via attribute_ns_info).
|
|
208
255
|
# The single choke point so DOM code doesn't touch parser internals.
|
|
209
256
|
def attribute_nodes(node)
|
data/lib/dommy/blob.rb
CHANGED
|
@@ -21,19 +21,26 @@ module Dommy
|
|
|
21
21
|
# Promises (they need a scheduler). A window-less Blob falls back to a
|
|
22
22
|
# synchronous result, which `await` still handles.
|
|
23
23
|
def initialize(parts = [], options = {}, window = nil)
|
|
24
|
+
# WebIDL: an omitted / `undefined` blobParts argument defaults to an empty
|
|
25
|
+
# sequence (`new Blob()` / `new Blob(undefined)` is a zero-length Blob), so
|
|
26
|
+
# it must not be coerced to the string "undefined".
|
|
27
|
+
parts = [] if parts.nil? || (defined?(Bridge::UNDEFINED) && parts.equal?(Bridge::UNDEFINED))
|
|
24
28
|
parts = [parts] unless parts.is_a?(Array)
|
|
25
29
|
@data = collect_bytes(parts)
|
|
26
30
|
@size = @data.bytesize
|
|
27
|
-
raw_type = options["type"] || options[:type] || ""
|
|
28
|
-
|
|
31
|
+
raw_type = (options["type"] || options[:type] || "").to_s
|
|
32
|
+
# A type string with any code point outside U+0020..U+007E is discarded
|
|
33
|
+
# (→ ""); otherwise it is ASCII-lowercased (FileAPI "parse a MIME type"
|
|
34
|
+
# gate, applied to both the constructor and slice's contentType).
|
|
35
|
+
@type = raw_type.match?(/[^ -~]/) ? "" : raw_type.downcase
|
|
29
36
|
@window = window
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
# Return a new Blob over a byte range of this one.
|
|
33
40
|
# Negative indices are treated as offsets from the end (per spec).
|
|
34
41
|
def slice(start = 0, last = @size, content_type = "")
|
|
35
|
-
s = clamp_index(start
|
|
36
|
-
e = clamp_index(last
|
|
42
|
+
s = clamp_index(clamp_long_long(start), @size)
|
|
43
|
+
e = clamp_index(clamp_long_long(last), @size)
|
|
37
44
|
e = s if e < s
|
|
38
45
|
Blob.new([@data.byteslice(s, e - s) || ""], {"type" => content_type.to_s}, @window)
|
|
39
46
|
end
|
|
@@ -51,6 +58,13 @@ module Dommy
|
|
|
51
58
|
Bridge::ArrayBuffer.new(@data.bytes)
|
|
52
59
|
end
|
|
53
60
|
|
|
61
|
+
# Read the bytes as a Uint8Array (the spec return type). The DOM spec returns
|
|
62
|
+
# a Promise<Uint8Array>; Dommy is synchronous. `Bridge::Bytes` crosses the JS
|
|
63
|
+
# boundary as a Uint8Array (vs `ArrayBuffer` for #array_buffer).
|
|
64
|
+
def bytes
|
|
65
|
+
Bridge::Bytes.new(@data.bytes)
|
|
66
|
+
end
|
|
67
|
+
|
|
54
68
|
# Raw binary bytes (Ruby ASCII-8BIT string). Used by FormData /
|
|
55
69
|
# fetch when serializing multipart bodies.
|
|
56
70
|
def __dommy_bytes__
|
|
@@ -71,17 +85,30 @@ module Dommy
|
|
|
71
85
|
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
72
86
|
# File < Blob inherits these (it adds only properties).
|
|
73
87
|
include Bridge::Methods
|
|
74
|
-
js_methods %w[slice text arrayBuffer]
|
|
88
|
+
js_methods %w[slice text arrayBuffer bytes]
|
|
75
89
|
def __js_call__(method, args)
|
|
76
90
|
case method
|
|
77
91
|
when "slice"
|
|
78
|
-
|
|
92
|
+
# An omitted / `undefined` start|end uses the default (0 / size); map
|
|
93
|
+
# UNDEFINED to nil so the `|| default` fallbacks apply (a bare UNDEFINED
|
|
94
|
+
# is truthy and has no #to_i). contentType is a plain DOMString: omitted
|
|
95
|
+
# / undefined → "" (default), but an explicit JS null coerces to "null".
|
|
96
|
+
a = args.map { |v| v.equal?(Bridge::UNDEFINED) ? nil : v }
|
|
97
|
+
ctype = if args.length < 3 || args[2].equal?(Bridge::UNDEFINED)
|
|
98
|
+
""
|
|
99
|
+
else
|
|
100
|
+
args[2].nil? ? "null" : args[2].to_s
|
|
101
|
+
end
|
|
102
|
+
slice(a[0] || 0, a[1] || @size, ctype)
|
|
79
103
|
when "text"
|
|
80
104
|
# WHATWG: Blob.text() returns a Promise<string>.
|
|
81
105
|
promise_or_value(text)
|
|
82
106
|
when "arrayBuffer"
|
|
83
107
|
# WHATWG: Blob.arrayBuffer() returns a Promise<ArrayBuffer>.
|
|
84
108
|
promise_or_value(array_buffer)
|
|
109
|
+
when "bytes"
|
|
110
|
+
# WHATWG: Blob.bytes() returns a Promise<Uint8Array>.
|
|
111
|
+
promise_or_value(bytes)
|
|
85
112
|
end
|
|
86
113
|
end
|
|
87
114
|
|
|
@@ -115,6 +142,21 @@ module Dommy
|
|
|
115
142
|
idx = length + idx if idx.negative?
|
|
116
143
|
idx.clamp(0, length)
|
|
117
144
|
end
|
|
145
|
+
|
|
146
|
+
# WebIDL `[Clamp] long long` conversion of a slice bound: a fractional value
|
|
147
|
+
# rounds to the nearest integer, ties to even (banker's rounding) — so
|
|
148
|
+
# `slice(1.5)` starts at 2 and `slice(3.5)` at 4, per the [Clamp] extended
|
|
149
|
+
# attribute on Blob.slice.
|
|
150
|
+
def clamp_long_long(value)
|
|
151
|
+
return value if value.is_a?(Integer)
|
|
152
|
+
|
|
153
|
+
f = value.to_f
|
|
154
|
+
return 0 if f.nan?
|
|
155
|
+
|
|
156
|
+
f.round(half: :even)
|
|
157
|
+
rescue StandardError
|
|
158
|
+
0
|
|
159
|
+
end
|
|
118
160
|
end
|
|
119
161
|
|
|
120
162
|
# `File` — Blob with a filename and an optional last-modified
|