Web Inspector: DOM: lazily create the agent
[WebKit-https.git] / Source / WebCore / inspector / InspectorAuditAccessibilityObject.cpp
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26
27 #include "config.h"
28 #include "InspectorAuditAccessibilityObject.h"
29
30 #include "AXObjectCache.h"
31 #include "AccessibilityNodeObject.h"
32 #include "AccessibilityObject.h"
33 #include "ContainerNode.h"
34 #include "Document.h"
35 #include "Element.h"
36 #include "ElementDescendantIterator.h"
37 #include "HTMLNames.h"
38 #include "Node.h"
39 #include "SpaceSplitString.h"
40 #include <wtf/Vector.h>
41 #include <wtf/text/WTFString.h>
42
43 namespace WebCore {
44
45 using namespace Inspector;
46
47 #define ERROR_IF_NO_ACTIVE_AUDIT() \
48     if (!m_auditAgent.hasActiveAudit()) \
49         return Exception { NotAllowedError, "Cannot be called outside of a Web Inspector Audit"_s };
50
51 InspectorAuditAccessibilityObject::InspectorAuditAccessibilityObject(InspectorAuditAgent& auditAgent)
52     : m_auditAgent(auditAgent)
53 {
54 }
55
56 static AccessibilityObject* accessiblityObjectForNode(Node& node)
57 {
58     if (!AXObjectCache::accessibilityEnabled())
59         AXObjectCache::enableAccessibility();
60
61     if (AXObjectCache* axObjectCache = node.document().axObjectCache())
62         return axObjectCache->getOrCreate(&node);
63
64     return nullptr;
65 }
66
67 ExceptionOr<Vector<Ref<Node>>> InspectorAuditAccessibilityObject::getElementsByComputedRole(Document& document, const String& role, Node* container)
68 {
69     ERROR_IF_NO_ACTIVE_AUDIT();
70
71     Vector<Ref<Node>> nodes;
72
73     for (Element& element : elementDescendants(is<ContainerNode>(container) ? downcast<ContainerNode>(*container) : document)) {
74         if (AccessibilityObject* axObject = accessiblityObjectForNode(element)) {
75             if (axObject->computedRoleString() == role)
76                 nodes.append(element);
77         }
78     }
79
80     return nodes;
81 }
82
83 ExceptionOr<RefPtr<Node>> InspectorAuditAccessibilityObject::getActiveDescendant(Node& node)
84 {
85     ERROR_IF_NO_ACTIVE_AUDIT();
86
87     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
88         if (AccessibilityObject* activeDescendant = axObject->activeDescendant())
89             return activeDescendant->node();
90     }
91
92     return nullptr;
93 }
94
95 static void addChildren(AccessibilityObject& parentObject, Vector<RefPtr<Node>>& childNodes)
96 {
97     for (const RefPtr<AccessibilityObject>& childObject : parentObject.children()) {
98         if (Node* childNode = childObject->node())
99             childNodes.append(childNode);
100         else
101             addChildren(*childObject, childNodes);
102     }
103 }
104
105 ExceptionOr<Optional<Vector<RefPtr<Node>>>> InspectorAuditAccessibilityObject::getChildNodes(Node& node)
106 {
107     ERROR_IF_NO_ACTIVE_AUDIT();
108
109     Optional<Vector<RefPtr<Node>>> result;
110
111     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
112         Vector<RefPtr<Node>> childNodes;
113         addChildren(*axObject, childNodes);
114         result = WTFMove(childNodes);
115     }
116
117     return result;
118 }
119
120 ExceptionOr<Optional<InspectorAuditAccessibilityObject::ComputedProperties>> InspectorAuditAccessibilityObject::getComputedProperties(Node& node)
121 {
122     ERROR_IF_NO_ACTIVE_AUDIT();
123
124     Optional<InspectorAuditAccessibilityObject::ComputedProperties> result;
125
126     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
127         ComputedProperties computedProperties;
128
129         AccessibilityObject* current = axObject;
130         while (current && (!computedProperties.busy || !computedProperties.busy.value())) {
131             computedProperties.busy = current->isBusy();
132             current = current->parentObject();
133         }
134
135         if (axObject->supportsChecked()) {
136             AccessibilityButtonState checkValue = axObject->checkboxOrRadioValue();
137             if (checkValue == AccessibilityButtonState::On)
138                 computedProperties.checked = "true"_s;
139             else if (checkValue == AccessibilityButtonState::Mixed)
140                 computedProperties.checked = "mixed"_s;
141             else if (axObject->isChecked())
142                 computedProperties.checked = "true"_s;
143             else
144                 computedProperties.checked = "false"_s;
145         }
146
147         switch (axObject->currentState()) {
148         case AccessibilityCurrentState::False:
149             computedProperties.currentState = "false"_s;
150             break;
151         case AccessibilityCurrentState::True:
152             computedProperties.currentState = "true"_s;
153             break;
154         case AccessibilityCurrentState::Page:
155             computedProperties.currentState = "page"_s;
156             break;
157         case AccessibilityCurrentState::Step:
158             computedProperties.currentState = "step"_s;
159             break;
160         case AccessibilityCurrentState::Location:
161             computedProperties.currentState = "location"_s;
162             break;
163         case AccessibilityCurrentState::Date:
164             computedProperties.currentState = "date"_s;
165             break;
166         case AccessibilityCurrentState::Time:
167             computedProperties.currentState = "time"_s;
168             break;
169         }
170
171         computedProperties.disabled = !axObject->isEnabled();
172
173         if (axObject->supportsExpanded())
174             computedProperties.expanded = axObject->isExpanded();
175
176         if (is<Element>(node) && axObject->canSetFocusAttribute())
177             computedProperties.focused = axObject->isFocused();
178
179         computedProperties.headingLevel = axObject->headingLevel();
180         computedProperties.hidden = axObject->isAXHidden() || axObject->isDOMHidden();
181         computedProperties.hierarchicalLevel = axObject->hierarchicalLevel();
182         computedProperties.ignored = axObject->accessibilityIsIgnored();
183         computedProperties.ignoredByDefault = axObject->accessibilityIsIgnoredByDefault();
184
185         String invalidValue = axObject->invalidStatus();
186         if (invalidValue == "false")
187             computedProperties.invalidStatus = "false"_s;
188         else if (invalidValue == "grammar")
189             computedProperties.invalidStatus = "grammar"_s;
190         else if (invalidValue == "spelling")
191             computedProperties.invalidStatus = "spelling"_s;
192         else
193             computedProperties.invalidStatus = "true"_s;
194
195         computedProperties.isPopUpButton = axObject->isPopUpButton() || axObject->hasPopup();
196         computedProperties.label = axObject->computedLabel();
197
198         if (axObject->supportsLiveRegion()) {
199             computedProperties.liveRegionAtomic = axObject->liveRegionAtomic();
200
201             String ariaRelevantAttrValue = axObject->liveRegionRelevant();
202             if (!ariaRelevantAttrValue.isEmpty()) {
203                 Vector<String> liveRegionRelevant;
204                 String ariaRelevantAdditions = "additions";
205                 String ariaRelevantRemovals = "removals";
206                 String ariaRelevantText = "text";
207
208                 const auto& values = SpaceSplitString(ariaRelevantAttrValue, true);
209                 if (values.contains("all")) {
210                     liveRegionRelevant.append(ariaRelevantAdditions);
211                     liveRegionRelevant.append(ariaRelevantRemovals);
212                     liveRegionRelevant.append(ariaRelevantText);
213                 } else {
214                     if (values.contains(ariaRelevantAdditions))
215                         liveRegionRelevant.append(ariaRelevantAdditions);
216                     if (values.contains(ariaRelevantRemovals))
217                         liveRegionRelevant.append(ariaRelevantRemovals);
218                     if (values.contains(ariaRelevantText))
219                         liveRegionRelevant.append(ariaRelevantText);
220                 }
221                 computedProperties.liveRegionRelevant = liveRegionRelevant;
222             }
223
224             computedProperties.liveRegionStatus = axObject->liveRegionStatus();
225         }
226
227         computedProperties.pressed = axObject->pressedIsPresent() && axObject->isPressed();
228
229         if (axObject->isTextControl())
230             computedProperties.readonly = !axObject->canSetValueAttribute();
231
232         if (axObject->supportsRequiredAttribute())
233             computedProperties.required = axObject->isRequired();
234
235         computedProperties.role = axObject->computedRoleString();
236         computedProperties.selected = axObject->isSelected();
237
238         result = computedProperties;
239     }
240
241     return result;
242 }
243
244 ExceptionOr<Optional<Vector<RefPtr<Node>>>> InspectorAuditAccessibilityObject::getControlledNodes(Node& node)
245 {
246     ERROR_IF_NO_ACTIVE_AUDIT();
247
248     Optional<Vector<RefPtr<Node>>> result;
249
250     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
251         Vector<RefPtr<Node>> controlledNodes;
252
253         Vector<Element*> controlledElements;
254         axObject->elementsFromAttribute(controlledElements, HTMLNames::aria_controlsAttr);
255         for (Element* controlledElement : controlledElements) {
256             if (controlledElement)
257                 controlledNodes.append(controlledElement);
258         }
259
260         result = WTFMove(controlledNodes);
261     }
262
263     return result;
264 }
265
266 ExceptionOr<Optional<Vector<RefPtr<Node>>>> InspectorAuditAccessibilityObject::getFlowedNodes(Node& node)
267 {
268     ERROR_IF_NO_ACTIVE_AUDIT();
269
270     Optional<Vector<RefPtr<Node>>> result;
271
272     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
273         Vector<RefPtr<Node>> flowedNodes;
274
275         Vector<Element*> flowedElements;
276         axObject->elementsFromAttribute(flowedElements, HTMLNames::aria_flowtoAttr);
277         for (Element* flowedElement : flowedElements) {
278             if (flowedElement)
279                 flowedNodes.append(flowedElement);
280         }
281
282         result = WTFMove(flowedNodes);
283     }
284
285     return result;
286 }
287
288 ExceptionOr<RefPtr<Node>> InspectorAuditAccessibilityObject::getMouseEventNode(Node& node)
289 {
290     ERROR_IF_NO_ACTIVE_AUDIT();
291
292     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
293         if (is<AccessibilityNodeObject>(axObject))
294             return downcast<AccessibilityNodeObject>(axObject)->mouseButtonListener(MouseButtonListenerResultFilter::IncludeBodyElement);
295     }
296
297     return nullptr;
298 }
299
300 ExceptionOr<Optional<Vector<RefPtr<Node>>>> InspectorAuditAccessibilityObject::getOwnedNodes(Node& node)
301 {
302     ERROR_IF_NO_ACTIVE_AUDIT();
303
304     Optional<Vector<RefPtr<Node>>> result;
305
306     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
307         if (axObject->supportsARIAOwns()) {
308             Vector<RefPtr<Node>> ownedNodes;
309
310             Vector<Element*> ownedElements;
311             axObject->elementsFromAttribute(ownedElements, HTMLNames::aria_ownsAttr);
312             for (Element* ownedElement : ownedElements) {
313                 if (ownedElement)
314                     ownedNodes.append(ownedElement);
315             }
316
317             result = WTFMove(ownedNodes);
318         }
319     }
320
321     return result;
322 }
323
324 ExceptionOr<RefPtr<Node>> InspectorAuditAccessibilityObject::getParentNode(Node& node)
325 {
326     ERROR_IF_NO_ACTIVE_AUDIT();
327
328     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
329         if (AccessibilityObject* parentObject = axObject->parentObjectUnignored())
330             return parentObject->node();
331     }
332
333     return nullptr;
334 }
335
336 ExceptionOr<Optional<Vector<RefPtr<Node>>>> InspectorAuditAccessibilityObject::getSelectedChildNodes(Node& node)
337 {
338     ERROR_IF_NO_ACTIVE_AUDIT();
339
340     Optional<Vector<RefPtr<Node>>> result;
341
342     if (AccessibilityObject* axObject = accessiblityObjectForNode(node)) {
343         Vector<RefPtr<Node>> selectedChildNodes;
344
345         AccessibilityObject::AccessibilityChildrenVector selectedChildren;
346         axObject->selectedChildren(selectedChildren);
347         for (RefPtr<AccessibilityObject>& selectedChildObject : selectedChildren) {
348             if (Node* selectedChildNode = selectedChildObject->node())
349                 selectedChildNodes.append(selectedChildNode);
350         }
351
352         result = WTFMove(selectedChildNodes);
353     }
354
355     return result;
356 }
357
358 } // namespace WebCore