dommy 0.8.0 → 0.9.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 +3 -3
- data/lib/dommy/animation.rb +4 -0
- data/lib/dommy/attr.rb +11 -5
- data/lib/dommy/backend/makiri_adapter.rb +330 -0
- data/lib/dommy/backend.rb +114 -33
- data/lib/dommy/blob.rb +2 -0
- data/lib/dommy/bridge.rb +11 -0
- data/lib/dommy/browser.rb +217 -0
- data/lib/dommy/compression_streams.rb +4 -0
- data/lib/dommy/crypto.rb +4 -0
- data/lib/dommy/css.rb +487 -50
- data/lib/dommy/custom_elements.rb +2 -2
- data/lib/dommy/data_transfer.rb +2 -0
- data/lib/dommy/data_uri.rb +35 -0
- data/lib/dommy/deferred_response.rb +59 -0
- data/lib/dommy/document.rb +386 -228
- data/lib/dommy/dom_exception.rb +2 -0
- data/lib/dommy/dom_parser.rb +7 -17
- data/lib/dommy/element.rb +502 -155
- data/lib/dommy/event.rb +240 -9
- data/lib/dommy/fetch.rb +152 -34
- data/lib/dommy/form_data.rb +2 -0
- data/lib/dommy/history.rb +2 -0
- data/lib/dommy/html_canvas_element.rb +230 -0
- data/lib/dommy/html_collection.rb +5 -6
- data/lib/dommy/html_elements.rb +304 -27
- data/lib/dommy/interaction/debug.rb +35 -0
- data/lib/dommy/interaction/dom_summary.rb +131 -0
- data/lib/dommy/interaction/driver.rb +244 -0
- data/lib/dommy/interaction/event_synthesis.rb +56 -0
- data/lib/dommy/interaction/field_interactor.rb +117 -0
- data/lib/dommy/interaction/form_submission.rb +268 -0
- data/lib/dommy/interaction/locator.rb +158 -0
- data/lib/dommy/interaction/role_query.rb +58 -0
- data/lib/dommy/interaction.rb +32 -0
- data/lib/dommy/internal/accessibility_tree.rb +215 -0
- data/lib/dommy/internal/accessible_description.rb +38 -0
- data/lib/dommy/internal/accessible_name.rb +301 -0
- data/lib/dommy/internal/aria_role.rb +252 -0
- data/lib/dommy/internal/aria_snapshot.rb +64 -0
- data/lib/dommy/internal/aria_state.rb +151 -0
- data/lib/dommy/internal/css/calc.rb +242 -0
- data/lib/dommy/internal/css/cascade.rb +430 -0
- data/lib/dommy/internal/css/color.rb +381 -0
- data/lib/dommy/internal/css/computed_style_declaration.rb +130 -0
- data/lib/dommy/internal/css/counters.rb +227 -0
- data/lib/dommy/internal/css/custom_properties.rb +183 -0
- data/lib/dommy/internal/css/media_query.rb +302 -0
- data/lib/dommy/internal/css/parser.rb +265 -0
- data/lib/dommy/internal/css/property_registry.rb +512 -0
- data/lib/dommy/internal/css/rule_index.rb +494 -0
- data/lib/dommy/internal/css/supports.rb +158 -0
- data/lib/dommy/internal/css/ua_stylesheet.rb +53 -0
- data/lib/dommy/internal/css_pseudo_handlers.rb +283 -42
- data/lib/dommy/internal/css_rule_text.rb +160 -0
- data/lib/dommy/internal/dom_matching.rb +80 -9
- data/lib/dommy/internal/element_matching.rb +109 -0
- data/lib/dommy/internal/global_functions.rb +33 -0
- data/lib/dommy/internal/mutation_coordinator.rb +95 -4
- data/lib/dommy/internal/namespaces.rb +49 -5
- data/lib/dommy/internal/node_wrapper_cache.rb +163 -26
- data/lib/dommy/internal/parent_node.rb +82 -5
- data/lib/dommy/internal/selector_ast.rb +124 -0
- data/lib/dommy/internal/selector_index.rb +146 -0
- data/lib/dommy/internal/selector_matcher.rb +756 -0
- data/lib/dommy/internal/selector_parser.rb +283 -131
- data/lib/dommy/internal/shadow_root_registry.rb +9 -2
- data/lib/dommy/internal/template_content_registry.rb +26 -18
- data/lib/dommy/internal/xml_serialization.rb +344 -0
- data/lib/dommy/intersection_observer.rb +2 -0
- data/lib/dommy/js/bridge_conformance.rb +80 -0
- data/lib/dommy/js/constructor_resolver.rb +44 -0
- data/lib/dommy/js/custom_element_bridge.rb +90 -0
- data/lib/dommy/js/dom_interfaces.rb +162 -0
- data/lib/dommy/js/handle_table.rb +60 -0
- data/lib/dommy/js/host_bridge.rb +517 -0
- data/lib/dommy/js/host_runtime.js +1495 -0
- data/lib/dommy/js/import_map.rb +58 -0
- data/lib/dommy/js/marshaller.rb +240 -0
- data/lib/dommy/js/module_loader.rb +99 -0
- data/lib/dommy/js/observable_runtime.js +742 -0
- data/lib/dommy/js/runtime.rb +115 -0
- data/lib/dommy/js/script_boot.rb +221 -0
- data/lib/dommy/js/wire_tags.rb +62 -0
- data/lib/dommy/location.rb +2 -0
- data/lib/dommy/media_query_list.rb +50 -14
- data/lib/dommy/message_channel.rb +22 -6
- data/lib/dommy/minitest/assertions.rb +27 -0
- data/lib/dommy/mutation_observer.rb +89 -4
- data/lib/dommy/navigator.rb +34 -2
- data/lib/dommy/node.rb +24 -14
- data/lib/dommy/notification.rb +2 -0
- data/lib/dommy/parser.rb +1 -1
- data/lib/dommy/performance.rb +21 -1
- data/lib/dommy/promise.rb +94 -10
- data/lib/dommy/range.rb +173 -31
- data/lib/dommy/resources.rb +178 -0
- data/lib/dommy/rspec/capy_style_matchers.rb +126 -0
- data/lib/dommy/scheduler.rb +149 -13
- data/lib/dommy/screen.rb +91 -0
- data/lib/dommy/shadow_root.rb +76 -13
- data/lib/dommy/storage.rb +2 -1
- data/lib/dommy/streams.rb +6 -0
- data/lib/dommy/text_codec.rb +7 -1
- data/lib/dommy/tree_walker.rb +33 -10
- data/lib/dommy/url.rb +13 -1
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/window.rb +199 -11
- data/lib/dommy/worker.rb +8 -4
- data/lib/dommy/xml_http_request.rb +47 -6
- data/lib/dommy.rb +36 -1
- metadata +96 -10
- data/lib/dommy/backend/nokogiri_adapter.rb +0 -127
- data/lib/dommy/backend/nokolexbor_adapter.rb +0 -117
data/lib/dommy/history.rb
CHANGED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dommy
|
|
4
|
+
# `<canvas>` — Dommy has no raster backend, so the API surface is implemented
|
|
5
|
+
# as inert stubs: `getContext('2d')` returns a CanvasRenderingContext2D whose
|
|
6
|
+
# draw operations are no-ops and whose read-backs are zeroed. The point is not
|
|
7
|
+
# to draw but to keep the *many* sites that merely touch a canvas from
|
|
8
|
+
# crashing: sprite/asset loaders, "am I a bot" canvas fingerprints, and chart
|
|
9
|
+
# libraries that feature-detect 2D support all call `getContext` then
|
|
10
|
+
# `fillRect` / `createImageData` / `measureText`. With no canvas element those
|
|
11
|
+
# are `undefined`, so the call throws "getContext is not a function" and aborts
|
|
12
|
+
# the whole bundle — exactly what hatena's bookmark.js does, which silently
|
|
13
|
+
# broke the page's bookmark button. WebGL is reported as genuinely unsupported
|
|
14
|
+
# (getContext returns null) so callers take their fallback path.
|
|
15
|
+
class HTMLCanvasElement < HTMLElement
|
|
16
|
+
DEFAULT_WIDTH = 300
|
|
17
|
+
DEFAULT_HEIGHT = 150
|
|
18
|
+
|
|
19
|
+
# A 1x1 transparent PNG — a constant so a canvas fingerprint reads a stable
|
|
20
|
+
# value instead of crashing; we render nothing into it.
|
|
21
|
+
BLANK_PNG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lE" \
|
|
22
|
+
"QVR42mNk+M8AAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
|
|
23
|
+
|
|
24
|
+
def width = int_dimension("width", DEFAULT_WIDTH)
|
|
25
|
+
def height = int_dimension("height", DEFAULT_HEIGHT)
|
|
26
|
+
|
|
27
|
+
def width=(value)
|
|
28
|
+
set_reflected_string("width", value.to_s)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def height=(value)
|
|
32
|
+
set_reflected_string("height", value.to_s)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Spec: getContext returns the SAME object across calls for one context id.
|
|
36
|
+
# Only '2d' is backed; webgl/webgl2/bitmaprenderer return null so feature
|
|
37
|
+
# detection cleanly fails over.
|
|
38
|
+
def get_context(context_id, *_options)
|
|
39
|
+
return nil unless context_id.to_s == "2d"
|
|
40
|
+
|
|
41
|
+
@__context_2d ||= CanvasRenderingContext2D.new(self)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_data_url(*_args) = BLANK_PNG
|
|
45
|
+
|
|
46
|
+
def to_blob(callback = nil, *_args)
|
|
47
|
+
return nil unless callback.respond_to?(:call)
|
|
48
|
+
|
|
49
|
+
callback.call(Blob.new([], {"type" => "image/png"}))
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def __js_get__(key)
|
|
54
|
+
case key
|
|
55
|
+
when "width" then width
|
|
56
|
+
when "height" then height
|
|
57
|
+
else super
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def __js_set__(key, value)
|
|
62
|
+
case key
|
|
63
|
+
when "width", "height" then set_reflected_string(key, value.to_s)
|
|
64
|
+
else super
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
include Bridge::Methods
|
|
69
|
+
js_methods %w[getContext toDataURL toBlob]
|
|
70
|
+
def __js_call__(method, args)
|
|
71
|
+
case method
|
|
72
|
+
when "getContext" then get_context(args[0], *args[1..])
|
|
73
|
+
when "toDataURL" then to_data_url(*args)
|
|
74
|
+
when "toBlob" then to_blob(*args)
|
|
75
|
+
else super
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def int_dimension(attr, default)
|
|
82
|
+
raw = @__node__[attr]
|
|
83
|
+
raw.nil? || raw.to_s.empty? ? default : raw.to_s.to_i
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The 2D drawing context — every drawing call is a no-op, state setters are
|
|
88
|
+
# remembered (so `ctx.fillStyle` round-trips), and read-backs return zeroed
|
|
89
|
+
# data of the right shape. Enough for asset loaders / fingerprint probes /
|
|
90
|
+
# chart feature-detection to run without throwing.
|
|
91
|
+
class CanvasRenderingContext2D
|
|
92
|
+
# Drawing-state attributes that round-trip a value (default per spec where it
|
|
93
|
+
# matters; a plain "" otherwise).
|
|
94
|
+
STATE_DEFAULTS = {
|
|
95
|
+
"fillStyle" => "#000000", "strokeStyle" => "#000000",
|
|
96
|
+
"globalAlpha" => 1.0, "globalCompositeOperation" => "source-over",
|
|
97
|
+
"lineWidth" => 1.0, "lineCap" => "butt", "lineJoin" => "miter", "miterLimit" => 10.0,
|
|
98
|
+
"lineDashOffset" => 0.0, "font" => "10px sans-serif", "textAlign" => "start",
|
|
99
|
+
"textBaseline" => "alphabetic", "direction" => "inherit",
|
|
100
|
+
"shadowBlur" => 0.0, "shadowColor" => "rgba(0, 0, 0, 0)",
|
|
101
|
+
"shadowOffsetX" => 0.0, "shadowOffsetY" => 0.0,
|
|
102
|
+
"imageSmoothingEnabled" => true, "imageSmoothingQuality" => "low", "filter" => "none"
|
|
103
|
+
}.freeze
|
|
104
|
+
|
|
105
|
+
def initialize(canvas)
|
|
106
|
+
@canvas = canvas
|
|
107
|
+
@state = STATE_DEFAULTS.dup
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def __js_get__(key)
|
|
111
|
+
return @canvas if key == "canvas"
|
|
112
|
+
return @state[key] if @state.key?(key)
|
|
113
|
+
|
|
114
|
+
Bridge::ABSENT
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def __js_set__(key, value)
|
|
118
|
+
return Bridge::UNHANDLED unless @state.key?(key)
|
|
119
|
+
|
|
120
|
+
@state[key] = value
|
|
121
|
+
value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
include Bridge::Methods
|
|
125
|
+
js_methods %w[
|
|
126
|
+
save restore scale rotate translate transform setTransform resetTransform getTransform
|
|
127
|
+
beginPath closePath moveTo lineTo bezierCurveTo quadraticCurveTo arc arcTo ellipse rect roundRect
|
|
128
|
+
fill stroke clip fillRect strokeRect clearRect fillText strokeText drawImage putImageData
|
|
129
|
+
setLineDash drawFocusIfNeeded scrollPathIntoView reset
|
|
130
|
+
getLineDash measureText createLinearGradient createRadialGradient createConicGradient
|
|
131
|
+
createPattern getImageData createImageData isPointInPath isPointInStroke getContextAttributes
|
|
132
|
+
]
|
|
133
|
+
def __js_call__(method, args)
|
|
134
|
+
case method
|
|
135
|
+
# All drawing/state-machine operations: nothing is painted.
|
|
136
|
+
when "save", "restore", "scale", "rotate", "translate", "transform", "setTransform",
|
|
137
|
+
"resetTransform", "getTransform", "beginPath", "closePath", "moveTo", "lineTo",
|
|
138
|
+
"bezierCurveTo", "quadraticCurveTo", "arc", "arcTo", "ellipse", "rect", "roundRect",
|
|
139
|
+
"fill", "stroke", "clip", "fillRect", "strokeRect", "clearRect", "fillText", "strokeText",
|
|
140
|
+
"drawImage", "putImageData", "setLineDash", "drawFocusIfNeeded", "scrollPathIntoView", "reset"
|
|
141
|
+
nil
|
|
142
|
+
when "getLineDash" then []
|
|
143
|
+
when "measureText" then TextMetrics.new(args[0].to_s)
|
|
144
|
+
when "createLinearGradient", "createRadialGradient", "createConicGradient", "createPattern"
|
|
145
|
+
CanvasGradient.new
|
|
146
|
+
when "getImageData" then ImageData.new(args[2].to_i.abs, args[3].to_i.abs)
|
|
147
|
+
when "createImageData" then created_image_data(args)
|
|
148
|
+
when "isPointInPath", "isPointInStroke" then false
|
|
149
|
+
when "getContextAttributes" then {}
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
# createImageData(imagedata) clones its dimensions; createImageData(w, h)
|
|
156
|
+
# builds a blank one. A zero/blank size still yields a valid 0-length buffer.
|
|
157
|
+
def created_image_data(args)
|
|
158
|
+
first = args[0]
|
|
159
|
+
if first.respond_to?(:__js_get__) && !first.__js_get__("width").equal?(Bridge::ABSENT)
|
|
160
|
+
ImageData.new(first.__js_get__("width").to_i, first.__js_get__("height").to_i)
|
|
161
|
+
else
|
|
162
|
+
ImageData.new(first.to_i.abs, args[1].to_i.abs)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# `ctx.measureText(...)` result. width is an approximation (no font metrics);
|
|
168
|
+
# the extended box metrics are zero. Enough that callers reading `.width`
|
|
169
|
+
# don't divide by undefined.
|
|
170
|
+
class TextMetrics
|
|
171
|
+
APPROX_CHAR_WIDTH = 6
|
|
172
|
+
|
|
173
|
+
def initialize(text)
|
|
174
|
+
@width = text.to_s.length * APPROX_CHAR_WIDTH
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def __js_get__(key)
|
|
178
|
+
case key
|
|
179
|
+
when "width" then @width
|
|
180
|
+
when "actualBoundingBoxLeft", "actualBoundingBoxRight",
|
|
181
|
+
"actualBoundingBoxAscent", "actualBoundingBoxDescent",
|
|
182
|
+
"fontBoundingBoxAscent", "fontBoundingBoxDescent",
|
|
183
|
+
"emHeightAscent", "emHeightDescent",
|
|
184
|
+
"hangingBaseline", "alphabeticBaseline", "ideographicBaseline" then 0
|
|
185
|
+
else Bridge::ABSENT
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def __js_set__(_key, _value) = Bridge::UNHANDLED
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# A gradient/pattern handle. addColorStop is a no-op (nothing is painted).
|
|
193
|
+
class CanvasGradient
|
|
194
|
+
def __js_get__(_key) = Bridge::ABSENT
|
|
195
|
+
def __js_set__(_key, _value) = Bridge::UNHANDLED
|
|
196
|
+
|
|
197
|
+
include Bridge::Methods
|
|
198
|
+
js_methods %w[addColorStop setTransform]
|
|
199
|
+
def __js_call__(method, _args)
|
|
200
|
+
case method
|
|
201
|
+
when "addColorStop", "setTransform" then nil
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# `ctx.getImageData(...)` / `createImageData(...)` result: width, height, and a
|
|
207
|
+
# zeroed RGBA `data` buffer of length width*height*4 (a plain numeric array,
|
|
208
|
+
# which supports the `.length` and `data[i]` reads canvas code does).
|
|
209
|
+
class ImageData
|
|
210
|
+
attr_reader :width, :height
|
|
211
|
+
|
|
212
|
+
def initialize(width, height)
|
|
213
|
+
@width = [width.to_i, 0].max
|
|
214
|
+
@height = [height.to_i, 0].max
|
|
215
|
+
@data = Array.new(@width * @height * 4, 0)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def __js_get__(key)
|
|
219
|
+
case key
|
|
220
|
+
when "width" then @width
|
|
221
|
+
when "height" then @height
|
|
222
|
+
when "data" then @data
|
|
223
|
+
when "colorSpace" then "srgb"
|
|
224
|
+
else Bridge::ABSENT
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def __js_set__(_key, _value) = Bridge::UNHANDLED
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -52,10 +52,9 @@ module Dommy
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def item(index)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
to_a[i]
|
|
55
|
+
# `index` is a WebIDL unsigned long, so it wraps modulo 2^32 (e.g. item(2^32)
|
|
56
|
+
# is item(0)); Ruby's modulo also normalizes negatives to that range.
|
|
57
|
+
to_a[index.to_i % 4_294_967_296]
|
|
59
58
|
end
|
|
60
59
|
|
|
61
60
|
# `namedItem(name)` returns the first element whose `id` or
|
|
@@ -119,8 +118,8 @@ module Dommy
|
|
|
119
118
|
item(s.to_i)
|
|
120
119
|
else
|
|
121
120
|
# Non-array-index strings (negative, ≥ 2^32-1, or names) use the named
|
|
122
|
-
# getter.
|
|
123
|
-
named_item(s) || (s == "length" ? length :
|
|
121
|
+
# getter; a miss is JS `undefined` (and `"x" in coll` false).
|
|
122
|
+
named_item(s) || (s == "length" ? length : Bridge::ABSENT)
|
|
124
123
|
end
|
|
125
124
|
end
|
|
126
125
|
end
|