dommy 0.16.0 → 0.17.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 +1 -0
- data/lib/dommy/backend.rb +3 -0
- data/lib/dommy/blob.rb +6 -0
- data/lib/dommy/browser.rb +12 -4
- data/lib/dommy/character_data.rb +1 -13
- data/lib/dommy/cookie_store.rb +2 -1
- data/lib/dommy/document.rb +67 -30
- data/lib/dommy/element.rb +11 -0
- data/lib/dommy/element_internals.rb +12 -0
- data/lib/dommy/encodings/legacy_multibyte.rb +800 -0
- data/lib/dommy/encodings/multibyte_overrides.rb +76 -0
- data/lib/dommy/encodings/prescan.rb +234 -0
- data/lib/dommy/encodings.rb +121 -57
- data/lib/dommy/event.rb +107 -54
- data/lib/dommy/event_source.rb +1 -0
- data/lib/dommy/fetch.rb +244 -53
- data/lib/dommy/file_reader.rb +1 -0
- data/lib/dommy/form_data.rb +22 -9
- data/lib/dommy/form_entry_list.rb +3 -3
- data/lib/dommy/html_canvas_element.rb +85 -12
- data/lib/dommy/html_elements/base.rb +1 -0
- data/lib/dommy/html_elements/forms/input.rb +3 -0
- data/lib/dommy/html_elements/interactive.rb +56 -48
- data/lib/dommy/html_elements/metadata.rb +23 -0
- data/lib/dommy/interaction/form_submission.rb +24 -42
- data/lib/dommy/internal/accessibility_tree.rb +149 -162
- data/lib/dommy/internal/accessibility_visibility.rb +16 -17
- data/lib/dommy/internal/accessible_name.rb +231 -246
- data/lib/dommy/internal/aria_role.rb +182 -195
- data/lib/dommy/internal/aria_snapshot.rb +35 -38
- data/lib/dommy/internal/aria_state.rb +102 -118
- data/lib/dommy/internal/autofill.rb +75 -75
- data/lib/dommy/internal/backend_prefilter.rb +127 -130
- data/lib/dommy/internal/child_navigable.rb +86 -83
- data/lib/dommy/internal/close_watcher_manager.rb +1 -0
- data/lib/dommy/internal/css/ancestor_filter.rb +64 -64
- data/lib/dommy/internal/css/calc.rb +90 -71
- data/lib/dommy/internal/css/cascade.rb +69 -67
- data/lib/dommy/internal/css/color.rb +249 -187
- data/lib/dommy/internal/css/computed_style_builder.rb +34 -0
- data/lib/dommy/internal/css/computed_style_declaration.rb +50 -11
- data/lib/dommy/internal/css/counters.rb +67 -67
- data/lib/dommy/internal/css/custom_properties.rb +114 -114
- data/lib/dommy/internal/css/parser.rb +277 -239
- data/lib/dommy/internal/css/property_registry.rb +321 -302
- data/lib/dommy/internal/css/renderability.rb +37 -35
- data/lib/dommy/internal/css/rule_index.rb +2 -2
- data/lib/dommy/internal/css/rule_index_inputs.rb +2 -2
- data/lib/dommy/internal/css/supports.rb +115 -113
- data/lib/dommy/internal/css/ua_stylesheet.rb +55 -53
- data/lib/dommy/internal/css_rule_text.rb +121 -119
- data/lib/dommy/internal/css_source.rb +1 -3
- data/lib/dommy/internal/css_syntax.rb +157 -92
- data/lib/dommy/internal/declarative_shadow_roots.rb +1 -0
- data/lib/dommy/internal/directionality.rb +120 -123
- data/lib/dommy/internal/document_live_ranges.rb +26 -1
- data/lib/dommy/internal/dom_matching.rb +185 -184
- data/lib/dommy/internal/element_geometry.rb +8 -1
- data/lib/dommy/internal/element_matching.rb +71 -69
- data/lib/dommy/internal/element_shadow.rb +1 -13
- data/lib/dommy/internal/element_state.rb +255 -265
- data/lib/dommy/internal/event_handlers.rb +66 -64
- data/lib/dommy/internal/exception_report.rb +88 -86
- data/lib/dommy/internal/focusability.rb +205 -205
- data/lib/dommy/internal/global_functions.rb +49 -47
- data/lib/dommy/internal/idl_reflection.rb +99 -97
- data/lib/dommy/internal/js_number.rb +35 -33
- data/lib/dommy/internal/light_dismiss.rb +130 -132
- data/lib/dommy/internal/literal_lookup.rb +75 -75
- data/lib/dommy/internal/mime_type.rb +54 -49
- data/lib/dommy/internal/mutation_coordinator.rb +46 -0
- data/lib/dommy/internal/mutation_observer_microtask.rb +60 -0
- data/lib/dommy/internal/namespaces.rb +51 -49
- data/lib/dommy/internal/node_adopter.rb +25 -1
- data/lib/dommy/internal/node_equality.rb +61 -59
- data/lib/dommy/internal/post_insertion_steps.rb +1 -0
- data/lib/dommy/internal/referrer.rb +144 -0
- data/lib/dommy/internal/rendered_text/collector.rb +22 -3
- data/lib/dommy/internal/rendered_text/computed_box.rb +3 -2
- data/lib/dommy/internal/rendered_text/fragment.rb +37 -38
- data/lib/dommy/internal/rendered_text/items.rb +7 -5
- data/lib/dommy/internal/selector_matcher.rb +361 -359
- data/lib/dommy/internal/selector_parser.rb +74 -62
- data/lib/dommy/internal/sequential_focus_navigation.rb +150 -151
- data/lib/dommy/internal/shadow_root_registry.rb +7 -0
- data/lib/dommy/internal/slot_assignment.rb +117 -0
- data/lib/dommy/internal/slot_signals.rb +125 -0
- data/lib/dommy/internal/unsafe_html.rb +79 -77
- data/lib/dommy/internal/url_parser.rb +5 -29
- data/lib/dommy/internal/web_idl.rb +158 -153
- data/lib/dommy/internal/window_bound.rb +14 -0
- data/lib/dommy/internal/window_constructors.rb +3 -0
- data/lib/dommy/internal/xml_serialization.rb +298 -296
- data/lib/dommy/js/dom_interfaces.rb +13 -1
- data/lib/dommy/js/host_bridge.rb +23 -1
- data/lib/dommy/js/host_callbacks.rb +7 -0
- data/lib/dommy/js/host_runtime.js +625 -61
- data/lib/dommy/js/import_map.rb +1 -1
- data/lib/dommy/js/observable_runtime.js +295 -128
- data/lib/dommy/js/webidl_members.js +1 -1
- data/lib/dommy/js/webidl_signatures.js +35 -6
- data/lib/dommy/media_query_list.rb +1 -0
- data/lib/dommy/message_channel.rb +2 -0
- data/lib/dommy/mutation_observer.rb +25 -29
- data/lib/dommy/navigator.rb +4 -1
- data/lib/dommy/node.rb +4 -0
- data/lib/dommy/notification.rb +1 -0
- data/lib/dommy/promise.rb +12 -0
- data/lib/dommy/range.rb +20 -3
- data/lib/dommy/shadow_root.rb +25 -1
- data/lib/dommy/streams.rb +18 -0
- data/lib/dommy/style_declaration.rb +15 -4
- data/lib/dommy/text_codec.rb +7 -5
- data/lib/dommy/tree_walker.rb +68 -61
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +1 -0
- data/lib/dommy/window.rb +22 -4
- data/lib/dommy/worker.rb +1 -0
- data/lib/dommy/xml_http_request.rb +12 -2
- data/lib/dommy.rb +17 -2
- metadata +11 -3
|
@@ -58,193 +58,180 @@ module Dommy
|
|
|
58
58
|
|
|
59
59
|
extend TextFlattening
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end
|
|
61
|
+
class << self
|
|
62
|
+
# Build the tree for `scope` (a Document — started at its <body> — or an
|
|
63
|
+
# Element). Returns a synthetic `:root` Node whose children are the
|
|
64
|
+
# accessible nodes the scope contributes (so a Document has no document
|
|
65
|
+
# line, matching Playwright).
|
|
66
|
+
def build(scope)
|
|
67
|
+
start = scope.respond_to?(:body) ? scope.body : scope
|
|
68
|
+
root = Node.new(role: :root, element: scope)
|
|
69
|
+
root.children = start ? nodes_for(start) : []
|
|
70
|
+
# Text is carried raw through construction so adjacent runs concatenate
|
|
71
|
+
# with correct (block vs inline) spacing; collapse it to its final form
|
|
72
|
+
# once, here.
|
|
73
|
+
finalize_text(root)
|
|
74
|
+
root
|
|
75
|
+
end
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# Squish each text node's accumulated content and drop the empties,
|
|
80
|
+
# depth-first. Coalescing already merged adjacent runs during build.
|
|
81
|
+
def finalize_text(node)
|
|
82
|
+
node.children = node.children.filter_map do |child|
|
|
83
|
+
if child.text?
|
|
84
|
+
text = squish(child.name)
|
|
85
|
+
Node.new(role: :text, name: text) unless text.empty?
|
|
86
|
+
else
|
|
87
|
+
finalize_text(child)
|
|
88
|
+
child
|
|
89
|
+
end
|
|
88
90
|
end
|
|
91
|
+
node
|
|
89
92
|
end
|
|
90
|
-
node
|
|
91
|
-
end
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
node = Node.new(
|
|
111
|
-
role: role,
|
|
112
|
-
name: AccessibleName.compute(element),
|
|
113
|
-
states: AriaState.compute(element, role),
|
|
114
|
-
element: element
|
|
115
|
-
)
|
|
116
|
-
# Name-from-content roles fold their descendant text into the name, so
|
|
117
|
-
# those text nodes are not emitted again; descendant roled nodes stay.
|
|
118
|
-
node.children = AccessibleName::NAME_FROM_CONTENT.include?(role) ? children.reject(&:text?) : children
|
|
119
|
-
# When a node's sole content is text that just repeats its (author-
|
|
120
|
-
# supplied) accessible name, that text is not exposed again
|
|
121
|
-
# (aria-label="X">X is `banner "X"`, but a text sibling keeps it).
|
|
122
|
-
node.children = [] if sole_name_text?(node)
|
|
123
|
-
# A native range / number / color control shows its value as inline text.
|
|
124
|
-
value = native_widget_value(element)
|
|
125
|
-
node.children << text_node(value) if value
|
|
126
|
-
[node]
|
|
127
|
-
end
|
|
94
|
+
# The accessible nodes an element contributes to its parent: 0 (excluded),
|
|
95
|
+
# 1 (a real node), or many (its promoted children when it is
|
|
96
|
+
# presentational / generic).
|
|
97
|
+
def nodes_for(element)
|
|
98
|
+
return [] if excluded?(element)
|
|
99
|
+
return [] if lone_unscoped_th?(element)
|
|
100
|
+
|
|
101
|
+
role = AriaRole.compute(element)
|
|
102
|
+
children = build_children(element)
|
|
103
|
+
|
|
104
|
+
# Presentational and generic/roleless containers drop out; their
|
|
105
|
+
# children are promoted to the parent. A block-level box separates its
|
|
106
|
+
# text from siblings, so its promoted run is padded with whitespace.
|
|
107
|
+
if role == "none" || role == "" || role == "generic"
|
|
108
|
+
return AccessibleName.block_level?(element) ? [text_node(" "), *children, text_node(" ")] : children
|
|
109
|
+
end
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
|
|
111
|
+
node = Node.new(
|
|
112
|
+
role: role,
|
|
113
|
+
name: AccessibleName.compute(element),
|
|
114
|
+
states: AriaState.compute(element, role),
|
|
115
|
+
element: element
|
|
116
|
+
)
|
|
117
|
+
# Name-from-content roles fold their descendant text into the name, so
|
|
118
|
+
# those text nodes are not emitted again; descendant roled nodes stay.
|
|
119
|
+
node.children = AccessibleName::NAME_FROM_CONTENT.include?(role) ? children.reject(&:text?) : children
|
|
120
|
+
# When a node's sole content is text that just repeats its (author-
|
|
121
|
+
# supplied) accessible name, that text is not exposed again
|
|
122
|
+
# (aria-label="X">X is `banner "X"`, but a text sibling keeps it).
|
|
123
|
+
node.children = [] if sole_name_text?(node)
|
|
124
|
+
# A native range / number / color control shows its value as inline text.
|
|
125
|
+
value = native_widget_value(element)
|
|
126
|
+
node.children << text_node(value) if value
|
|
127
|
+
[node]
|
|
128
|
+
end
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
end
|
|
130
|
+
def sole_name_text?(node)
|
|
131
|
+
return false unless node.children.size == 1 && !node.name.to_s.empty?
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def build_children(element)
|
|
139
|
-
out = []
|
|
140
|
-
element.child_nodes.each do |child|
|
|
141
|
-
if child.is_a?(Dommy::Element)
|
|
142
|
-
out.concat(nodes_for(child))
|
|
143
|
-
elsif child.is_a?(Dommy::TextNode)
|
|
144
|
-
# Keep the text raw (whitespace and all); spacing is resolved when
|
|
145
|
-
# adjacent runs are coalesced and finally squished.
|
|
146
|
-
out << text_node(child.text_content.to_s)
|
|
147
|
-
end
|
|
133
|
+
child = node.children.first
|
|
134
|
+
child.text? && squish(child.name) == node.name
|
|
148
135
|
end
|
|
149
|
-
coalesce_text(out)
|
|
150
|
-
end
|
|
151
136
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
137
|
+
# Walk an element's child nodes in document order: significant text
|
|
138
|
+
# becomes a :text node; elements recurse (and may promote).
|
|
139
|
+
def build_children(element)
|
|
140
|
+
out = []
|
|
141
|
+
element.child_nodes.each do |child|
|
|
142
|
+
if child.is_a?(Dommy::Element)
|
|
143
|
+
out.concat(nodes_for(child))
|
|
144
|
+
elsif child.is_a?(Dommy::TextNode)
|
|
145
|
+
# Keep the text raw (whitespace and all); spacing is resolved when
|
|
146
|
+
# adjacent runs are coalesced and finally squished.
|
|
147
|
+
out << text_node(child.text_content.to_s)
|
|
148
|
+
end
|
|
162
149
|
end
|
|
150
|
+
coalesce_text(out)
|
|
163
151
|
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def text_node(text) = Node.new(role: :text, name: text)
|
|
167
152
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
153
|
+
# Merge runs of adjacent text nodes into one by direct concatenation —
|
|
154
|
+
# inline content glues ("Save" + "Save" -> "SaveSave"); the whitespace that
|
|
155
|
+
# separates block-level content comes from the padding added when a
|
|
156
|
+
# block box is promoted (see nodes_for).
|
|
157
|
+
def coalesce_text(nodes)
|
|
158
|
+
nodes.each_with_object([]) do |node, out|
|
|
159
|
+
if node.text? && out.last&.text?
|
|
160
|
+
out[-1] = text_node("#{out.last.name}#{node.name}")
|
|
161
|
+
else
|
|
162
|
+
out << node
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
175
166
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
167
|
+
def text_node(text) = Node.new(role: :text, name: text)
|
|
168
|
+
|
|
169
|
+
# The displayed value of a native range / number / color input (the only
|
|
170
|
+
# widgets whose value Chromium puts in the snapshot — ARIA aria-valuenow /
|
|
171
|
+
# progress / meter show nothing). A range always shows a value (defaulting
|
|
172
|
+
# to the midpoint of its min/max); color defaults to "#000000"; a number
|
|
173
|
+
# shows one only when non-empty.
|
|
174
|
+
def native_widget_value(element)
|
|
175
|
+
return nil unless element.local_name.to_s.casecmp?("input")
|
|
176
|
+
|
|
177
|
+
value = element.value.to_s
|
|
178
|
+
case element.__internal_attribute_value__("type").to_s.downcase
|
|
179
|
+
when "range" then value.empty? ? range_default(element) : value
|
|
180
|
+
when "color" then value.empty? ? "#000000" : value
|
|
181
|
+
when "number" then value.empty? ? nil : value
|
|
182
|
+
end
|
|
181
183
|
end
|
|
182
|
-
end
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
def range_default(element)
|
|
186
|
+
min = numeric(element.__internal_attribute_value__("min"), 0.0)
|
|
187
|
+
max = numeric(element.__internal_attribute_value__("max"), 100.0)
|
|
188
|
+
format_number(min + ((max - min) / 2.0))
|
|
189
|
+
end
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
def numeric(value, fallback)
|
|
192
|
+
Float(value)
|
|
193
|
+
rescue ArgumentError, TypeError
|
|
194
|
+
fallback
|
|
195
|
+
end
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
def format_number(number)
|
|
198
|
+
number == number.to_i ? number.to_i.to_s : number.to_s
|
|
199
|
+
end
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
# An element (and its whole subtree) is excluded from the tree when
|
|
202
|
+
# `aria-hidden="true"` or when it is not visually rendered. `visible?`
|
|
203
|
+
# deliberately ignores aria-hidden, so it is checked here. An element
|
|
204
|
+
# with the inert attribute is excluded too (HTML: inert nodes are not
|
|
205
|
+
# exposed to accessibility APIs) — unless the modal dialog that
|
|
206
|
+
# escapes its inertness sits inside it.
|
|
207
|
+
def excluded?(element)
|
|
208
|
+
AccessibilityVisibility.hidden?(element) || inert_subtree?(element)
|
|
209
|
+
end
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
def inert_subtree?(element)
|
|
212
|
+
return false unless element.namespace_uri == Namespaces::HTML && element.__internal_has_attribute__?("inert")
|
|
212
213
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
blocker = element.owner_document.__internal_blocking_modal_dialog__
|
|
215
|
+
!(blocker && element.contains?(blocker))
|
|
216
|
+
end
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
# A lone, unscoped <th> that is the only cell of the only row of its table
|
|
219
|
+
# is not emitted as a header cell — Chromium folds it into the row's
|
|
220
|
+
# accessible name. An explicit `scope`, a sibling cell, or a second row all
|
|
221
|
+
# make it a real header.
|
|
222
|
+
def lone_unscoped_th?(element)
|
|
223
|
+
return false unless element.local_name.to_s.casecmp?("th")
|
|
224
|
+
return false unless element.__internal_attribute_value__("scope").to_s.empty?
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
table = element.respond_to?(:closest) ? element.closest("table") : nil
|
|
227
|
+
return false unless table
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
rows = table.query_selector_all("tr").to_a
|
|
230
|
+
return false unless rows.size == 1
|
|
230
231
|
|
|
231
|
-
|
|
232
|
+
rows.first.query_selector_all("td, th").to_a.size == 1
|
|
233
|
+
end
|
|
232
234
|
end
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
# Everything below compute is how it is computed, not API.
|
|
236
|
-
private_class_method :finalize_text
|
|
237
|
-
private_class_method :nodes_for
|
|
238
|
-
private_class_method :sole_name_text?
|
|
239
|
-
private_class_method :build_children
|
|
240
|
-
private_class_method :coalesce_text
|
|
241
|
-
private_class_method :text_node
|
|
242
|
-
private_class_method :native_widget_value
|
|
243
|
-
private_class_method :range_default
|
|
244
|
-
private_class_method :numeric
|
|
245
|
-
private_class_method :format_number
|
|
246
|
-
private_class_method :excluded?, :inert_subtree?
|
|
247
|
-
private_class_method :lone_unscoped_th?
|
|
248
235
|
end
|
|
249
236
|
end
|
|
250
237
|
end
|
|
@@ -16,29 +16,28 @@ module Dommy
|
|
|
16
16
|
# `display: none` by the UA sheet, so the two agree wherever CSS is
|
|
17
17
|
# available and this covers the case where it is not.
|
|
18
18
|
module AccessibilityVisibility
|
|
19
|
-
|
|
19
|
+
class << self
|
|
20
|
+
def hidden?(element)
|
|
21
|
+
return true if aria_hidden?(element)
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
!DomMatching.visible?(element)
|
|
24
|
+
end
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
# The name-from-content variant. A node the author pointed at directly
|
|
27
|
+
# with aria-labelledby is still named even when hidden — this only governs
|
|
28
|
+
# the traversal INTO a subtree.
|
|
29
|
+
def hidden_for_name?(element)
|
|
30
|
+
return true if element.respond_to?(:__internal_has_attribute__?) && element.__internal_has_attribute__?("hidden")
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# the traversal INTO a subtree.
|
|
30
|
-
def hidden_for_name?(element)
|
|
31
|
-
return true if element.respond_to?(:__internal_has_attribute__?) && element.__internal_has_attribute__?("hidden")
|
|
32
|
+
hidden?(element)
|
|
33
|
+
end
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
+
private
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
def aria_hidden?(element)
|
|
38
|
+
element.__internal_attribute_value__("aria-hidden").to_s.casecmp?("true")
|
|
39
|
+
end
|
|
38
40
|
end
|
|
39
|
-
|
|
40
|
-
# Everything above is the module; everything below is how.
|
|
41
|
-
private_class_method :aria_hidden?
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
end
|