952bbd7f29b792c77acaf0f881d49751d5bc14d7
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityObject.cpp
1 /*
2  * Copyright (C) 2008-2009, 2011, 2017 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "AccessibilityObject.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityRenderObject.h"
34 #include "AccessibilityScrollView.h"
35 #include "AccessibilityTable.h"
36 #include "Chrome.h"
37 #include "ChromeClient.h"
38 #include "DOMTokenList.h"
39 #include "Editing.h"
40 #include "Editor.h"
41 #include "ElementIterator.h"
42 #include "Event.h"
43 #include "EventDispatcher.h"
44 #include "EventHandler.h"
45 #include "EventNames.h"
46 #include "FloatRect.h"
47 #include "FocusController.h"
48 #include "Frame.h"
49 #include "FrameLoader.h"
50 #include "FrameSelection.h"
51 #include "HTMLBodyElement.h"
52 #include "HTMLDataListElement.h"
53 #include "HTMLDetailsElement.h"
54 #include "HTMLFormControlElement.h"
55 #include "HTMLInputElement.h"
56 #include "HTMLMediaElement.h"
57 #include "HTMLNames.h"
58 #include "HTMLParserIdioms.h"
59 #include "HTMLTextAreaElement.h"
60 #include "HitTestResult.h"
61 #include "LocalizedStrings.h"
62 #include "MathMLNames.h"
63 #include "NodeList.h"
64 #include "NodeTraversal.h"
65 #include "Page.h"
66 #include "RenderImage.h"
67 #include "RenderInline.h"
68 #include "RenderLayer.h"
69 #include "RenderListItem.h"
70 #include "RenderListMarker.h"
71 #include "RenderMenuList.h"
72 #include "RenderText.h"
73 #include "RenderTextControl.h"
74 #include "RenderTheme.h"
75 #include "RenderView.h"
76 #include "RenderWidget.h"
77 #include "RenderedPosition.h"
78 #include "Settings.h"
79 #include "TextCheckerClient.h"
80 #include "TextCheckingHelper.h"
81 #include "TextIterator.h"
82 #include "UserGestureIndicator.h"
83 #include "VisibleUnits.h"
84 #include <wtf/NeverDestroyed.h>
85 #include <wtf/StdLibExtras.h>
86 #include <wtf/text/StringBuilder.h>
87 #include <wtf/text/StringView.h>
88 #include <wtf/text/WTFString.h>
89 #include <wtf/unicode/CharacterNames.h>
90
91 namespace WebCore {
92
93 using namespace HTMLNames;
94
95 AccessibilityObject::~AccessibilityObject()
96 {
97     ASSERT(isDetached());
98 }
99
100 void AccessibilityObject::detachRemoteParts(AccessibilityDetachmentType detachmentType)
101 {
102     // Menu close events need to notify the platform. No element is used in the notification because it's a destruction event.
103     if (detachmentType == AccessibilityDetachmentType::ElementDestroyed && roleValue() == AccessibilityRole::Menu) {
104         if (auto* cache = axObjectCache())
105             cache->postNotification(nullptr, &cache->document(), AXObjectCache::AXMenuClosed);
106     }
107
108     // Clear any children and call detachFromParent on them so that
109     // no children are left with dangling pointers to their parent.
110     clearChildren();
111 }
112
113 bool AccessibilityObject::isDetached() const
114 {
115 #if ENABLE(ACCESSIBILITY)
116     return !wrapper();
117 #else
118     return true;
119 #endif
120 }
121
122 // ARIA marks elements as having their accessible name derive from either their contents, or their author provide name.
123 bool AccessibilityObject::accessibleNameDerivesFromContent() const
124 {
125     // First check for objects specifically identified by ARIA.
126     switch (ariaRoleAttribute()) {
127     case AccessibilityRole::ApplicationAlert:
128     case AccessibilityRole::ApplicationAlertDialog:
129     case AccessibilityRole::ApplicationDialog:
130     case AccessibilityRole::ApplicationGroup:
131     case AccessibilityRole::ApplicationLog:
132     case AccessibilityRole::ApplicationMarquee:
133     case AccessibilityRole::ApplicationStatus:
134     case AccessibilityRole::ApplicationTimer:
135     case AccessibilityRole::ComboBox:
136     case AccessibilityRole::Definition:
137     case AccessibilityRole::Document:
138     case AccessibilityRole::DocumentArticle:
139     case AccessibilityRole::DocumentMath:
140     case AccessibilityRole::DocumentNote:
141     case AccessibilityRole::LandmarkRegion:
142     case AccessibilityRole::LandmarkDocRegion:
143     case AccessibilityRole::Form:
144     case AccessibilityRole::Grid:
145     case AccessibilityRole::Group:
146     case AccessibilityRole::Image:
147     case AccessibilityRole::List:
148     case AccessibilityRole::ListBox:
149     case AccessibilityRole::LandmarkBanner:
150     case AccessibilityRole::LandmarkComplementary:
151     case AccessibilityRole::LandmarkContentInfo:
152     case AccessibilityRole::LandmarkNavigation:
153     case AccessibilityRole::LandmarkMain:
154     case AccessibilityRole::LandmarkSearch:
155     case AccessibilityRole::Menu:
156     case AccessibilityRole::MenuBar:
157     case AccessibilityRole::ProgressIndicator:
158     case AccessibilityRole::Meter:
159     case AccessibilityRole::RadioGroup:
160     case AccessibilityRole::ScrollBar:
161     case AccessibilityRole::Slider:
162     case AccessibilityRole::SpinButton:
163     case AccessibilityRole::Splitter:
164     case AccessibilityRole::Table:
165     case AccessibilityRole::TabList:
166     case AccessibilityRole::TabPanel:
167     case AccessibilityRole::TextArea:
168     case AccessibilityRole::TextField:
169     case AccessibilityRole::Toolbar:
170     case AccessibilityRole::TreeGrid:
171     case AccessibilityRole::Tree:
172     case AccessibilityRole::WebApplication:
173         return false;
174     default:
175         break;
176     }
177     
178     // Now check for generically derived elements now that we know the element does not match a specific ARIA role.
179     switch (roleValue()) {
180     case AccessibilityRole::Slider:
181     case AccessibilityRole::ListBox:
182         return false;
183     default:
184         break;
185     }
186     
187     return true;
188 }
189     
190 String AccessibilityObject::computedLabel()
191 {
192     // This method is being called by WebKit inspector, which may happen at any time, so we need to update our backing store now.
193     // Also hold onto this object in case updateBackingStore deletes this node.
194     RefPtr<AccessibilityObject> protectedThis(this);
195     updateBackingStore();
196     Vector<AccessibilityText> text;
197     accessibilityText(text);
198     if (text.size())
199         return text[0].text;
200     return String();
201 }
202
203 bool AccessibilityObject::isBlockquote() const
204 {
205     return roleValue() == AccessibilityRole::Blockquote;
206 }
207
208 bool AccessibilityObject::isTextControl() const
209 {
210     switch (roleValue()) {
211     case AccessibilityRole::ComboBox:
212     case AccessibilityRole::SearchField:
213     case AccessibilityRole::TextArea:
214     case AccessibilityRole::TextField:
215         return true;
216     default:
217         return false;
218     }
219 }
220     
221 bool AccessibilityObject::isARIATextControl() const
222 {
223     return ariaRoleAttribute() == AccessibilityRole::TextArea || ariaRoleAttribute() == AccessibilityRole::TextField || ariaRoleAttribute() == AccessibilityRole::SearchField;
224 }
225
226 bool AccessibilityObject::isNonNativeTextControl() const
227 {
228     return (isARIATextControl() || hasContentEditableAttributeSet()) && !isNativeTextControl();
229 }
230
231 bool AccessibilityObject::isLandmark() const
232 {
233     switch (roleValue()) {
234     case AccessibilityRole::LandmarkBanner:
235     case AccessibilityRole::LandmarkComplementary:
236     case AccessibilityRole::LandmarkContentInfo:
237     case AccessibilityRole::LandmarkDocRegion:
238     case AccessibilityRole::LandmarkMain:
239     case AccessibilityRole::LandmarkNavigation:
240     case AccessibilityRole::LandmarkRegion:
241     case AccessibilityRole::LandmarkSearch:
242         return true;
243     default:
244         return false;
245     }
246 }
247
248 bool AccessibilityObject::hasMisspelling() const
249 {
250     if (!node())
251         return false;
252     
253     Frame* frame = node()->document().frame();
254     if (!frame)
255         return false;
256     
257     Editor& editor = frame->editor();
258     
259     TextCheckerClient* textChecker = editor.textChecker();
260     if (!textChecker)
261         return false;
262     
263     bool isMisspelled = false;
264
265     if (unifiedTextCheckerEnabled(frame)) {
266         Vector<TextCheckingResult> results;
267         checkTextOfParagraph(*textChecker, stringValue(), TextCheckingType::Spelling, results, frame->selection().selection());
268         if (!results.isEmpty())
269             isMisspelled = true;
270         return isMisspelled;
271     }
272
273     int misspellingLength = 0;
274     int misspellingLocation = -1;
275     textChecker->checkSpellingOfString(stringValue(), &misspellingLocation, &misspellingLength);
276     if (misspellingLength || misspellingLocation != -1)
277         isMisspelled = true;
278     
279     return isMisspelled;
280 }
281
282 RefPtr<Range> AccessibilityObject::getMisspellingRange(RefPtr<Range> const& start, AccessibilitySearchDirection direction) const
283 {
284     auto node = this->node();
285     if (!node)
286         return nullptr;
287
288     Frame* frame = node->document().frame();
289     if (!frame)
290         return nullptr;
291
292     if (!unifiedTextCheckerEnabled(frame))
293         return nullptr;
294
295     Editor& editor = frame->editor();
296
297     TextCheckerClient* textChecker = editor.textChecker();
298     if (!textChecker)
299         return nullptr;
300
301     Vector<TextCheckingResult> misspellings;
302     checkTextOfParagraph(*textChecker, stringValue(), TextCheckingType::Spelling, misspellings, frame->selection().selection());
303
304     // The returned misspellings are assumed to be ordered in the document
305     // logical order, which should be matched by Range::compareBoundaryPoints.
306     // So iterate forward or backwards depending on the desired search
307     // direction to find the closest misspelling in that direction.
308     if (direction == AccessibilitySearchDirection::Next) {
309         for (const auto& misspelling : misspellings) {
310             auto misspellingRange = editor.rangeForTextCheckingResult(misspelling);
311             if (!misspellingRange)
312                 continue;
313
314             if (misspellingRange->compareBoundaryPoints(Range::END_TO_END, *start).releaseReturnValue() > 0)
315                 return misspellingRange;
316         }
317     } else if (direction == AccessibilitySearchDirection::Previous) {
318         for (auto rit = misspellings.rbegin(); rit != misspellings.rend(); ++rit) {
319             auto misspellingRange = editor.rangeForTextCheckingResult(*rit);
320             if (!misspellingRange)
321                 continue;
322
323             if (misspellingRange->compareBoundaryPoints(Range::START_TO_START, *start).releaseReturnValue() < 0)
324                 return misspellingRange;
325         }
326     }
327
328     return nullptr;
329 }
330
331 unsigned AccessibilityObject::blockquoteLevel() const
332 {
333     unsigned level = 0;
334     for (Node* elementNode = node(); elementNode; elementNode = elementNode->parentNode()) {
335         if (elementNode->hasTagName(blockquoteTag))
336             ++level;
337     }
338     
339     return level;
340 }
341
342 AXCoreObject* AccessibilityObject::parentObjectUnignored() const
343 {
344     return Accessibility::findAncestor<AccessibilityObject>(*this, false, [] (const AccessibilityObject& object) {
345         return !object.accessibilityIsIgnored();
346     });
347 }
348
349 AccessibilityObject* AccessibilityObject::previousSiblingUnignored(int limit) const
350 {
351     AccessibilityObject* previous;
352     ASSERT(limit >= 0);
353     for (previous = previousSibling(); previous && previous->accessibilityIsIgnored(); previous = previous->previousSibling()) {
354         limit--;
355         if (limit <= 0)
356             break;
357     }
358     return previous;
359 }
360     
361 FloatRect AccessibilityObject::convertFrameToSpace(const FloatRect& frameRect, AccessibilityConversionSpace conversionSpace) const
362 {
363     ASSERT(isMainThread());
364     
365     // Find the appropriate scroll view to use to convert the contents to the window.
366     const auto parentAccessibilityScrollView = ancestorAccessibilityScrollView(false /* includeSelf */);
367     auto* parentScrollView = parentAccessibilityScrollView ? parentAccessibilityScrollView->scrollView() : nullptr;
368
369     auto snappedFrameRect = snappedIntRect(IntRect(frameRect));
370     if (parentScrollView)
371         snappedFrameRect = parentScrollView->contentsToRootView(snappedFrameRect);
372     
373     if (conversionSpace == AccessibilityConversionSpace::Screen) {
374         auto page = this->page();
375         if (!page)
376             return snappedFrameRect;
377
378         // If we have an empty chrome client (like SVG) then we should use the page
379         // of the scroll view parent to help us get to the screen rect.
380         if (parentAccessibilityScrollView && page->chrome().client().isEmptyChromeClient())
381             page = parentAccessibilityScrollView->page();
382         
383         snappedFrameRect = page->chrome().rootViewToAccessibilityScreen(snappedFrameRect);
384     }
385     
386     return snappedFrameRect;
387 }
388     
389 FloatRect AccessibilityObject::relativeFrame() const
390 {
391     return convertFrameToSpace(elementRect(), AccessibilityConversionSpace::Page);
392 }
393
394 AccessibilityObject* AccessibilityObject::nextSiblingUnignored(int limit) const
395 {
396     AccessibilityObject* next;
397     ASSERT(limit >= 0);
398     for (next = nextSibling(); next && next->accessibilityIsIgnored(); next = next->nextSibling()) {
399         limit--;
400         if (limit <= 0)
401             break;
402     }
403     return next;
404 }
405
406 AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
407 {
408     return WebCore::firstAccessibleObjectFromNode(node, [] (const AccessibilityObject& accessible) {
409         return !accessible.accessibilityIsIgnored();
410     });
411 }
412
413 AccessibilityObject* firstAccessibleObjectFromNode(const Node* node, const WTF::Function<bool(const AccessibilityObject&)>& isAccessible)
414 {
415     if (!node)
416         return nullptr;
417
418     AXObjectCache* cache = node->document().axObjectCache();
419     if (!cache)
420         return nullptr;
421
422     AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
423     while (accessibleObject && !isAccessible(*accessibleObject)) {
424         node = NodeTraversal::next(*node);
425
426         while (node && !node->renderer())
427             node = NodeTraversal::nextSkippingChildren(*node);
428
429         if (!node)
430             return nullptr;
431
432         accessibleObject = cache->getOrCreate(node->renderer());
433     }
434
435     return accessibleObject;
436 }
437
438 bool AccessibilityObject::isDescendantOfRole(AccessibilityRole role) const
439 {
440     return Accessibility::findAncestor<AccessibilityObject>(*this, false, [&role] (const AccessibilityObject& object) {
441         return object.roleValue() == role;
442     }) != nullptr;
443 }
444
445 static void appendAccessibilityObject(AXCoreObject* object, AccessibilityObject::AccessibilityChildrenVector& results)
446 {
447     // Find the next descendant of this attachment object so search can continue through frames.
448     if (object->isAttachment()) {
449         Widget* widget = object->widgetForAttachmentView();
450         if (!is<FrameView>(widget))
451             return;
452         
453         Document* document = downcast<FrameView>(*widget).frame().document();
454         if (!document || !document->hasLivingRenderTree())
455             return;
456         
457         object = object->axObjectCache()->getOrCreate(document);
458     }
459
460     if (object)
461         results.append(object);
462 }
463     
464 void AccessibilityObject::insertChild(AXCoreObject* child, unsigned index)
465 {
466     if (!child)
467         return;
468     
469     // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
470     // or its visibility has changed. In the latter case, this child may have a stale child cached.
471     // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
472     // Only clear the child's children when we know it's in the updating chain in order to avoid unnecessary work.
473     if (child->needsToUpdateChildren() || m_subtreeDirty) {
474         child->clearChildren();
475         // Pass m_subtreeDirty flag down to the child so that children cache gets reset properly.
476         if (m_subtreeDirty)
477             child->setNeedsToUpdateSubtree();
478     } else {
479         // For some reason the grand children might be detached so that we need to regenerate the
480         // children list of this child.
481         for (const auto& grandChild : child->children(false)) {
482             if (grandChild->isDetachedFromParent()) {
483                 child->clearChildren();
484                 break;
485             }
486         }
487     }
488     
489     setIsIgnoredFromParentDataForChild(child);
490     if (child->accessibilityIsIgnored()) {
491         const auto& children = child->children();
492         size_t length = children.size();
493         for (size_t i = 0; i < length; ++i)
494             m_children.insert(index + i, children[i]);
495     } else {
496         ASSERT(child->parentObject() == this);
497         m_children.insert(index, child);
498     }
499     
500     // Reset the child's m_isIgnoredFromParentData since we are done adding that child and its children.
501     child->clearIsIgnoredFromParentData();
502 }
503     
504 void AccessibilityObject::addChild(AXCoreObject* child)
505 {
506     insertChild(child, m_children.size());
507 }
508     
509 static void appendChildrenToArray(AXCoreObject* object, bool isForward, AXCoreObject* startObject, AccessibilityObject::AccessibilityChildrenVector& results)
510 {
511     // A table's children includes elements whose own children are also the table's children (due to the way the Mac exposes tables).
512     // The rows from the table should be queried, since those are direct descendants of the table, and they contain content.
513     const auto& searchChildren = object->isTable() && object->isExposable() ? object->rows() : object->children();
514
515     size_t childrenSize = searchChildren.size();
516
517     size_t startIndex = isForward ? childrenSize : 0;
518     size_t endIndex = isForward ? 0 : childrenSize;
519
520     // If the startObject is ignored, we should use an accessible sibling as a start element instead.
521     if (startObject && startObject->accessibilityIsIgnored() && startObject->isDescendantOfObject(object)) {
522         AXCoreObject* parentObject = startObject->parentObject();
523         // Go up the parent chain to find the highest ancestor that's also being ignored.
524         while (parentObject && parentObject->accessibilityIsIgnored()) {
525             if (parentObject == object)
526                 break;
527             startObject = parentObject;
528             parentObject = parentObject->parentObject();
529         }
530         // Get the un-ignored sibling based on the search direction, and update the searchPosition.
531         while (startObject && startObject->accessibilityIsIgnored())
532             startObject = isForward ? startObject->previousSibling() : startObject->nextSibling();
533     }
534     
535     size_t searchPosition = startObject ? searchChildren.find(startObject) : WTF::notFound;
536     
537     if (searchPosition != WTF::notFound) {
538         if (isForward)
539             endIndex = searchPosition + 1;
540         else
541             endIndex = searchPosition;
542     }
543
544     // This is broken into two statements so that it's easier read.
545     if (isForward) {
546         for (size_t i = startIndex; i > endIndex; i--)
547             appendAccessibilityObject(searchChildren.at(i - 1).get(), results);
548     } else {
549         for (size_t i = startIndex; i < endIndex; i++)
550             appendAccessibilityObject(searchChildren.at(i).get(), results);
551     }
552 }
553
554 void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* criteria, AccessibilityChildrenVector& results)
555 {
556     ASSERT(criteria);
557     if (!criteria)
558         return;
559
560     if (AXObjectCache* cache = axObjectCache())
561         cache->startCachingComputedObjectAttributesUntilTreeMutates();
562
563     criteria->anchorObject = this;
564     Accessibility::findMatchingObjects(*criteria, results);
565 }
566
567 // Returns the range that is fewer positions away from the reference range.
568 // NOTE: The after range is expected to ACTUALLY be after the reference range and the before
569 // range is expected to ACTUALLY be before. These are not checked for performance reasons.
570 static RefPtr<Range> rangeClosestToRange(RefPtr<Range> const& referenceRange, RefPtr<Range>&& afterRange, RefPtr<Range>&& beforeRange)
571 {
572     if (!referenceRange)
573         return nullptr;
574
575     // The treeScope for shadow nodes may not be the same scope as another element in a document.
576     // Comparisons may fail in that case, which are expected behavior and should not assert.
577     if (afterRange && (referenceRange->endPosition().isNull() || ((afterRange->startPosition().anchorNode()->compareDocumentPosition(*referenceRange->endPosition().anchorNode()) & Node::DOCUMENT_POSITION_DISCONNECTED) == Node::DOCUMENT_POSITION_DISCONNECTED)))
578         return nullptr;
579     ASSERT(!afterRange || afterRange->compareBoundaryPoints(Range::START_TO_START, *referenceRange).releaseReturnValue() >= 0);
580
581     if (beforeRange && (referenceRange->startPosition().isNull() || ((beforeRange->endPosition().anchorNode()->compareDocumentPosition(*referenceRange->startPosition().anchorNode()) & Node::DOCUMENT_POSITION_DISCONNECTED) == Node::DOCUMENT_POSITION_DISCONNECTED)))
582         return nullptr;
583     ASSERT(!beforeRange || beforeRange->compareBoundaryPoints(Range::START_TO_START, *referenceRange).releaseReturnValue() <= 0);
584
585     if (!afterRange && !beforeRange)
586         return nullptr;
587     if (afterRange && !beforeRange)
588         return WTFMove(afterRange);
589     if (!afterRange && beforeRange)
590         return WTFMove(beforeRange);
591     
592     unsigned positionsToAfterRange = Position::positionCountBetweenPositions(afterRange->startPosition(), referenceRange->endPosition());
593     unsigned positionsToBeforeRange = Position::positionCountBetweenPositions(beforeRange->endPosition(), referenceRange->startPosition());
594     
595     return positionsToAfterRange < positionsToBeforeRange ? afterRange : beforeRange;
596 }
597
598 RefPtr<Range> AccessibilityObject::rangeOfStringClosestToRangeInDirection(Range* referenceRange, AccessibilitySearchDirection searchDirection, Vector<String> const& searchStrings) const
599 {
600     Frame* frame = this->frame();
601     if (!frame)
602         return nullptr;
603     
604     if (!referenceRange)
605         return nullptr;
606     
607     bool isBackwardSearch = searchDirection == AccessibilitySearchDirection::Previous;
608     FindOptions findOptions { AtWordStarts, AtWordEnds, CaseInsensitive, StartInSelection };
609     if (isBackwardSearch)
610         findOptions.add(FindOptionFlag::Backwards);
611     
612     RefPtr<Range> closestStringRange = nullptr;
613     for (const auto& searchString : searchStrings) {
614         if (RefPtr<Range> searchStringRange = frame->editor().rangeOfString(searchString, referenceRange, findOptions)) {
615             if (!closestStringRange)
616                 closestStringRange = searchStringRange;
617             else {
618                 // If searching backward, use the trailing range edges to correctly determine which
619                 // range is closest. Similarly, if searching forward, use the leading range edges.
620                 Position closestStringPosition = isBackwardSearch ? closestStringRange->endPosition() : closestStringRange->startPosition();
621                 Position searchStringPosition = isBackwardSearch ? searchStringRange->endPosition() : searchStringRange->startPosition();
622                 
623                 int closestPositionOffset = closestStringPosition.computeOffsetInContainerNode();
624                 int searchPositionOffset = searchStringPosition.computeOffsetInContainerNode();
625                 Node* closestContainerNode = closestStringPosition.containerNode();
626                 Node* searchContainerNode = searchStringPosition.containerNode();
627                 
628                 short result = Range::compareBoundaryPoints(closestContainerNode, closestPositionOffset, searchContainerNode, searchPositionOffset).releaseReturnValue();
629                 if ((!isBackwardSearch && result > 0) || (isBackwardSearch && result < 0))
630                     closestStringRange = searchStringRange;
631             }
632         }
633     }
634     return closestStringRange;
635 }
636
637 // Returns the range of the entire document if there is no selection.
638 RefPtr<Range> AccessibilityObject::selectionRange() const
639 {
640     Frame* frame = this->frame();
641     if (!frame)
642         return nullptr;
643     
644     const VisibleSelection& selection = frame->selection().selection();
645     if (!selection.isNone())
646         return selection.firstRange();
647     
648     return Range::create(*frame->document());
649 }
650
651 RefPtr<Range> AccessibilityObject::elementRange() const
652 {    
653     return AXObjectCache::rangeForNodeContents(node());
654 }
655
656 RefPtr<Range> AccessibilityObject::findTextRange(Vector<String> const& searchStrings, RefPtr<Range> const& start, AccessibilitySearchTextDirection direction) const
657 {
658     RefPtr<Range> found;
659     if (direction == AccessibilitySearchTextDirection::Forward)
660         found = rangeOfStringClosestToRangeInDirection(start.get(), AccessibilitySearchDirection::Next, searchStrings);
661     else if (direction == AccessibilitySearchTextDirection::Backward)
662         found = rangeOfStringClosestToRangeInDirection(start.get(), AccessibilitySearchDirection::Previous, searchStrings);
663     else if (direction == AccessibilitySearchTextDirection::Closest) {
664         auto foundAfter = rangeOfStringClosestToRangeInDirection(start.get(), AccessibilitySearchDirection::Next, searchStrings);
665         auto foundBefore = rangeOfStringClosestToRangeInDirection(start.get(), AccessibilitySearchDirection::Previous, searchStrings);
666         found = rangeClosestToRange(start.get(), WTFMove(foundAfter), WTFMove(foundBefore));
667     }
668
669     if (found) {
670         // If the search started within a text control, ensure that the result is inside that element.
671         if (element() && element()->isTextField()) {
672             if (!found->startContainer().isDescendantOrShadowDescendantOf(element())
673                 || !found->endContainer().isDescendantOrShadowDescendantOf(element()))
674                 return nullptr;
675         }
676     }
677     return found;
678 }
679
680 Vector<RefPtr<Range>> AccessibilityObject::findTextRanges(AccessibilitySearchTextCriteria const& criteria) const
681 {
682     Vector<RefPtr<Range>> result;
683
684     // Determine start range.
685     RefPtr<Range> startRange;
686     if (criteria.start == AccessibilitySearchTextStartFrom::Selection)
687         startRange = selectionRange();
688     else
689         startRange = elementRange();
690
691     if (startRange) {
692         // Collapse the range to the start unless searching from the end of the doc or searching backwards.
693         if (criteria.start == AccessibilitySearchTextStartFrom::Begin)
694             startRange->collapse(true);
695         else if (criteria.start == AccessibilitySearchTextStartFrom::End)
696             startRange->collapse(false);
697         else
698             startRange->collapse(criteria.direction != AccessibilitySearchTextDirection::Backward);
699     } else
700         return result;
701
702     RefPtr<Range> found;
703     switch (criteria.direction) {
704     case AccessibilitySearchTextDirection::Forward:
705     case AccessibilitySearchTextDirection::Backward:
706     case AccessibilitySearchTextDirection::Closest:
707         found = findTextRange(criteria.searchStrings, startRange, criteria.direction);
708         if (found)
709             result.append(found);
710         break;
711     case AccessibilitySearchTextDirection::All: {
712         auto findAll = [&](AccessibilitySearchTextDirection dir) {
713             found = findTextRange(criteria.searchStrings, startRange, dir);
714             while (found) {
715                 result.append(found);
716                 found = findTextRange(criteria.searchStrings, found, dir);
717             }
718         };
719         findAll(AccessibilitySearchTextDirection::Forward);
720         findAll(AccessibilitySearchTextDirection::Backward);
721         break;
722     }
723     }
724
725     return result;
726 }
727
728 Vector<String> AccessibilityObject::performTextOperation(AccessibilityTextOperation const& operation)
729 {
730     Vector<String> result;
731
732     if (operation.textRanges.isEmpty())
733         return result;
734
735     Frame* frame = this->frame();
736     if (!frame)
737         return result;
738
739     for (const auto& textRange : operation.textRanges) {
740         if (!frame->selection().setSelectedRange(textRange.get(), DOWNSTREAM, FrameSelection::ShouldCloseTyping::Yes))
741             continue;
742
743         String text = textRange->text();
744         String replacementString = operation.replacementText;
745         bool replaceSelection = false;
746         switch (operation.type) {
747         case AccessibilityTextOperationType::Capitalize:
748             replacementString = capitalize(text, ' '); // FIXME: Needs to take locale into account to work correctly.
749             replaceSelection = true;
750             break;
751         case AccessibilityTextOperationType::Uppercase:
752             replacementString = text.convertToUppercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
753             replaceSelection = true;
754             break;
755         case AccessibilityTextOperationType::Lowercase:
756             replacementString = text.convertToLowercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
757             replaceSelection = true;
758             break;
759         case AccessibilityTextOperationType::Replace: {
760             replaceSelection = true;
761             // When applying find and replace activities, we want to match the capitalization of the replaced text,
762             // (unless we're replacing with an abbreviation.)
763             if (text.length() > 0
764                 && replacementString.length() > 2
765                 && replacementString != replacementString.convertToUppercaseWithoutLocale()) {
766                 if (text[0] == u_toupper(text[0]))
767                     replacementString = capitalize(replacementString, ' '); // FIXME: Needs to take locale into account to work correctly.
768                 else
769                     replacementString = replacementString.convertToLowercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
770             }
771             break;
772         }
773         case AccessibilityTextOperationType::Select:
774             break;
775         }
776
777         // A bit obvious, but worth noting the API contract for this method is that we should
778         // return the replacement string when replacing, but the selected string if not.
779         if (replaceSelection) {
780             frame->editor().replaceSelectionWithText(replacementString, Editor::SelectReplacement::Yes, Editor::SmartReplace::Yes);
781             result.append(replacementString);
782         } else
783             result.append(text);
784     }
785
786     return result;
787 }
788
789 bool AccessibilityObject::hasAttributesRequiredForInclusion() const
790 {
791     // These checks are simplified in the interest of execution speed.
792     if (!getAttribute(aria_helpAttr).isEmpty()
793         || !getAttribute(aria_describedbyAttr).isEmpty()
794         || !getAttribute(altAttr).isEmpty()
795         || !getAttribute(titleAttr).isEmpty())
796         return true;
797
798 #if ENABLE(MATHML)
799     if (!getAttribute(MathMLNames::alttextAttr).isEmpty())
800         return true;
801 #endif
802
803     return false;
804 }
805
806 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
807 {
808     return ariaRole == AccessibilityRole::RadioButton || ariaRole == AccessibilityRole::CheckBox || ariaRole == AccessibilityRole::TextField || ariaRole == AccessibilityRole::Switch || ariaRole == AccessibilityRole::SearchField;
809 }    
810     
811 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
812 {
813     return isARIAInput(ariaRole) || ariaRole == AccessibilityRole::TextArea || ariaRole == AccessibilityRole::Button || ariaRole == AccessibilityRole::ComboBox || ariaRole == AccessibilityRole::Slider || ariaRole == AccessibilityRole::ListBox;
814 }
815     
816 bool AccessibilityObject::isRangeControl() const
817 {
818     switch (roleValue()) {
819     case AccessibilityRole::Meter:
820     case AccessibilityRole::ProgressIndicator:
821     case AccessibilityRole::Slider:
822     case AccessibilityRole::ScrollBar:
823     case AccessibilityRole::SpinButton:
824         return true;
825     case AccessibilityRole::Splitter:
826         return canSetFocusAttribute();
827     default:
828         return false;
829     }
830 }
831
832 bool AccessibilityObject::isMeter() const
833 {
834     if (ariaRoleAttribute() == AccessibilityRole::Meter)
835         return true;
836
837 #if ENABLE(METER_ELEMENT)
838     RenderObject* renderer = this->renderer();
839     return renderer && renderer->isMeter();
840 #else
841     return false;
842 #endif
843 }
844
845 IntPoint AccessibilityObject::clickPoint()
846 {
847     LayoutRect rect = elementRect();
848     return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2));
849 }
850
851 IntRect AccessibilityObject::boundingBoxForQuads(RenderObject* obj, const Vector<FloatQuad>& quads)
852 {
853     ASSERT(obj);
854     if (!obj)
855         return IntRect();
856     
857     FloatRect result;
858     for (const auto& quad : quads) {
859         FloatRect r = quad.enclosingBoundingBox();
860         if (!r.isEmpty()) {
861             if (obj->style().hasAppearance())
862                 obj->theme().adjustRepaintRect(*obj, r);
863             result.unite(r);
864         }
865     }
866     return snappedIntRect(LayoutRect(result));
867 }
868     
869 bool AccessibilityObject::press()
870 {
871     // The presence of the actionElement will confirm whether we should even attempt a press.
872     Element* actionElem = actionElement();
873     if (!actionElem)
874         return false;
875     if (Frame* f = actionElem->document().frame())
876         f->loader().resetMultipleFormSubmissionProtection();
877     
878     // Hit test at this location to determine if there is a sub-node element that should act
879     // as the target of the action.
880     Element* hitTestElement = nullptr;
881     Document* document = this->document();
882     if (document) {
883         HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AccessibilityHitTest);
884         HitTestResult hitTestResult(clickPoint());
885         document->hitTest(request, hitTestResult);
886         if (auto* innerNode = hitTestResult.innerNode()) {
887             if (auto* shadowHost = innerNode->shadowHost())
888                 hitTestElement = shadowHost;
889             else if (is<Element>(*innerNode))
890                 hitTestElement = &downcast<Element>(*innerNode);
891             else
892                 hitTestElement = innerNode->parentElement();
893         }
894     }
895
896     // Prefer the actionElement instead of this node, if the actionElement is inside this node.
897     Element* pressElement = this->element();
898     if (!pressElement || actionElem->isDescendantOf(*pressElement))
899         pressElement = actionElem;
900     
901     ASSERT(pressElement);
902     // Prefer the hit test element, if it is inside the target element.
903     if (hitTestElement && hitTestElement->isDescendantOf(*pressElement))
904         pressElement = hitTestElement;
905     
906     UserGestureIndicator gestureIndicator(ProcessingUserGesture, document);
907     
908     bool dispatchedTouchEvent = false;
909 #if PLATFORM(IOS_FAMILY)
910     if (hasTouchEventListener())
911         dispatchedTouchEvent = dispatchTouchEvent();
912 #endif
913     if (!dispatchedTouchEvent)
914         pressElement->accessKeyAction(true);
915     
916     return true;
917 }
918     
919 bool AccessibilityObject::dispatchTouchEvent()
920 {
921 #if ENABLE(IOS_TOUCH_EVENTS)
922     if (auto* frame = mainFrame())
923         return frame->eventHandler().dispatchSimulatedTouchEvent(clickPoint());
924 #endif
925     return false;
926 }
927
928 Frame* AccessibilityObject::frame() const
929 {
930     Node* node = this->node();
931     return node ? node->document().frame() : nullptr;
932 }
933
934 Frame* AccessibilityObject::mainFrame() const
935 {
936     Document* document = topDocument();
937     if (!document)
938         return nullptr;
939     
940     Frame* frame = document->frame();
941     if (!frame)
942         return nullptr;
943     
944     return &frame->mainFrame();
945 }
946
947 Document* AccessibilityObject::topDocument() const
948 {
949     if (!document())
950         return nullptr;
951     return &document()->topDocument();
952 }
953
954 String AccessibilityObject::language() const
955 {
956     const AtomString& lang = getAttribute(langAttr);
957     if (!lang.isEmpty())
958         return lang;
959
960     AccessibilityObject* parent = parentObject();
961     
962     // as a last resort, fall back to the content language specified in the meta tag
963     if (!parent) {
964         Document* doc = document();
965         if (doc)
966             return doc->contentLanguage();
967         return nullAtom();
968     }
969     
970     return parent->language();
971 }
972     
973 VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
974 {
975     if (visiblePos1.isNull() || visiblePos2.isNull())
976         return VisiblePositionRange();
977
978     // If there's no common tree scope between positions, return early.
979     if (!commonTreeScope(visiblePos1.deepEquivalent().deprecatedNode(), visiblePos2.deepEquivalent().deprecatedNode()))
980         return VisiblePositionRange();
981     
982     VisiblePosition startPos;
983     VisiblePosition endPos;
984     bool alreadyInOrder;
985
986     // upstream is ordered before downstream for the same position
987     if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
988         alreadyInOrder = false;
989
990     // use selection order to see if the positions are in order
991     else
992         alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
993
994     if (alreadyInOrder) {
995         startPos = visiblePos1;
996         endPos = visiblePos2;
997     } else {
998         startPos = visiblePos2;
999         endPos = visiblePos1;
1000     }
1001
1002     return VisiblePositionRange(startPos, endPos);
1003 }
1004
1005 VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
1006 {
1007     VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
1008     VisiblePosition endPosition = endOfWord(startPosition);
1009     return VisiblePositionRange(startPosition, endPosition);
1010 }
1011
1012 VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
1013 {
1014     VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
1015     VisiblePosition endPosition = endOfWord(startPosition);
1016     return VisiblePositionRange(startPosition, endPosition);
1017 }
1018
1019 static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
1020 {
1021     // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
1022     // So let's update the position to include that.
1023     VisiblePosition tempPosition;
1024     VisiblePosition startPosition = visiblePosition;
1025     while (true) {
1026         tempPosition = startPosition.previous();
1027         if (tempPosition.isNull())
1028             break;
1029         Position p = tempPosition.deepEquivalent();
1030         RenderObject* renderer = p.deprecatedNode()->renderer();
1031         if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
1032             break;
1033         if (!RenderedPosition(tempPosition).isNull())
1034             break;
1035         startPosition = tempPosition;
1036     }
1037
1038     return startPosition;
1039 }
1040
1041 VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
1042 {
1043     if (visiblePos.isNull())
1044         return VisiblePositionRange();
1045
1046     // make a caret selection for the position before marker position (to make sure
1047     // we move off of a line start)
1048     VisiblePosition prevVisiblePos = visiblePos.previous();
1049     if (prevVisiblePos.isNull())
1050         return VisiblePositionRange();
1051
1052     VisiblePosition startPosition = startOfLine(prevVisiblePos);
1053
1054     // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
1055     // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
1056     // since floating object doesn't really belong to any line.
1057     // This check will reposition the marker before the floating object, to ensure we get a line start.
1058     if (startPosition.isNull()) {
1059         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
1060             prevVisiblePos = prevVisiblePos.previous();
1061             startPosition = startOfLine(prevVisiblePos);
1062         }
1063     } else
1064         startPosition = updateAXLineStartForVisiblePosition(startPosition);
1065
1066     VisiblePosition endPosition = endOfLine(prevVisiblePos);
1067     return VisiblePositionRange(startPosition, endPosition);
1068 }
1069
1070 VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
1071 {
1072     if (visiblePos.isNull())
1073         return VisiblePositionRange();
1074
1075     // make sure we move off of a line end
1076     VisiblePosition nextVisiblePos = visiblePos.next();
1077     if (nextVisiblePos.isNull())
1078         return VisiblePositionRange();
1079
1080     VisiblePosition startPosition = startOfLine(nextVisiblePos);
1081
1082     // fetch for a valid line start position
1083     if (startPosition.isNull()) {
1084         startPosition = visiblePos;
1085         nextVisiblePos = nextVisiblePos.next();
1086     } else
1087         startPosition = updateAXLineStartForVisiblePosition(startPosition);
1088
1089     VisiblePosition endPosition = endOfLine(nextVisiblePos);
1090
1091     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
1092     // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
1093     // return null for position by a floating object, since floating object doesn't really belong to any line.
1094     // This check will reposition the marker after the floating object, to ensure we get a line end.
1095     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
1096         nextVisiblePos = nextVisiblePos.next();
1097         endPosition = endOfLine(nextVisiblePos);
1098     }
1099
1100     return VisiblePositionRange(startPosition, endPosition);
1101 }
1102
1103 VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
1104 {
1105     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
1106     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1107     VisiblePosition startPosition = startOfSentence(visiblePos);
1108     VisiblePosition endPosition = endOfSentence(startPosition);
1109     return VisiblePositionRange(startPosition, endPosition);
1110 }
1111
1112 VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
1113 {
1114     VisiblePosition startPosition = startOfParagraph(visiblePos);
1115     VisiblePosition endPosition = endOfParagraph(startPosition);
1116     return VisiblePositionRange(startPosition, endPosition);
1117 }
1118
1119 static VisiblePosition startOfStyleRange(const VisiblePosition& visiblePos)
1120 {
1121     RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
1122     RenderObject* startRenderer = renderer;
1123     auto* style = &renderer->style();
1124
1125     // traverse backward by renderer to look for style change
1126     for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
1127         // skip non-leaf nodes
1128         if (r->firstChildSlow())
1129             continue;
1130
1131         // stop at style change
1132         if (&r->style() != style)
1133             break;
1134
1135         // remember match
1136         startRenderer = r;
1137     }
1138
1139     return firstPositionInOrBeforeNode(startRenderer->node());
1140 }
1141
1142 static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
1143 {
1144     RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
1145     RenderObject* endRenderer = renderer;
1146     const RenderStyle& style = renderer->style();
1147
1148     // traverse forward by renderer to look for style change
1149     for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
1150         // skip non-leaf nodes
1151         if (r->firstChildSlow())
1152             continue;
1153
1154         // stop at style change
1155         if (&r->style() != &style)
1156             break;
1157
1158         // remember match
1159         endRenderer = r;
1160     }
1161
1162     return lastPositionInOrAfterNode(endRenderer->node());
1163 }
1164
1165 VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
1166 {
1167     if (visiblePos.isNull())
1168         return VisiblePositionRange();
1169
1170     return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
1171 }
1172
1173 // NOTE: Consider providing this utility method as AX API
1174 VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
1175 {
1176     unsigned textLength = getLengthForTextRange();
1177     if (range.start + range.length > textLength)
1178         return VisiblePositionRange();
1179
1180     VisiblePosition startPosition = visiblePositionForIndex(range.start);
1181     startPosition.setAffinity(DOWNSTREAM);
1182     VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
1183     return VisiblePositionRange(startPosition, endPosition);
1184 }
1185
1186 RefPtr<Range> AccessibilityObject::rangeForPlainTextRange(const PlainTextRange& range) const
1187 {
1188     unsigned textLength = getLengthForTextRange();
1189     if (range.start + range.length > textLength)
1190         return nullptr;
1191     // Avoid setting selection to uneditable parent node in FrameSelection::setSelectedRange. See webkit.org/b/206093.
1192     if (range.isNull() && !textLength)
1193         return nullptr;
1194     
1195     if (AXObjectCache* cache = axObjectCache()) {
1196         CharacterOffset start = cache->characterOffsetForIndex(range.start, this);
1197         CharacterOffset end = cache->characterOffsetForIndex(range.start + range.length, this);
1198         return cache->rangeForUnorderedCharacterOffsets(start, end);
1199     }
1200     return nullptr;
1201 }
1202
1203 VisiblePositionRange AccessibilityObject::lineRangeForPosition(const VisiblePosition& visiblePosition) const
1204 {
1205     VisiblePosition startPosition = startOfLine(visiblePosition);
1206     VisiblePosition endPosition = endOfLine(visiblePosition);
1207     return VisiblePositionRange(startPosition, endPosition);
1208 }
1209
1210 bool AccessibilityObject::replacedNodeNeedsCharacter(Node* replacedNode)
1211 {
1212     // we should always be given a rendered node and a replaced node, but be safe
1213     // replaced nodes are either attachments (widgets) or images
1214     if (!replacedNode || !isRendererReplacedElement(replacedNode->renderer()) || replacedNode->isTextNode())
1215         return false;
1216
1217     // create an AX object, but skip it if it is not supposed to be seen
1218     AccessibilityObject* object = replacedNode->renderer()->document().axObjectCache()->getOrCreate(replacedNode);
1219     if (object->accessibilityIsIgnored())
1220         return false;
1221
1222     return true;
1223 }
1224
1225 // Finds a RenderListItem parent give a node.
1226 static RenderListItem* renderListItemContainerForNode(Node* node)
1227 {
1228     for (; node; node = node->parentNode()) {
1229         RenderBoxModelObject* renderer = node->renderBoxModelObject();
1230         if (is<RenderListItem>(renderer))
1231             return downcast<RenderListItem>(renderer);
1232     }
1233     return nullptr;
1234 }
1235
1236 static String listMarkerTextForNode(Node* node)
1237 {
1238     RenderListItem* listItem = renderListItemContainerForNode(node);
1239     if (!listItem)
1240         return String();
1241     
1242     // If this is in a list item, we need to manually add the text for the list marker
1243     // because a RenderListMarker does not have a Node equivalent and thus does not appear
1244     // when iterating text.
1245     return listItem->markerTextWithSuffix();
1246 }
1247
1248 // Returns the text associated with a list marker if this node is contained within a list item.
1249 String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart)
1250 {
1251     // If the range does not contain the start of the line, the list marker text should not be included.
1252     if (!isStartOfLine(visiblePositionStart))
1253         return String();
1254
1255     // We should speak the list marker only for the first line.
1256     RenderListItem* listItem = renderListItemContainerForNode(node);
1257     if (!listItem)
1258         return String();
1259     if (!inSameLine(visiblePositionStart, firstPositionInNode(&listItem->element())))
1260         return String();
1261     
1262     return listMarkerTextForNode(node);
1263 }
1264
1265 String AccessibilityObject::stringForRange(RefPtr<Range> range) const
1266 {
1267     if (!range)
1268         return String();
1269     
1270     TextIterator it(range.get());
1271     if (it.atEnd())
1272         return String();
1273     
1274     StringBuilder builder;
1275     for (; !it.atEnd(); it.advance()) {
1276         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1277         if (it.text().length()) {
1278             // Add a textual representation for list marker text.
1279             // Don't add list marker text for new line character.
1280             if (it.text().length() != 1 || !isSpaceOrNewline(it.text()[0]))
1281                 builder.append(listMarkerTextForNodeAndPosition(it.node(), VisiblePosition(range->startPosition())));
1282             it.appendTextToStringBuilder(builder);
1283         } else {
1284             // locate the node and starting offset for this replaced range
1285             Node& node = it.range()->startContainer();
1286             ASSERT(&node == &it.range()->endContainer());
1287             int offset = it.range()->startOffset();
1288             if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
1289                 builder.append(objectReplacementCharacter);
1290         }
1291     }
1292     
1293     return builder.toString();
1294 }
1295
1296 String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange)
1297 {
1298     if (visiblePositionRange.isNull())
1299         return String();
1300
1301     StringBuilder builder;
1302     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
1303     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
1304         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1305         if (it.text().length()) {
1306             // Add a textual representation for list marker text.
1307             builder.append(listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start));
1308             it.appendTextToStringBuilder(builder);
1309         } else {
1310             // locate the node and starting offset for this replaced range
1311             Node& node = it.range()->startContainer();
1312             ASSERT(&node == &it.range()->endContainer());
1313             int offset = it.range()->startOffset();
1314             if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
1315                 builder.append(objectReplacementCharacter);
1316         }
1317     }
1318
1319     return builder.toString();
1320 }
1321
1322 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
1323 {
1324     // FIXME: Multi-byte support
1325     if (visiblePositionRange.isNull())
1326         return -1;
1327     
1328     int length = 0;
1329     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
1330     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
1331         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1332         if (it.text().length())
1333             length += it.text().length();
1334         else {
1335             // locate the node and starting offset for this replaced range
1336             Node& node = it.range()->startContainer();
1337             ASSERT(&node == &it.range()->endContainer());
1338             int offset = it.range()->startOffset();
1339
1340             if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
1341                 ++length;
1342         }
1343     }
1344     
1345     return length;
1346 }
1347
1348 VisiblePosition AccessibilityObject::visiblePositionForBounds(const IntRect& rect, AccessibilityVisiblePositionForBounds visiblePositionForBounds) const
1349 {
1350     if (rect.isEmpty())
1351         return VisiblePosition();
1352     
1353     auto* mainFrame = this->mainFrame();
1354     if (!mainFrame)
1355         return VisiblePosition();
1356     
1357     // FIXME: Add support for right-to-left languages.
1358     IntPoint corner = (visiblePositionForBounds == AccessibilityVisiblePositionForBounds::First) ? rect.minXMinYCorner() : rect.maxXMaxYCorner();
1359     VisiblePosition position = mainFrame->visiblePositionForPoint(corner);
1360     
1361     if (rect.contains(position.absoluteCaretBounds().center()))
1362         return position;
1363     
1364     // If the initial position is located outside the bounds adjust it incrementally as needed.
1365     VisiblePosition nextPosition = position.next();
1366     VisiblePosition previousPosition = position.previous();
1367     while (nextPosition.isNotNull() || previousPosition.isNotNull()) {
1368         if (rect.contains(nextPosition.absoluteCaretBounds().center()))
1369             return nextPosition;
1370         if (rect.contains(previousPosition.absoluteCaretBounds().center()))
1371             return previousPosition;
1372         
1373         nextPosition = nextPosition.next();
1374         previousPosition = previousPosition.previous();
1375     }
1376     
1377     return VisiblePosition();
1378 }
1379
1380 VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
1381 {
1382     if (visiblePos.isNull())
1383         return VisiblePosition();
1384
1385     // make sure we move off of a word end
1386     VisiblePosition nextVisiblePos = visiblePos.next();
1387     if (nextVisiblePos.isNull())
1388         return VisiblePosition();
1389
1390     return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
1391 }
1392
1393 VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
1394 {
1395     if (visiblePos.isNull())
1396         return VisiblePosition();
1397
1398     // make sure we move off of a word start
1399     VisiblePosition prevVisiblePos = visiblePos.previous();
1400     if (prevVisiblePos.isNull())
1401         return VisiblePosition();
1402
1403     return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
1404 }
1405
1406 VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
1407 {
1408     if (visiblePos.isNull())
1409         return VisiblePosition();
1410
1411     // to make sure we move off of a line end
1412     VisiblePosition nextVisiblePos = visiblePos.next();
1413     if (nextVisiblePos.isNull())
1414         return VisiblePosition();
1415
1416     VisiblePosition endPosition = endOfLine(nextVisiblePos);
1417
1418     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
1419     // There are cases like when the position is next to a floating object that'll return null for end of line. This code will avoid returning null.
1420     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
1421         nextVisiblePos = nextVisiblePos.next();
1422         endPosition = endOfLine(nextVisiblePos);
1423     }
1424
1425     return endPosition;
1426 }
1427
1428 VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
1429 {
1430     if (visiblePos.isNull())
1431         return VisiblePosition();
1432
1433     // make sure we move off of a line start
1434     VisiblePosition prevVisiblePos = visiblePos.previous();
1435     if (prevVisiblePos.isNull())
1436         return VisiblePosition();
1437
1438     VisiblePosition startPosition = startOfLine(prevVisiblePos);
1439
1440     // as long as the position hasn't reached the beginning of the doc,  keep searching for a valid line start position
1441     // There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null.
1442     if (startPosition.isNull()) {
1443         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
1444             prevVisiblePos = prevVisiblePos.previous();
1445             startPosition = startOfLine(prevVisiblePos);
1446         }
1447     } else
1448         startPosition = updateAXLineStartForVisiblePosition(startPosition);
1449
1450     return startPosition;
1451 }
1452
1453 VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
1454 {
1455     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
1456     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1457     if (visiblePos.isNull())
1458         return VisiblePosition();
1459
1460     // make sure we move off of a sentence end
1461     VisiblePosition nextVisiblePos = visiblePos.next();
1462     if (nextVisiblePos.isNull())
1463         return VisiblePosition();
1464
1465     // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
1466     // see this empty line.  Instead, return the end position of the empty line.
1467     VisiblePosition endPosition;
1468     
1469     String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
1470     if (lineString.isEmpty())
1471         endPosition = nextVisiblePos;
1472     else
1473         endPosition = endOfSentence(nextVisiblePos);
1474
1475     return endPosition;
1476 }
1477
1478 VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
1479 {
1480     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
1481     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1482     if (visiblePos.isNull())
1483         return VisiblePosition();
1484
1485     // make sure we move off of a sentence start
1486     VisiblePosition previousVisiblePos = visiblePos.previous();
1487     if (previousVisiblePos.isNull())
1488         return VisiblePosition();
1489
1490     // treat empty line as a separate sentence.
1491     VisiblePosition startPosition;
1492     
1493     String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
1494     if (lineString.isEmpty())
1495         startPosition = previousVisiblePos;
1496     else
1497         startPosition = startOfSentence(previousVisiblePos);
1498
1499     return startPosition;
1500 }
1501
1502 VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
1503 {
1504     if (visiblePos.isNull())
1505         return VisiblePosition();
1506
1507     // make sure we move off of a paragraph end
1508     VisiblePosition nextPos = visiblePos.next();
1509     if (nextPos.isNull())
1510         return VisiblePosition();
1511
1512     return endOfParagraph(nextPos);
1513 }
1514
1515 VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
1516 {
1517     if (visiblePos.isNull())
1518         return VisiblePosition();
1519
1520     // make sure we move off of a paragraph start
1521     VisiblePosition previousPos = visiblePos.previous();
1522     if (previousPos.isNull())
1523         return VisiblePosition();
1524
1525     return startOfParagraph(previousPos);
1526 }
1527
1528 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
1529 {
1530     if (visiblePos.isNull())
1531         return nullptr;
1532
1533     RenderObject* obj = visiblePos.deepEquivalent().deprecatedNode()->renderer();
1534     if (!obj)
1535         return nullptr;
1536
1537     return obj->document().axObjectCache()->getOrCreate(obj);
1538 }
1539     
1540 // If you call node->hasEditableStyle() since that will return true if an ancestor is editable.
1541 // This only returns true if this is the element that actually has the contentEditable attribute set.
1542 bool AccessibilityObject::hasContentEditableAttributeSet() const
1543 {
1544     return contentEditableAttributeIsEnabled(element());
1545 }
1546
1547 bool AccessibilityObject::supportsReadOnly() const
1548 {
1549     AccessibilityRole role = roleValue();
1550
1551     return role == AccessibilityRole::CheckBox
1552         || role == AccessibilityRole::ColumnHeader
1553         || role == AccessibilityRole::ComboBox
1554         || role == AccessibilityRole::Grid
1555         || role == AccessibilityRole::GridCell
1556         || role == AccessibilityRole::ListBox
1557         || role == AccessibilityRole::MenuItemCheckbox
1558         || role == AccessibilityRole::MenuItemRadio
1559         || role == AccessibilityRole::RadioGroup
1560         || role == AccessibilityRole::RowHeader
1561         || role == AccessibilityRole::SearchField
1562         || role == AccessibilityRole::Slider
1563         || role == AccessibilityRole::SpinButton
1564         || role == AccessibilityRole::Switch
1565         || role == AccessibilityRole::TextField
1566         || role == AccessibilityRole::TreeGrid
1567         || isPasswordField();
1568 }
1569
1570 String AccessibilityObject::readOnlyValue() const
1571 {
1572     if (!hasAttribute(aria_readonlyAttr))
1573         return ariaRoleAttribute() != AccessibilityRole::Unknown && supportsReadOnly() ? "false" : String();
1574
1575     return getAttribute(aria_readonlyAttr).string().convertToASCIILowercase();
1576 }
1577
1578 bool AccessibilityObject::supportsAutoComplete() const
1579 {
1580     return (isComboBox() || isARIATextControl()) && hasAttribute(aria_autocompleteAttr);
1581 }
1582
1583 String AccessibilityObject::autoCompleteValue() const
1584 {
1585     const AtomString& autoComplete = getAttribute(aria_autocompleteAttr);
1586     if (equalLettersIgnoringASCIICase(autoComplete, "inline")
1587         || equalLettersIgnoringASCIICase(autoComplete, "list")
1588         || equalLettersIgnoringASCIICase(autoComplete, "both"))
1589         return autoComplete;
1590
1591     return "none";
1592 }
1593
1594 bool AccessibilityObject::contentEditableAttributeIsEnabled(Element* element)
1595 {
1596     if (!element)
1597         return false;
1598     
1599     const AtomString& contentEditableValue = element->attributeWithoutSynchronization(contenteditableAttr);
1600     if (contentEditableValue.isNull())
1601         return false;
1602     
1603     // Both "true" (case-insensitive) and the empty string count as true.
1604     return contentEditableValue.isEmpty() || equalLettersIgnoringASCIICase(contentEditableValue, "true");
1605 }
1606     
1607 #if ENABLE(ACCESSIBILITY)
1608 int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
1609 {
1610     if (visiblePos.isNull() || !node())
1611         return -1;
1612
1613     // If the position is not in the same editable region as this AX object, return -1.
1614     Node* containerNode = visiblePos.deepEquivalent().containerNode();
1615     if (!containerNode->containsIncludingShadowDOM(node()) && !node()->containsIncludingShadowDOM(containerNode))
1616         return -1;
1617
1618     int lineCount = -1;
1619     VisiblePosition currentVisiblePos = visiblePos;
1620     VisiblePosition savedVisiblePos;
1621
1622     // move up until we get to the top
1623     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
1624     // top document.
1625     do {
1626         savedVisiblePos = currentVisiblePos;
1627         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0, HasEditableAXRole);
1628         currentVisiblePos = prevVisiblePos;
1629         ++lineCount;
1630     }  while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos)));
1631
1632     return lineCount;
1633 }
1634 #endif
1635
1636 // NOTE: Consider providing this utility method as AX API
1637 PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
1638 {
1639     int index1 = index(positionRange.start);
1640     int index2 = index(positionRange.end);
1641     if (index1 < 0 || index2 < 0 || index1 > index2)
1642         return PlainTextRange();
1643
1644     return PlainTextRange(index1, index2 - index1);
1645 }
1646
1647 // The composed character range in the text associated with this accessibility object that
1648 // is specified by the given screen coordinates. This parameterized attribute returns the
1649 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
1650 // screen coordinates.
1651 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
1652 // an error in that case. We return textControl->text().length(), 1. Does this matter?
1653 PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
1654 {
1655     int i = index(visiblePositionForPoint(point));
1656     if (i < 0)
1657         return PlainTextRange();
1658
1659     return PlainTextRange(i, 1);
1660 }
1661
1662 // Given a character index, the range of text associated with this accessibility object
1663 // over which the style in effect at that character index applies.
1664 PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
1665 {
1666     VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
1667     return plainTextRangeForVisiblePositionRange(range);
1668 }
1669
1670 // Given an indexed character, the line number of the text associated with this accessibility
1671 // object that contains the character.
1672 unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
1673 {
1674     return lineForPosition(visiblePositionForIndex(index, false));
1675 }
1676
1677 #if ENABLE(ACCESSIBILITY)
1678 void AccessibilityObject::updateBackingStore()
1679 {
1680     if (!axObjectCache())
1681         return;
1682     
1683     // Updating the layout may delete this object.
1684     RefPtr<AccessibilityObject> protectedThis(this);
1685     if (auto* document = this->document()) {
1686         if (!document->view()->layoutContext().isInRenderTreeLayout() && !document->inRenderTreeUpdate() && !document->inStyleRecalc())
1687             document->updateLayoutIgnorePendingStylesheets();
1688     }
1689
1690     if (auto cache = axObjectCache())
1691         cache->performDeferredCacheUpdate();
1692     
1693     updateChildrenIfNecessary();
1694 }
1695 #endif
1696
1697 const AccessibilityScrollView* AccessibilityObject::ancestorAccessibilityScrollView(bool includeSelf) const
1698 {
1699     return downcast<AccessibilityScrollView>(Accessibility::findAncestor<AccessibilityObject>(*this, includeSelf, [] (const auto& object) {
1700         return is<AccessibilityScrollView>(object);
1701     }));
1702 }
1703
1704 ScrollView* AccessibilityObject::scrollViewAncestor() const
1705 {
1706     if (auto parentScrollView = ancestorAccessibilityScrollView(true/* includeSelf */))
1707         return parentScrollView->scrollView();
1708     
1709     return nullptr;
1710 }
1711     
1712 Document* AccessibilityObject::document() const
1713 {
1714     FrameView* frameView = documentFrameView();
1715     if (!frameView)
1716         return nullptr;
1717     
1718     return frameView->frame().document();
1719 }
1720     
1721 Page* AccessibilityObject::page() const
1722 {
1723     Document* document = this->document();
1724     if (!document)
1725         return nullptr;
1726     return document->page();
1727 }
1728
1729 FrameView* AccessibilityObject::documentFrameView() const 
1730
1731     const AccessibilityObject* object = this;
1732     while (object && !object->isAccessibilityRenderObject()) 
1733         object = object->parentObject();
1734         
1735     if (!object)
1736         return nullptr;
1737
1738     return object->documentFrameView();
1739 }
1740
1741 #if ENABLE(ACCESSIBILITY)
1742 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children(bool updateChildrenIfNeeded)
1743 {
1744     if (updateChildrenIfNeeded)
1745         updateChildrenIfNecessary();
1746
1747     return m_children;
1748 }
1749 #endif
1750
1751 void AccessibilityObject::updateChildrenIfNecessary()
1752 {
1753     if (!hasChildren()) {
1754         // Enable the cache in case we end up adding a lot of children, we don't want to recompute axIsIgnored each time.
1755         AXAttributeCacheEnabler enableCache(axObjectCache());
1756         addChildren();
1757     }
1758 }
1759     
1760 void AccessibilityObject::clearChildren()
1761 {
1762     // Some objects have weak pointers to their parents and those associations need to be detached.
1763     for (const auto& child : m_children)
1764         child->detachFromParent();
1765     
1766     m_children.clear();
1767     m_haveChildren = false;
1768 }
1769
1770 AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
1771 {
1772     RenderObject* obj = node->renderer();
1773     if (!obj)
1774         return nullptr;
1775     
1776     RefPtr<AccessibilityObject> axObj = obj->document().axObjectCache()->getOrCreate(obj);
1777     Element* anchor = axObj->anchorElement();
1778     if (!anchor)
1779         return nullptr;
1780     
1781     RenderObject* anchorRenderer = anchor->renderer();
1782     if (!anchorRenderer)
1783         return nullptr;
1784     
1785     return anchorRenderer->document().axObjectCache()->getOrCreate(anchorRenderer);
1786 }
1787
1788 AccessibilityObject* AccessibilityObject::headingElementForNode(Node* node)
1789 {
1790     if (!node)
1791         return nullptr;
1792
1793     RenderObject* renderObject = node->renderer();
1794     if (!renderObject)
1795         return nullptr;
1796
1797     AccessibilityObject* axObject = renderObject->document().axObjectCache()->getOrCreate(renderObject);
1798
1799     return Accessibility::findAncestor<AccessibilityObject>(*axObject, true, [] (const AccessibilityObject& object) {
1800         return object.roleValue() == AccessibilityRole::Heading;
1801     });
1802 }
1803
1804 void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
1805 {
1806     for (const auto& child : children()) {
1807         // Add tree items as the rows.
1808         if (child->roleValue() == AccessibilityRole::TreeItem)
1809             result.append(child);
1810
1811         // Now see if this item also has rows hiding inside of it.
1812         child->ariaTreeRows(result);
1813     }
1814 }
1815     
1816 void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
1817 {
1818     // The ARIA tree item content are the item that are not other tree items or their containing groups.
1819     for (const auto& child : children()) {
1820         if (!child->isGroup() && child->roleValue() != AccessibilityRole::TreeItem)
1821             result.append(child);
1822     }
1823 }
1824
1825 void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
1826 {
1827     for (const auto& obj : children()) {
1828         // Add tree items as the rows.
1829         if (obj->roleValue() == AccessibilityRole::TreeItem)
1830             result.append(obj);
1831         // If it's not a tree item, then descend into the group to find more tree items.
1832         else 
1833             obj->ariaTreeRows(result);
1834     }    
1835 }
1836     
1837 const String AccessibilityObject::defaultLiveRegionStatusForRole(AccessibilityRole role)
1838 {
1839     switch (role) {
1840     case AccessibilityRole::ApplicationAlertDialog:
1841     case AccessibilityRole::ApplicationAlert:
1842         return "assertive"_s;
1843     case AccessibilityRole::ApplicationLog:
1844     case AccessibilityRole::ApplicationStatus:
1845         return "polite"_s;
1846     case AccessibilityRole::ApplicationTimer:
1847     case AccessibilityRole::ApplicationMarquee:
1848         return "off"_s;
1849     default:
1850         return nullAtom();
1851     }
1852 }
1853     
1854 #if ENABLE(ACCESSIBILITY)
1855 String AccessibilityObject::actionVerb() const
1856 {
1857 #if !PLATFORM(IOS_FAMILY)
1858     // FIXME: Need to add verbs for select elements.
1859     static NeverDestroyed<const String> buttonAction(AXButtonActionVerb());
1860     static NeverDestroyed<const String> textFieldAction(AXTextFieldActionVerb());
1861     static NeverDestroyed<const String> radioButtonAction(AXRadioButtonActionVerb());
1862     static NeverDestroyed<const String> checkedCheckBoxAction(AXCheckedCheckBoxActionVerb());
1863     static NeverDestroyed<const String> uncheckedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
1864     static NeverDestroyed<const String> linkAction(AXLinkActionVerb());
1865     static NeverDestroyed<const String> menuListAction(AXMenuListActionVerb());
1866     static NeverDestroyed<const String> menuListPopupAction(AXMenuListPopupActionVerb());
1867     static NeverDestroyed<const String> listItemAction(AXListItemActionVerb());
1868
1869     switch (roleValue()) {
1870     case AccessibilityRole::Button:
1871     case AccessibilityRole::ToggleButton:
1872         return buttonAction;
1873     case AccessibilityRole::TextField:
1874     case AccessibilityRole::TextArea:
1875         return textFieldAction;
1876     case AccessibilityRole::RadioButton:
1877         return radioButtonAction;
1878     case AccessibilityRole::CheckBox:
1879     case AccessibilityRole::Switch:
1880         return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
1881     case AccessibilityRole::Link:
1882     case AccessibilityRole::WebCoreLink:
1883         return linkAction;
1884     case AccessibilityRole::PopUpButton:
1885         return menuListAction;
1886     case AccessibilityRole::MenuListPopup:
1887         return menuListPopupAction;
1888     case AccessibilityRole::ListItem:
1889         return listItemAction;
1890     default:
1891         return nullAtom();
1892     }
1893 #else
1894     return nullAtom();
1895 #endif
1896 }
1897 #endif
1898
1899 bool AccessibilityObject::ariaIsMultiline() const
1900 {
1901     return equalLettersIgnoringASCIICase(getAttribute(aria_multilineAttr), "true");
1902 }
1903
1904 String AccessibilityObject::invalidStatus() const
1905 {
1906     String grammarValue = "grammar"_s;
1907     String falseValue = "false"_s;
1908     String spellingValue = "spelling"_s;
1909     String trueValue = "true"_s;
1910     String undefinedValue = "undefined"_s;
1911
1912     // aria-invalid can return false (default), grammar, spelling, or true.
1913     String ariaInvalid = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_invalidAttr));
1914     
1915     if (ariaInvalid.isEmpty()) {
1916         // We should expose invalid status for input types.
1917         Node* node = this->node();
1918         if (node && is<HTMLInputElement>(*node)) {
1919             HTMLInputElement& input = downcast<HTMLInputElement>(*node);
1920             if (input.hasBadInput() || input.typeMismatch())
1921                 return trueValue;
1922         }
1923         return falseValue;
1924     }
1925     
1926     // If "false", "undefined" [sic, string value], empty, or missing, return "false".
1927     if (ariaInvalid == falseValue || ariaInvalid == undefinedValue)
1928         return falseValue;
1929     // Besides true/false/undefined, the only tokens defined by WAI-ARIA 1.0...
1930     // ...for @aria-invalid are "grammar" and "spelling".
1931     if (ariaInvalid == grammarValue)
1932         return grammarValue;
1933     if (ariaInvalid == spellingValue)
1934         return spellingValue;
1935     // Any other non empty string should be treated as "true".
1936     return trueValue;
1937 }
1938
1939 bool AccessibilityObject::supportsCurrent() const
1940 {
1941     return hasAttribute(aria_currentAttr);
1942 }
1943  
1944 AccessibilityCurrentState AccessibilityObject::currentState() const
1945 {
1946     // aria-current can return false (default), true, page, step, location, date or time.
1947     String currentStateValue = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_currentAttr));
1948     
1949     // If "false", empty, or missing, return false state.
1950     if (currentStateValue.isEmpty() || currentStateValue == "false")
1951         return AccessibilityCurrentState::False;
1952     
1953     if (currentStateValue == "page")
1954         return AccessibilityCurrentState::Page;
1955     if (currentStateValue == "step")
1956         return AccessibilityCurrentState::Step;
1957     if (currentStateValue == "location")
1958         return AccessibilityCurrentState::Location;
1959     if (currentStateValue == "date")
1960         return AccessibilityCurrentState::Date;
1961     if (currentStateValue == "time")
1962         return AccessibilityCurrentState::Time;
1963     
1964     // Any value not included in the list of allowed values should be treated as "true".
1965     return AccessibilityCurrentState::True;
1966 }
1967
1968 String AccessibilityObject::currentValue() const
1969 {
1970     switch (currentState()) {
1971     case AccessibilityCurrentState::False:
1972         return "false";
1973     case AccessibilityCurrentState::Page:
1974         return "page";
1975     case AccessibilityCurrentState::Step:
1976         return "step";
1977     case AccessibilityCurrentState::Location:
1978         return "location";
1979     case AccessibilityCurrentState::Time:
1980         return "time";
1981     case AccessibilityCurrentState::Date:
1982         return "date";
1983     default:
1984     case AccessibilityCurrentState::True:
1985         return "true";
1986     }
1987 }
1988
1989 bool AccessibilityObject::isModalDescendant(Node* modalNode) const
1990 {
1991     Node* node = this->node();
1992     if (!modalNode || !node)
1993         return false;
1994     
1995     if (node == modalNode)
1996         return true;
1997     
1998     // ARIA 1.1 aria-modal, indicates whether an element is modal when displayed.
1999     // For the decendants of the modal object, they should also be considered as aria-modal=true.
2000     return node->isDescendantOf(*modalNode);
2001 }
2002
2003 bool AccessibilityObject::isModalNode() const
2004 {
2005     if (AXObjectCache* cache = axObjectCache())
2006         return node() && cache->modalNode() == node();
2007
2008     return false;
2009 }
2010
2011 bool AccessibilityObject::ignoredFromModalPresence() const
2012 {
2013     // We shouldn't ignore the top node.
2014     if (!node() || !node()->parentNode())
2015         return false;
2016     
2017     AXObjectCache* cache = axObjectCache();
2018     if (!cache)
2019         return false;
2020     
2021     // modalNode is the current displayed modal dialog.
2022     Node* modalNode = cache->modalNode();
2023     if (!modalNode)
2024         return false;
2025     
2026     // We only want to ignore the objects within the same frame as the modal dialog.
2027     if (modalNode->document().frame() != this->frame())
2028         return false;
2029     
2030     return !isModalDescendant(modalNode);
2031 }
2032
2033 bool AccessibilityObject::hasTagName(const QualifiedName& tagName) const
2034 {
2035     Node* node = this->node();
2036     return is<Element>(node) && downcast<Element>(*node).hasTagName(tagName);
2037 }
2038     
2039 bool AccessibilityObject::hasAttribute(const QualifiedName& attribute) const
2040 {
2041     Node* node = this->node();
2042     if (!is<Element>(node))
2043         return false;
2044     
2045     return downcast<Element>(*node).hasAttributeWithoutSynchronization(attribute);
2046 }
2047     
2048 const AtomString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
2049 {
2050     if (auto* element = this->element())
2051         return element->attributeWithoutSynchronization(attribute);
2052     return nullAtom();
2053 }
2054
2055 bool AccessibilityObject::replaceTextInRange(const String& replacementString, const PlainTextRange& range)
2056 {
2057     // If this is being called on the web area, redirect it to be on the body, which will have a renderer associated with it.
2058     if (is<Document>(node())) {
2059         if (auto bodyObject = axObjectCache()->getOrCreate(downcast<Document>(node())->body()))
2060             return bodyObject->replaceTextInRange(replacementString, range);
2061         return false;
2062     }
2063     
2064     if (!renderer() || !is<Element>(node()))
2065         return false;
2066
2067     auto& element = downcast<Element>(*renderer()->node());
2068
2069     // We should use the editor's insertText to mimic typing into the field.
2070     // Also only do this when the field is in editing mode.
2071     auto& frame = renderer()->frame();
2072     if (element.shouldUseInputMethod()) {
2073         frame.selection().setSelectedRange(rangeForPlainTextRange(range).get(), DOWNSTREAM, FrameSelection::ShouldCloseTyping::Yes);
2074         frame.editor().replaceSelectionWithText(replacementString, Editor::SelectReplacement::No, Editor::SmartReplace::No);
2075         return true;
2076     }
2077     
2078     if (is<HTMLInputElement>(element)) {
2079         downcast<HTMLInputElement>(element).setRangeText(replacementString, range.start, range.length, "");
2080         return true;
2081     }
2082     if (is<HTMLTextAreaElement>(element)) {
2083         downcast<HTMLTextAreaElement>(element).setRangeText(replacementString, range.start, range.length, "");
2084         return true;
2085     }
2086
2087     return false;
2088 }
2089
2090 bool AccessibilityObject::insertText(const String& text)
2091 {
2092     if (!renderer() || !is<Element>(node()))
2093         return false;
2094
2095     auto& element = downcast<Element>(*renderer()->node());
2096
2097     // Only try to insert text if the field is in editing mode.
2098     if (!element.shouldUseInputMethod())
2099         return false;
2100
2101     // Use Editor::insertText to mimic typing into the field.
2102     auto& editor = renderer()->frame().editor();
2103     return editor.insertText(text, nullptr);
2104 }
2105
2106 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
2107 AccessibilityOrientation AccessibilityObject::orientation() const
2108 {
2109     LayoutRect bounds = elementRect();
2110     if (bounds.size().width() > bounds.size().height())
2111         return AccessibilityOrientation::Horizontal;
2112     if (bounds.size().height() > bounds.size().width())
2113         return AccessibilityOrientation::Vertical;
2114
2115     return AccessibilityOrientation::Undefined;
2116 }    
2117
2118 AccessibilityObject* AccessibilityObject::firstAnonymousBlockChild() const
2119 {
2120     for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
2121         if (child->renderer() && child->renderer()->isAnonymousBlock())
2122             return child;
2123     }
2124     return nullptr;
2125 }
2126
2127 using ARIARoleMap = HashMap<String, AccessibilityRole, ASCIICaseInsensitiveHash>;
2128 using ARIAReverseRoleMap = HashMap<AccessibilityRole, String, DefaultHash<int>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int>>;
2129
2130 static ARIARoleMap* gAriaRoleMap = nullptr;
2131 static ARIAReverseRoleMap* gAriaReverseRoleMap = nullptr;
2132
2133 struct RoleEntry {
2134     String ariaRole;
2135     AccessibilityRole webcoreRole;
2136 };
2137
2138 static void initializeRoleMap()
2139 {
2140     if (gAriaRoleMap)
2141         return;
2142     ASSERT(!gAriaReverseRoleMap);
2143
2144     const RoleEntry roles[] = {
2145         { "alert", AccessibilityRole::ApplicationAlert },
2146         { "alertdialog", AccessibilityRole::ApplicationAlertDialog },
2147         { "application", AccessibilityRole::WebApplication },
2148         { "article", AccessibilityRole::DocumentArticle },
2149         { "banner", AccessibilityRole::LandmarkBanner },
2150         { "blockquote", AccessibilityRole::Blockquote },
2151         { "button", AccessibilityRole::Button },
2152         { "caption", AccessibilityRole::Caption },
2153         { "checkbox", AccessibilityRole::CheckBox },
2154         { "complementary", AccessibilityRole::LandmarkComplementary },
2155         { "contentinfo", AccessibilityRole::LandmarkContentInfo },
2156         { "deletion", AccessibilityRole::Deletion },
2157         { "dialog", AccessibilityRole::ApplicationDialog },
2158         { "directory", AccessibilityRole::Directory },
2159         // The 'doc-*' roles are defined the ARIA DPUB mobile: https://www.w3.org/TR/dpub-aam-1.0/ 
2160         // Editor's draft is currently at https://rawgit.com/w3c/aria/master/dpub-aam/dpub-aam.html 
2161         { "doc-abstract", AccessibilityRole::ApplicationTextGroup },
2162         { "doc-acknowledgments", AccessibilityRole::LandmarkDocRegion },
2163         { "doc-afterword", AccessibilityRole::LandmarkDocRegion },
2164         { "doc-appendix", AccessibilityRole::LandmarkDocRegion },
2165         { "doc-backlink", AccessibilityRole::WebCoreLink },
2166         { "doc-biblioentry", AccessibilityRole::ListItem },
2167         { "doc-bibliography", AccessibilityRole::LandmarkDocRegion },
2168         { "doc-biblioref", AccessibilityRole::WebCoreLink },
2169         { "doc-chapter", AccessibilityRole::LandmarkDocRegion },
2170         { "doc-colophon", AccessibilityRole::ApplicationTextGroup },
2171         { "doc-conclusion", AccessibilityRole::LandmarkDocRegion },
2172         { "doc-cover", AccessibilityRole::Image },
2173         { "doc-credit", AccessibilityRole::ApplicationTextGroup },
2174         { "doc-credits", AccessibilityRole::LandmarkDocRegion },
2175         { "doc-dedication", AccessibilityRole::ApplicationTextGroup },
2176         { "doc-endnote", AccessibilityRole::ListItem },
2177         { "doc-endnotes", AccessibilityRole::LandmarkDocRegion },
2178         { "doc-epigraph", AccessibilityRole::ApplicationTextGroup },
2179         { "doc-epilogue", AccessibilityRole::LandmarkDocRegion },
2180         { "doc-errata", AccessibilityRole::LandmarkDocRegion },
2181         { "doc-example", AccessibilityRole::ApplicationTextGroup },
2182         { "doc-footnote", AccessibilityRole::Footnote },
2183         { "doc-foreword", AccessibilityRole::LandmarkDocRegion },
2184         { "doc-glossary", AccessibilityRole::LandmarkDocRegion },
2185         { "doc-glossref", AccessibilityRole::WebCoreLink },
2186         { "doc-index", AccessibilityRole::LandmarkNavigation },
2187         { "doc-introduction", AccessibilityRole::LandmarkDocRegion },
2188         { "doc-noteref", AccessibilityRole::WebCoreLink },
2189         { "doc-notice", AccessibilityRole::DocumentNote },
2190         { "doc-pagebreak", AccessibilityRole::Splitter },
2191         { "doc-pagelist", AccessibilityRole::LandmarkNavigation },
2192         { "doc-part", AccessibilityRole::LandmarkDocRegion },
2193         { "doc-preface", AccessibilityRole::LandmarkDocRegion },
2194         { "doc-prologue", AccessibilityRole::LandmarkDocRegion },
2195         { "doc-pullquote", AccessibilityRole::ApplicationTextGroup },
2196         { "doc-qna", AccessibilityRole::ApplicationTextGroup },
2197         { "doc-subtitle", AccessibilityRole::Heading },
2198         { "doc-tip", AccessibilityRole::DocumentNote },
2199         { "doc-toc", AccessibilityRole::LandmarkNavigation },
2200         { "figure", AccessibilityRole::Figure },
2201         // The mappings for 'graphics-*' roles are defined in this spec: https://w3c.github.io/graphics-aam/
2202         { "graphics-document", AccessibilityRole::GraphicsDocument },
2203         { "graphics-object", AccessibilityRole::GraphicsObject },
2204         { "graphics-symbol", AccessibilityRole::GraphicsSymbol },
2205         { "grid", AccessibilityRole::Grid },
2206         { "gridcell", AccessibilityRole::GridCell },
2207         { "table", AccessibilityRole::Table },
2208         { "cell", AccessibilityRole::Cell },
2209         { "columnheader", AccessibilityRole::ColumnHeader },
2210         { "combobox", AccessibilityRole::ComboBox },
2211         { "definition", AccessibilityRole::Definition },
2212         { "document", AccessibilityRole::Document },
2213         { "feed", AccessibilityRole::Feed },
2214         { "form", AccessibilityRole::Form },
2215         { "rowheader", AccessibilityRole::RowHeader },
2216         { "group", AccessibilityRole::ApplicationGroup },
2217         { "heading", AccessibilityRole::Heading },
2218         { "img", AccessibilityRole::Image },
2219         { "insertion", AccessibilityRole::Insertion },
2220         { "link", AccessibilityRole::WebCoreLink },
2221         { "list", AccessibilityRole::List },
2222         { "listitem", AccessibilityRole::ListItem },
2223         { "listbox", AccessibilityRole::ListBox },
2224         { "log", AccessibilityRole::ApplicationLog },
2225         { "main", AccessibilityRole::LandmarkMain },
2226         { "marquee", AccessibilityRole::ApplicationMarquee },
2227         { "math", AccessibilityRole::DocumentMath },
2228         { "menu", AccessibilityRole::Menu },
2229         { "menubar", AccessibilityRole::MenuBar },
2230         { "menuitem", AccessibilityRole::MenuItem },
2231         { "menuitemcheckbox", AccessibilityRole::MenuItemCheckbox },
2232         { "menuitemradio", AccessibilityRole::MenuItemRadio },
2233         { "meter", AccessibilityRole::Meter },
2234         { "none", AccessibilityRole::Presentational },
2235         { "note", AccessibilityRole::DocumentNote },
2236         { "navigation", AccessibilityRole::LandmarkNavigation },
2237         { "option", AccessibilityRole::ListBoxOption },
2238         { "paragraph", AccessibilityRole::Paragraph },
2239         { "presentation", AccessibilityRole::Presentational },
2240         { "progressbar", AccessibilityRole::ProgressIndicator },
2241         { "radio", AccessibilityRole::RadioButton },
2242         { "radiogroup", AccessibilityRole::RadioGroup },
2243         { "region", AccessibilityRole::LandmarkRegion },
2244         { "row", AccessibilityRole::Row },
2245         { "rowgroup", AccessibilityRole::RowGroup },
2246         { "scrollbar", AccessibilityRole::ScrollBar },
2247         { "search", AccessibilityRole::LandmarkSearch },
2248         { "searchbox", AccessibilityRole::SearchField },
2249         { "separator", AccessibilityRole::Splitter },
2250         { "slider", AccessibilityRole::Slider },
2251         { "spinbutton", AccessibilityRole::SpinButton },
2252         { "status", AccessibilityRole::ApplicationStatus },
2253         { "subscript", AccessibilityRole::Subscript },
2254         { "superscript", AccessibilityRole::Superscript },
2255         { "switch", AccessibilityRole::Switch },
2256         { "tab", AccessibilityRole::Tab },
2257         { "tablist", AccessibilityRole::TabList },
2258         { "tabpanel", AccessibilityRole::TabPanel },
2259         { "text", AccessibilityRole::StaticText },
2260         { "textbox", AccessibilityRole::TextArea },
2261         { "term", AccessibilityRole::Term },
2262         { "time", AccessibilityRole::Time },
2263         { "timer", AccessibilityRole::ApplicationTimer },
2264         { "toolbar", AccessibilityRole::Toolbar },
2265         { "tooltip", AccessibilityRole::UserInterfaceTooltip },
2266         { "tree", AccessibilityRole::Tree },
2267         { "treegrid", AccessibilityRole::TreeGrid },
2268         { "treeitem", AccessibilityRole::TreeItem }
2269     };
2270
2271     gAriaRoleMap = new ARIARoleMap;
2272     gAriaReverseRoleMap = new ARIAReverseRoleMap;
2273     size_t roleLength = WTF_ARRAY_LENGTH(roles);
2274     for (size_t i = 0; i < roleLength; ++i) {
2275         gAriaRoleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
2276         gAriaReverseRoleMap->set(static_cast<int>(roles[i].webcoreRole), roles[i].ariaRole);
2277     }
2278 }
2279
2280 static ARIARoleMap& ariaRoleMap()
2281 {
2282     initializeRoleMap();
2283     return *gAriaRoleMap;
2284 }
2285
2286 static ARIAReverseRoleMap& reverseAriaRoleMap()
2287 {
2288     initializeRoleMap();
2289     return *gAriaReverseRoleMap;
2290 }
2291
2292 AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
2293 {
2294     if (value.isNull() || value.isEmpty())
2295         return AccessibilityRole::Unknown;
2296
2297     for (auto roleName : StringView(value).split(' ')) {
2298         AccessibilityRole role = ariaRoleMap().get<ASCIICaseInsensitiveStringViewHashTranslator>(roleName);
2299         if (static_cast<int>(role))
2300             return role;
2301     }
2302     return AccessibilityRole::Unknown;
2303 }
2304
2305 String AccessibilityObject::computedRoleString() const
2306 {
2307     // FIXME: Need a few special cases that aren't in the RoleMap: option, etc. http://webkit.org/b/128296
2308     AccessibilityRole role = roleValue();
2309
2310     if (role == AccessibilityRole::Image && accessibilityIsIgnored())
2311         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::Presentational));
2312
2313     // We do not compute a role string for generic block elements with user-agent assigned roles.
2314     if (role == AccessibilityRole::Group || role == AccessibilityRole::TextGroup)
2315         return "";
2316
2317     // We do compute a role string for block elements with author-provided roles.
2318     if (role == AccessibilityRole::ApplicationTextGroup
2319         || role == AccessibilityRole::Footnote
2320         || role == AccessibilityRole::GraphicsObject)
2321         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::ApplicationGroup));
2322
2323     if (role == AccessibilityRole::GraphicsDocument)
2324         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::Document));
2325
2326     if (role == AccessibilityRole::GraphicsSymbol)
2327         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::Image));
2328
2329     if (role == AccessibilityRole::HorizontalRule)
2330         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::Splitter));
2331
2332     if (role == AccessibilityRole::PopUpButton || role == AccessibilityRole::ToggleButton)
2333         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::Button));
2334
2335     if (role == AccessibilityRole::LandmarkDocRegion)
2336         return reverseAriaRoleMap().get(static_cast<int>(AccessibilityRole::LandmarkRegion));
2337
2338     return reverseAriaRoleMap().get(static_cast<int>(role));
2339 }
2340
2341 bool AccessibilityObject::hasHighlighting() const
2342 {
2343     for (Node* node = this->node(); node; node = node->parentNode()) {
2344         if (node->hasTagName(markTag))
2345             return true;
2346     }
2347     
2348     return false;
2349 }
2350
2351 #if !PLATFORM(MAC)
2352 String AccessibilityObject::rolePlatformString() const
2353 {
2354     // FIXME: implement in other platforms.
2355     return String();
2356 }
2357
2358 String AccessibilityObject::rolePlatformDescription() const
2359 {
2360     // FIXME: implement in other platforms.
2361     return String();
2362 }
2363 #endif
2364
2365 String AccessibilityObject::ariaLandmarkRoleDescription() const
2366 {
2367     switch (roleValue()) {
2368     case AccessibilityRole::LandmarkBanner:
2369         return AXARIAContentGroupText("ARIALandmarkBanner");
2370     case AccessibilityRole::LandmarkComplementary:
2371         return AXARIAContentGroupText("ARIALandmarkComplementary");
2372     case AccessibilityRole::LandmarkContentInfo:
2373         return AXARIAContentGroupText("ARIALandmarkContentInfo");
2374     case AccessibilityRole::LandmarkMain:
2375         return AXARIAContentGroupText("ARIALandmarkMain");
2376     case AccessibilityRole::LandmarkNavigation:
2377         return AXARIAContentGroupText("ARIALandmarkNavigation");
2378     case AccessibilityRole::LandmarkDocRegion:
2379     case AccessibilityRole::LandmarkRegion:
2380         return AXARIAContentGroupText("ARIALandmarkRegion");
2381     case AccessibilityRole::LandmarkSearch:
2382         return AXARIAContentGroupText("ARIALandmarkSearch");
2383     case AccessibilityRole::ApplicationAlert:
2384         return AXARIAContentGroupText("ARIAApplicationAlert");
2385     case AccessibilityRole::ApplicationAlertDialog:
2386         return AXARIAContentGroupText("ARIAApplicationAlertDialog");
2387     case AccessibilityRole::ApplicationDialog:
2388         return AXARIAContentGroupText("ARIAApplicationDialog");
2389     case AccessibilityRole::ApplicationLog:
2390         return AXARIAContentGroupText("ARIAApplicationLog");
2391     case AccessibilityRole::ApplicationMarquee:
2392         return AXARIAContentGroupText("ARIAApplicationMarquee");
2393     case AccessibilityRole::ApplicationStatus:
2394         return AXARIAContentGroupText("ARIAApplicationStatus");
2395     case AccessibilityRole::ApplicationTimer:
2396         return AXARIAContentGroupText("ARIAApplicationTimer");
2397     case AccessibilityRole::Document:
2398         return AXARIAContentGroupText("ARIADocument");
2399     case AccessibilityRole::DocumentArticle:
2400         return AXARIAContentGroupText("ARIADocumentArticle");
2401     case AccessibilityRole::DocumentMath:
2402         return AXARIAContentGroupText("ARIADocumentMath");
2403     case AccessibilityRole::DocumentNote:
2404         return AXARIAContentGroupText("ARIADocumentNote");
2405     case AccessibilityRole::UserInterfaceTooltip:
2406         return AXARIAContentGroupText("ARIAUserInterfaceTooltip");
2407     case AccessibilityRole::TabPanel:
2408         return AXARIAContentGroupText("ARIATabPanel");
2409     case AccessibilityRole::WebApplication:
2410         return AXARIAContentGroupText("ARIAWebApplication");
2411     default:
2412         return String();
2413     }
2414 }
2415
2416 String AccessibilityObject::roleDescription() const
2417 {
2418     // aria-roledescription takes precedence over any other rule.
2419     String roleDescription = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_roledescriptionAttr));
2420     if (!roleDescription.isEmpty())
2421         return roleDescription;
2422
2423     roleDescription = rolePlatformDescription();
2424     if (!roleDescription.isEmpty())
2425         return roleDescription;
2426
2427     if (roleValue() == AccessibilityRole::Figure)
2428         return AXFigureText();
2429
2430     return roleDescription;
2431 }
2432
2433 bool nodeHasPresentationRole(Node* node)
2434 {
2435     return nodeHasRole(node, "presentation") || nodeHasRole(node, "none");
2436 }
2437     
2438 bool AccessibilityObject::supportsPressAction() const
2439 {
2440     if (isButton())
2441         return true;
2442     if (roleValue() == AccessibilityRole::Details)
2443         return true;
2444     
2445     Element* actionElement = this->actionElement();
2446     if (!actionElement)
2447         return false;
2448     
2449     // [Bug: 136247] Heuristic: element handlers that have more than one accessible descendant should not be exposed as supporting press.
2450     if (actionElement != element()) {
2451         if (AccessibilityObject* axObj = axObjectCache()->getOrCreate(actionElement)) {
2452             AccessibilityChildrenVector results;
2453             // Search within for immediate descendants that are static text. If we find more than one
2454             // then this is an event delegator actionElement and we should expose the press action.
2455             Vector<AccessibilitySearchKey> keys({ AccessibilitySearchKey::StaticText, AccessibilitySearchKey::Control, AccessibilitySearchKey::Graphic, AccessibilitySearchKey::Heading, AccessibilitySearchKey::Link });
2456             AccessibilitySearchCriteria criteria(axObj, AccessibilitySearchDirection::Next, emptyString(), 2, false, false);
2457             criteria.searchKeys = keys;
2458             axObj->findMatchingObjects(&criteria, results);
2459             if (results.size() > 1)
2460                 return false;
2461         }
2462     }
2463     
2464     // [Bug: 133613] Heuristic: If the action element is presentational, we shouldn't expose press as a supported action.
2465     return !nodeHasPresentationRole(actionElement);
2466 }
2467
2468 bool AccessibilityObject::supportsDatetimeAttribute() const
2469 {
2470     return hasTagName(insTag) || hasTagName(delTag) || hasTagName(timeTag);
2471 }
2472
2473 String AccessibilityObject::datetimeAttributeValue() const
2474 {
2475     return getAttribute(datetimeAttr);
2476 }
2477     
2478 String AccessibilityObject::linkRelValue() const
2479 {
2480     return getAttribute(relAttr);
2481 }
2482     
2483 bool AccessibilityObject::isInlineText() const
2484 {
2485     return is<RenderInline>(renderer());
2486 }
2487
2488 const String AccessibilityObject::keyShortcutsValue() const
2489 {
2490     return getAttribute(aria_keyshortcutsAttr);
2491 }
2492
2493 Element* AccessibilityObject::element() const
2494 {
2495     Node* node = this->node();
2496     if (is<Element>(node))
2497         return downcast<Element>(node);
2498     return nullptr;
2499 }
2500     
2501 bool AccessibilityObject::isValueAutofillAvailable() const
2502 {
2503     if (!isNativeTextControl())
2504         return false;
2505     
2506     Node* node = this->node();
2507     if (!is<HTMLInputElement>(node))
2508         return false;
2509     
2510     return downcast<HTMLInputElement>(*node).isAutoFillAvailable() || downcast<HTMLInputElement>(*node).autoFillButtonType() != AutoFillButtonType::None;
2511 }
2512
2513 AutoFillButtonType AccessibilityObject::valueAutofillButtonType() const
2514 {
2515     if (!isValueAutofillAvailable())
2516         return AutoFillButtonType::None;
2517     
2518     return downcast<HTMLInputElement>(*this->node()).autoFillButtonType();
2519 }
2520     
2521 bool AccessibilityObject::isValueAutofilled() const
2522 {
2523     if (!isNativeTextControl())
2524         return false;
2525     
2526     Node* node = this->node();
2527     if (!is<HTMLInputElement>(node))
2528         return false;
2529     
2530     return downcast<HTMLInputElement>(*node).isAutoFilled();
2531 }
2532
2533 const String AccessibilityObject::placeholderValue() const
2534 {
2535     const AtomString& placeholder = getAttribute(placeholderAttr);
2536     if (!placeholder.isEmpty())
2537         return placeholder;
2538     
2539     const AtomString& ariaPlaceholder = getAttribute(aria_placeholderAttr);
2540     if (!ariaPlaceholder.isEmpty())
2541         return ariaPlaceholder;
2542     
2543     return nullAtom();
2544 }
2545     
2546 bool AccessibilityObject::isInsideLiveRegion(bool excludeIfOff) const
2547 {
2548     return liveRegionAncestor(excludeIfOff);
2549 }
2550     
2551 AccessibilityObject* AccessibilityObject::liveRegionAncestor(bool excludeIfOff) const
2552 {
2553     return Accessibility::findAncestor<AccessibilityObject>(*this, true, [excludeIfOff] (const AccessibilityObject& object) {
2554         return object.supportsLiveRegion(excludeIfOff);
2555     });
2556 }
2557
2558 bool AccessibilityObject::supportsARIAAttributes() const
2559 {
2560     // This returns whether the element supports any global ARIA attributes.
2561     return supportsLiveRegion()
2562         || supportsARIADragging()
2563         || supportsARIADropping()
2564         || supportsARIAOwns()
2565         || hasAttribute(aria_atomicAttr)
2566         || hasAttribute(aria_busyAttr)
2567         || hasAttribute(aria_controlsAttr)
2568         || hasAttribute(aria_currentAttr)
2569         || hasAttribute(aria_describedbyAttr)
2570         || hasAttribute(aria_detailsAttr)
2571         || hasAttribute(aria_disabledAttr)
2572         || hasAttribute(aria_errormessageAttr)
2573         || hasAttribute(aria_flowtoAttr)
2574         || hasAttribute(aria_haspopupAttr)
2575         || hasAttribute(aria_invalidAttr)
2576         || hasAttribute(aria_labelAttr)
2577         || hasAttribute(aria_labelledbyAttr)
2578         || hasAttribute(aria_relevantAttr);
2579 }
2580     
2581 bool AccessibilityObject::liveRegionStatusIsEnabled(const AtomString& liveRegionStatus)
2582 {
2583     return equalLettersIgnoringASCIICase(liveRegionStatus, "polite") || equalLettersIgnoringASCIICase(liveRegionStatus, "assertive");
2584 }
2585     
2586 bool AccessibilityObject::supportsLiveRegion(bool excludeIfOff) const
2587 {
2588     const AtomString& liveRegionStatusValue = liveRegionStatus();
2589     return excludeIfOff ? liveRegionStatusIsEnabled(liveRegionStatusValue) : !liveRegionStatusValue.isEmpty();
2590 }
2591
2592 AXCoreObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
2593
2594     // Send the hit test back into the sub-frame if necessary.
2595     if (isAttachment()) {
2596         Widget* widget = widgetForAttachmentView();
2597         // Normalize the point for the widget's bounds.
2598         if (widget && widget->isFrameView()) {
2599             if (AXObjectCache* cache = axObjectCache())
2600                 return cache->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
2601         }
2602     }
2603     
2604     // Check if there are any mock elements that need to be handled.
2605     for (const auto& child : m_children) {
2606         if (child->isMockObject() && child->elementRect().contains(point))
2607             return child->elementAccessibilityHitTest(point);
2608     }
2609
2610     return const_cast<AccessibilityObject*>(this);
2611 }
2612     
2613 AXObjectCache* AccessibilityObject::axObjectCache() const
2614 {
2615     auto* document = this->document();
2616     return document ? document->axObjectCache() : nullptr;
2617 }
2618     
2619 AXCoreObject* AccessibilityObject::focusedUIElement() const
2620 {
2621     auto* page = this->page();
2622     auto* axObjectCache = this->axObjectCache();
2623     return page && axObjectCache ? axObjectCache->focusedUIElementForPage(page) : nullptr;
2624 }
2625     
2626 AccessibilitySortDirection AccessibilityObject::sortDirection() const
2627 {
2628     AccessibilityRole role = roleValue();
2629     if (role != AccessibilityRole::RowHeader && role != AccessibilityRole::ColumnHeader)
2630         return AccessibilitySortDirection::Invalid;
2631
2632     const AtomString& sortAttribute = getAttribute(aria_sortAttr);
2633     if (equalLettersIgnoringASCIICase(sortAttribute, "ascending"))
2634         return AccessibilitySortDirection::Ascending;
2635     if (equalLettersIgnoringASCIICase(sortAttribute, "descending"))
2636         return AccessibilitySortDirection::Descending;
2637     if (equalLettersIgnoringASCIICase(sortAttribute, "other"))
2638         return AccessibilitySortDirection::Other;
2639     
2640     return AccessibilitySortDirection::None;
2641 }
2642
2643 bool AccessibilityObject::supportsRangeValue() const
2644 {
2645     return isProgressIndicator()
2646         || isSlider()
2647         || isScrollbar()
2648         || isSpinButton()
2649         || (isSplitter() && canSetFocusAttribute())
2650         || isAttachmentElement();
2651 }
2652     
2653 bool AccessibilityObject::supportsHasPopup() const
2654 {
2655     return hasAttribute(aria_haspopupAttr) || isComboBox();
2656 }
2657
2658 String AccessibilityObject::popupValue() const
2659 {
2660     static const NeverDestroyed<HashSet<String>> allowedPopupValues(std::initializer_list<String> {
2661         "menu", "listbox", "tree", "grid", "dialog"
2662     });
2663
2664     auto hasPopup = getAttribute(aria_haspopupAttr).convertToASCIILowercase();
2665     if (hasPopup.isNull() || hasPopup.isEmpty()) {
2666         // In ARIA 1.1, the implicit value for combobox became "listbox."
2667         if (isComboBox() || hasDatalist())
2668             return "listbox";
2669         return "false";
2670     }
2671
2672     if (allowedPopupValues->contains(hasPopup))
2673         return hasPopup;
2674
2675     // aria-haspopup specification states that true must be treated as menu.
2676     if (hasPopup == "true")
2677         return "menu";
2678
2679     // The spec states that "User agents must treat any value of aria-haspopup that is not
2680     // included in the list of allowed values, including an empty string, as if the value
2681     // false had been provided."
2682     return "false";
2683 }
2684
2685 bool AccessibilityObject::hasDatalist() const
2686 {
2687 #if ENABLE(DATALIST_ELEMENT)
2688     auto datalistId = getAttribute(listAttr);
2689     if (datalistId.isNull() || datalistId.isEmpty())
2690         return false;
2691
2692     auto element = this->element();
2693     if (!element)
2694         return false;
2695
2696     auto datalist = element->treeScope().getElementById(datalistId);
2697     return is<HTMLDataListElement>(datalist);
2698 #else
2699     return false;
2700 #endif
2701 }
2702
2703 bool AccessibilityObject::supportsSetSize() const
2704 {
2705     return hasAttribute(aria_setsizeAttr);
2706 }
2707
2708 bool AccessibilityObject::supportsPosInSet() const
2709 {
2710     return hasAttribute(aria_posinsetAttr);
2711 }
2712     
2713 int AccessibilityObject::setSize() const
2714 {
2715     return getAttribute(aria_setsizeAttr).toInt();
2716 }
2717
2718 int AccessibilityObject::posInSet() const
2719 {
2720     return getAttribute(aria_posinsetAttr).toInt();
2721 }
2722     
2723 String AccessibilityObject::identifierAttribute() const
2724 {
2725     return getAttribute(idAttr);
2726 }
2727     
2728 void AccessibilityObject::classList(Vector<String>& classList) const
2729 {
2730     Node* node = this->node();
2731     if (!is<Element>(node))
2732         return;
2733     
2734     Element* element = downcast<Element>(node);
2735     DOMTokenList& list = element->classList();
2736     unsigned length = list.length();
2737     for (unsigned k = 0; k < length; k++)
2738         classList.append(list.item(k).string());
2739 }
2740
2741 bool AccessibilityObject::supportsPressed() const
2742 {
2743     const AtomString& expanded = getAttribute(aria_pressedAttr);
2744     return equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false");
2745 }
2746     
2747 bool AccessibilityObject::supportsExpanded() const
2748 {
2749     // Undefined values should not result in this attribute being exposed to ATs according to ARIA.
2750     const AtomString& expanded = getAttribute(aria_expandedAttr);
2751     if (equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false"))
2752         return true;
2753     switch (roleValue()) {
2754     case AccessibilityRole::ComboBox:
2755     case AccessibilityRole::DisclosureTriangle:
2756     case AccessibilityRole::Details:
2757         return true;
2758     default:
2759         return false;
2760     }
2761 }
2762     
2763 bool AccessibilityObject::isExpanded() const
2764 {
2765     if (equalLettersIgnoringASCIICase(getAttribute(aria_expandedAttr), "true"))
2766         return true;
2767     
2768     if (is<HTMLDetailsElement>(node()))
2769         return downcast<HTMLDetailsElement>(node())->isOpen();
2770     
2771     // Summary element should use its details parent's expanded status.
2772     if (isSummary()) {
2773         if (const AccessibilityObject* parent = Accessibility::findAncestor<AccessibilityObject>(*this, false, [] (const AccessibilityObject& object) {
2774             return is<HTMLDetailsElement>(object.node());
2775         }))
2776             return parent->isExpanded();
2777     }
2778
2779     return false;  
2780 }
2781
2782 bool AccessibilityObject::supportsChecked() const
2783 {
2784     switch (roleValue()) {
2785     case AccessibilityRole::CheckBox:
2786     case AccessibilityRole::MenuItemCheckbox:
2787     case AccessibilityRole::MenuItemRadio:
2788     case AccessibilityRole::RadioButton:
2789     case AccessibilityRole::Switch:
2790         return true;
2791     default:
2792         return false;
2793     }
2794 }
2795
2796 AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
2797 {
2798     // If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
2799     // If it's an ARIA checkbox, radio, or switch the aria-checked attribute should be used.
2800     // If it's a toggle button, the aria-pressed attribute is consulted.
2801
2802     if (isToggleButton()) {
2803         const AtomString& ariaPressed = getAttribute(aria_pressedAttr);
2804         if (equalLettersIgnoringASCIICase(ariaPressed, "true"))
2805             return AccessibilityButtonState::On;
2806         if (equalLettersIgnoringASCIICase(ariaPressed, "mixed"))
2807             return AccessibilityButtonState::Mixed;
2808         return AccessibilityButtonState::Off;
2809     }
2810     
2811     const AtomString& result = getAttribute(aria_checkedAttr);
2812     if (equalLettersIgnoringASCIICase(result, "true"))
2813         return AccessibilityButtonState::On;
2814     if (equalLettersIgnoringASCIICase(result, "mixed")) {
2815         // ARIA says that radio, menuitemradio, and switch elements must NOT expose button state mixed.
2816         AccessibilityRole ariaRole = ariaRoleAttribute();
2817         if (ariaRole == AccessibilityRole::RadioButton || ariaRole == AccessibilityRole::MenuItemRadio || ariaRole == AccessibilityRole::Switch)
2818             return AccessibilityButtonState::Off;
2819         return AccessibilityButtonState::Mixed;
2820     }
2821     
2822     if (isIndeterminate())
2823         return AccessibilityButtonState::Mixed;
2824     
2825     return AccessibilityButtonState::Off;
2826 }
2827
2828 // This is a 1-dimensional scroll offset helper function that's applied
2829 // separately in the horizontal and vertical directions, because the
2830 // logic is the same. The goal is to compute the best scroll offset
2831 // in order to make an object visible within a viewport.
2832 //
2833 // If the object is already fully visible, returns the same scroll
2834 // offset.
2835 //
2836 // In case the whole object cannot fit, you can specify a
2837 // subfocus - a smaller region within the object that should
2838 // be prioritized. If the whole object can fit, the subfocus is
2839 // ignored.
2840 //
2841 // If possible, the object and subfocus are centered within the
2842 // viewport.
2843 //
2844 // Example 1: the object is already visible, so nothing happens.
2845 //   +----------Viewport---------+
2846 //                 +---Object---+
2847 //                 +--SubFocus--+
2848 //
2849 // Example 2: the object is not fully visible, so it's centered
2850 // within the viewport.
2851 //   Before:
2852 //   +----------Viewport---------+
2853 //                         +---Object---+
2854 //                         +--SubFocus--+
2855 //
2856 //   After:
2857 //                 +----------Viewport---------+
2858 //                         +---Object---+
2859 //                         +--SubFocus--+
2860 //
2861 // Example 3: the object is larger than the viewport, so the
2862 // viewport moves to show as much of the object as possible,
2863 // while also trying to center the subfocus.
2864 //   Before:
2865 //   +----------Viewport---------+
2866 //     +---------------Object--------------+
2867 //                         +-SubFocus-+
2868 //
2869 //   After:
2870 //             +----------Viewport---------+
2871 //     +---------------Object--------------+
2872 //                         +-SubFocus-+
2873 //
2874 // When constraints cannot be fully satisfied, the min
2875 // (left/top) position takes precedence over the max (right/bottom).
2876 //
2877 // Note that the return value represents the ideal new scroll offset.
2878 // This may be out of range - the calling function should clip this
2879 // to the available range.
2880 static int computeBestScrollOffset(int currentScrollOffset, int subfocusMin, int subfocusMax, int objectMin, int objectMax, int viewportMin, int viewportMax)
2881 {
2882     int viewportSize = viewportMax - viewportMin;
2883     
2884     // If the object size is larger than the viewport size, consider
2885     // only a portion that's as large as the viewport, centering on
2886     // the subfocus as much as possible.
2887     if (objectMax - objectMin > viewportSize) {
2888         // Since it's impossible to fit the whole object in the
2889         // viewport, exit now if the subfocus is already within the viewport.
2890         if (subfocusMin - currentScrollOffset >= viewportMin && subfocusMax - currentScrollOffset <= viewportMax)
2891             return currentScrollOffset;
2892         
2893         // Subfocus must be within focus.
2894         subfocusMin = std::max(subfocusMin, objectMin);
2895         subfocusMax = std::min(subfocusMax, objectMax);
2896         
2897         // Subfocus must be no larger than the viewport size; favor top/left.
2898         if (subfocusMax - subfocusMin > viewportSize)
2899             subfocusMax = subfocusMin + viewportSize;
2900         
2901         // Compute the size of an object centered on the subfocus, the size of the viewport.
2902         int centeredObjectMin = (subfocusMin + subfocusMax - viewportSize) / 2;
2903         int centeredObjectMax = centeredObjectMin + viewportSize;
2904
2905         objectMin = std::max(objectMin, centeredObjectMin);
2906         objectMax = std::min(objectMax, centeredObjectMax);
2907     }
2908
2909     // Exit now if the focus is already within the viewport.
2910     if (objectMin - currentScrollOffset >= viewportMin
2911         && objectMax - currentScrollOffset <= viewportMax)
2912         return currentScrollOffset;
2913     
2914     // Center the object in the viewport.
2915     return (objectMin + objectMax - viewportMin - viewportMax) / 2;
2916 }
2917
2918 bool AccessibilityObject::isOnScreen() const
2919 {   
2920     bool isOnscreen = true;
2921
2922     // To figure out if the element is onscreen, we start by building of a stack starting with the
2923     // element, and then include every scrollable parent in the hierarchy.
2924     Vector<const AccessibilityObject*> objects;
2925     
2926     objects.append(this);
2927     for (AccessibilityObject* parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
2928         if (parentObject->getScrollableAreaIfScrollable())
2929             objects.append(parentObject);
2930     }
2931
2932     // Now, go back through that chain and make sure each inner object is within the
2933     // visible bounds of the outer object.
2934     size_t levels = objects.size() - 1;
2935     
2936     for (size_t i = levels; i >= 1; i--) {
2937         const AccessibilityObject* outer = objects[i];
2938         const AccessibilityObject* inner = objects[i - 1];
2939         // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
2940         const IntRect outerRect = i < levels ? snappedIntRect(outer->boundingBoxRect()) : outer->getScrollableAreaIfScrollable()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
2941         const IntRect innerRect = snappedIntRect(inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect());
2942         
2943         if (!outerRect.intersects(innerRect)) {
2944             isOnscreen = false;
2945             break;
2946         }
2947     }
2948     
2949     return isOnscreen;
2950 }
2951
2952 void AccessibilityObject::scrollToMakeVisible() const
2953 {
2954     scrollToMakeVisible({ SelectionRevealMode::Reveal, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, ShouldAllowCrossOriginScrolling::Yes });
2955 }
2956
2957 void AccessibilityObject::scrollToMakeVisible(const ScrollRectToVisibleOptions& options) const
2958 {
2959     if (isScrollView() && parentObject())
2960         parentObject()->scrollToMakeVisible();
2961
2962     if (auto* renderer = this->renderer())
2963         renderer->scrollRectToVisible(boundingBoxRect(), false, options);
2964 }
2965
2966 void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const
2967 {
2968     // Search up the parent chain until we find the first one that's scrollable.
2969     AccessibilityObject* scrollParent = parentObject();
2970     ScrollableArea* scrollableArea;
2971     for (scrollableArea = nullptr;
2972          scrollParent && !(scrollableArea = scrollParent->getScrollableAreaIfScrollable());
2973          scrollParent = scrollParent->parentObject()) { }
2974     if (!scrollableArea)
2975         return;
2976
2977     LayoutRect objectRect = boundingBoxRect();
2978     IntPoint scrollPosition = scrollableArea->scrollPosition();
2979     // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
2980     IntRect scrollVisibleRect = scrollableArea->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
2981
2982     if (!scrollParent->isScrollView()) {
2983         objectRect.moveBy(scrollPosition);
2984         objectRect.moveBy(-snappedIntRect(scrollParent->elementRect()).location());
2985     }
2986     
2987     int desiredX = computeBestScrollOffset(
2988         scrollPosition.x(),
2989         objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(),
2990         objectRect.x(), objectRect.maxX(),
2991         0, scrollVisibleRect.width());
2992     int desiredY = computeBestScrollOffset(
2993         scrollPosition.y(),
2994         objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(),
2995         objectRect.y(), objectRect.maxY(),
2996         0, scrollVisibleRect.height());
2997
2998     scrollParent->scrollTo(IntPoint(desiredX, desiredY));
2999
3000     // Convert the subfocus into the coordinates of the scroll parent.
3001     IntRect newSubfocus = subfocus;
3002     IntRect newElementRect = snappedIntRect(elementRect());
3003     IntRect scrollParentRect = snappedIntRect(scrollParent->elementRect());
3004     newSubfocus.move(newElementRect.x(), newElementRect.y());
3005     newSubfocus.move(-scrollParentRect.x(), -scrollParentRect.y());
3006     
3007     // Recursively make sure the scroll parent itself is visible.
3008     if (scrollParent->parentObject())
3009         scrollParent->scrollToMakeVisibleWithSubFocus(newSubfocus);
3010 }
3011
3012 void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
3013 {
3014     // Search up the parent chain and create a vector of all scrollable parent objects
3015     // and ending with this object itself.
3016     Vector<const AccessibilityObject*> objects;
3017
3018     objects.append(this);
3019     for (AccessibilityObject* parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
3020         if (parentObject->getScrollableAreaIfScrollable())
3021             objects.append(parentObject);
3022     }
3023
3024     objects.reverse();
3025
3026     // Start with the outermost scrollable (the main window) and try to scroll the
3027     // next innermost object to the given point.
3028     int offsetX = 0, offsetY = 0;
3029     IntPoint point = globalPoint;
3030     size_t levels = objects.size() - 1;
3031     for (size_t i = 0; i < levels; i++) {
3032         const AccessibilityObject* outer = objects[i];
3033         const AccessibilityObject* inner = objects[i + 1];
3034
3035         ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable();
3036
3037         LayoutRect innerRect = inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect();
3038         LayoutRect objectRect = innerRect;
3039         IntPoint scrollPosition = scrollableArea->scrollPosition();
3040
3041         // Convert the object rect into local coordinates.
3042         objectRect.move(offsetX, offsetY);
3043         if (!outer->isAccessibilityScrollView())
3044             objectRect.move(scrollPosition.x(), scrollPosition.y());
3045
3046         int desiredX = computeBestScrollOffset(
3047             0,
3048             objectRect.x(), objectRect.maxX(),
3049             objectRect.x(), objectRect.maxX(),
3050             point.x(), point.x());
3051         int desiredY = computeBestScrollOffset(
3052             0,
3053             objectRect.y(), objectRect.maxY(),
3054             objectRect.y(), objectRect.maxY(),
3055             point.y(), point.y());
3056         outer->scrollTo(IntPoint(desiredX, desiredY));
3057
3058         if (outer->isAccessibilityScrollView() && !inner->isAccessibilityScrollView()) {
3059             // If outer object we just scrolled is a scroll view (main window or iframe) but the
3060             // inner object is not, keep track of the coordinate transformation to apply to
3061             // future nested calculations.
3062             scrollPosition = scrollableArea->scrollPosition();
3063             offsetX -= (scrollPosition.x() + point.x());
3064             offsetY -= (scrollPosition.y() + point.y());
3065             point.move(scrollPosition.x() - innerRect.x(),
3066                        scrollPosition.y() - innerRect.y());
3067         } else if (inner->isAccessibilityScrollView()) {
3068             // Otherwise, if the inner object is a scroll view, reset the coordinate transformation.
3069             offsetX = 0;
3070             offsetY = 0;
3071         }
3072     }
3073 }
3074     
3075 void AccessibilityObject::scrollAreaAndAncestor(std::pair<ScrollableArea*, AccessibilityObject*>& scrollers) const
3076 {
3077     // Search up the parent chain until we find the first one that's scrollable.
3078     scrollers.first = nullptr;
3079     for (scrollers.second = parentObject(); scrollers.second; scrollers.second = scrollers.second->parentObject()) {
3080         if ((scrollers.first = scrollers.second->getScrollableAreaIfScrollable()))
3081             break;
3082     }
3083 }
3084     
3085 ScrollableArea* AccessibilityObject::scrollableAreaAncestor() const
3086 {
3087     std::pair<ScrollableArea*, AccessibilityObject*> scrollers;
3088     scrollAreaAndAncestor(scrollers);
3089     return scrollers.first;
3090 }
3091     
3092 IntPoint AccessibilityObject::scrollPosition() const
3093 {
3094     if (auto scroller = scrollableAreaAncestor())
3095         return scroller->scrollPosition();
3096
3097     return IntPoint();
3098 }
3099
3100 IntRect AccessibilityObject::scrollVisibleContentRect() const
3101 {
3102     if (auto scroller = scrollableAreaAncestor())
3103         return scroller->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
3104     
3105     return IntRect();
3106 }
3107     
3108 IntSize AccessibilityObject::scrollContentsSize() const
3109 {
3110     if (auto scroller = scrollableAreaAncestor())
3111         return scroller->contentsSize();
3112
3113     return IntSize();
3114 }
3115     
3116 bool AccessibilityObject::scrollByPage(ScrollByPageDirection direction) const
3117 {
3118     std::pair<ScrollableArea*, AccessibilityObject*> scrollers;
3119     scrollAreaAndAncestor(scrollers);
3120     ScrollableArea* scrollableArea = scrollers.first;
3121     AccessibilityObject* scrollParent = scrollers.second;
3122     
3123     if (!scrollableArea)
3124         return false;
3125     
3126     IntPoint scrollPosition = scrollableArea->scrollPosition();
3127     IntPoint newScrollPosition = scrollPosition;
3128     IntSize scrollSize = scrollableArea->contentsSize();
3129     IntRect scrollVisibleRect = scrollableArea->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
3130     switch (direction) {
3131     case ScrollByPageDirection::Right: {
3132         int scrollAmount = scrollVisibleRect.size().width();
3133         int newX = scrollPosition.x() - scrollAmount;
3134         newScrollPosition.setX(std::max(newX, 0));
3135         break;
3136     }
3137     case ScrollByPageDirection::Left: {
3138         int scrollAmount = scrollVisibleRect.size().width();
3139         int newX = scrollAmount + scrollPosition.x();
3140         int maxX = scrollSize.width() - scrollAmount;
3141         newScrollPosition.setX(std::min(newX, maxX));
3142         break;
3143     }
3144     case ScrollByPageDirection::Up: {
3145         int scrollAmount = scrollVisibleRect.size().height();
3146         int newY = scrollPosition.y() - scrollAmount;
3147         newScrollPosition.setY(std::max(newY, 0));
3148         break;
3149     }
3150     case ScrollByPageDirection::Down: {
3151         int scrollAmount = scrollVisibleRect.size().height();
3152         int newY = scrollAmount + scrollPosition.y();
3153         int maxY = scrollSize.height() - scrollAmount;
3154         newScrollPosition.setY(std::min(newY, maxY));
3155         break;
3156     }
3157     }
3158     
3159     if (newScrollPosition != scrollPosition) {
3160         scrollParent->scrollTo(newScrollPosition);
3161         document()->updateLayoutIgnorePendingStylesheets();
3162         return true;
3163     }
3164     
3165     return false;
3166 }
3167
3168
3169 bool AccessibilityObject::lastKnownIsIgnoredValue()
3170 {
3171     if (m_lastKnownIsIgnoredValue == AccessibilityObjectInclusion::DefaultBehavior)
3172         m_lastKnownIsIgnoredValue = accessibilityIsIgnored() ? AccessibilityObjectInclusion::IgnoreObject : AccessibilityObjectInclusion::IncludeObject;
3173
3174     return m_lastKnownIsIgnoredValue == AccessibilityObjectInclusion::IgnoreObject;
3175 }
3176
3177 void AccessibilityObject::setLastKnownIsIgnoredValue(bool isIgnored)
3178 {
3179     m_lastKnownIsIgnoredValue = isIgnored ? AccessibilityObjectInclusion::IgnoreObject : AccessibilityObjectInclusion::IncludeObject;
3180 }
3181
3182 void AccessibilityObject::notifyIfIgnoredValueChanged()
3183 {
3184     bool isIgnored = accessibilityIsIgnored();
3185     if (lastKnownIsIgnoredValue() != isIgnored) {
3186         if (AXObjectCache* cache = axObjectCache())
3187             cache->childrenChanged(parentObject());
3188         setLastKnownIsIgnoredValue(isIgnored);
3189     }
3190 }
3191
3192 bool AccessibilityObject::pressedIsPresent() const
3193 {
3194     return !getAttribute(aria_pressedAttr).isEmpty();
3195 }
3196
3197 TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() const
3198 {
3199     TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
3200     
3201 #if USE(ATK)
3202     // We need to emit replaced elements for GTK, and present
3203     // them with the 'object replacement character' (0xFFFC).
3204     behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
3205 #endif
3206     
3207     return behavior;
3208 }
3209     
3210 AccessibilityRole AccessibilityObject::buttonRoleType() const
3211 {
3212     // If aria-pressed is present, then it should be exposed as a toggle button.
3213     // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
3214     if (pressedIsPresent())
3215         return AccessibilityRole::ToggleButton;
3216     if (hasPopup())
3217         return AccessibilityRole::PopUpButton;
3218     // We don't contemplate AccessibilityRole::RadioButton, as it depends on the input
3219     // type.
3220
3221     return AccessibilityRole::Button;
3222 }
3223
3224 bool AccessibilityObject::isButton() const
3225 {
3226     AccessibilityRole role = roleValue();
3227
3228     return role == AccessibilityRole::Button || role == AccessibilityRole::PopUpButton || role == AccessibilityRole::ToggleButton;
3229 }
3230
3231 bool AccessibilityObject::accessibilityIsIgnoredByDefault() const
3232 {
3233     return defaultObjectInclusion() == AccessibilityObjectInclusion::IgnoreObject;
3234 }
3235
3236 // ARIA component of hidden definition.
3237 // http://www.w3.org/TR/wai-aria/terms#def_hidden
3238 bool AccessibilityObject::isAXHidden() const
3239 {
3240     return Accessibility::findAncestor<AccessibilityObject>(*this, true, [] (const AccessibilityObject& object) {
3241         return equalLettersIgnoringASCIICase(object.getAttribute(aria_hiddenAttr), "true");
3242     }) != nullptr;
3243 }
3244
3245 // DOM component of hidden definition.
3246 // http://www.w3.org/TR/wai-aria/terms#def_hidden
3247 bool AccessibilityObject::isDOMHidden() const
3248 {
3249     RenderObject* renderer = this->renderer();
3250     if (!renderer)
3251         return true;
3252     
3253     const RenderStyle& style = renderer->style();
3254     return style.display() == DisplayType::None || style.visibility() != Visibility::Visible;
3255 }
3256
3257 bool AccessibilityObject::isShowingValidationMessage() const
3258 {
3259     if (is<HTMLFormControlElement>(node()))
3260         return downcast<HTMLFormControlElement>(*node()).isShowingValidationMessage();
3261     return false;
3262 }
3263
3264 String AccessibilityObject::validationMessage() const
3265 {
3266     if (is<HTMLFormControlElement>(node()))
3267         return downcast<HTMLFormControlElement>(*node()).validationMessage();
3268     return String();
3269 }
3270
3271 AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
3272 {
3273     bool useParentData = !m_isIgnoredFromParentData.isNull();
3274     
3275     if (useParentData ? m_isIgnoredFromParentData.isAXHidden : isAXHidden())
3276         return AccessibilityObjectInclusion::IgnoreObject;
3277     
3278     if (ignoredFromModalPresence())
3279         return AccessibilityObjectInclusion::IgnoreObject;
3280     
3281     if (useParentData ? m_isIgnoredFromParentData.isPresentationalChildOfAriaRole : isPresentationalChildOfAriaRole())
3282         return AccessibilityObjectInclusion::IgnoreObject;
3283     
3284     return accessibilityPlatformIncludesObject();
3285 }
3286     
3287 bool AccessibilityObject::accessibilityIsIgnored() const
3288 {
3289     AXComputedObjectAttributeCache* attributeCache = nullptr;
3290     AXObjectCache* cache = axObjectCache();
3291     if (cache)
3292         attributeCache = cache->computedObjectAttributeCache();
3293     
3294     if (attributeCache) {
3295         AccessibilityObjectInclusion ignored = attributeCache->getIgnored(objectID());
3296         switch (ignored) {
3297         case AccessibilityObjectInclusion::IgnoreObject:
3298             return true;
3299         case AccessibilityObjectInclusion::IncludeObject:
3300             return false;
3301         case AccessibilityObjectInclusion::DefaultBehavior:
3302             break;
3303         }
3304     }
3305
3306     bool result = computeAccessibilityIsIgnored();
3307
3308     // In case computing axIsIgnored disables attribute caching, we should refetch the object to see if it exists.
3309     if (cache && (attributeCache = cache->computedObjectAttributeCache()))
3310         attributeCache->setIgnored(objectID(), result ? AccessibilityObjectInclusion::IgnoreObject : AccessibilityObjectInclusion::IncludeObject);
3311
3312     return result;
3313 }
3314
3315 void AccessibilityObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
3316 {
3317     Node* node = this->node();
3318     if (!node || !node->isElementNode())
3319         return;
3320
3321     TreeScope& treeScope = node->treeScope();
3322
3323     const AtomString& idList = getAttribute(attribute);
3324     if (idList.isEmpty())
3325         return;
3326
3327     auto spaceSplitString = SpaceSplitString(idList, false);
3328     size_t length = spaceSplitString.size();
3329     for (size_t i = 0; i < length; ++i) {
3330         if (auto* idElement = treeScope.getElementById(spaceSplitString[i]))
3331             elements.append(idElement);
3332     }
3333 }
3334
3335 #if PLATFORM(COCOA)
3336 bool AccessibilityObject::preventKeyboardDOMEventDispatch() const
3337 {
3338     Frame* frame = this->frame();
3339     return frame && frame->settings().preventKeyboardDOMEventDispatch();
3340 }
3341
3342 void AccessibilityObject::setPreventKeyboardDOMEventDispatch(bool on)
3343 {
3344     Frame* frame = this->frame();
3345     if (!frame)
3346         return;
3347     frame->settings().setPreventKeyboardDOMEventDispatch(on);
3348 }
3349 #endif
3350
3351 AccessibilityObject* AccessibilityObject::focusableAncestor()
3352 {
3353     return Accessibility::findAncestor<AccessibilityObject>(*this, true, [] (const AccessibilityObject& object) {
3354         return object.canSetFocusAttribute();
3355     });
3356 }
3357
3358 AccessibilityObject* AccessibilityObject::editableAncestor()
3359 {
3360     return Accessibility::findAncestor<AccessibilityObject>(*this, true, [] (const AccessibilityObject& object) {
3361         return object.isTextControl();
3362     });
3363 }
3364
3365 AccessibilityObject* AccessibilityObject::highestEditableAncestor()
3366 {
3367     AccessibilityObject* editableAncestor = this->editableAncestor();
3368     AccessibilityObject* previousEditableAncestor = nullptr;
3369     while (editableAncestor) {
3370         if (editableAncestor == previousEditableAncestor) {
3371             if (AccessibilityObject* parent = editableAncestor->parentObject()) {
3372                 editableAncestor = parent->editableAncestor();
3373                 continue;
3374             }
3375             break;
3376         }
3377         previousEditableAncestor = editableAncestor;
3378         editableAncestor = editableAncestor->editableAncestor();
3379     }
3380     return previousEditableAncestor;
3381 }
3382
3383 AccessibilityObject* AccessibilityObject::radioGroupAncestor() const
3384 {
3385     return Accessibility::findAncestor<AccessibilityObject>(*this, false, [] (const AccessibilityObject& object) {
3386         return object.isRadioGroup();
3387     });
3388 }
3389
3390 String AccessibilityObject::documentURI() const
3391 {
3392     if (auto* document = this->document())
3393         return document->documentURI();
3394     return String();
3395 }
3396
3397 String AccessibilityObject::documentEncoding() const
3398 {
3399     if (auto* document = this->document())
3400         return document->encoding();
3401     return String();
3402 }
3403
3404 uint64_t AccessibilityObject::sessionID() const
3405 {
3406     if (auto* document = topDocument()) {
3407         if (auto* page = document->page())
3408             return page->sessionID().toUInt64();
3409     }
3410     return 0;
3411 }
3412
3413 String AccessibilityObject::tagName() const
3414 {
3415     if (Element* element = this->element())
3416         return element->localName();
3417
3418     return String();
3419 }
3420
3421 bool AccessibilityObject::isStyleFormatGroup() const
3422 {
3423     Node* node = this->node();
3424     if (!node)
3425         return false;
3426     
3427     return node->hasTagName(kbdTag) || node->hasTagName(codeTag)
3428     || node->hasTagName(preTag) || node->hasTagName(sampTag)
3429     || node->hasTagName(varTag) || node->hasTagName(citeTag)
3430     || node->hasTagName(insTag) || node->hasTagName(delTag)
3431     || node->hasTagName(supTag) || node->hasTagName(subTag);
3432 }
3433
3434 bool AccessibilityObject::isFigureElement() const
3435 {
3436     Node* node = this->node();
3437     return node && node->hasTagName(figureTag);
3438 }
3439
3440 bool AccessibilityObject::isKeyboardFocusable() const
3441 {
3442     if (auto element = this->element())
3443         return element->isFocusable();
3444     return false;
3445 }
3446
3447 bool AccessibilityObject::isOutput() const
3448 {
3449     Node* node = this->node();
3450     return node && node->hasTagName(outputTag);
3451 }
3452     
3453 bool AccessibilityObject::isContainedByPasswordField() const
3454 {
3455     Node* node = this->node();
3456     if (!node)
3457         return false;
3458     
3459     if (ariaRoleAttribute() != AccessibilityRole::Unknown)
3460         return false;
3461
3462     Element* element = node->shadowHost();
3463     return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(*element).isPasswordField();
3464 }
3465     
3466 AXCoreObject* AccessibilityObject::selectedListItem()
3467 {
3468     for (const auto& child : children()) {
3469         if (child->isListItem() && (child->isSelected() || child->isActiveDescendantOfFocusedContainer()))
3470             return child.get();
3471     }
3472     
3473     return nullptr;
3474 }
3475
3476 void AccessibilityObject::ariaElementsFromAttribute(AccessibilityChildrenVector& children, const QualifiedName& attributeName) const
3477 {
3478     Vector<Element*> elements;
3479     elementsFromAttribute(elements, attributeName);
3480     AXObjectCache* cache = axObjectCache();
3481     for (const auto& element : elements) {
3482         if (AccessibilityObject* axObject = cache->getOrCreate(element))
3483             children.append(axObject);
3484     }
3485 }
3486
3487 void AccessibilityObject::ariaElementsReferencedByAttribute(AccessibilityChildrenVector& elements, const QualifiedName& attribute) const
3488 {
3489     auto id = identifierAttribute();
3490     if (id.isEmpty())
3491         return;
3492
3493     AXObjectCache* cache = axObjectCache();
3494     if (!cache)
3495         return;
3496
3497     for (auto& element : descendantsOfType<Element>(node()->treeScope().rootNode())) {
3498         const AtomString& idList = element.attributeWithoutSynchronization(attribute);
3499         if (!SpaceSplitString(idList, false).contains(id))
3500             continue;
3501
3502         if (AccessibilityObject* axObject = cache->getOrCreate(&element))
3503             elements.append(axObject);
3504     }
3505 }
3506
3507 bool AccessibilityObject::isActiveDescendantOfFocusedContainer() const
3508 {
3509     AccessibilityChildrenVector containers;
3510     ariaActiveDescendantReferencingElements(containers);
3511     for (auto& container : containers) {
3512         if (container->isFocused())
3513             return true;
3514     }
3515
3516     return false;
3517 }
3518
3519 void AccessibilityObject::ariaActiveDescendantReferencingElements(AccessibilityChildrenVector& containers) const
3520 {
3521     ariaElementsReferencedByAttribute(containers, aria_activedescendantAttr);
3522 }
3523
3524 void AccessibilityObject::ariaControlsElements(AccessibilityChildrenVector& ariaControls) const
3525 {
3526     ariaElementsFromAttribute(ariaControls, aria_controlsAttr);
3527 }
3528
3529 void AccessibilityObject::ariaControlsReferencingElements(AccessibilityChildrenVector& controllers) const
3530 {
3531     ariaElementsReferencedByAttribute(controllers, aria_controlsAttr);
3532 }
3533
3534 void AccessibilityObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const
3535 {
3536     ariaElementsFromAttribute(ariaDescribedBy, aria_describedbyAttr);
3537 }
3538
3539 void AccessibilityObject::ariaDescribedByReferencingElements(AccessibilityChildrenVector& describers) const
3540 {
3541     ariaElementsReferencedByAttribute(describers, aria_describedbyAttr);
3542 }
3543
3544 void AccessibilityObject::ariaDetailsElements(AccessibilityChildrenVector& ariaDetails) const
3545 {
3546     ariaElementsFromAttribute(ariaDetails, aria_detailsAttr);
3547 }
3548
3549 void AccessibilityObject::ariaDetailsReferencingElements(AccessibilityChildrenVector& detailsFor) const
3550 {
3551     ariaElementsReferencedByAttribute(detailsFor, aria_detailsAttr);
3552 }
3553
3554 void AccessibilityObject::ariaErrorMessageElements(AccessibilityChildrenVector& ariaErrorMessage) const
3555 {
3556     ariaElementsFromAttribute(ariaErrorMessage, aria_errormessageAttr);
3557 }
3558
3559 void AccessibilityObject::ariaErrorMessageReferencingElements(AccessibilityChildrenVector& errorMessageFor) const
3560 {
3561     ariaElementsReferencedByAttribute(errorMessageFor, aria_errormessageAttr);
3562 }
3563
3564 void AccessibilityObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
3565 {
3566     ariaElementsFromAttribute(flowTo, aria_flowtoAttr);
3567 }
3568
3569 void AccessibilityObject::ariaFlowToReferencingElements(AccessibilityChildrenVector& flowFrom) const
3570 {
3571     ariaElementsReferencedByAttribute(flowFrom, aria_flowtoAttr);
3572 }
3573
3574 void AccessibilityObject::ariaLabelledByElements(AccessibilityChildrenVector& ariaLabelledBy) const
3575 {
3576     ariaElementsFromAttribute(ariaLabelledBy, aria_labelledbyAttr);
3577     if (!ariaLabelledBy.size())
3578         ariaElementsFromAttribute(ariaLabelledBy, aria_labeledbyAttr);
3579 }
3580
3581 void AccessibilityObject::ariaLabelledByReferencingElements(AccessibilityChildrenVector& labels) const
3582 {
3583     ariaElementsReferencedByAttribute(labels, aria_labelledbyAttr);
3584     if (!labels.size())
3585         ariaElementsReferencedByAttribute(labels, aria_labeledbyAttr);
3586 }
3587
3588 void AccessibilityObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
3589 {
3590     ariaElementsFromAttribute(axObjects, aria_ownsAttr);
3591 }
3592
3593 void AccessibilityObject::ariaOwnsReferencingElements(AccessibilityChildrenVector& owners) const
3594 {
3595     ariaElementsReferencedByAttribute(owners, aria_ownsAttr);
3596 }
3597
3598 void AccessibilityObject::setIsIgnoredFromParentDataForChild(AXCoreObject* child)
3599 {
3600     if (!child)
3601         return;
3602     
3603     if (child->parentObject() != this) {
3604         child->clearIsIgnoredFromParentData();
3605         return;
3606     }
3607     
3608     AccessibilityIsIgnoredFromParentData result = AccessibilityIsIgnoredFromParentData(this);
3609     if (!m_isIgnoredFromParentData.isNull()) {
3610         result.isAXHidden = m_isIgnoredFromParentData.isAXHidden || equalLettersIgnoringASCIICase(child->getAttribute(aria_hiddenAttr), "true");
3611         result.isPresentationalChildOfAriaRole = m_isIgnoredFromParentData.isPresentationalChildOfAriaRole || ariaRoleHasPresentationalChildren();
3612         result.isDescendantOfBarrenParent = m_isIgnoredFromParentData.isDescendantOfBarrenParent || !canHaveChildren();
3613     } else {
3614         result.isAXHidden = child->isAXHidden();
3615         result.isPresentationalChildOfAriaRole = child->isPresentationalChildOfAriaRole();
3616         result.isDescendantOfBarrenParent = child->isDescendantOfBarrenParent();
3617     }
3618     
3619     child->setIsIgnoredFromParentData(result);
3620 }
3621
3622 namespace Accessibility {
3623
3624 #if !PLATFORM(MAC)
3625 // FIXME: implement in other platforms.
3626 PlatformRoleMap createPlatformRoleMap() { return PlatformRoleMap(); }
3627 #endif
3628
3629 String roleToPlatformString(AccessibilityRole role)
3630 {
3631     static NeverDestroyed<PlatformRoleMap> roleMap = createPlatformRoleMap();
3632     return roleMap->get(static_cast<unsigned>(role));
3633 }
3634
3635 static bool isAccessibilityObjectSearchMatchAtIndex(AXCoreObject* axObject, AccessibilitySearchCriteria const& criteria, size_t index)
3636 {
3637     switch (criteria.searchKeys[index]) {
3638     case AccessibilitySearchKey::AnyType:
3639         // The AccessibilitySearchKey::AnyType matches any non-null AccessibilityObject.
3640         return true;
3641     case AccessibilitySearchKey::Article:
3642         return axObject->roleValue() == AccessibilityRole::DocumentArticle;
3643     case AccessibilitySearchKey::BlockquoteSameLevel:
3644         return criteria.startObject
3645             && axObject->isBlockquote()
3646             && axObject->blockquoteLevel() == criteria.startObject->blockquoteLevel();
3647     case AccessibilitySearchKey::Blockquote:
3648         return axObject->isBlockquote();
3649     case AccessibilitySearchKey::BoldFont:
3650         return axObject->hasBoldFont();
3651     case AccessibilitySearchKey::Button:
3652         return axObject->isButton();
3653     case AccessibilitySearchKey::CheckBox:
3654         return axObject->isCheckbox();
3655     case AccessibilitySearchKey::Control:
3656         return axObject->isControl();
3657     case AccessibilitySearchKey::DifferentType:
3658         return criteria.startObject
3659             && axObject->roleValue() != criteria.startObject->roleValue();
3660     case AccessibilitySearchKey::FontChange:
3661         return criteria.startObject
3662             && !axObject->hasSameFont(criteria.startObject->renderer());
3663     case AccessibilitySearchKey::FontColorChange:
3664         return criteria.startObject
3665             && !axObject->hasSameFontColor(criteria.startObject->renderer());
3666     case AccessibilitySearchKey::Frame:
3667         return axObject->isWebArea();
3668     case AccessibilitySearchKey::Graphic:
3669         return axObject->isImage();
3670     case AccessibilitySearchKey::HeadingLevel1:
3671         return axObject->headingLevel() == 1;
3672     case AccessibilitySearchKey::HeadingLevel2:
3673         return axObject->headingLevel() == 2;
3674     case AccessibilitySearchKey::HeadingLevel3:
3675         return axObject->headingLevel() == 3;
3676     case AccessibilitySearchKey::HeadingLevel4:
3677         return axObject->headingLevel() == 4;
3678     case AccessibilitySearchKey::HeadingLevel5:
3679         return axObject->headingLevel() == 5;
3680     case AccessibilitySearchKey::HeadingLevel6:
3681         return axObject->headingLevel() == 6;
3682     case AccessibilitySearchKey::HeadingSameLevel:
3683         return criteria.startObject
3684             && axObject->isHeading()
3685             && axObject->headingLevel() == criteria.startObject->headingLevel();
3686     case AccessibilitySearchKey::Heading:
3687         return axObject->isHeading();
3688     case AccessibilitySearchKey::Highlighted:
3689         return axObject->hasHighlighting();
3690     case AccessibilitySearchKey::KeyboardFocusable:
3691         return axObject->isKeyboardFocusable();
3692     case AccessibilitySearchKey::ItalicFont:
3693         return axObject->hasItalicFont();
3694     case AccessibilitySearchKey::Landmark:
3695         return axObject->isLandmark();
3696     case AccessibilitySearchKey::Link: {
3697         bool isLink = axObject->isLink();
3698 #if PLATFORM(IOS_FAMILY)
3699         if (!isLink)
3700             isLink = axObject->isDescendantOfRole(AccessibilityRole::WebCoreLink);
3701 #endif
3702         return isLink;
3703     }
3704     case AccessibilitySearchKey::List:
3705         return axObject->isList();
3706     case AccessibilitySearchKey::LiveRegion:
3707         return axObject->supportsLiveRegion();
3708     case AccessibilitySearchKey::MisspelledWord:
3709         return axObject->hasMisspelling();
3710     case AccessibilitySearchKey::Outline:
3711         return axObject->isTree();
3712     case AccessibilitySearchKey::PlainText:
3713         return axObject->hasPlainText();
3714     case AccessibilitySearchKey::RadioGroup:
3715         return axObject->isRadioGroup();
3716     case AccessibilitySearchKey::SameType:
3717         return criteria.startObject
3718             && axObject->roleValue() == criteria.startObject->roleValue();
3719     case AccessibilitySearchKey::StaticText:
3720         return axObject->isStaticText();
3721     case AccessibilitySearchKey::StyleChange:
3722         return criteria.startObject
3723             && !axObject->hasSameStyle(criteria.startObject->renderer());
3724     case AccessibilitySearchKey::TableSameLevel:
3725         return criteria.startObject
3726             && axObject->isTable() && axObject->isExposable()
3727             && axObject->tableLevel() == criteria.startObject->tableLevel();
3728     case AccessibilitySearchKey::Table:
3729         return axObject->isTable() && axObject->isExposable();
3730     case AccessibilitySearchKey::TextField:
3731         return axObject->isTextControl();
3732     case AccessibilitySearchKey::Underline:
3733         return axObject->hasUnderline();
3734     case AccessibilitySearchKey::UnvisitedLink:
3735         return axObject->isUnvisited();
3736     case AccessibilitySearchKey::VisitedLink:
3737         return axObject->isVisited();
3738     default:
3739         return false;
3740     }
3741 }
3742
3743 static bool isAccessibilityObjectSearchMatch(AXCoreObject* axObject, AccessibilitySearchCriteria const& criteria)
3744 {
3745     if (!axObject)
3746         return false;
3747
3748     size_t length = criteria.searchKeys.size();
3749     for (size_t i = 0; i < length; ++i) {
3750         if (isAccessibilityObjectSearchMatchAtIndex(axObject, criteria, i)) {
3751             if (criteria.visibleOnly && !axObject->isOnScreen())
3752                 return false;
3753             return true;
3754         }
3755     }
3756     return false;
3757 }
3758
3759 static bool isAccessibilityTextSearchMatch(AXCoreObject* axObject, AccessibilitySearchCriteria const& criteria)
3760 {
3761     if (!axObject)
3762         return false;
3763     return axObject->containsText(criteria.searchText);
3764 }
3765
3766 static bool objectMatchesSearchCriteriaWithResultLimit(AXCoreObject* object, AccessibilitySearchCriteria const& criteria, AXCoreObject::AccessibilityChildrenVector& results)
3767 {
3768     if (isAccessibilityObjectSearchMatch(object, criteria) && isAccessibilityTextSearchMatch(object, criteria)) {
3769         results.append(object);
3770
3771         // Enough results were found to stop searching.
3772         if (results.size() >= criteria.resultsLimit)
3773             return true;
3774     }
3775
3776     return false;
3777 }
3778
3779 void findMatchingObjects(AccessibilitySearchCriteria const& criteria, AXCoreObject::AccessibilityChildrenVector& results)
3780 {
3781     // This search algorithm only searches the elements before/after the starting object.
3782     // It does this by stepping up the parent chain and at each level doing a DFS.
3783
3784     // If there's no start object, it means we want to search everything.
3785     AXCoreObject* startObject = criteria.startObject;
3786     if (!startObject)
3787         startObject = criteria.anchorObject;
3788
3789     bool isForward = criteria.searchDirection == AccessibilitySearchDirection::Next;
3790
3791     // The first iteration of the outer loop will examine the children of the start object for matches. However, when
3792     // iterating backwards, the start object children should not be considered, so the loop is skipped ahead. We make an
3793     // exception when no start object was specified because we want to search everything regardless of search direction.
3794     AXCoreObject* previousObject = nullptr;
3795     if (!isForward && startObject != criteria.anchorObject) {
3796         previousObject = startObject;
3797         startObject = startObject->parentObjectUnignored();
3798     }
3799
3800     // The outer loop steps up the parent chain each time (unignored is important here because otherwise elements would be searched twice)
3801     for (auto* stopSearchElement = criteria.anchorObject->parentObjectUnignored(); startObject && startObject != stopSearchElement; startObject = startObject->parentObjectUnignored()) {
3802         // Only append the children after/before the previous element, so that the search does not check elements that are
3803         // already behind/ahead of start element.
3804         AXCoreObject::AccessibilityChildrenVector searchStack;
3805         if (!criteria.immediateDescendantsOnly || startObject == criteria.anchorObject)
3806             appendChildrenToArray(startObject, isForward, previousObject, searchStack);
3807
3808         // This now does a DFS at the current level of the parent.
3809         while (!searchStack.isEmpty()) {
3810             AXCoreObject* searchObject = searchStack.last().get();
3811             searchStack.removeLast();
3812
3813             if (objectMatchesSearchCriteriaWithResultLimit(searchObject, criteria, results))
3814                 break;
3815
3816             if (!criteria.immediateDescendantsOnly)
3817                 appendChildrenToArray(searchObject, isForward, 0, searchStack);
3818         }
3819
3820         if (results.size() >= criteria.resultsLimit)
3821             break;
3822
3823         // When moving backwards, the parent object needs to be checked, because technically it's "before" the starting element.
3824         if (!isForward && startObject != criteria.anchorObject && objectMatchesSearchCriteriaWithResultLimit(startObject, criteria, results))
3825             break;
3826
3827         previousObject = startObject;
3828     }
3829 }
3830
3831 } // namespace Accessibility
3832
3833 } // namespace WebCore