dommy 0.7.0 → 0.8.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/animation.rb +9 -1
- data/lib/dommy/attr.rb +192 -39
- data/lib/dommy/backend/nokogiri_adapter.rb +76 -0
- data/lib/dommy/backend/nokolexbor_adapter.rb +37 -0
- data/lib/dommy/backend.rb +46 -0
- data/lib/dommy/blob.rb +28 -9
- data/lib/dommy/bridge/constructor_registry.rb +28 -0
- data/lib/dommy/bridge/methods.rb +57 -0
- data/lib/dommy/bridge.rb +97 -0
- data/lib/dommy/callable_invoker.rb +36 -0
- data/lib/dommy/cookie_store.rb +3 -1
- data/lib/dommy/crypto.rb +7 -1
- data/lib/dommy/css.rb +46 -0
- data/lib/dommy/custom_elements.rb +27 -3
- data/lib/dommy/data_transfer.rb +4 -0
- data/lib/dommy/document.rb +615 -48
- data/lib/dommy/dom_parser.rb +28 -15
- data/lib/dommy/element.rb +999 -471
- data/lib/dommy/event.rb +260 -96
- data/lib/dommy/event_source.rb +6 -2
- data/lib/dommy/fetch.rb +505 -43
- data/lib/dommy/file_reader.rb +11 -3
- data/lib/dommy/form_data.rb +2 -0
- data/lib/dommy/history.rb +43 -8
- data/lib/dommy/html_collection.rb +55 -2
- data/lib/dommy/html_elements.rb +102 -1519
- data/lib/dommy/internal/css_pseudo_handlers.rb +109 -0
- data/lib/dommy/internal/global_functions.rb +26 -0
- data/lib/dommy/internal/idna.rb +16 -7
- data/lib/dommy/internal/ipv4_parser.rb +22 -7
- data/lib/dommy/internal/mutation_coordinator.rb +11 -2
- data/lib/dommy/internal/namespaces.rb +70 -0
- data/lib/dommy/internal/node_equality.rb +86 -0
- data/lib/dommy/internal/node_wrapper_cache.rb +62 -27
- data/lib/dommy/internal/observable_callback.rb +1 -5
- data/lib/dommy/internal/parent_node.rb +126 -0
- data/lib/dommy/internal/reflected_attributes.rb +103 -13
- data/lib/dommy/internal/selector_parser.rb +664 -0
- data/lib/dommy/internal/url_parser.rb +677 -0
- data/lib/dommy/intersection_observer.rb +2 -0
- data/lib/dommy/location.rb +2 -0
- data/lib/dommy/media_query_list.rb +7 -1
- data/lib/dommy/message_channel.rb +32 -2
- data/lib/dommy/mutation_observer.rb +55 -12
- data/lib/dommy/navigator.rb +26 -12
- data/lib/dommy/node.rb +158 -28
- data/lib/dommy/notification.rb +3 -1
- data/lib/dommy/performance.rb +4 -0
- data/lib/dommy/performance_observer.rb +2 -0
- data/lib/dommy/promise.rb +14 -14
- data/lib/dommy/range.rb +74 -5
- data/lib/dommy/resize_observer.rb +2 -0
- data/lib/dommy/scheduler.rb +34 -13
- data/lib/dommy/shadow_root.rb +23 -54
- data/lib/dommy/storage.rb +2 -0
- data/lib/dommy/streams.rb +18 -27
- data/lib/dommy/svg_elements.rb +204 -3606
- data/lib/dommy/text_codec.rb +174 -21
- data/lib/dommy/tree_walker.rb +255 -66
- data/lib/dommy/url.rb +287 -449
- data/lib/dommy/url_pattern.rb +2 -0
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +37 -7
- data/lib/dommy/window.rb +202 -213
- data/lib/dommy/worker.rb +7 -7
- data/lib/dommy/xml_http_request.rb +15 -5
- data/lib/dommy.rb +7 -0
- metadata +12 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dommy
|
|
4
|
+
module Bridge
|
|
5
|
+
# Maps JS global constructor names (e.g. "Event", "URL", "XMLHttpRequest")
|
|
6
|
+
# to their `Bridge::Constructor` instances. Window builds one and routes
|
|
7
|
+
# `__js_get__` name lookups through it, instead of carrying one ivar plus
|
|
8
|
+
# one `when` arm per constructor.
|
|
9
|
+
class ConstructorRegistry
|
|
10
|
+
def initialize(map)
|
|
11
|
+
@map = map.freeze
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def [](name)
|
|
15
|
+
@map[name]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def key?(name)
|
|
19
|
+
@map.key?(name)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The set of constructor names, e.g. for host-side enumeration / tests.
|
|
23
|
+
def names
|
|
24
|
+
@map.keys
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dommy
|
|
4
|
+
module Bridge
|
|
5
|
+
# Declares, in one line, the set of JS-callable method names a bridge class
|
|
6
|
+
# routes through `__js_call__` (as opposed to data properties read via
|
|
7
|
+
# `__js_get__`). The QuickJS host reads `__js_method_names__` once per
|
|
8
|
+
# interface to decide which property names to expose as callable functions.
|
|
9
|
+
#
|
|
10
|
+
# class Blob
|
|
11
|
+
# include Bridge::Methods
|
|
12
|
+
# js_methods %w[slice text arrayBuffer]
|
|
13
|
+
# def __js_call__(method, args) = ...
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# Subclasses compose automatically: a subclass's own `js_methods` are merged
|
|
17
|
+
# with its ancestors' (ancestors first), so `__js_call__ ... else super`
|
|
18
|
+
# chains stay in sync with the exposed names without a manual `super + own`.
|
|
19
|
+
#
|
|
20
|
+
# The per-class `JS_METHOD_NAMES` constant holds the class's OWN names; the
|
|
21
|
+
# suite asserts it matches the class's own `__js_call__` `when` arms — see
|
|
22
|
+
# test/test_js_call_dispatch_invariant.rb.
|
|
23
|
+
module Methods
|
|
24
|
+
def self.included(base)
|
|
25
|
+
base.extend(ClassMethods)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# `extend` is per-singleton, so a subclass of an includer would not
|
|
30
|
+
# inherit `js_methods`. Re-extend each subclass as it is defined.
|
|
31
|
+
def inherited(subclass)
|
|
32
|
+
super
|
|
33
|
+
subclass.extend(ClassMethods)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def js_methods(names)
|
|
37
|
+
own = names.map(&:to_s).freeze
|
|
38
|
+
const_set(:JS_METHOD_NAMES, own) unless const_defined?(:JS_METHOD_NAMES, false)
|
|
39
|
+
|
|
40
|
+
# Capture the ancestor's __js_method_names__ as a real method (if any)
|
|
41
|
+
# at definition time. We can't use `super` here: classes like
|
|
42
|
+
# StyleDeclaration define `method_missing`, so `super` would fall
|
|
43
|
+
# through to it and return a CSS-property String instead of raising.
|
|
44
|
+
parent =
|
|
45
|
+
if superclass.method_defined?(:__js_method_names__)
|
|
46
|
+
superclass.instance_method(:__js_method_names__)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
define_method(:__js_method_names__) do
|
|
50
|
+
base = parent ? parent.bind(self).call : []
|
|
51
|
+
(base + own).uniq.freeze
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/dommy/bridge.rb
CHANGED
|
@@ -18,6 +18,97 @@ module Dommy
|
|
|
18
18
|
# args (Array)
|
|
19
19
|
# - `__js_new__(args)` invokes the value as a JS constructor
|
|
20
20
|
module Bridge
|
|
21
|
+
# Sentinel returned by `__js_set__` when a key is not a known DOM property
|
|
22
|
+
# (so the JS host can keep it as a JS-side expando, preserving identity)
|
|
23
|
+
# rather than silently dropping it.
|
|
24
|
+
UNHANDLED = :__js_unhandled__
|
|
25
|
+
|
|
26
|
+
# Sentinel for the JS `undefined` value, used in both directions:
|
|
27
|
+
# - a `__js_call__` returns it for a void (undefined-returning) op, so the
|
|
28
|
+
# host marshals JS `undefined` rather than the `null` a bare Ruby `nil`
|
|
29
|
+
# would (e.g. DOMTokenList add/remove return undefined);
|
|
30
|
+
# - a top-level JS `undefined` *argument* arrives as it (whereas JS `null`
|
|
31
|
+
# arrives as `nil`), so WebIDL-style dispatch can tell an omitted optional
|
|
32
|
+
# argument from an explicit null.
|
|
33
|
+
# Its `to_s` is "undefined" so a DOMString coercion of a stray undefined is
|
|
34
|
+
# still spec-faithful.
|
|
35
|
+
UNDEFINED = Object.new
|
|
36
|
+
def UNDEFINED.to_s = "undefined"
|
|
37
|
+
def UNDEFINED.inspect = "#<Dommy::Bridge::UNDEFINED>"
|
|
38
|
+
UNDEFINED.freeze
|
|
39
|
+
|
|
40
|
+
# An opaque handle to a JS-side value that Ruby only stores and hands back
|
|
41
|
+
# (an AbortSignal's reason, a CustomEvent's detail). A non-plain JS object
|
|
42
|
+
# (Error, class instance, …) crosses as one of these instead of being
|
|
43
|
+
# flattened to a Hash, so it round-trips with IDENTITY preserved. `to_s`
|
|
44
|
+
# exposes the captured JS string form for the rare Ruby consumer that needs
|
|
45
|
+
# text (e.g. building a message).
|
|
46
|
+
class JSValue
|
|
47
|
+
attr_reader :ref
|
|
48
|
+
|
|
49
|
+
def initialize(ref, label = nil)
|
|
50
|
+
@ref = ref
|
|
51
|
+
@label = label
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def to_s = (@label || "[object]").to_s
|
|
55
|
+
def inspect = "#<Dommy::Bridge::JSValue #{to_s}>"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Raised by a host method that must throw an ARBITRARY value back to JS —
|
|
59
|
+
# not a DOMException/Error, but e.g. `signal.throwIfAborted()` throwing the
|
|
60
|
+
# exact abort reason (a string, number, or opaque JSValue). The bridge
|
|
61
|
+
# re-throws the wrapped value verbatim (identity preserved). Subclasses
|
|
62
|
+
# RuntimeError (with the value's string form as the message) so standalone
|
|
63
|
+
# CRuby callers still see a normal `raise`-able error.
|
|
64
|
+
class ThrowValue < RuntimeError
|
|
65
|
+
attr_reader :value
|
|
66
|
+
|
|
67
|
+
def initialize(value)
|
|
68
|
+
@value = value
|
|
69
|
+
super(value.to_s)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A byte buffer that crosses the JS boundary as a `Uint8Array` (rather than a
|
|
74
|
+
# plain Array). Wrap a host method's byte-array result in this so JS sees a
|
|
75
|
+
# real typed array — e.g. `TextEncoder#encode`, `Blob#arrayBuffer`. The
|
|
76
|
+
# reverse direction (a JS ArrayBuffer/TypedArray argument) arrives as a
|
|
77
|
+
# `Bytes` too. It subclasses Array so plain-Array callers (and `== [..]`
|
|
78
|
+
# comparisons) keep working; only the bridge treats it specially.
|
|
79
|
+
class Bytes < ::Array
|
|
80
|
+
def initialize(bytes = [])
|
|
81
|
+
super()
|
|
82
|
+
concat(Array(bytes).map { |b| b.to_i & 0xFF })
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
alias bytes to_a
|
|
86
|
+
def pack_bytes = pack("C*")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Like `Bytes`, but crosses the JS boundary as a bare `ArrayBuffer` rather
|
|
90
|
+
# than a `Uint8Array` view. Use this for the spec methods whose return type
|
|
91
|
+
# is `ArrayBuffer` — `Response`/`Blob`/`FileReader`/`XMLHttpRequest`'s
|
|
92
|
+
# `arrayBuffer`. Subclasses `Bytes` so Ruby-level `== [..]` comparisons still
|
|
93
|
+
# hold; only the bridge distinguishes it (and must check it before `Bytes`).
|
|
94
|
+
class ArrayBuffer < Bytes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# A Ruby-side signal that the JS boundary should surface a JS `TypeError`
|
|
98
|
+
# (not a `DOMException`). Some WebIDL operations — notably the `URL`
|
|
99
|
+
# constructor and its `href` setter — throw `TypeError` on failure rather
|
|
100
|
+
# than a DOMException; raising this lets a host bridge rethrow the correct
|
|
101
|
+
# JS error type, while Ruby callers can still rescue it like any other
|
|
102
|
+
# error. Kept distinct from Ruby's built-in `::TypeError` so a host can map
|
|
103
|
+
# only deliberate, spec-mandated TypeErrors (and not mask genuine Ruby type
|
|
104
|
+
# bugs) across the boundary.
|
|
105
|
+
class TypeError < ::StandardError; end
|
|
106
|
+
|
|
107
|
+
# Like `Bridge::TypeError`, but for spec-mandated `RangeError`s (e.g. the
|
|
108
|
+
# `Response` constructor rejecting a status outside 200–599). A host bridge
|
|
109
|
+
# rethrows a real JS `RangeError`; kept distinct from Ruby's `::RangeError`.
|
|
110
|
+
class RangeError < ::StandardError; end
|
|
111
|
+
|
|
21
112
|
# Wraps an external callback handle (registered in a host-side
|
|
22
113
|
# callback table) so the JS bridge can resolve / invoke it. The
|
|
23
114
|
# external host that creates these is responsible for honoring
|
|
@@ -83,6 +174,12 @@ module Dommy
|
|
|
83
174
|
handler = @class_methods[method.to_s]
|
|
84
175
|
handler&.call(args)
|
|
85
176
|
end
|
|
177
|
+
|
|
178
|
+
# Names of the registered class-level (static) methods, so a JS host can
|
|
179
|
+
# expose them on the constructor function (e.g. `URL.createObjectURL`).
|
|
180
|
+
def __js_class_method_names__
|
|
181
|
+
@class_methods.keys
|
|
182
|
+
end
|
|
86
183
|
end
|
|
87
184
|
|
|
88
185
|
# `JS.global[:Promise]` view. Implements the `resolve` / `reject`
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dommy
|
|
4
|
+
# Invokes a callback that may be a JS-bridged object (responds to `__js_call__`)
|
|
5
|
+
# or a plain Ruby callable (responds to `call`). Centralizes the dispatch used
|
|
6
|
+
# by promises, the scheduler, and streams so the JS/Ruby fork lives in one place.
|
|
7
|
+
module CallableInvoker
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Invoke `callback` with `args`. A JS-bridged callable goes through
|
|
11
|
+
# `__js_call__("call", args)`; a Ruby callable through `call(*args)`. A nil
|
|
12
|
+
# or non-callable callback is a no-op (returns nil).
|
|
13
|
+
def invoke(callback, *args)
|
|
14
|
+
return if callback.nil?
|
|
15
|
+
|
|
16
|
+
if callback.respond_to?(:__js_call__)
|
|
17
|
+
callback.__js_call__("call", args)
|
|
18
|
+
elsif callback.respond_to?(:call)
|
|
19
|
+
callback.call(*args)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Invoke a DOM event listener per the EventTarget rule: an object with
|
|
24
|
+
# `handle_event`, else a Ruby callable, else a JS-bridged callable (tried in
|
|
25
|
+
# that order).
|
|
26
|
+
def invoke_listener(listener, event)
|
|
27
|
+
if listener.respond_to?(:handle_event)
|
|
28
|
+
listener.handle_event(event)
|
|
29
|
+
elsif listener.respond_to?(:call) && !listener.is_a?(Module)
|
|
30
|
+
listener.call(event)
|
|
31
|
+
elsif listener.respond_to?(:__js_call__)
|
|
32
|
+
listener.__js_call__("call", [event])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/dommy/cookie_store.rb
CHANGED
|
@@ -60,6 +60,8 @@ module Dommy
|
|
|
60
60
|
PromiseValue.resolve(@window, nil)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
include Bridge::Methods
|
|
64
|
+
js_methods %w[get getAll set delete addEventListener removeEventListener dispatchEvent]
|
|
63
65
|
def __js_call__(method, args)
|
|
64
66
|
case method
|
|
65
67
|
when "get"
|
|
@@ -73,7 +75,7 @@ module Dommy
|
|
|
73
75
|
when "addEventListener"
|
|
74
76
|
add_event_listener(args[0], args[1], args[2])
|
|
75
77
|
when "removeEventListener"
|
|
76
|
-
remove_event_listener(args[0], args[1])
|
|
78
|
+
remove_event_listener(args[0], args[1], args[2])
|
|
77
79
|
when "dispatchEvent"
|
|
78
80
|
dispatch_event(args[0])
|
|
79
81
|
end
|
data/lib/dommy/crypto.rb
CHANGED
|
@@ -62,6 +62,8 @@ module Dommy
|
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
include Bridge::Methods
|
|
66
|
+
js_methods %w[randomUUID getRandomValues]
|
|
65
67
|
def __js_call__(method, args)
|
|
66
68
|
case method
|
|
67
69
|
when "randomUUID"
|
|
@@ -99,7 +101,9 @@ module Dommy
|
|
|
99
101
|
hasher = ALGORITHMS[name]
|
|
100
102
|
raise ArgumentError, "unsupported algorithm: #{name}" unless hasher
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
# WHATWG: digest() resolves to an ArrayBuffer — wrap so it crosses the
|
|
105
|
+
# JS boundary as a bare ArrayBuffer (not a plain Array).
|
|
106
|
+
Bridge::ArrayBuffer.new(hasher.call(coerce_bytes(data)).bytes)
|
|
103
107
|
end
|
|
104
108
|
end
|
|
105
109
|
|
|
@@ -230,6 +234,8 @@ module Dommy
|
|
|
230
234
|
end
|
|
231
235
|
end
|
|
232
236
|
|
|
237
|
+
include Bridge::Methods
|
|
238
|
+
js_methods %w[digest generateKey importKey sign verify encrypt decrypt]
|
|
233
239
|
def __js_call__(method, args)
|
|
234
240
|
case method
|
|
235
241
|
when "digest"
|
data/lib/dommy/css.rb
CHANGED
|
@@ -125,6 +125,8 @@ module Dommy
|
|
|
125
125
|
nil
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
+
include Bridge::Methods
|
|
129
|
+
js_methods %w[insertRule deleteRule replaceSync replace]
|
|
128
130
|
def __js_call__(method, args)
|
|
129
131
|
case method
|
|
130
132
|
when "insertRule"
|
|
@@ -196,6 +198,8 @@ module Dommy
|
|
|
196
198
|
end
|
|
197
199
|
end
|
|
198
200
|
|
|
201
|
+
include Bridge::Methods
|
|
202
|
+
js_methods %w[item]
|
|
199
203
|
def __js_call__(method, args)
|
|
200
204
|
case method
|
|
201
205
|
when "item"
|
|
@@ -280,4 +284,46 @@ module Dommy
|
|
|
280
284
|
nil
|
|
281
285
|
end
|
|
282
286
|
end
|
|
287
|
+
|
|
288
|
+
# `window.CSS` namespace object — `escape()` for safe selector building
|
|
289
|
+
# (used by Turbo and friends) and a `supports()` stub (no CSS engine).
|
|
290
|
+
class CSSNamespace
|
|
291
|
+
def __js_get__(_key) = nil
|
|
292
|
+
def __js_set__(_key, _value) = Bridge::UNHANDLED
|
|
293
|
+
|
|
294
|
+
include Bridge::Methods
|
|
295
|
+
js_methods %w[escape supports]
|
|
296
|
+
def __js_call__(method, args)
|
|
297
|
+
case method
|
|
298
|
+
when "escape"
|
|
299
|
+
self.class.escape(args[0])
|
|
300
|
+
when "supports"
|
|
301
|
+
false
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# CSSOM `CSS.escape` — escape a string for use as an identifier in a
|
|
306
|
+
# selector. Follows the spec's char rules closely enough for selectors.
|
|
307
|
+
def self.escape(value)
|
|
308
|
+
str = value.to_s
|
|
309
|
+
out = +""
|
|
310
|
+
str.each_char.with_index do |ch, i|
|
|
311
|
+
code = ch.ord
|
|
312
|
+
if code.zero?
|
|
313
|
+
out << "\uFFFD"
|
|
314
|
+
elsif (code >= 0x01 && code <= 0x1F) || code == 0x7F ||
|
|
315
|
+
(i.zero? && code >= 0x30 && code <= 0x39) ||
|
|
316
|
+
(i == 1 && code >= 0x30 && code <= 0x39 && str[0] == "-")
|
|
317
|
+
out << "\\#{code.to_s(16)} "
|
|
318
|
+
elsif code >= 0x80 || code == 0x2D || code == 0x5F ||
|
|
319
|
+
(code >= 0x30 && code <= 0x39) ||
|
|
320
|
+
(code >= 0x41 && code <= 0x5A) || (code >= 0x61 && code <= 0x7A)
|
|
321
|
+
out << ch
|
|
322
|
+
else
|
|
323
|
+
out << "\\#{ch}"
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
out
|
|
327
|
+
end
|
|
328
|
+
end
|
|
283
329
|
end
|
|
@@ -10,7 +10,21 @@ module Dommy
|
|
|
10
10
|
#
|
|
11
11
|
# Names must contain a hyphen per the HTML spec (e.g., `my-button`).
|
|
12
12
|
class CustomElementRegistry
|
|
13
|
-
|
|
13
|
+
# https://html.spec.whatwg.org/#valid-custom-element-name
|
|
14
|
+
# PCENChar — the characters allowed after the first (ASCII-lower) char: a
|
|
15
|
+
# superset of [-._0-9a-z] plus wide Unicode ranges. A valid name is
|
|
16
|
+
# `[a-z] PCENChar* - PCENChar*` (i.e. lower-alpha start + at least one "-").
|
|
17
|
+
PCEN = "\\-._0-9a-z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D" \
|
|
18
|
+
"\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F" \
|
|
19
|
+
"\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u{10000}-\\u{EFFFF}"
|
|
20
|
+
NAME_RE = Regexp.new("\\A[a-z][#{PCEN}]*-[#{PCEN}]*\\z")
|
|
21
|
+
|
|
22
|
+
# Hyphenated names that the HTML spec reserves (SVG / MathML elements), so
|
|
23
|
+
# they are NOT valid custom element names even though they match NAME_RE.
|
|
24
|
+
RESERVED_NAMES = %w[
|
|
25
|
+
annotation-xml color-profile font-face font-face-src font-face-uri
|
|
26
|
+
font-face-format font-face-name missing-glyph
|
|
27
|
+
].to_set.freeze
|
|
14
28
|
|
|
15
29
|
def initialize(window)
|
|
16
30
|
@window = window
|
|
@@ -23,7 +37,10 @@ module Dommy
|
|
|
23
37
|
def define(name, klass, _options = nil)
|
|
24
38
|
key = name.to_s
|
|
25
39
|
unless key.match?(NAME_RE)
|
|
26
|
-
raise DOMException::SyntaxError, "name
|
|
40
|
+
raise DOMException::SyntaxError, "#{name.inspect} is not a valid custom element name"
|
|
41
|
+
end
|
|
42
|
+
if RESERVED_NAMES.include?(key)
|
|
43
|
+
raise DOMException::SyntaxError, "#{name.inspect} is a reserved element name"
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
raise DOMException::NotSupportedError, "#{key} already defined" if @definitions.key?(key)
|
|
@@ -83,6 +100,8 @@ module Dommy
|
|
|
83
100
|
nil
|
|
84
101
|
end
|
|
85
102
|
|
|
103
|
+
include Bridge::Methods
|
|
104
|
+
js_methods %w[define get whenDefined upgrade]
|
|
86
105
|
def __js_call__(method, args)
|
|
87
106
|
case method
|
|
88
107
|
when "define"
|
|
@@ -108,7 +127,12 @@ module Dommy
|
|
|
108
127
|
# new class and fire connectedCallback.
|
|
109
128
|
def upgrade_existing(name)
|
|
110
129
|
doc = @window.document
|
|
111
|
-
|
|
130
|
+
# Match by tag name rather than interpolating `name` into a CSS selector:
|
|
131
|
+
# a spec-valid custom element name may contain "." (a CSS class selector
|
|
132
|
+
# char) or other metacharacters, which would corrupt the query.
|
|
133
|
+
doc.nokogiri_doc.css("*").each do |nk|
|
|
134
|
+
next unless nk.name == name
|
|
135
|
+
|
|
112
136
|
doc.__internal_reset_wrapper__(nk)
|
|
113
137
|
wrapped = doc.wrap_node(nk)
|
|
114
138
|
doc.__internal_notify_connected__(wrapped) if wrapped
|
data/lib/dommy/data_transfer.rb
CHANGED
|
@@ -64,11 +64,15 @@ module Dommy
|
|
|
64
64
|
@drop_effect = value.to_s
|
|
65
65
|
when "effectAllowed"
|
|
66
66
|
@effect_allowed = value.to_s
|
|
67
|
+
else
|
|
68
|
+
return Bridge::UNHANDLED
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
nil
|
|
70
72
|
end
|
|
71
73
|
|
|
74
|
+
include Bridge::Methods
|
|
75
|
+
js_methods %w[getData setData clearData]
|
|
72
76
|
def __js_call__(method, args)
|
|
73
77
|
case method
|
|
74
78
|
when "getData"
|