1 /*
2  * Copyright (C) 2008, 2009, 2011 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 Computer, 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 "third_party/blink/renderer/modules/accessibility/ax_object.h"
30 
31 #include <algorithm>
32 #include "base/strings/string_util.h"
33 #include "third_party/blink/public/common/input/web_menu_source_type.h"
34 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
35 #include "third_party/blink/renderer/core/aom/accessible_node.h"
36 #include "third_party/blink/renderer/core/aom/accessible_node_list.h"
37 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
38 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
39 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
40 #include "third_party/blink/renderer/core/dom/focus_params.h"
41 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
42 #include "third_party/blink/renderer/core/frame/local_frame.h"
43 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
44 #include "third_party/blink/renderer/core/frame/settings.h"
45 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
46 #include "third_party/blink/renderer/core/html/custom/element_internals.h"
47 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
48 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
49 #include "third_party/blink/renderer/core/html/html_dialog_element.h"
50 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
51 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
52 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
53 #include "third_party/blink/renderer/core/input/event_handler.h"
54 #include "third_party/blink/renderer/core/input_type_names.h"
55 #include "third_party/blink/renderer/core/layout/layout_box.h"
56 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
57 #include "third_party/blink/renderer/core/layout/layout_view.h"
58 #include "third_party/blink/renderer/core/page/chrome_client.h"
59 #include "third_party/blink/renderer/core/page/focus_controller.h"
60 #include "third_party/blink/renderer/core/page/page.h"
61 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
62 #include "third_party/blink/renderer/core/svg/svg_element.h"
63 #include "third_party/blink/renderer/core/svg/svg_g_element.h"
64 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
65 #include "third_party/blink/renderer/modules/accessibility/ax_menu_list.h"
66 #include "third_party/blink/renderer/modules/accessibility/ax_menu_list_option.h"
67 #include "third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.h"
68 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
69 #include "third_party/blink/renderer/modules/accessibility/ax_range.h"
70 #include "third_party/blink/renderer/modules/accessibility/ax_selection.h"
71 #include "third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.h"
72 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
73 #include "third_party/blink/renderer/platform/language.h"
74 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
75 #include "third_party/blink/renderer/platform/text/platform_locale.h"
76 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
77 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
78 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
79 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
80 #include "third_party/skia/include/core/SkMatrix44.h"
81 #include "ui/accessibility/ax_node_data.h"
82 #include "ui/accessibility/ax_role_properties.h"
83 
84 namespace blink {
85 
86 namespace {
87 
88 struct RoleHashTraits : HashTraits<ax::mojom::blink::Role> {
89   static const bool kEmptyValueIsZero = true;
EmptyValueblink::__anond856eca50111::RoleHashTraits90   static ax::mojom::blink::Role EmptyValue() {
91     return ax::mojom::blink::Role::kUnknown;
92   }
93 };
94 
95 using ARIARoleMap = HashMap<String,
96                             ax::mojom::blink::Role,
97                             CaseFoldingHash,
98                             HashTraits<String>,
99                             RoleHashTraits>;
100 
101 struct RoleEntry {
102   const char* aria_role;
103   ax::mojom::blink::Role webcore_role;
104 };
105 
106 // Mapping of ARIA role name to internal role name.
107 const RoleEntry kRoles[] = {
108     {"alert", ax::mojom::blink::Role::kAlert},
109     {"alertdialog", ax::mojom::blink::Role::kAlertDialog},
110     {"application", ax::mojom::blink::Role::kApplication},
111     {"article", ax::mojom::blink::Role::kArticle},
112     {"banner", ax::mojom::blink::Role::kBanner},
113     {"blockquote", ax::mojom::blink::Role::kBlockquote},
114     {"button", ax::mojom::blink::Role::kButton},
115     {"caption", ax::mojom::blink::Role::kCaption},
116     {"cell", ax::mojom::blink::Role::kCell},
117     {"code", ax::mojom::blink::Role::kCode},
118     {"checkbox", ax::mojom::blink::Role::kCheckBox},
119     {"columnheader", ax::mojom::blink::Role::kColumnHeader},
120     {"combobox", ax::mojom::blink::Role::kComboBoxGrouping},
121     {"comment", ax::mojom::blink::Role::kComment},
122     {"complementary", ax::mojom::blink::Role::kComplementary},
123     {"contentinfo", ax::mojom::blink::Role::kContentInfo},
124     {"definition", ax::mojom::blink::Role::kDefinition},
125     {"deletion", ax::mojom::blink::Role::kContentDeletion},
126     {"dialog", ax::mojom::blink::Role::kDialog},
127     {"directory", ax::mojom::blink::Role::kDirectory},
128     // -------------------------------------------------
129     // DPub Roles:
130     // www.w3.org/TR/dpub-aam-1.0/#mapping_role_table
131     {"doc-abstract", ax::mojom::blink::Role::kDocAbstract},
132     {"doc-acknowledgments", ax::mojom::blink::Role::kDocAcknowledgments},
133     {"doc-afterword", ax::mojom::blink::Role::kDocAfterword},
134     {"doc-appendix", ax::mojom::blink::Role::kDocAppendix},
135     {"doc-backlink", ax::mojom::blink::Role::kDocBackLink},
136     {"doc-biblioentry", ax::mojom::blink::Role::kDocBiblioEntry},
137     {"doc-bibliography", ax::mojom::blink::Role::kDocBibliography},
138     {"doc-biblioref", ax::mojom::blink::Role::kDocBiblioRef},
139     {"doc-chapter", ax::mojom::blink::Role::kDocChapter},
140     {"doc-colophon", ax::mojom::blink::Role::kDocColophon},
141     {"doc-conclusion", ax::mojom::blink::Role::kDocConclusion},
142     {"doc-cover", ax::mojom::blink::Role::kDocCover},
143     {"doc-credit", ax::mojom::blink::Role::kDocCredit},
144     {"doc-credits", ax::mojom::blink::Role::kDocCredits},
145     {"doc-dedication", ax::mojom::blink::Role::kDocDedication},
146     {"doc-endnote", ax::mojom::blink::Role::kDocEndnote},
147     {"doc-endnotes", ax::mojom::blink::Role::kDocEndnotes},
148     {"doc-epigraph", ax::mojom::blink::Role::kDocEpigraph},
149     {"doc-epilogue", ax::mojom::blink::Role::kDocEpilogue},
150     {"doc-errata", ax::mojom::blink::Role::kDocErrata},
151     {"doc-example", ax::mojom::blink::Role::kDocExample},
152     {"doc-footnote", ax::mojom::blink::Role::kDocFootnote},
153     {"doc-foreword", ax::mojom::blink::Role::kDocForeword},
154     {"doc-glossary", ax::mojom::blink::Role::kDocGlossary},
155     {"doc-glossref", ax::mojom::blink::Role::kDocGlossRef},
156     {"doc-index", ax::mojom::blink::Role::kDocIndex},
157     {"doc-introduction", ax::mojom::blink::Role::kDocIntroduction},
158     {"doc-noteref", ax::mojom::blink::Role::kDocNoteRef},
159     {"doc-notice", ax::mojom::blink::Role::kDocNotice},
160     {"doc-pagebreak", ax::mojom::blink::Role::kDocPageBreak},
161     {"doc-pagefooter", ax::mojom::blink::Role::kDocPageFooter},
162     {"doc-pageheader", ax::mojom::blink::Role::kDocPageHeader},
163     {"doc-pagelist", ax::mojom::blink::Role::kDocPageList},
164     {"doc-part", ax::mojom::blink::Role::kDocPart},
165     {"doc-preface", ax::mojom::blink::Role::kDocPreface},
166     {"doc-prologue", ax::mojom::blink::Role::kDocPrologue},
167     {"doc-pullquote", ax::mojom::blink::Role::kDocPullquote},
168     {"doc-qna", ax::mojom::blink::Role::kDocQna},
169     {"doc-subtitle", ax::mojom::blink::Role::kDocSubtitle},
170     {"doc-tip", ax::mojom::blink::Role::kDocTip},
171     {"doc-toc", ax::mojom::blink::Role::kDocToc},
172     // End DPub roles.
173     // -------------------------------------------------
174     {"document", ax::mojom::blink::Role::kDocument},
175     {"emphasis", ax::mojom::blink::Role::kEmphasis},
176     {"feed", ax::mojom::blink::Role::kFeed},
177     {"figure", ax::mojom::blink::Role::kFigure},
178     {"form", ax::mojom::blink::Role::kForm},
179     {"generic", ax::mojom::blink::Role::kGenericContainer},
180     // -------------------------------------------------
181     // ARIA Graphics module roles:
182     // https://rawgit.com/w3c/graphics-aam/master/
183     {"graphics-document", ax::mojom::blink::Role::kGraphicsDocument},
184     {"graphics-object", ax::mojom::blink::Role::kGraphicsObject},
185     {"graphics-symbol", ax::mojom::blink::Role::kGraphicsSymbol},
186     // End ARIA Graphics module roles.
187     // -------------------------------------------------
188     {"grid", ax::mojom::blink::Role::kGrid},
189     {"gridcell", ax::mojom::blink::Role::kCell},
190     {"group", ax::mojom::blink::Role::kGroup},
191     {"heading", ax::mojom::blink::Role::kHeading},
192     {"img", ax::mojom::blink::Role::kImage},
193     {"insertion", ax::mojom::blink::Role::kContentInsertion},
194     {"link", ax::mojom::blink::Role::kLink},
195     {"list", ax::mojom::blink::Role::kList},
196     {"listbox", ax::mojom::blink::Role::kListBox},
197     {"listitem", ax::mojom::blink::Role::kListItem},
198     {"log", ax::mojom::blink::Role::kLog},
199     {"main", ax::mojom::blink::Role::kMain},
200     {"marquee", ax::mojom::blink::Role::kMarquee},
201     {"math", ax::mojom::blink::Role::kMath},
202     {"menu", ax::mojom::blink::Role::kMenu},
203     {"menubar", ax::mojom::blink::Role::kMenuBar},
204     {"menuitem", ax::mojom::blink::Role::kMenuItem},
205     {"menuitemcheckbox", ax::mojom::blink::Role::kMenuItemCheckBox},
206     {"menuitemradio", ax::mojom::blink::Role::kMenuItemRadio},
207     {"mark", ax::mojom::blink::Role::kMark},
208     {"meter", ax::mojom::blink::Role::kMeter},
209     {"navigation", ax::mojom::blink::Role::kNavigation},
210     {"none", ax::mojom::blink::Role::kNone},
211     {"note", ax::mojom::blink::Role::kNote},
212     {"option", ax::mojom::blink::Role::kListBoxOption},
213     {"paragraph", ax::mojom::blink::Role::kParagraph},
214     {"presentation", ax::mojom::blink::Role::kPresentational},
215     {"progressbar", ax::mojom::blink::Role::kProgressIndicator},
216     {"radio", ax::mojom::blink::Role::kRadioButton},
217     {"radiogroup", ax::mojom::blink::Role::kRadioGroup},
218     // TODO(accessibility) region should only be mapped
219     // if name present. See http://crbug.com/840819.
220     {"region", ax::mojom::blink::Role::kRegion},
221     {"row", ax::mojom::blink::Role::kRow},
222     {"rowgroup", ax::mojom::blink::Role::kRowGroup},
223     {"rowheader", ax::mojom::blink::Role::kRowHeader},
224     {"scrollbar", ax::mojom::blink::Role::kScrollBar},
225     {"search", ax::mojom::blink::Role::kSearch},
226     {"searchbox", ax::mojom::blink::Role::kSearchBox},
227     {"separator", ax::mojom::blink::Role::kSplitter},
228     {"slider", ax::mojom::blink::Role::kSlider},
229     {"spinbutton", ax::mojom::blink::Role::kSpinButton},
230     {"status", ax::mojom::blink::Role::kStatus},
231     {"strong", ax::mojom::blink::Role::kStrong},
232     {"suggestion", ax::mojom::blink::Role::kSuggestion},
233     {"switch", ax::mojom::blink::Role::kSwitch},
234     {"tab", ax::mojom::blink::Role::kTab},
235     {"table", ax::mojom::blink::Role::kTable},
236     {"tablist", ax::mojom::blink::Role::kTabList},
237     {"tabpanel", ax::mojom::blink::Role::kTabPanel},
238     {"term", ax::mojom::blink::Role::kTerm},
239     {"text", ax::mojom::blink::Role::kStaticText},
240     {"textbox", ax::mojom::blink::Role::kTextField},
241     {"time", ax::mojom::blink::Role::kTime},
242     {"timer", ax::mojom::blink::Role::kTimer},
243     {"toolbar", ax::mojom::blink::Role::kToolbar},
244     {"tooltip", ax::mojom::blink::Role::kTooltip},
245     {"tree", ax::mojom::blink::Role::kTree},
246     {"treegrid", ax::mojom::blink::Role::kTreeGrid},
247     {"treeitem", ax::mojom::blink::Role::kTreeItem}};
248 
249 struct InternalRoleEntry {
250   ax::mojom::blink::Role webcore_role;
251   const char* internal_role_name;
252 };
253 
254 const InternalRoleEntry kInternalRoles[] = {
255     {ax::mojom::blink::Role::kNone, "None"},
256     {ax::mojom::blink::Role::kAbbr, "Abbr"},
257     {ax::mojom::blink::Role::kAlertDialog, "AlertDialog"},
258     {ax::mojom::blink::Role::kAlert, "Alert"},
259     {ax::mojom::blink::Role::kAnchor, "Anchor"},
260     {ax::mojom::blink::Role::kComment, "Comment"},
261     {ax::mojom::blink::Role::kApplication, "Application"},
262     {ax::mojom::blink::Role::kArticle, "Article"},
263     {ax::mojom::blink::Role::kAudio, "Audio"},
264     {ax::mojom::blink::Role::kBanner, "Banner"},
265     {ax::mojom::blink::Role::kBlockquote, "Blockquote"},
266     {ax::mojom::blink::Role::kButton, "Button"},
267     {ax::mojom::blink::Role::kCanvas, "Canvas"},
268     {ax::mojom::blink::Role::kCaption, "Caption"},
269     {ax::mojom::blink::Role::kCaret, "Caret"},
270     {ax::mojom::blink::Role::kCell, "Cell"},
271     {ax::mojom::blink::Role::kCheckBox, "CheckBox"},
272     {ax::mojom::blink::Role::kClient, "Client"},
273     {ax::mojom::blink::Role::kCode, "Code"},
274     {ax::mojom::blink::Role::kColorWell, "ColorWell"},
275     {ax::mojom::blink::Role::kColumnHeader, "ColumnHeader"},
276     {ax::mojom::blink::Role::kColumn, "Column"},
277     {ax::mojom::blink::Role::kComboBoxGrouping, "ComboBox"},
278     {ax::mojom::blink::Role::kComboBoxMenuButton, "ComboBox"},
279     {ax::mojom::blink::Role::kComplementary, "Complementary"},
280     {ax::mojom::blink::Role::kContentDeletion, "ContentDeletion"},
281     {ax::mojom::blink::Role::kContentInsertion, "ContentInsertion"},
282     {ax::mojom::blink::Role::kContentInfo, "ContentInfo"},
283     {ax::mojom::blink::Role::kDate, "Date"},
284     {ax::mojom::blink::Role::kDateTime, "DateTime"},
285     {ax::mojom::blink::Role::kDefinition, "Definition"},
286     {ax::mojom::blink::Role::kDescriptionListDetail, "DescriptionListDetail"},
287     {ax::mojom::blink::Role::kDescriptionList, "DescriptionList"},
288     {ax::mojom::blink::Role::kDescriptionListTerm, "DescriptionListTerm"},
289     {ax::mojom::blink::Role::kDesktop, "Desktop"},
290     {ax::mojom::blink::Role::kDetails, "Details"},
291     {ax::mojom::blink::Role::kDialog, "Dialog"},
292     {ax::mojom::blink::Role::kDirectory, "Directory"},
293     {ax::mojom::blink::Role::kDisclosureTriangle, "DisclosureTriangle"},
294     // --------------------------------------------------------------
295     // DPub Roles:
296     // https://www.w3.org/TR/dpub-aam-1.0/#mapping_role_table
297     {ax::mojom::blink::Role::kDocAbstract, "DocAbstract"},
298     {ax::mojom::blink::Role::kDocAcknowledgments, "DocAcknowledgments"},
299     {ax::mojom::blink::Role::kDocAfterword, "DocAfterword"},
300     {ax::mojom::blink::Role::kDocAppendix, "DocAppendix"},
301     {ax::mojom::blink::Role::kDocBackLink, "DocBackLink"},
302     {ax::mojom::blink::Role::kDocBiblioEntry, "DocBiblioentry"},
303     {ax::mojom::blink::Role::kDocBibliography, "DocBibliography"},
304     {ax::mojom::blink::Role::kDocBiblioRef, "DocBiblioref"},
305     {ax::mojom::blink::Role::kDocChapter, "DocChapter"},
306     {ax::mojom::blink::Role::kDocColophon, "DocColophon"},
307     {ax::mojom::blink::Role::kDocConclusion, "DocConclusion"},
308     {ax::mojom::blink::Role::kDocCover, "DocCover"},
309     {ax::mojom::blink::Role::kDocCredit, "DocCredit"},
310     {ax::mojom::blink::Role::kDocCredits, "DocCredits"},
311     {ax::mojom::blink::Role::kDocDedication, "DocDedication"},
312     {ax::mojom::blink::Role::kDocEndnote, "DocEndnote"},
313     {ax::mojom::blink::Role::kDocEndnotes, "DocEndnotes"},
314     {ax::mojom::blink::Role::kDocEpigraph, "DocEpigraph"},
315     {ax::mojom::blink::Role::kDocEpilogue, "DocEpilogue"},
316     {ax::mojom::blink::Role::kDocErrata, "DocErrata"},
317     {ax::mojom::blink::Role::kDocExample, "DocExample"},
318     {ax::mojom::blink::Role::kDocFootnote, "DocFootnote"},
319     {ax::mojom::blink::Role::kDocForeword, "DocForeword"},
320     {ax::mojom::blink::Role::kDocGlossary, "DocGlossary"},
321     {ax::mojom::blink::Role::kDocGlossRef, "DocGlossref"},
322     {ax::mojom::blink::Role::kDocIndex, "DocIndex"},
323     {ax::mojom::blink::Role::kDocIntroduction, "DocIntroduction"},
324     {ax::mojom::blink::Role::kDocNoteRef, "DocNoteref"},
325     {ax::mojom::blink::Role::kDocNotice, "DocNotice"},
326     {ax::mojom::blink::Role::kDocPageBreak, "DocPagebreak"},
327     {ax::mojom::blink::Role::kDocPageFooter, "DocPageFooter"},
328     {ax::mojom::blink::Role::kDocPageHeader, "DocPageHeader"},
329     {ax::mojom::blink::Role::kDocPageList, "DocPagelist"},
330     {ax::mojom::blink::Role::kDocPart, "DocPart"},
331     {ax::mojom::blink::Role::kDocPreface, "DocPreface"},
332     {ax::mojom::blink::Role::kDocPrologue, "DocPrologue"},
333     {ax::mojom::blink::Role::kDocPullquote, "DocPullquote"},
334     {ax::mojom::blink::Role::kDocQna, "DocQna"},
335     {ax::mojom::blink::Role::kDocSubtitle, "DocSubtitle"},
336     {ax::mojom::blink::Role::kDocTip, "DocTip"},
337     {ax::mojom::blink::Role::kDocToc, "DocToc"},
338     // End DPub roles.
339     // --------------------------------------------------------------
340     {ax::mojom::blink::Role::kDocument, "Document"},
341     {ax::mojom::blink::Role::kEmbeddedObject, "EmbeddedObject"},
342     {ax::mojom::blink::Role::kEmphasis, "Emphasis"},
343     {ax::mojom::blink::Role::kFeed, "feed"},
344     {ax::mojom::blink::Role::kFigcaption, "Figcaption"},
345     {ax::mojom::blink::Role::kFigure, "Figure"},
346     {ax::mojom::blink::Role::kFooter, "Footer"},
347     {ax::mojom::blink::Role::kFooterAsNonLandmark, "FooterAsNonLandmark"},
348     {ax::mojom::blink::Role::kForm, "Form"},
349     {ax::mojom::blink::Role::kGenericContainer, "GenericContainer"},
350     // --------------------------------------------------------------
351     // ARIA Graphics module roles:
352     // https://rawgit.com/w3c/graphics-aam/master/#mapping_role_table
353     {ax::mojom::blink::Role::kGraphicsDocument, "GraphicsDocument"},
354     {ax::mojom::blink::Role::kGraphicsObject, "GraphicsObject"},
355     {ax::mojom::blink::Role::kGraphicsSymbol, "GraphicsSymbol"},
356     // End ARIA Graphics module roles.
357     // --------------------------------------------------------------
358     {ax::mojom::blink::Role::kGrid, "Grid"},
359     {ax::mojom::blink::Role::kGroup, "Group"},
360     {ax::mojom::blink::Role::kHeader, "Header"},
361     {ax::mojom::blink::Role::kHeaderAsNonLandmark, "HeaderAsNonLandmark"},
362     {ax::mojom::blink::Role::kHeading, "Heading"},
363     {ax::mojom::blink::Role::kIframePresentational, "IframePresentational"},
364     {ax::mojom::blink::Role::kIframe, "Iframe"},
365     {ax::mojom::blink::Role::kIgnored, "Ignored"},
366     {ax::mojom::blink::Role::kImageMap, "ImageMap"},
367     {ax::mojom::blink::Role::kImage, "Image"},
368     {ax::mojom::blink::Role::kImeCandidate, "ImeCandidate"},
369     {ax::mojom::blink::Role::kInlineTextBox, "InlineTextBox"},
370     {ax::mojom::blink::Role::kInputTime, "InputTime"},
371     {ax::mojom::blink::Role::kKeyboard, "Keyboard"},
372     {ax::mojom::blink::Role::kLabelText, "Label"},
373     {ax::mojom::blink::Role::kLayoutTable, "LayoutTable"},
374     {ax::mojom::blink::Role::kLayoutTableCell, "LayoutCellTable"},
375     {ax::mojom::blink::Role::kLayoutTableRow, "LayoutRowTable"},
376     {ax::mojom::blink::Role::kLegend, "Legend"},
377     {ax::mojom::blink::Role::kLink, "Link"},
378     {ax::mojom::blink::Role::kLineBreak, "LineBreak"},
379     {ax::mojom::blink::Role::kListBox, "ListBox"},
380     {ax::mojom::blink::Role::kListBoxOption, "ListBoxOption"},
381     {ax::mojom::blink::Role::kListGrid, "ListGrid"},
382     {ax::mojom::blink::Role::kListItem, "ListItem"},
383     {ax::mojom::blink::Role::kListMarker, "ListMarker"},
384     {ax::mojom::blink::Role::kList, "List"},
385     {ax::mojom::blink::Role::kLog, "Log"},
386     {ax::mojom::blink::Role::kMain, "Main"},
387     {ax::mojom::blink::Role::kMark, "Mark"},
388     {ax::mojom::blink::Role::kMarquee, "Marquee"},
389     {ax::mojom::blink::Role::kMath, "Math"},
390     {ax::mojom::blink::Role::kMenuBar, "MenuBar"},
391     {ax::mojom::blink::Role::kMenuItem, "MenuItem"},
392     {ax::mojom::blink::Role::kMenuItemCheckBox, "MenuItemCheckBox"},
393     {ax::mojom::blink::Role::kMenuItemRadio, "MenuItemRadio"},
394     {ax::mojom::blink::Role::kMenuListOption, "MenuListOption"},
395     {ax::mojom::blink::Role::kMenuListPopup, "MenuListPopup"},
396     {ax::mojom::blink::Role::kMenu, "Menu"},
397     {ax::mojom::blink::Role::kMeter, "Meter"},
398     {ax::mojom::blink::Role::kNavigation, "Navigation"},
399     {ax::mojom::blink::Role::kNote, "Note"},
400     {ax::mojom::blink::Role::kPane, "Pane"},
401     {ax::mojom::blink::Role::kParagraph, "Paragraph"},
402     {ax::mojom::blink::Role::kPdfActionableHighlight, "PdfActionableHighlight"},
403     {ax::mojom::blink::Role::kPluginObject, "PluginObject"},
404     {ax::mojom::blink::Role::kPopUpButton, "PopUpButton"},
405     {ax::mojom::blink::Role::kPortal, "Portal"},
406     {ax::mojom::blink::Role::kPre, "Pre"},
407     {ax::mojom::blink::Role::kPresentational, "Presentational"},
408     {ax::mojom::blink::Role::kProgressIndicator, "ProgressIndicator"},
409     {ax::mojom::blink::Role::kRadioButton, "RadioButton"},
410     {ax::mojom::blink::Role::kRadioGroup, "RadioGroup"},
411     {ax::mojom::blink::Role::kRegion, "Region"},
412     {ax::mojom::blink::Role::kRootWebArea, "WebArea"},
413     {ax::mojom::blink::Role::kRow, "Row"},
414     {ax::mojom::blink::Role::kRowGroup, "RowGroup"},
415     {ax::mojom::blink::Role::kRowHeader, "RowHeader"},
416     {ax::mojom::blink::Role::kRuby, "Ruby"},
417     {ax::mojom::blink::Role::kRubyAnnotation, "RubyAnnotation"},
418     {ax::mojom::blink::Role::kSection, "Section"},
419     {ax::mojom::blink::Role::kSvgRoot, "SVGRoot"},
420     {ax::mojom::blink::Role::kScrollBar, "ScrollBar"},
421     {ax::mojom::blink::Role::kScrollView, "ScrollView"},
422     {ax::mojom::blink::Role::kSearch, "Search"},
423     {ax::mojom::blink::Role::kSearchBox, "SearchBox"},
424     {ax::mojom::blink::Role::kSlider, "Slider"},
425     {ax::mojom::blink::Role::kSliderThumb, "SliderThumb"},
426     {ax::mojom::blink::Role::kSpinButton, "SpinButton"},
427     {ax::mojom::blink::Role::kSplitter, "Splitter"},
428     {ax::mojom::blink::Role::kStaticText, "StaticText"},
429     {ax::mojom::blink::Role::kStatus, "Status"},
430     {ax::mojom::blink::Role::kStrong, "Strong"},
431     {ax::mojom::blink::Role::kSuggestion, "Suggestion"},
432     {ax::mojom::blink::Role::kSwitch, "Switch"},
433     {ax::mojom::blink::Role::kTab, "Tab"},
434     {ax::mojom::blink::Role::kTabList, "TabList"},
435     {ax::mojom::blink::Role::kTabPanel, "TabPanel"},
436     {ax::mojom::blink::Role::kTable, "Table"},
437     {ax::mojom::blink::Role::kTableHeaderContainer, "TableHeaderContainer"},
438     {ax::mojom::blink::Role::kTerm, "Term"},
439     {ax::mojom::blink::Role::kTextField, "TextField"},
440     {ax::mojom::blink::Role::kTextFieldWithComboBox, "ComboBox"},
441     {ax::mojom::blink::Role::kTime, "Time"},
442     {ax::mojom::blink::Role::kTimer, "Timer"},
443     {ax::mojom::blink::Role::kTitleBar, "TitleBar"},
444     {ax::mojom::blink::Role::kToggleButton, "ToggleButton"},
445     {ax::mojom::blink::Role::kToolbar, "Toolbar"},
446     {ax::mojom::blink::Role::kTreeGrid, "TreeGrid"},
447     {ax::mojom::blink::Role::kTreeItem, "TreeItem"},
448     {ax::mojom::blink::Role::kTree, "Tree"},
449     {ax::mojom::blink::Role::kTooltip, "UserInterfaceTooltip"},
450     {ax::mojom::blink::Role::kUnknown, "Unknown"},
451     {ax::mojom::blink::Role::kVideo, "Video"},
452     {ax::mojom::blink::Role::kWebArea, "WebArea"},
453     {ax::mojom::blink::Role::kWebView, "WebView"},
454     {ax::mojom::blink::Role::kWindow, "Window"}};
455 
456 static_assert(base::size(kInternalRoles) ==
457                   static_cast<size_t>(ax::mojom::blink::Role::kMaxValue) + 1,
458               "Not all internal roles have an entry in internalRoles array");
459 
460 // Roles which we need to map in the other direction
461 const RoleEntry kReverseRoles[] = {
462     {"banner", ax::mojom::blink::Role::kHeader},
463     {"button", ax::mojom::blink::Role::kToggleButton},
464     {"combobox", ax::mojom::blink::Role::kPopUpButton},
465     {"contentinfo", ax::mojom::blink::Role::kFooter},
466     {"menuitem", ax::mojom::blink::Role::kMenuListOption},
467     {"progressbar", ax::mojom::blink::Role::kMeter},
468     {"region", ax::mojom::blink::Role::kSection},
469     {"textbox", ax::mojom::blink::Role::kTextField},
470     {"combobox", ax::mojom::blink::Role::kComboBoxMenuButton},
471     {"combobox", ax::mojom::blink::Role::kTextFieldWithComboBox}};
472 
CreateARIARoleMap()473 static ARIARoleMap* CreateARIARoleMap() {
474   ARIARoleMap* role_map = new ARIARoleMap;
475 
476   for (size_t i = 0; i < base::size(kRoles); ++i)
477     role_map->Set(String(kRoles[i].aria_role), kRoles[i].webcore_role);
478 
479   return role_map;
480 }
481 
CreateRoleNameVector()482 static Vector<AtomicString>* CreateRoleNameVector() {
483   Vector<AtomicString>* role_name_vector =
484       new Vector<AtomicString>(base::size(kInternalRoles));
485   for (wtf_size_t i = 0; i < base::size(kInternalRoles); i++)
486     (*role_name_vector)[i] = g_null_atom;
487 
488   for (wtf_size_t i = 0; i < base::size(kRoles); ++i) {
489     (*role_name_vector)[static_cast<wtf_size_t>(kRoles[i].webcore_role)] =
490         AtomicString(kRoles[i].aria_role);
491   }
492 
493   for (wtf_size_t i = 0; i < base::size(kReverseRoles); ++i) {
494     (*role_name_vector)[static_cast<wtf_size_t>(
495         kReverseRoles[i].webcore_role)] =
496         AtomicString(kReverseRoles[i].aria_role);
497   }
498 
499   return role_name_vector;
500 }
501 
CreateInternalRoleNameVector()502 static Vector<AtomicString>* CreateInternalRoleNameVector() {
503   Vector<AtomicString>* internal_role_name_vector =
504       new Vector<AtomicString>(base::size(kInternalRoles));
505   for (wtf_size_t i = 0; i < base::size(kInternalRoles); i++) {
506     (*internal_role_name_vector)[static_cast<wtf_size_t>(
507         kInternalRoles[i].webcore_role)] =
508         AtomicString(kInternalRoles[i].internal_role_name);
509   }
510 
511   return internal_role_name_vector;
512 }
513 
GetActiveDialogElement(Node * node)514 HTMLDialogElement* GetActiveDialogElement(Node* node) {
515   return node->GetDocument().ActiveModalDialog();
516 }
517 
518 // TODO(dmazzoni): replace this with a call to RoleName().
GetEquivalentAriaRoleString(const ax::mojom::blink::Role role)519 std::string GetEquivalentAriaRoleString(const ax::mojom::blink::Role role) {
520   switch (role) {
521     case ax::mojom::blink::Role::kArticle:
522       return "article";
523     case ax::mojom::blink::Role::kBanner:
524       return "banner";
525     case ax::mojom::blink::Role::kButton:
526       return "button";
527     case ax::mojom::blink::Role::kComplementary:
528       return "complementary";
529     case ax::mojom::blink::Role::kFigure:
530       return "figure";
531     case ax::mojom::blink::Role::kFooter:
532       return "contentinfo";
533     case ax::mojom::blink::Role::kHeader:
534       return "banner";
535     case ax::mojom::blink::Role::kHeading:
536       return "heading";
537     case ax::mojom::blink::Role::kImage:
538       return "img";
539     case ax::mojom::blink::Role::kMain:
540       return "main";
541     case ax::mojom::blink::Role::kNavigation:
542       return "navigation";
543     case ax::mojom::blink::Role::kRadioButton:
544       return "radio";
545     case ax::mojom::blink::Role::kRegion:
546       return "region";
547     case ax::mojom::blink::Role::kSection:
548       // A <section> element uses the 'region' ARIA role mapping.
549       return "region";
550     case ax::mojom::blink::Role::kSlider:
551       return "slider";
552     case ax::mojom::blink::Role::kTime:
553       return "time";
554     default:
555       break;
556   }
557 
558   return std::string();
559 }
560 
561 }  // namespace
562 
563 unsigned AXObject::number_of_live_ax_objects_ = 0;
564 
AXObject(AXObjectCacheImpl & ax_object_cache)565 AXObject::AXObject(AXObjectCacheImpl& ax_object_cache)
566     : id_(0),
567       have_children_(false),
568       role_(ax::mojom::blink::Role::kUnknown),
569       aria_role_(ax::mojom::blink::Role::kUnknown),
570       last_known_is_ignored_value_(kDefaultBehavior),
571       last_known_is_ignored_but_included_in_tree_value_(kDefaultBehavior),
572       explicit_container_id_(0),
573       parent_(nullptr),
574       last_modification_count_(-1),
575       cached_is_ignored_(false),
576       cached_is_ignored_but_included_in_tree_(false),
577       cached_is_inert_or_aria_hidden_(false),
578       cached_is_descendant_of_leaf_node_(false),
579       cached_is_descendant_of_disabled_node_(false),
580       cached_has_inherited_presentational_role_(false),
581       cached_is_editable_root_(false),
582       cached_live_region_root_(nullptr),
583       cached_aria_column_index_(0),
584       cached_aria_row_index_(0),
585       ax_object_cache_(&ax_object_cache) {
586   ++number_of_live_ax_objects_;
587 }
588 
~AXObject()589 AXObject::~AXObject() {
590   DCHECK(IsDetached());
591   --number_of_live_ax_objects_;
592 }
593 
Init()594 void AXObject::Init() {
595   role_ = DetermineAccessibilityRole();
596 }
597 
Detach()598 void AXObject::Detach() {
599   // Clear any children and call detachFromParent on them so that
600   // no children are left with dangling pointers to their parent.
601   ClearChildren();
602 
603   ax_object_cache_ = nullptr;
604 }
605 
IsDetached() const606 bool AXObject::IsDetached() const {
607   return !ax_object_cache_;
608 }
609 
SetParent(AXObject * parent)610 void AXObject::SetParent(AXObject* parent) {
611   parent_ = parent;
612 }
613 
GetAOMPropertyOrARIAAttribute(AOMStringProperty property) const614 const AtomicString& AXObject::GetAOMPropertyOrARIAAttribute(
615     AOMStringProperty property) const {
616   Element* element = this->GetElement();
617   if (!element)
618     return g_null_atom;
619 
620   return AccessibleNode::GetPropertyOrARIAAttribute(element, property);
621 }
622 
GetAOMPropertyOrARIAAttribute(AOMRelationProperty property) const623 Element* AXObject::GetAOMPropertyOrARIAAttribute(
624     AOMRelationProperty property) const {
625   Element* element = this->GetElement();
626   if (!element)
627     return nullptr;
628 
629   return AccessibleNode::GetPropertyOrARIAAttribute(element, property);
630 }
631 
HasAOMProperty(AOMRelationListProperty property,HeapVector<Member<Element>> & result) const632 bool AXObject::HasAOMProperty(AOMRelationListProperty property,
633                               HeapVector<Member<Element>>& result) const {
634   Element* element = this->GetElement();
635   if (!element)
636     return false;
637 
638   return AccessibleNode::GetProperty(element, property, result);
639 }
640 
HasAOMPropertyOrARIAAttribute(AOMRelationListProperty property,HeapVector<Member<Element>> & result) const641 bool AXObject::HasAOMPropertyOrARIAAttribute(
642     AOMRelationListProperty property,
643     HeapVector<Member<Element>>& result) const {
644   Element* element = this->GetElement();
645   if (!element)
646     return false;
647 
648   return AccessibleNode::GetPropertyOrARIAAttribute(element, property, result);
649 }
650 
HasAOMPropertyOrARIAAttribute(AOMBooleanProperty property,bool & result) const651 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMBooleanProperty property,
652                                              bool& result) const {
653   Element* element = this->GetElement();
654   if (!element)
655     return false;
656 
657   bool is_null = true;
658   result =
659       AccessibleNode::GetPropertyOrARIAAttribute(element, property, is_null);
660   return !is_null;
661 }
662 
AOMPropertyOrARIAAttributeIsTrue(AOMBooleanProperty property) const663 bool AXObject::AOMPropertyOrARIAAttributeIsTrue(
664     AOMBooleanProperty property) const {
665   bool result;
666   if (HasAOMPropertyOrARIAAttribute(property, result))
667     return result;
668   return false;
669 }
670 
AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty property) const671 bool AXObject::AOMPropertyOrARIAAttributeIsFalse(
672     AOMBooleanProperty property) const {
673   bool result;
674   if (HasAOMPropertyOrARIAAttribute(property, result))
675     return !result;
676   return false;
677 }
678 
HasAOMPropertyOrARIAAttribute(AOMUIntProperty property,uint32_t & result) const679 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMUIntProperty property,
680                                              uint32_t& result) const {
681   Element* element = this->GetElement();
682   if (!element)
683     return false;
684 
685   bool is_null = true;
686   result =
687       AccessibleNode::GetPropertyOrARIAAttribute(element, property, is_null);
688   return !is_null;
689 }
690 
HasAOMPropertyOrARIAAttribute(AOMIntProperty property,int32_t & result) const691 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMIntProperty property,
692                                              int32_t& result) const {
693   Element* element = this->GetElement();
694   if (!element)
695     return false;
696 
697   bool is_null = true;
698   result =
699       AccessibleNode::GetPropertyOrARIAAttribute(element, property, is_null);
700   return !is_null;
701 }
702 
HasAOMPropertyOrARIAAttribute(AOMFloatProperty property,float & result) const703 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMFloatProperty property,
704                                              float& result) const {
705   Element* element = this->GetElement();
706   if (!element)
707     return false;
708 
709   bool is_null = true;
710   result =
711       AccessibleNode::GetPropertyOrARIAAttribute(element, property, is_null);
712   return !is_null;
713 }
714 
HasAOMPropertyOrARIAAttribute(AOMStringProperty property,AtomicString & result) const715 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMStringProperty property,
716                                              AtomicString& result) const {
717   Element* element = this->GetElement();
718   if (!element)
719     return false;
720 
721   result = AccessibleNode::GetPropertyOrARIAAttribute(element, property);
722   return !result.IsNull();
723 }
724 
GetAccessibleNode() const725 AccessibleNode* AXObject::GetAccessibleNode() const {
726   Element* element = GetElement();
727   if (!element)
728     return nullptr;
729 
730   return element->ExistingAccessibleNode();
731 }
732 
GetSparseAXAttributes(AXSparseAttributeClient & sparse_attribute_client) const733 void AXObject::GetSparseAXAttributes(
734     AXSparseAttributeClient& sparse_attribute_client) const {
735   AXSparseAttributeAOMPropertyClient property_client(*ax_object_cache_,
736                                                      sparse_attribute_client);
737   AccessibleNode* accessible_node = GetAccessibleNode();
738 
739   // Virtual nodes for AOM are still tied to the AXTree.
740   if (accessible_node && IsVirtualObject())
741     accessible_node->GetAllAOMProperties(&property_client);
742 
743   Element* element = GetElement();
744   if (!element)
745     return;
746 
747   AXSparseAttributeSetterMap& ax_sparse_attribute_setter_map =
748       GetSparseAttributeSetterMap();
749   AttributeCollection attributes = element->AttributesWithoutUpdate();
750   HashSet<QualifiedName> set_attributes;
751   for (const Attribute& attr : attributes) {
752     set_attributes.insert(attr.GetName());
753 
754     AXSparseAttributeSetter* setter =
755         ax_sparse_attribute_setter_map.at(attr.GetName());
756     if (setter)
757       setter->Run(*this, sparse_attribute_client, attr.Value());
758   }
759   if (!element->DidAttachInternals())
760     return;
761   const auto& internals_attributes =
762       element->EnsureElementInternals().GetAttributes();
763   for (const QualifiedName& attr : internals_attributes.Keys()) {
764     if (set_attributes.Contains(attr))
765       continue;
766     AXSparseAttributeSetter* setter = ax_sparse_attribute_setter_map.at(attr);
767     if (setter) {
768       setter->Run(*this, sparse_attribute_client,
769                   internals_attributes.at(attr));
770     }
771   }
772 }
773 
Serialize(ui::AXNodeData * node_data,ui::AXMode accessibility_mode)774 void AXObject::Serialize(ui::AXNodeData* node_data,
775                          ui::AXMode accessibility_mode) {
776   AccessibilityExpanded expanded = IsExpanded();
777   if (expanded) {
778     if (expanded == kExpandedCollapsed)
779       node_data->AddState(ax::mojom::blink::State::kCollapsed);
780     else if (expanded == kExpandedExpanded)
781       node_data->AddState(ax::mojom::blink::State::kExpanded);
782   }
783 
784   if (CanSetFocusAttribute())
785     node_data->AddState(ax::mojom::blink::State::kFocusable);
786 
787   if (HasPopup() != ax::mojom::blink::HasPopup::kFalse)
788     node_data->SetHasPopup(HasPopup());
789   else if (RoleValue() == ax::mojom::blink::Role::kPopUpButton)
790     node_data->SetHasPopup(ax::mojom::blink::HasPopup::kMenu);
791 
792   if (IsAutofillAvailable())
793     node_data->AddState(ax::mojom::blink::State::kAutofillAvailable);
794 
795   if (IsDefault())
796     node_data->AddState(ax::mojom::blink::State::kDefault);
797 
798   // aria-grabbed is deprecated in WAI-ARIA 1.1.
799   if (IsGrabbed() != kGrabbedStateUndefined) {
800     node_data->AddBoolAttribute(ax::mojom::blink::BoolAttribute::kGrabbed,
801                                 IsGrabbed() == kGrabbedStateTrue);
802   }
803 
804   if (IsHovered())
805     node_data->AddState(ax::mojom::blink::State::kHovered);
806 
807   if (!IsVisible())
808     node_data->AddState(ax::mojom::blink::State::kInvisible);
809 
810   if (IsLinked())
811     node_data->AddState(ax::mojom::blink::State::kLinked);
812 
813   if (IsMultiline())
814     node_data->AddState(ax::mojom::blink::State::kMultiline);
815 
816   if (IsMultiSelectable())
817     node_data->AddState(ax::mojom::blink::State::kMultiselectable);
818 
819   if (IsPasswordField())
820     node_data->AddState(ax::mojom::blink::State::kProtected);
821 
822   if (IsRequired())
823     node_data->AddState(ax::mojom::blink::State::kRequired);
824 
825   if (IsEditable())
826     node_data->AddState(ax::mojom::blink::State::kEditable);
827 
828   if (IsSelected() != blink::kSelectedStateUndefined) {
829     node_data->AddBoolAttribute(ax::mojom::blink::BoolAttribute::kSelected,
830                                 IsSelected() == blink::kSelectedStateTrue);
831     node_data->AddBoolAttribute(
832         ax::mojom::blink::BoolAttribute::kSelectedFromFocus,
833         IsSelectedFromFocus());
834   }
835 
836   if (IsNotUserSelectable()) {
837     node_data->AddBoolAttribute(
838         ax::mojom::blink::BoolAttribute::kNotUserSelectableStyle, true);
839   }
840 
841   if (IsRichlyEditable())
842     node_data->AddState(ax::mojom::blink::State::kRichlyEditable);
843 
844   if (IsVisited())
845     node_data->AddState(ax::mojom::blink::State::kVisited);
846 
847   if (Orientation() == kAccessibilityOrientationVertical)
848     node_data->AddState(ax::mojom::blink::State::kVertical);
849   else if (Orientation() == blink::kAccessibilityOrientationHorizontal)
850     node_data->AddState(ax::mojom::blink::State::kHorizontal);
851 
852   if (AccessibilityIsIgnored())
853     node_data->AddState(ax::mojom::blink::State::kIgnored);
854 
855   if (GetTextAlign() != ax::mojom::blink::TextAlign::kNone) {
856     node_data->SetTextAlign(GetTextAlign());
857   }
858 
859   if (GetTextIndent() != 0.0f) {
860     node_data->AddFloatAttribute(ax::mojom::blink::FloatAttribute::kTextIndent,
861                                  GetTextIndent());
862   }
863 
864   // If this is an HTMLFrameOwnerElement (such as an iframe), we may need
865   // to embed the ID of the child frame.
866   if (auto* html_frame_owner_element =
867           DynamicTo<HTMLFrameOwnerElement>(GetElement())) {
868     if (Frame* child_frame = html_frame_owner_element->ContentFrame()) {
869       base::Optional<base::UnguessableToken> child_token =
870           child_frame->GetEmbeddingToken();
871       if (child_token && !(IsDetached() || ChildCountIncludingIgnored())) {
872         node_data->AddStringAttribute(
873             ax::mojom::blink::StringAttribute::kChildTreeId,
874             child_token->ToString());
875       }
876     }
877   }
878 
879   if (accessibility_mode.has_mode(ui::AXMode::kScreenReader) ||
880       accessibility_mode.has_mode(ui::AXMode::kPDF)) {
881     // The DOMNodeID from Blink. Currently only populated when using
882     // the accessibility tree for PDF exporting. Warning, this is totally
883     // unrelated to the accessibility node ID, or the ID attribute for an
884     // HTML element - it's an ID used to uniquely identify nodes in Blink.
885     int dom_node_id = GetDOMNodeId();
886     if (dom_node_id) {
887       node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kDOMNodeId,
888                                  dom_node_id);
889     }
890 
891     SerializeTableAttributes(node_data);
892   }
893 
894   if (accessibility_mode.has_mode(ui::AXMode::kPDF)) {
895     // Return early. None of the following attributes are needed for PDFs.
896     return;
897   }
898 
899   if (ValueDescription().length()) {
900     TruncateAndAddStringAttribute(node_data,
901                                   ax::mojom::blink::StringAttribute::kValue,
902                                   ValueDescription().Utf8());
903   } else {
904     TruncateAndAddStringAttribute(node_data,
905                                   ax::mojom::blink::StringAttribute::kValue,
906                                   StringValue().Utf8());
907   }
908 
909   switch (Restriction()) {
910     case AXRestriction::kRestrictionReadOnly:
911       node_data->SetRestriction(ax::mojom::blink::Restriction::kReadOnly);
912       break;
913     case AXRestriction::kRestrictionDisabled:
914       node_data->SetRestriction(ax::mojom::blink::Restriction::kDisabled);
915       break;
916     case AXRestriction::kRestrictionNone:
917       if (CanSetValueAttribute())
918         node_data->AddAction(ax::mojom::blink::Action::kSetValue);
919       break;
920   }
921 
922   if (!Url().IsEmpty()) {
923     TruncateAndAddStringAttribute(node_data,
924                                   ax::mojom::blink::StringAttribute::kUrl,
925                                   Url().GetString().Utf8());
926   }
927 
928   if (accessibility_mode.has_mode(ui::AXMode::kScreenReader)) {
929     SerializeStyleAttributes(node_data);
930   }
931 
932   SerializePartialSparseAttributes(node_data);
933 
934   if (Element* element = this->GetElement()) {
935     if (const AtomicString& class_name = element->GetClassAttribute()) {
936       TruncateAndAddStringAttribute(
937           node_data, ax::mojom::blink::StringAttribute::kClassName,
938           class_name.Utf8());
939     }
940 
941     if (const AtomicString& aria_role =
942             GetAOMPropertyOrARIAAttribute(AOMStringProperty::kRole)) {
943       TruncateAndAddStringAttribute(node_data,
944                                     ax::mojom::blink::StringAttribute::kRole,
945                                     aria_role.Utf8());
946     } else {
947       std::string role_str = GetEquivalentAriaRoleString(RoleValue());
948       if (!role_str.empty()) {
949         TruncateAndAddStringAttribute(node_data,
950                                       ax::mojom::blink::StringAttribute::kRole,
951                                       GetEquivalentAriaRoleString(RoleValue()));
952       }
953     }
954 
955     if (IsEditable()) {
956       if (IsEditableRoot()) {
957         node_data->AddBoolAttribute(
958             ax::mojom::blink::BoolAttribute::kEditableRoot, true);
959       }
960 
961       if (IsNativeTextControl()) {
962         // Selection offsets are only used for plain text controls, (input of a
963         // text field type, and textarea). Rich editable areas, such as
964         // contenteditables, use AXTreeData.
965         //
966         // TODO(nektar): Remove kTextSelStart and kTextSelEnd from the renderer.
967         const auto ax_selection =
968             AXSelection::FromCurrentSelection(ToTextControl(*element));
969         int start = ax_selection.Base().IsTextPosition()
970                         ? ax_selection.Base().TextOffset()
971                         : ax_selection.Base().ChildIndex();
972         int end = ax_selection.Extent().IsTextPosition()
973                       ? ax_selection.Extent().TextOffset()
974                       : ax_selection.Extent().ChildIndex();
975         node_data->AddIntAttribute(
976             ax::mojom::blink::IntAttribute::kTextSelStart, start);
977         node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kTextSelEnd,
978                                    end);
979       }
980     }
981   }
982 }
983 
SerializeTableAttributes(ui::AXNodeData * node_data)984 void AXObject::SerializeTableAttributes(ui::AXNodeData* node_data) {
985   if (ui::IsTableLike(RoleValue())) {
986     int aria_colcount = AriaColumnCount();
987     if (aria_colcount) {
988       node_data->AddIntAttribute(
989           ax::mojom::blink::IntAttribute::kAriaColumnCount, aria_colcount);
990     }
991     int aria_rowcount = AriaRowCount();
992     if (aria_rowcount) {
993       node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kAriaRowCount,
994                                  aria_rowcount);
995     }
996   }
997 
998   if (ui::IsTableRow(RoleValue())) {
999     AXObject* header = HeaderObject();
1000     if (header && !header->IsDetached()) {
1001       // TODO(accessibility): these should be computed by ui::AXTableInfo and
1002       // removed here.
1003       node_data->AddIntAttribute(
1004           ax::mojom::blink::IntAttribute::kTableRowHeaderId,
1005           header->AXObjectID());
1006     }
1007   }
1008 
1009   if (ui::IsCellOrTableHeader(RoleValue())) {
1010     node_data->AddIntAttribute(
1011         ax::mojom::blink::IntAttribute::kTableCellColumnSpan, ColumnSpan());
1012     node_data->AddIntAttribute(
1013         ax::mojom::blink::IntAttribute::kTableCellRowSpan, RowSpan());
1014   }
1015 
1016   if (ui::IsCellOrTableHeader(RoleValue()) || ui::IsTableRow(RoleValue())) {
1017     // aria-rowindex and aria-colindex are supported on cells, headers and
1018     // rows.
1019     int aria_rowindex = AriaRowIndex();
1020     if (aria_rowindex) {
1021       node_data->AddIntAttribute(
1022           ax::mojom::blink::IntAttribute::kAriaCellRowIndex, aria_rowindex);
1023     }
1024 
1025     int aria_colindex = AriaColumnIndex();
1026     if (aria_colindex) {
1027       node_data->AddIntAttribute(
1028           ax::mojom::blink::IntAttribute::kAriaCellColumnIndex, aria_colindex);
1029     }
1030   }
1031 
1032   if (ui::IsTableHeader(RoleValue()) &&
1033       GetSortDirection() != ax::mojom::blink::SortDirection::kNone) {
1034     node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kSortDirection,
1035                                static_cast<int32_t>(GetSortDirection()));
1036   }
1037 }
1038 
SerializeStyleAttributes(ui::AXNodeData * node_data)1039 void AXObject::SerializeStyleAttributes(ui::AXNodeData* node_data) {
1040   // Text attributes.
1041   if (BackgroundColor()) {
1042     node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kBackgroundColor,
1043                                BackgroundColor());
1044   }
1045 
1046   if (GetColor()) {
1047     node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kColor,
1048                                GetColor());
1049   }
1050 
1051   AXObject* parent = ParentObjectUnignored();
1052   if (FontFamily().length()) {
1053     if (!parent || parent->FontFamily() != FontFamily()) {
1054       TruncateAndAddStringAttribute(
1055           node_data, ax::mojom::blink::StringAttribute::kFontFamily,
1056           FontFamily().Utf8());
1057     }
1058   }
1059 
1060   // Font size is in pixels.
1061   if (FontSize()) {
1062     node_data->AddFloatAttribute(ax::mojom::blink::FloatAttribute::kFontSize,
1063                                  FontSize());
1064   }
1065 
1066   if (FontWeight()) {
1067     node_data->AddFloatAttribute(ax::mojom::blink::FloatAttribute::kFontWeight,
1068                                  FontWeight());
1069   }
1070 
1071   if (RoleValue() == ax::mojom::blink::Role::kListItem &&
1072       GetListStyle() != ax::mojom::blink::ListStyle::kNone) {
1073     node_data->SetListStyle(GetListStyle());
1074   }
1075 
1076   if (GetTextDirection() != ax::mojom::blink::WritingDirection::kNone) {
1077     node_data->SetTextDirection(GetTextDirection());
1078   }
1079 
1080   if (GetTextPosition() != ax::mojom::blink::TextPosition::kNone) {
1081     node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kTextPosition,
1082                                static_cast<int32_t>(GetTextPosition()));
1083   }
1084 
1085   int32_t text_style = 0;
1086   ax::mojom::blink::TextDecorationStyle text_overline_style;
1087   ax::mojom::blink::TextDecorationStyle text_strikethrough_style;
1088   ax::mojom::blink::TextDecorationStyle text_underline_style;
1089   GetTextStyleAndTextDecorationStyle(&text_style, &text_overline_style,
1090                                      &text_strikethrough_style,
1091                                      &text_underline_style);
1092   if (text_style) {
1093     node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kTextStyle,
1094                                text_style);
1095   }
1096 
1097   if (text_overline_style != ax::mojom::blink::TextDecorationStyle::kNone) {
1098     node_data->AddIntAttribute(
1099         ax::mojom::blink::IntAttribute::kTextOverlineStyle,
1100         static_cast<int32_t>(text_overline_style));
1101   }
1102 
1103   if (text_strikethrough_style !=
1104       ax::mojom::blink::TextDecorationStyle::kNone) {
1105     node_data->AddIntAttribute(
1106         ax::mojom::blink::IntAttribute::kTextStrikethroughStyle,
1107         static_cast<int32_t>(text_strikethrough_style));
1108   }
1109 
1110   if (text_underline_style != ax::mojom::blink::TextDecorationStyle::kNone) {
1111     node_data->AddIntAttribute(
1112         ax::mojom::blink::IntAttribute::kTextUnderlineStyle,
1113         static_cast<int32_t>(text_underline_style));
1114   }
1115 }
1116 
SerializePartialSparseAttributes(ui::AXNodeData * node_data)1117 void AXObject::SerializePartialSparseAttributes(ui::AXNodeData* node_data) {
1118   if (IsVirtualObject()) {
1119     AccessibleNode* accessible_node = GetAccessibleNode();
1120     if (accessible_node) {
1121       AXNodeDataAOMPropertyClient property_client(*ax_object_cache_,
1122                                                   *node_data);
1123       accessible_node->GetAllAOMProperties(&property_client);
1124     }
1125   }
1126 
1127   Element* element = GetElement();
1128   if (!element)
1129     return;
1130 
1131   TempSetterMap& setter_map = GetTempSetterMap();
1132   AttributeCollection attributes = element->AttributesWithoutUpdate();
1133   HashSet<QualifiedName> set_attributes;
1134   for (const Attribute& attr : attributes) {
1135     set_attributes.insert(attr.GetName());
1136     AXSparseSetterFunc callback = setter_map.at(attr.GetName());
1137 
1138     if (callback)
1139       callback.Run(this, node_data, attr.Value());
1140   }
1141 
1142   if (!element->DidAttachInternals())
1143     return;
1144   const auto& internals_attributes =
1145       element->EnsureElementInternals().GetAttributes();
1146   for (const QualifiedName& attr : internals_attributes.Keys()) {
1147     if (set_attributes.Contains(attr))
1148       continue;
1149 
1150     AXSparseSetterFunc callback = setter_map.at(attr);
1151 
1152     if (callback)
1153       callback.Run(this, node_data, internals_attributes.at(attr));
1154   }
1155 }
1156 
TruncateAndAddStringAttribute(ui::AXNodeData * dst,ax::mojom::blink::StringAttribute attribute,const std::string & value,uint32_t max_len) const1157 void AXObject::TruncateAndAddStringAttribute(
1158     ui::AXNodeData* dst,
1159     ax::mojom::blink::StringAttribute attribute,
1160     const std::string& value,
1161     uint32_t max_len) const {
1162   if (value.size() > max_len) {
1163     std::string truncated;
1164     base::TruncateUTF8ToByteSize(value, max_len, &truncated);
1165     dst->AddStringAttribute(attribute, truncated);
1166   } else {
1167     dst->AddStringAttribute(attribute, value);
1168   }
1169 }
1170 
IsAXNodeObject() const1171 bool AXObject::IsAXNodeObject() const {
1172   return false;
1173 }
1174 
IsAXLayoutObject() const1175 bool AXObject::IsAXLayoutObject() const {
1176   return false;
1177 }
1178 
IsAXInlineTextBox() const1179 bool AXObject::IsAXInlineTextBox() const {
1180   return false;
1181 }
1182 
IsList() const1183 bool AXObject::IsList() const {
1184   return ui::IsList(RoleValue());
1185 }
1186 
IsAXListBox() const1187 bool AXObject::IsAXListBox() const {
1188   return false;
1189 }
1190 
IsAXListBoxOption() const1191 bool AXObject::IsAXListBoxOption() const {
1192   return false;
1193 }
1194 
IsMenuList() const1195 bool AXObject::IsMenuList() const {
1196   return false;
1197 }
1198 
IsMenuListOption() const1199 bool AXObject::IsMenuListOption() const {
1200   return false;
1201 }
1202 
IsMenuListPopup() const1203 bool AXObject::IsMenuListPopup() const {
1204   return false;
1205 }
1206 
IsMockObject() const1207 bool AXObject::IsMockObject() const {
1208   return false;
1209 }
1210 
IsProgressIndicator() const1211 bool AXObject::IsProgressIndicator() const {
1212   return false;
1213 }
1214 
IsAXRadioInput() const1215 bool AXObject::IsAXRadioInput() const {
1216   return false;
1217 }
1218 
IsSlider() const1219 bool AXObject::IsSlider() const {
1220   return false;
1221 }
1222 
IsAXSVGRoot() const1223 bool AXObject::IsAXSVGRoot() const {
1224   return false;
1225 }
1226 
IsValidationMessage() const1227 bool AXObject::IsValidationMessage() const {
1228   return false;
1229 }
1230 
IsVirtualObject() const1231 bool AXObject::IsVirtualObject() const {
1232   return false;
1233 }
1234 
RoleValue() const1235 ax::mojom::blink::Role AXObject::RoleValue() const {
1236   return role_;
1237 }
1238 
IsARIATextControl() const1239 bool AXObject::IsARIATextControl() const {
1240   return AriaRoleAttribute() == ax::mojom::blink::Role::kTextField ||
1241          AriaRoleAttribute() == ax::mojom::blink::Role::kSearchBox ||
1242          AriaRoleAttribute() == ax::mojom::blink::Role::kTextFieldWithComboBox;
1243 }
1244 
IsAnchor() const1245 bool AXObject::IsAnchor() const {
1246   return IsLink() && !IsNativeImage();
1247 }
1248 
IsButton() const1249 bool AXObject::IsButton() const {
1250   return ui::IsButton(RoleValue());
1251 }
1252 
IsCanvas() const1253 bool AXObject::IsCanvas() const {
1254   return RoleValue() == ax::mojom::blink::Role::kCanvas;
1255 }
1256 
IsCheckbox() const1257 bool AXObject::IsCheckbox() const {
1258   return RoleValue() == ax::mojom::blink::Role::kCheckBox;
1259 }
1260 
IsCheckboxOrRadio() const1261 bool AXObject::IsCheckboxOrRadio() const {
1262   return IsCheckbox() || IsRadioButton();
1263 }
1264 
IsColorWell() const1265 bool AXObject::IsColorWell() const {
1266   return RoleValue() == ax::mojom::blink::Role::kColorWell;
1267 }
1268 
IsControl() const1269 bool AXObject::IsControl() const {
1270   return ui::IsControl(RoleValue());
1271 }
1272 
IsDefault() const1273 bool AXObject::IsDefault() const {
1274   return false;
1275 }
1276 
IsFieldset() const1277 bool AXObject::IsFieldset() const {
1278   return false;
1279 }
1280 
IsHeading() const1281 bool AXObject::IsHeading() const {
1282   return ui::IsHeading(RoleValue());
1283 }
1284 
IsImage() const1285 bool AXObject::IsImage() const {
1286   // Canvas is not currently included so that it is not exposed unless there is
1287   // a label, fallback content or something to make it accessible. This decision
1288   // may be revisited at a later date.
1289   return ui::IsImage(RoleValue()) &&
1290          RoleValue() != ax::mojom::blink::Role::kCanvas;
1291 }
1292 
IsInputImage() const1293 bool AXObject::IsInputImage() const {
1294   return false;
1295 }
1296 
IsLink() const1297 bool AXObject::IsLink() const {
1298   return ui::IsLink(RoleValue());
1299 }
1300 
IsInPageLinkTarget() const1301 bool AXObject::IsInPageLinkTarget() const {
1302   return false;
1303 }
1304 
IsImageMapLink() const1305 bool AXObject::IsImageMapLink() const {
1306   return false;
1307 }
1308 
IsMenu() const1309 bool AXObject::IsMenu() const {
1310   return RoleValue() == ax::mojom::blink::Role::kMenu;
1311 }
1312 
IsCheckable() const1313 bool AXObject::IsCheckable() const {
1314   switch (RoleValue()) {
1315     case ax::mojom::blink::Role::kCheckBox:
1316     case ax::mojom::blink::Role::kMenuItemCheckBox:
1317     case ax::mojom::blink::Role::kMenuItemRadio:
1318     case ax::mojom::blink::Role::kRadioButton:
1319     case ax::mojom::blink::Role::kSwitch:
1320     case ax::mojom::blink::Role::kToggleButton:
1321       return true;
1322     case ax::mojom::blink::Role::kTreeItem:
1323     case ax::mojom::blink::Role::kListBoxOption:
1324     case ax::mojom::blink::Role::kMenuListOption:
1325       return AriaCheckedIsPresent();
1326     default:
1327       return false;
1328   }
1329 }
1330 
1331 // Why this is here instead of AXNodeObject:
1332 // Because an AXMenuListOption (<option>) can
1333 // have an ARIA role of menuitemcheckbox/menuitemradio
1334 // yet does not inherit from AXNodeObject
CheckedState() const1335 ax::mojom::blink::CheckedState AXObject::CheckedState() const {
1336   if (!IsCheckable())
1337     return ax::mojom::blink::CheckedState::kNone;
1338 
1339   // Try ARIA checked/pressed state
1340   const ax::mojom::blink::Role role = RoleValue();
1341   const auto prop = role == ax::mojom::blink::Role::kToggleButton
1342                         ? AOMStringProperty::kPressed
1343                         : AOMStringProperty::kChecked;
1344   const AtomicString& checked_attribute = GetAOMPropertyOrARIAAttribute(prop);
1345   if (checked_attribute) {
1346     if (EqualIgnoringASCIICase(checked_attribute, "mixed")) {
1347       // Only checkable role that doesn't support mixed is the switch.
1348       if (role != ax::mojom::blink::Role::kSwitch)
1349         return ax::mojom::blink::CheckedState::kMixed;
1350     }
1351 
1352     // Anything other than "false" should be treated as "true".
1353     return EqualIgnoringASCIICase(checked_attribute, "false")
1354                ? ax::mojom::blink::CheckedState::kFalse
1355                : ax::mojom::blink::CheckedState::kTrue;
1356   }
1357 
1358   // Native checked state
1359   if (role != ax::mojom::blink::Role::kToggleButton) {
1360     const Node* node = this->GetNode();
1361     if (!node)
1362       return ax::mojom::blink::CheckedState::kNone;
1363 
1364     // Expose native checkbox mixed state as accessibility mixed state. However,
1365     // do not expose native radio mixed state as accessibility mixed state.
1366     // This would confuse the JAWS screen reader, which reports a mixed radio as
1367     // both checked and partially checked, but a native mixed native radio
1368     // button sinply means no radio buttons have been checked in the group yet.
1369     if (IsNativeCheckboxInMixedState(node))
1370       return ax::mojom::blink::CheckedState::kMixed;
1371 
1372     auto* html_input_element = DynamicTo<HTMLInputElement>(node);
1373     if (html_input_element && html_input_element->ShouldAppearChecked()) {
1374       return ax::mojom::blink::CheckedState::kTrue;
1375     }
1376   }
1377 
1378   return ax::mojom::blink::CheckedState::kFalse;
1379 }
1380 
IsNativeCheckboxInMixedState(const Node * node)1381 bool AXObject::IsNativeCheckboxInMixedState(const Node* node) {
1382   const auto* input = DynamicTo<HTMLInputElement>(node);
1383   if (!input)
1384     return false;
1385 
1386   const auto inputType = input->type();
1387   if (inputType != input_type_names::kCheckbox)
1388     return false;
1389   return input->ShouldAppearIndeterminate();
1390 }
1391 
IsLandmarkRelated() const1392 bool AXObject::IsLandmarkRelated() const {
1393   switch (RoleValue()) {
1394     case ax::mojom::blink::Role::kApplication:
1395     case ax::mojom::blink::Role::kArticle:
1396     case ax::mojom::blink::Role::kBanner:
1397     case ax::mojom::blink::Role::kComplementary:
1398     case ax::mojom::blink::Role::kContentInfo:
1399     case ax::mojom::blink::Role::kDocAcknowledgments:
1400     case ax::mojom::blink::Role::kDocAfterword:
1401     case ax::mojom::blink::Role::kDocAppendix:
1402     case ax::mojom::blink::Role::kDocBibliography:
1403     case ax::mojom::blink::Role::kDocChapter:
1404     case ax::mojom::blink::Role::kDocConclusion:
1405     case ax::mojom::blink::Role::kDocCredits:
1406     case ax::mojom::blink::Role::kDocEndnotes:
1407     case ax::mojom::blink::Role::kDocEpilogue:
1408     case ax::mojom::blink::Role::kDocErrata:
1409     case ax::mojom::blink::Role::kDocForeword:
1410     case ax::mojom::blink::Role::kDocGlossary:
1411     case ax::mojom::blink::Role::kDocIntroduction:
1412     case ax::mojom::blink::Role::kDocPart:
1413     case ax::mojom::blink::Role::kDocPreface:
1414     case ax::mojom::blink::Role::kDocPrologue:
1415     case ax::mojom::blink::Role::kDocToc:
1416     case ax::mojom::blink::Role::kFooter:
1417     case ax::mojom::blink::Role::kForm:
1418     case ax::mojom::blink::Role::kHeader:
1419     case ax::mojom::blink::Role::kMain:
1420     case ax::mojom::blink::Role::kNavigation:
1421     case ax::mojom::blink::Role::kRegion:
1422     case ax::mojom::blink::Role::kSearch:
1423     case ax::mojom::blink::Role::kSection:
1424       return true;
1425     default:
1426       return false;
1427   }
1428 }
1429 
IsMenuRelated() const1430 bool AXObject::IsMenuRelated() const {
1431   return ui::IsMenuRelated(RoleValue());
1432 }
1433 
IsMeter() const1434 bool AXObject::IsMeter() const {
1435   return RoleValue() == ax::mojom::blink::Role::kMeter;
1436 }
1437 
IsNativeImage() const1438 bool AXObject::IsNativeImage() const {
1439   return false;
1440 }
1441 
IsNativeSpinButton() const1442 bool AXObject::IsNativeSpinButton() const {
1443   return false;
1444 }
1445 
IsNativeTextControl() const1446 bool AXObject::IsNativeTextControl() const {
1447   return false;
1448 }
1449 
IsNonNativeTextControl() const1450 bool AXObject::IsNonNativeTextControl() const {
1451   return false;
1452 }
1453 
IsPasswordField() const1454 bool AXObject::IsPasswordField() const {
1455   return false;
1456 }
1457 
IsPasswordFieldAndShouldHideValue() const1458 bool AXObject::IsPasswordFieldAndShouldHideValue() const {
1459   Settings* settings = GetDocument()->GetSettings();
1460   if (!settings || settings->GetAccessibilityPasswordValuesEnabled())
1461     return false;
1462 
1463   return IsPasswordField();
1464 }
1465 
IsPresentational() const1466 bool AXObject::IsPresentational() const {
1467   return ui::IsPresentational(RoleValue());
1468 }
1469 
IsTextObject() const1470 bool AXObject::IsTextObject() const {
1471   // Objects with |ax::mojom::blink::Role::kLineBreak| are HTML <br> elements
1472   // and are not backed by DOM text nodes. We can't mark them as text objects
1473   // for that reason.
1474   switch (RoleValue()) {
1475     case ax::mojom::blink::Role::kInlineTextBox:
1476     case ax::mojom::blink::Role::kStaticText:
1477       return true;
1478     default:
1479       return false;
1480   }
1481 }
1482 
IsRangeValueSupported() const1483 bool AXObject::IsRangeValueSupported() const {
1484   if (RoleValue() == ax::mojom::blink::Role::kSplitter) {
1485     // According to the ARIA spec, role="separator" acts as a splitter only
1486     // when focusable, and supports a range only in that case.
1487     return CanSetFocusAttribute();
1488   }
1489   return ui::IsRangeValueSupported(RoleValue());
1490 }
1491 
IsClickable() const1492 bool AXObject::IsClickable() const {
1493   return ui::IsClickable(RoleValue());
1494 }
1495 
AccessibilityIsIgnored() const1496 bool AXObject::AccessibilityIsIgnored() const {
1497   UpdateDistributionForFlatTreeTraversal();
1498   UpdateCachedAttributeValuesIfNeeded();
1499   return cached_is_ignored_;
1500 }
1501 
AccessibilityIsIgnoredButIncludedInTree() const1502 bool AXObject::AccessibilityIsIgnoredButIncludedInTree() const {
1503   UpdateDistributionForFlatTreeTraversal();
1504   UpdateCachedAttributeValuesIfNeeded();
1505   return cached_is_ignored_but_included_in_tree_;
1506 }
1507 
1508 // AccessibilityIsIncludedInTree should be true for all nodes that should be
1509 // included in the tree, even if they are ignored
AccessibilityIsIncludedInTree() const1510 bool AXObject::AccessibilityIsIncludedInTree() const {
1511   return !AccessibilityIsIgnored() || AccessibilityIsIgnoredButIncludedInTree();
1512 }
1513 
UpdateCachedAttributeValuesIfNeeded() const1514 void AXObject::UpdateCachedAttributeValuesIfNeeded() const {
1515   if (IsDetached())
1516     return;
1517 
1518   AXObjectCacheImpl& cache = AXObjectCache();
1519 
1520   if (cache.ModificationCount() == last_modification_count_)
1521     return;
1522 
1523 #if DCHECK_IS_ON()  // Required in order to get Lifecycle().ToString()
1524   DCHECK(!GetDocument() || GetDocument()->Lifecycle().GetState() >=
1525                                DocumentLifecycle::kAfterPerformLayout)
1526       << "Unclean document at lifecycle "
1527       << GetDocument()->Lifecycle().ToString();
1528 #endif
1529 
1530   last_modification_count_ = cache.ModificationCount();
1531 
1532   cached_background_color_ = ComputeBackgroundColor();
1533   // TODO(aleventhal) Temporary crash fix until CL:2485519 lands.
1534   if (IsDetached())
1535     return;
1536 
1537   cached_is_hidden_via_style = ComputeIsHiddenViaStyle();
1538   cached_is_inert_or_aria_hidden_ = ComputeIsInertOrAriaHidden();
1539   cached_is_descendant_of_leaf_node_ = !!LeafNodeAncestor();
1540   cached_is_descendant_of_disabled_node_ = !!DisabledAncestor();
1541   cached_has_inherited_presentational_role_ =
1542       !!InheritsPresentationalRoleFrom();
1543   cached_is_ignored_ = ComputeAccessibilityIsIgnored();
1544   cached_is_ignored_but_included_in_tree_ =
1545       cached_is_ignored_ && ComputeAccessibilityIsIgnoredButIncludedInTree();
1546   cached_is_editable_root_ = ComputeIsEditableRoot();
1547   // Compute live region root, which can be from any ARIA live value, including
1548   // "off", or from an automatic ARIA live value, e.g. from role="status".
1549   // TODO(dmazzoni): remove this const_cast.
1550   AtomicString aria_live;
1551   cached_live_region_root_ =
1552       IsLiveRegionRoot()
1553           ? const_cast<AXObject*>(this)
1554           : (ParentObjectIfExists() ? ParentObjectIfExists()->LiveRegionRoot()
1555                                     : nullptr);
1556   cached_aria_column_index_ = ComputeAriaColumnIndex();
1557   cached_aria_row_index_ = ComputeAriaRowIndex();
1558 
1559   bool ignored_states_changed = false;
1560   if (cached_is_ignored_ != LastKnownIsIgnoredValue()) {
1561     last_known_is_ignored_value_ =
1562         cached_is_ignored_ ? kIgnoreObject : kIncludeObject;
1563     ignored_states_changed = true;
1564   }
1565 
1566   if (cached_is_ignored_but_included_in_tree_ !=
1567       LastKnownIsIgnoredButIncludedInTreeValue()) {
1568     last_known_is_ignored_but_included_in_tree_value_ =
1569         cached_is_ignored_but_included_in_tree_ ? kIncludeObject
1570                                                 : kIgnoreObject;
1571     ignored_states_changed = true;
1572   }
1573 
1574   if (ignored_states_changed) {
1575     if (AXObject* parent = ParentObjectIfExists())
1576       parent->ChildrenChanged();
1577   }
1578 
1579   if (GetLayoutObject() && GetLayoutObject()->IsText()) {
1580     cached_local_bounding_box_rect_for_accessibility_ =
1581         GetLayoutObject()->LocalBoundingBoxRectForAccessibility();
1582   }
1583 }
1584 
AccessibilityIsIgnoredByDefault(IgnoredReasons * ignored_reasons) const1585 bool AXObject::AccessibilityIsIgnoredByDefault(
1586     IgnoredReasons* ignored_reasons) const {
1587   return DefaultObjectInclusion(ignored_reasons) == kIgnoreObject;
1588 }
1589 
DefaultObjectInclusion(IgnoredReasons * ignored_reasons) const1590 AXObjectInclusion AXObject::DefaultObjectInclusion(
1591     IgnoredReasons* ignored_reasons) const {
1592   if (IsInertOrAriaHidden()) {
1593     // Keep focusable elements that are aria-hidden in tree, so that they can
1594     // still fire events such as focus and value changes.
1595     if (!CanSetFocusAttribute()) {
1596       if (ignored_reasons)
1597         ComputeIsInertOrAriaHidden(ignored_reasons);
1598       return kIgnoreObject;
1599     }
1600   }
1601 
1602   return kDefaultBehavior;
1603 }
1604 
IsInertOrAriaHidden() const1605 bool AXObject::IsInertOrAriaHidden() const {
1606   UpdateCachedAttributeValuesIfNeeded();
1607   return cached_is_inert_or_aria_hidden_;
1608 }
1609 
ComputeIsInertOrAriaHidden(IgnoredReasons * ignored_reasons) const1610 bool AXObject::ComputeIsInertOrAriaHidden(
1611     IgnoredReasons* ignored_reasons) const {
1612   if (GetNode()) {
1613     if (GetNode()->IsInert()) {
1614       if (ignored_reasons) {
1615         HTMLDialogElement* dialog = GetActiveDialogElement(GetNode());
1616         if (dialog) {
1617           AXObject* dialog_object = AXObjectCache().GetOrCreate(dialog);
1618           if (dialog_object) {
1619             ignored_reasons->push_back(
1620                 IgnoredReason(kAXActiveModalDialog, dialog_object));
1621           } else {
1622             ignored_reasons->push_back(IgnoredReason(kAXInertElement));
1623           }
1624         } else {
1625           const AXObject* inert_root_el = InertRoot();
1626           if (inert_root_el == this) {
1627             ignored_reasons->push_back(IgnoredReason(kAXInertElement));
1628           } else {
1629             ignored_reasons->push_back(
1630                 IgnoredReason(kAXInertSubtree, inert_root_el));
1631           }
1632         }
1633       }
1634       return true;
1635     } else if (IsBlockedByAriaModalDialog(ignored_reasons)) {
1636       return true;
1637     }
1638   } else {
1639     AXObject* parent = ParentObject();
1640     if (parent && parent->IsInertOrAriaHidden()) {
1641       if (ignored_reasons)
1642         parent->ComputeIsInertOrAriaHidden(ignored_reasons);
1643       return true;
1644     }
1645   }
1646 
1647   const AXObject* hidden_root = AriaHiddenRoot();
1648   if (hidden_root) {
1649     if (ignored_reasons) {
1650       if (hidden_root == this) {
1651         ignored_reasons->push_back(IgnoredReason(kAXAriaHiddenElement));
1652       } else {
1653         ignored_reasons->push_back(
1654             IgnoredReason(kAXAriaHiddenSubtree, hidden_root));
1655       }
1656     }
1657     return true;
1658   }
1659 
1660   return false;
1661 }
1662 
IsBlockedByAriaModalDialog(IgnoredReasons * ignored_reasons) const1663 bool AXObject::IsBlockedByAriaModalDialog(
1664     IgnoredReasons* ignored_reasons) const {
1665   AXObject* active_aria_modal_dialog =
1666       AXObjectCache().GetActiveAriaModalDialog();
1667 
1668   // On platforms that don't require manual pruning of the accessibility tree,
1669   // the active aria modal dialog should never be set, so has no effect.
1670   if (!active_aria_modal_dialog)
1671     return false;
1672 
1673   if (this == active_aria_modal_dialog ||
1674       IsDescendantOf(*active_aria_modal_dialog))
1675     return false;
1676 
1677   if (ignored_reasons) {
1678     ignored_reasons->push_back(
1679         IgnoredReason(kAXAriaModalDialog, active_aria_modal_dialog));
1680   }
1681   return true;
1682 }
1683 
IsVisible() const1684 bool AXObject::IsVisible() const {
1685   return !IsInertOrAriaHidden() && !IsHiddenViaStyle();
1686 }
1687 
IsDescendantOfLeafNode() const1688 bool AXObject::IsDescendantOfLeafNode() const {
1689   UpdateCachedAttributeValuesIfNeeded();
1690   return cached_is_descendant_of_leaf_node_;
1691 }
1692 
LeafNodeAncestor() const1693 AXObject* AXObject::LeafNodeAncestor() const {
1694   if (AXObject* parent = ParentObject()) {
1695     if (!parent->CanHaveChildren())
1696       return parent;
1697 
1698     return parent->LeafNodeAncestor();
1699   }
1700 
1701   return nullptr;
1702 }
1703 
AriaHiddenRoot() const1704 const AXObject* AXObject::AriaHiddenRoot() const {
1705   for (const AXObject* object = this; object; object = object->ParentObject()) {
1706     if (object->AOMPropertyOrARIAAttributeIsTrue(AOMBooleanProperty::kHidden))
1707       return object;
1708   }
1709 
1710   return nullptr;
1711 }
1712 
InertRoot() const1713 const AXObject* AXObject::InertRoot() const {
1714   const AXObject* object = this;
1715   if (!RuntimeEnabledFeatures::InertAttributeEnabled())
1716     return nullptr;
1717 
1718   while (object && !object->IsAXNodeObject())
1719     object = object->ParentObject();
1720   Node* node = object->GetNode();
1721   auto* element = DynamicTo<Element>(node);
1722   if (!element)
1723     element = FlatTreeTraversal::ParentElement(*node);
1724 
1725   while (element) {
1726     if (element->FastHasAttribute(html_names::kInertAttr))
1727       return AXObjectCache().GetOrCreate(element);
1728     element = FlatTreeTraversal::ParentElement(*element);
1729   }
1730 
1731   return nullptr;
1732 }
1733 
DispatchEventToAOMEventListeners(Event & event)1734 bool AXObject::DispatchEventToAOMEventListeners(Event& event) {
1735   HeapVector<Member<AccessibleNode>> event_path;
1736   for (AXObject* ancestor = this; ancestor;
1737        ancestor = ancestor->ParentObject()) {
1738     AccessibleNode* ancestor_accessible_node = ancestor->GetAccessibleNode();
1739     if (!ancestor_accessible_node)
1740       continue;
1741 
1742     if (!ancestor_accessible_node->HasEventListeners(event.type()))
1743       continue;
1744 
1745     event_path.push_back(ancestor_accessible_node);
1746   }
1747 
1748   // Short-circuit: if there are no AccessibleNodes attached anywhere
1749   // in the ancestry of this node, exit.
1750   if (!event_path.size())
1751     return false;
1752 
1753   // Check if the user has granted permission for this domain to use
1754   // AOM event listeners yet. This may trigger an infobar, but we shouldn't
1755   // block, so whatever decision the user makes will apply to the next
1756   // event received after that.
1757   //
1758   // Note that we only ask the user about this permission the first
1759   // time an event is received that actually would have triggered an
1760   // event listener. However, if the user grants this permission, it
1761   // persists for this origin from then on.
1762   if (!AXObjectCache().CanCallAOMEventListeners()) {
1763     AXObjectCache().RequestAOMEventListenerPermission();
1764     return false;
1765   }
1766 
1767   // Since we now know the AOM is being used in this document, get the
1768   // AccessibleNode for the target element and create it if necessary -
1769   // otherwise we wouldn't be able to set the event target. However note
1770   // that if it didn't previously exist it won't be part of the event path.
1771   AccessibleNode* target = GetAccessibleNode();
1772   if (!target) {
1773     if (Element* element = GetElement())
1774       target = element->accessibleNode();
1775   }
1776   if (!target)
1777     return false;
1778   event.SetTarget(target);
1779 
1780   // Capturing phase.
1781   event.SetEventPhase(Event::kCapturingPhase);
1782   for (int i = static_cast<int>(event_path.size()) - 1; i >= 0; i--) {
1783     // Don't call capturing event listeners on the target. Note that
1784     // the target may not necessarily be in the event path which is why
1785     // we check here.
1786     if (event_path[i] == target)
1787       break;
1788 
1789     event.SetCurrentTarget(event_path[i]);
1790     event_path[i]->FireEventListeners(event);
1791     if (event.PropagationStopped())
1792       return true;
1793   }
1794 
1795   // Targeting phase.
1796   event.SetEventPhase(Event::kAtTarget);
1797   event.SetCurrentTarget(event_path[0]);
1798   event_path[0]->FireEventListeners(event);
1799   if (event.PropagationStopped())
1800     return true;
1801 
1802   // Bubbling phase.
1803   event.SetEventPhase(Event::kBubblingPhase);
1804   for (wtf_size_t i = 1; i < event_path.size(); i++) {
1805     event.SetCurrentTarget(event_path[i]);
1806     event_path[i]->FireEventListeners(event);
1807     if (event.PropagationStopped())
1808       return true;
1809   }
1810 
1811   if (event.defaultPrevented())
1812     return true;
1813 
1814   return false;
1815 }
1816 
IsDescendantOfDisabledNode() const1817 bool AXObject::IsDescendantOfDisabledNode() const {
1818   UpdateCachedAttributeValuesIfNeeded();
1819   return cached_is_descendant_of_disabled_node_;
1820 }
1821 
DisabledAncestor() const1822 const AXObject* AXObject::DisabledAncestor() const {
1823   bool disabled = false;
1824   if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kDisabled, disabled)) {
1825     if (disabled)
1826       return this;
1827     return nullptr;
1828   }
1829 
1830   if (AXObject* parent = ParentObject())
1831     return parent->DisabledAncestor();
1832 
1833   return nullptr;
1834 }
1835 
ComputeAccessibilityIsIgnoredButIncludedInTree() const1836 bool AXObject::ComputeAccessibilityIsIgnoredButIncludedInTree() const {
1837   if (RuntimeEnabledFeatures::AccessibilityExposeIgnoredNodesEnabled())
1838     return true;
1839 
1840   if (AXObjectCache().IsAriaOwned(this) || HasARIAOwns(GetElement())) {
1841     // Always include an aria-owned object. It must be a child of the
1842     // element with aria-owns.
1843     return true;
1844   }
1845 
1846   if (!GetNode())
1847     return false;
1848 
1849   // Use a flag to control whether or not the <html> element is included
1850   // in the accessibility tree. Either way it's always marked as "ignored",
1851   // but eventually we want to always include it in the tree to simplify
1852   // some logic.
1853   if (GetNode() && IsA<HTMLHtmlElement>(GetNode()))
1854     return RuntimeEnabledFeatures::AccessibilityExposeHTMLElementEnabled();
1855 
1856   // If the node is part of the user agent shadow dom, or has the explicit
1857   // internal Role::kIgnored, they aren't interesting for paragraph navigation
1858   // or LabelledBy/DescribedBy relationships.
1859   if (RoleValue() == ax::mojom::blink::Role::kIgnored ||
1860       GetNode()->IsInUserAgentShadowRoot()) {
1861     return false;
1862   }
1863 
1864   // Keep the internal accessibility tree consistent for videos which lack
1865   // a player and also inner text.
1866   if (RoleValue() == ax::mojom::blink::Role::kVideo ||
1867       RoleValue() == ax::mojom::blink::Role::kAudio) {
1868     return true;
1869   }
1870 
1871   // Always pass through Line Breaking objects, this is necessary to
1872   // detect paragraph edges, which are defined as hard-line breaks.
1873   if (IsLineBreakingObject())
1874     return true;
1875 
1876   // Allow the browser side ax tree to access "visibility: [hidden|collapse]"
1877   // and "display: none" nodes. This is useful for APIs that return the node
1878   // referenced by aria-labeledby and aria-describedby.
1879   // An element must have an id attribute or it cannot be referenced by
1880   // aria-labelledby or aria-describedby.
1881   if (RuntimeEnabledFeatures::AccessibilityExposeDisplayNoneEnabled()) {
1882     if (Element* element = GetElement()) {
1883       if (element->FastHasAttribute(html_names::kIdAttr) &&
1884           IsHiddenViaStyle()) {
1885         return true;
1886       }
1887     }
1888   } else if (GetLayoutObject()) {
1889     if (GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible)
1890       return true;
1891   }
1892 
1893   // Allow the browser side ax tree to access "aria-hidden" nodes.
1894   // This is useful for APIs that return the node referenced by
1895   // aria-labeledby and aria-describedby.
1896   if (GetLayoutObject() && AriaHiddenRoot())
1897     return true;
1898 
1899   // Preserve SVG grouping elements.
1900   if (GetNode() && IsA<SVGGElement>(GetNode()))
1901     return true;
1902 
1903   // Preserve nodes with language attributes.
1904   if (HasAttribute(html_names::kLangAttr))
1905     return true;
1906 
1907   return false;
1908 }
1909 
GetNativeTextControlAncestor(int max_levels_to_check) const1910 const AXObject* AXObject::GetNativeTextControlAncestor(
1911     int max_levels_to_check) const {
1912   if (IsNativeTextControl())
1913     return this;
1914 
1915   if (max_levels_to_check == 0)
1916     return nullptr;
1917 
1918   if (AXObject* parent = ParentObject())
1919     return parent->GetNativeTextControlAncestor(max_levels_to_check - 1);
1920 
1921   return nullptr;
1922 }
1923 
DatetimeAncestor(int max_levels_to_check) const1924 const AXObject* AXObject::DatetimeAncestor(int max_levels_to_check) const {
1925   switch (RoleValue()) {
1926     case ax::mojom::blink::Role::kDateTime:
1927     case ax::mojom::blink::Role::kDate:
1928     case ax::mojom::blink::Role::kInputTime:
1929     case ax::mojom::blink::Role::kTime:
1930       return this;
1931     default:
1932       break;
1933   }
1934 
1935   if (max_levels_to_check == 0)
1936     return nullptr;
1937 
1938   if (AXObject* parent = ParentObject())
1939     return parent->DatetimeAncestor(max_levels_to_check - 1);
1940 
1941   return nullptr;
1942 }
1943 
LastKnownIsIgnoredValue() const1944 bool AXObject::LastKnownIsIgnoredValue() const {
1945   if (last_known_is_ignored_value_ == kDefaultBehavior) {
1946     last_known_is_ignored_value_ =
1947         AccessibilityIsIgnored() ? kIgnoreObject : kIncludeObject;
1948   }
1949 
1950   return last_known_is_ignored_value_ == kIgnoreObject;
1951 }
1952 
SetLastKnownIsIgnoredValue(bool is_ignored)1953 void AXObject::SetLastKnownIsIgnoredValue(bool is_ignored) {
1954   last_known_is_ignored_value_ = is_ignored ? kIgnoreObject : kIncludeObject;
1955 }
1956 
LastKnownIsIgnoredButIncludedInTreeValue() const1957 bool AXObject::LastKnownIsIgnoredButIncludedInTreeValue() const {
1958   if (last_known_is_ignored_but_included_in_tree_value_ == kDefaultBehavior) {
1959     last_known_is_ignored_but_included_in_tree_value_ =
1960         AccessibilityIsIgnoredButIncludedInTree() ? kIncludeObject
1961                                                   : kIgnoreObject;
1962   }
1963 
1964   return last_known_is_ignored_but_included_in_tree_value_ == kIncludeObject;
1965 }
1966 
SetLastKnownIsIgnoredButIncludedInTreeValue(bool is_ignored_but_included_in_tree)1967 void AXObject::SetLastKnownIsIgnoredButIncludedInTreeValue(
1968     bool is_ignored_but_included_in_tree) {
1969   last_known_is_ignored_but_included_in_tree_value_ =
1970       is_ignored_but_included_in_tree ? kIncludeObject : kIgnoreObject;
1971 }
1972 
LastKnownIsIncludedInTreeValue() const1973 bool AXObject::LastKnownIsIncludedInTreeValue() const {
1974   return !LastKnownIsIgnoredValue() ||
1975          LastKnownIsIgnoredButIncludedInTreeValue();
1976 }
1977 
HasInheritedPresentationalRole() const1978 bool AXObject::HasInheritedPresentationalRole() const {
1979   UpdateCachedAttributeValuesIfNeeded();
1980   return cached_has_inherited_presentational_role_;
1981 }
1982 
CanSetValueAttribute() const1983 bool AXObject::CanSetValueAttribute() const {
1984   switch (RoleValue()) {
1985     case ax::mojom::blink::Role::kColorWell:
1986     case ax::mojom::blink::Role::kDate:
1987     case ax::mojom::blink::Role::kDateTime:
1988     case ax::mojom::blink::Role::kInputTime:
1989     case ax::mojom::blink::Role::kScrollBar:
1990     case ax::mojom::blink::Role::kSearchBox:
1991     case ax::mojom::blink::Role::kSlider:
1992     case ax::mojom::blink::Role::kSpinButton:
1993     case ax::mojom::blink::Role::kSplitter:
1994     case ax::mojom::blink::Role::kTextField:
1995     case ax::mojom::blink::Role::kTextFieldWithComboBox:
1996       return Restriction() == kRestrictionNone;
1997     default:
1998       return false;
1999   }
2000 }
2001 
IsFocusableStyleUsingBestAvailableState() const2002 bool AXObject::IsFocusableStyleUsingBestAvailableState() const {
2003   auto* element = GetElement();
2004   DCHECK(element);
2005 
2006   // If this element's layout tree does not need an update, it means that we can
2007   // rely on Element's IsFocusableStyle directly, which is the best available
2008   // source of information.
2009   // Note that we also allow this to be used if we're in a style recalc, since
2010   // we might get here through layout object attachment. In that case, the dirty
2011   // bits may not have been cleared yet, but all relevant style and layout tree
2012   // should be up to date. Note that this quirk can be fixed by deferring AX
2013   // tree updates to happen after the layout tree attachment has finished.
2014   if (GetDocument()->InStyleRecalc() ||
2015       !GetDocument()->NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(
2016           *element)) {
2017     return element->IsFocusableStyle();
2018   }
2019 
2020   // The best available source of information is now the AX tree, so use that to
2021   // figure out whether we have focusable style.
2022 
2023   // If we're in a canvas subtree, then use the canvas visibility instead of
2024   // self visibility. The elements in a canvas subtree are fallback elements,
2025   // which are not necessarily rendered but are allowed to be focusable.
2026   if (element->IsInCanvasSubtree()) {
2027     const HTMLCanvasElement* canvas =
2028         Traversal<HTMLCanvasElement>::FirstAncestorOrSelf(*element);
2029     DCHECK(canvas);
2030     return canvas->GetLayoutObject() &&
2031            canvas->GetLayoutObject()->Style()->Visibility() ==
2032                EVisibility::kVisible;
2033   }
2034 
2035   return GetLayoutObject() &&
2036          GetLayoutObject()->Style()->Visibility() == EVisibility::kVisible;
2037 }
2038 
2039 // This does not use Element::IsFocusable(), as that can sometimes recalculate
2040 // styles because of IsFocusableStyle() check, resetting the document lifecycle.
CanSetFocusAttribute() const2041 bool AXObject::CanSetFocusAttribute() const {
2042   if (IsDetached())
2043     return false;
2044 
2045   // Objects within a portal are not focusable.
2046   // Note that they are ignored but can be included in the tree.
2047   bool inside_portal = GetDocument() && GetDocument()->GetPage() &&
2048                        GetDocument()->GetPage()->InsidePortal();
2049   if (inside_portal)
2050     return false;
2051 
2052   // Display-locked nodes that have content-visibility: hidden are not exposed
2053   // to accessibility in any way, so they are not focusable. Note that for
2054   // content-visibility: auto cases, `ShouldIgnoreNodeDueToDisplayLock()` would
2055   // return false, since we're not ignoring the element in that case.
2056   if (GetNode() &&
2057       DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock(
2058           *GetNode(), DisplayLockActivationReason::kAccessibility)) {
2059     return false;
2060   }
2061 
2062   // Focusable: web area -- this is the only focusable non-element. Web areas
2063   // inside portals are not focusable though (portal contents cannot get focus).
2064   if (IsWebArea())
2065     return true;
2066 
2067   // NOT focusable: objects with no DOM node, e.g. extra layout blocks inserted
2068   // as filler, or objects where the node is not an element, such as a text
2069   // node or an HTML comment.
2070   Element* elem = GetElement();
2071   if (!elem)
2072     return false;
2073 
2074   // NOT focusable: inert elements.
2075   if (elem->IsInert())
2076     return false;
2077 
2078   // NOT focusable: disabled form controls.
2079   if (IsDisabledFormControl(elem))
2080     return false;
2081 
2082   // Focusable: options in a combobox or listbox.
2083   // Even though they are not treated as supporting focus by Blink (the parent
2084   // widget is), they are considered focusable in the accessibility sense,
2085   // behaving like potential active descendants, and handling focus actions.
2086   // Menu list options are handled before visibility check, because they
2087   // are considered focusable even when part of collapsed drop down.
2088   if (RoleValue() == ax::mojom::blink::Role::kMenuListOption)
2089     return true;
2090 
2091   // NOT focusable: hidden elements.
2092   // TODO(aleventhal) Consider caching visibility when it's safe to compute.
2093   if (!IsA<HTMLAreaElement>(elem) && !IsFocusableStyleUsingBestAvailableState())
2094     return false;
2095 
2096   // Focusable: options in a combobox or listbox.
2097   // Similar to menu list option treatment above, but not focusable if hidden.
2098   if (RoleValue() == ax::mojom::blink::Role::kListBoxOption)
2099     return true;
2100 
2101   // Focusable: element supports focus.
2102   if (elem->SupportsFocus())
2103     return true;
2104 
2105   // TODO(accessibility) Focusable: scrollable with the keyboard.
2106   // Keyboard-focusable scroll containers feature:
2107   // https://www.chromestatus.com/feature/5231964663578624
2108   // When adding here, remove similar check from ::SupportsNameFromContents().
2109   // if (RuntimeEnabledFeatures::KeyboardFocusableScrollersEnabled() &&
2110   //     IsUserScrollable()) {
2111   //   return true;
2112   // }
2113 
2114   // Focusable: can be an active descendant.
2115   if (CanBeActiveDescendant())
2116     return true;
2117 
2118   // NOT focusable: everything else.
2119   return false;
2120 }
2121 
2122 // From ARIA 1.1.
2123 // 1. The value of aria-activedescendant refers to an element that is either a
2124 // descendant of the element with DOM focus or is a logical descendant as
2125 // indicated by the aria-owns attribute. 2. The element with DOM focus is a
2126 // textbox with aria-controls referring to an element that supports
2127 // aria-activedescendant, and the value of aria-activedescendant specified for
2128 // the textbox refers to either a descendant of the element controlled by the
2129 // textbox or is a logical descendant of that controlled element as indicated by
2130 // the aria-owns attribute.
CanBeActiveDescendant() const2131 bool AXObject::CanBeActiveDescendant() const {
2132   // Require an element with an id attribute.
2133   // TODO(accessibility): this code currently requires both an id and role
2134   // attribute, as well as an ancestor or controlling aria-activedescendant.
2135   // However, with element reflection it may be possible to set an active
2136   // descendant without an id, so at some point we may need to remove the
2137   // requirement for an id attribute.
2138   if (!GetElement() || !GetElement()->FastHasAttribute(html_names::kIdAttr))
2139     return false;
2140 
2141   // Does not make sense to use aria-activedescendant to point to a
2142   // presentational object.
2143   if (IsPresentational())
2144     return false;
2145 
2146   // Does not make sense to use aria-activedescendant to point to an HTML
2147   // element that requires real focus, therefore an ARIA role is necessary.
2148   if (AriaRoleAttribute() == ax::mojom::blink::Role::kUnknown)
2149     return false;
2150 
2151   return IsARIAControlledByTextboxWithActiveDescendant() ||
2152          AncestorExposesActiveDescendant();
2153 }
2154 
UpdateDistributionForFlatTreeTraversal() const2155 void AXObject::UpdateDistributionForFlatTreeTraversal() const {
2156   Node* node = GetNode();
2157   if (!node) {
2158     AXObject* parent = this->ParentObject();
2159     while (!node && parent) {
2160       node = parent->GetNode();
2161       parent = parent->ParentObject();
2162     }
2163   }
2164 
2165   if (node)
2166     node->UpdateDistributionForFlatTreeTraversal();
2167 
2168   // TODO(aboxhall): Instead of this, propagate inert down through frames
2169   Document* document = GetDocument();
2170   while (document && document->LocalOwner()) {
2171     document->LocalOwner()->UpdateDistributionForFlatTreeTraversal();
2172     document = document->LocalOwner()->ownerDocument();
2173   }
2174 }
2175 
IsARIAControlledByTextboxWithActiveDescendant() const2176 bool AXObject::IsARIAControlledByTextboxWithActiveDescendant() const {
2177   if (IsDetached() || !GetDocument())
2178     return false;
2179 
2180   // This situation should mostly arise when using an active descendant on a
2181   // textbox inside an ARIA 1.1 combo box widget, which points to the selected
2182   // option in a list. In such situations, the active descendant is useful only
2183   // when the textbox is focused. Therefore, we don't currently need to keep
2184   // track of all aria-controls relationships.
2185   const Element* focused_element = GetDocument()->FocusedElement();
2186   if (!focused_element)
2187     return false;
2188 
2189   const AXObject* focused_object = AXObjectCache().GetOrCreate(focused_element);
2190   if (!focused_object || !focused_object->IsTextControl())
2191     return false;
2192 
2193   if (!focused_object->GetAOMPropertyOrARIAAttribute(
2194           AOMRelationProperty::kActiveDescendant)) {
2195     return false;
2196   }
2197 
2198   HeapVector<Member<Element>> controlled_by_elements;
2199   if (!focused_object->HasAOMPropertyOrARIAAttribute(
2200           AOMRelationListProperty::kControls, controlled_by_elements)) {
2201     return false;
2202   }
2203 
2204   for (const auto& controlled_by_element : controlled_by_elements) {
2205     const AXObject* controlled_by_object =
2206         AXObjectCache().GetOrCreate(controlled_by_element);
2207     if (!controlled_by_object)
2208       continue;
2209 
2210     const AXObject* object = this;
2211     while (object && object != controlled_by_object)
2212       object = object->ParentObjectUnignored();
2213     if (object)
2214       return true;
2215   }
2216 
2217   return false;
2218 }
2219 
AncestorExposesActiveDescendant() const2220 bool AXObject::AncestorExposesActiveDescendant() const {
2221   const AXObject* parent = ParentObjectUnignored();
2222   if (!parent)
2223     return false;
2224 
2225   if (parent->GetAOMPropertyOrARIAAttribute(
2226           AOMRelationProperty::kActiveDescendant)) {
2227     return true;
2228   }
2229 
2230   return parent->AncestorExposesActiveDescendant();
2231 }
2232 
HasIndirectChildren() const2233 bool AXObject::HasIndirectChildren() const {
2234   return RoleValue() == ax::mojom::blink::Role::kTableHeaderContainer;
2235 }
2236 
CanSetSelectedAttribute() const2237 bool AXObject::CanSetSelectedAttribute() const {
2238   // Sub-widget elements can be selected if not disabled (native or ARIA)
2239   return IsSubWidget() && Restriction() != kRestrictionDisabled;
2240 }
2241 
IsSubWidget() const2242 bool AXObject::IsSubWidget() const {
2243   switch (RoleValue()) {
2244     case ax::mojom::blink::Role::kCell:
2245     case ax::mojom::blink::Role::kColumnHeader:
2246     case ax::mojom::blink::Role::kRowHeader:
2247     case ax::mojom::blink::Role::kColumn:
2248     case ax::mojom::blink::Role::kRow:
2249       // If it has an explicit ARIA role, it's a subwidget.
2250       //
2251       // Reasoning:
2252       // Static table cells are not selectable, but ARIA grid cells
2253       // and rows definitely are according to the spec. To support
2254       // ARIA 1.0, it's sufficient to just check if there's any
2255       // ARIA role at all, because if so then it must be a grid-related
2256       // role so it must be selectable.
2257       //
2258       // TODO(accessibility): an ARIA 1.1+ role of "cell", or a role of "row"
2259       // inside an ARIA 1.1 role of "table", should not be selectable. We may
2260       // need to create separate role enums for grid cells vs table cells
2261       // to implement this.
2262       if (AriaRoleAttribute() != ax::mojom::blink::Role::kUnknown)
2263         return true;
2264 
2265       // Otherwise it's only a subwidget if it's in a grid or treegrid,
2266       // not in a table.
2267       return std::any_of(
2268           UnignoredAncestorsBegin(), UnignoredAncestorsEnd(),
2269           [](const AXObject& ancestor) {
2270             return ancestor.RoleValue() == ax::mojom::blink::Role::kGrid ||
2271                    ancestor.RoleValue() == ax::mojom::blink::Role::kTreeGrid;
2272           });
2273 
2274     case ax::mojom::blink::Role::kListBoxOption:
2275     case ax::mojom::blink::Role::kMenuListOption:
2276     case ax::mojom::blink::Role::kTab:
2277     case ax::mojom::blink::Role::kTreeItem:
2278       return true;
2279     default:
2280       return false;
2281   }
2282 }
2283 
SupportsARIASetSizeAndPosInSet() const2284 bool AXObject::SupportsARIASetSizeAndPosInSet() const {
2285   return ui::IsSetLike(RoleValue()) || ui::IsItemLike(RoleValue());
2286 }
2287 
2288 // Simplify whitespace, but preserve a single leading and trailing whitespace
2289 // character if it's present.
2290 // static
CollapseWhitespace(const String & str)2291 String AXObject::CollapseWhitespace(const String& str) {
2292   StringBuilder result;
2293   if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[0]))
2294     result.Append(' ');
2295   result.Append(str.SimplifyWhiteSpace(IsHTMLSpace<UChar>));
2296   if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[str.length() - 1]))
2297     result.Append(' ');
2298   return result.ToString();
2299 }
2300 
ComputedName() const2301 String AXObject::ComputedName() const {
2302   ax::mojom::blink::NameFrom name_from;
2303   AXObjectVector name_objects;
2304   return GetName(name_from, &name_objects);
2305 }
2306 
GetName(ax::mojom::blink::NameFrom & name_from,AXObject::AXObjectVector * name_objects) const2307 String AXObject::GetName(ax::mojom::blink::NameFrom& name_from,
2308                          AXObject::AXObjectVector* name_objects) const {
2309   HeapHashSet<Member<const AXObject>> visited;
2310   AXRelatedObjectVector related_objects;
2311 
2312   // Initialize |name_from|, as TextAlternative() might never set it in some
2313   // cases.
2314   name_from = ax::mojom::blink::NameFrom::kNone;
2315   String text = TextAlternative(false, false, visited, name_from,
2316                                 &related_objects, nullptr);
2317 
2318   ax::mojom::blink::Role role = RoleValue();
2319   if (!GetNode() || (!IsA<HTMLBRElement>(GetNode()) &&
2320                      role != ax::mojom::blink::Role::kStaticText &&
2321                      role != ax::mojom::blink::Role::kInlineTextBox))
2322     text = CollapseWhitespace(text);
2323 
2324   if (name_objects) {
2325     name_objects->clear();
2326     for (NameSourceRelatedObject* related_object : related_objects)
2327       name_objects->push_back(related_object->object);
2328   }
2329 
2330   return text;
2331 }
2332 
GetName(NameSources * name_sources) const2333 String AXObject::GetName(NameSources* name_sources) const {
2334   AXObjectSet visited;
2335   ax::mojom::blink::NameFrom tmp_name_from;
2336   AXRelatedObjectVector tmp_related_objects;
2337   String text = TextAlternative(false, false, visited, tmp_name_from,
2338                                 &tmp_related_objects, name_sources);
2339   text = text.SimplifyWhiteSpace(IsHTMLSpace<UChar>);
2340   return text;
2341 }
2342 
RecursiveTextAlternative(const AXObject & ax_obj,bool in_aria_labelled_by_traversal,AXObjectSet & visited)2343 String AXObject::RecursiveTextAlternative(const AXObject& ax_obj,
2344                                           bool in_aria_labelled_by_traversal,
2345                                           AXObjectSet& visited) {
2346   ax::mojom::blink::NameFrom tmp_name_from;
2347   return RecursiveTextAlternative(ax_obj, in_aria_labelled_by_traversal,
2348                                   visited, tmp_name_from);
2349 }
2350 
RecursiveTextAlternative(const AXObject & ax_obj,bool in_aria_labelled_by_traversal,AXObjectSet & visited,ax::mojom::blink::NameFrom & name_from)2351 String AXObject::RecursiveTextAlternative(
2352     const AXObject& ax_obj,
2353     bool in_aria_labelled_by_traversal,
2354     AXObjectSet& visited,
2355     ax::mojom::blink::NameFrom& name_from) {
2356   if (visited.Contains(&ax_obj) && !in_aria_labelled_by_traversal)
2357     return String();
2358 
2359   return ax_obj.TextAlternative(true, in_aria_labelled_by_traversal, visited,
2360                                 name_from, nullptr, nullptr);
2361 }
2362 
ComputeIsHiddenViaStyle() const2363 bool AXObject::ComputeIsHiddenViaStyle() const {
2364   Node* node = GetNode();
2365   if (!node)
2366     return false;
2367 
2368   // Display-locked nodes are always hidden.
2369   if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*node))
2370     return true;
2371 
2372   // Style elements in SVG are not display: none, unlike HTML style elements,
2373   // but they are still hidden and thus treated as hidden from style.
2374   if (IsA<SVGStyleElement>(node))
2375     return true;
2376 
2377   // For elements with layout objects we can get their style directly.
2378   if (GetLayoutObject())
2379     return GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible;
2380 
2381   // No layout object: must ensure computed style.
2382   if (Element* element = DynamicTo<Element>(node)) {
2383     const ComputedStyle* style = element->EnsureComputedStyle();
2384     return !style || style->IsEnsuredInDisplayNone() ||
2385            style->Visibility() != EVisibility::kVisible;
2386   }
2387   return false;
2388 }
2389 
IsHiddenViaStyle() const2390 bool AXObject::IsHiddenViaStyle() const {
2391   UpdateCachedAttributeValuesIfNeeded();
2392   return cached_is_hidden_via_style;
2393 }
2394 
2395 // Return true if this should be removed from accessible name computations,
2396 // unless it is reached by following an aria-labelledby. When that happens, this
2397 // is not checked, because aria-labelledby can use hidden subtrees.
2398 // Because aria-labelledby can use hidden subtrees, when it has entered a hidden
2399 // subtree, it is not enough to check if the element was hidden by an ancestor.
2400 // In this case, return true only if the hiding style targeted the node
2401 // directly, as opposed to having inherited the hiding style. Using inherited
2402 // hiding styles is problematic because it would prevent name contributions from
2403 // deeper nodes in hidden aria-labelledby subtrees.
IsHiddenForTextAlternativeCalculation() const2404 bool AXObject::IsHiddenForTextAlternativeCalculation() const {
2405   // aria-hidden=false allows hidden contents to be used in name from contents.
2406   if (AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty::kHidden))
2407     return false;
2408 
2409   auto* node = GetNode();
2410   if (!node)
2411     return false;
2412 
2413   // Display-locked elements are available for text/name resolution.
2414   if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*node))
2415     return false;
2416 
2417   Document* document = GetDocument();
2418   if (!document || !document->GetFrame())
2419     return false;
2420 
2421   // Do not contribute <noscript> to text alternative of an ancestor.
2422   if (IsA<HTMLNoScriptElement>(node))
2423     return true;
2424 
2425   // Always contribute SVG <title> despite it having a hidden style by default.
2426   if (IsA<SVGTitleElement>(node))
2427     return false;
2428 
2429   // If this is hidden but its parent isn't, then it appears the hiding style
2430   // targeted this node directly. Do not recurse into it for name from contents.
2431   return IsHiddenViaStyle() &&
2432          (!ParentObject() || !ParentObject()->IsHiddenViaStyle());
2433 }
2434 
AriaTextAlternative(bool recursive,bool in_aria_labelled_by_traversal,AXObjectSet & visited,ax::mojom::blink::NameFrom & name_from,AXRelatedObjectVector * related_objects,NameSources * name_sources,bool * found_text_alternative) const2435 String AXObject::AriaTextAlternative(bool recursive,
2436                                      bool in_aria_labelled_by_traversal,
2437                                      AXObjectSet& visited,
2438                                      ax::mojom::blink::NameFrom& name_from,
2439                                      AXRelatedObjectVector* related_objects,
2440                                      NameSources* name_sources,
2441                                      bool* found_text_alternative) const {
2442   String text_alternative;
2443   bool already_visited = visited.Contains(this);
2444   visited.insert(this);
2445 
2446   // Step 2A from: http://www.w3.org/TR/accname-aam-1.1
2447   // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
2448   if (!in_aria_labelled_by_traversal &&
2449       IsHiddenForTextAlternativeCalculation()) {
2450     *found_text_alternative = true;
2451     return String();
2452   }
2453 
2454   // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
2455   // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
2456   if (!in_aria_labelled_by_traversal && !already_visited) {
2457     name_from = ax::mojom::blink::NameFrom::kRelatedElement;
2458 
2459     // Check ARIA attributes.
2460     const QualifiedName& attr =
2461         HasAttribute(html_names::kAriaLabeledbyAttr) &&
2462                 !HasAttribute(html_names::kAriaLabelledbyAttr)
2463             ? html_names::kAriaLabeledbyAttr
2464             : html_names::kAriaLabelledbyAttr;
2465 
2466     if (name_sources) {
2467       name_sources->push_back(NameSource(*found_text_alternative, attr));
2468       name_sources->back().type = name_from;
2469     }
2470 
2471     Element* element = GetElement();
2472     if (element) {
2473       HeapVector<Member<Element>> elements_from_attribute;
2474       Vector<String> ids;
2475       ElementsFromAttribute(elements_from_attribute, attr, ids);
2476 
2477       const AtomicString& aria_labelledby = GetAttribute(attr);
2478 
2479       if (!aria_labelledby.IsNull()) {
2480         if (name_sources)
2481           name_sources->back().attribute_value = aria_labelledby;
2482 
2483         // Operate on a copy of |visited| so that if |name_sources| is not
2484         // null, the set of visited objects is preserved unmodified for future
2485         // calculations.
2486         AXObjectSet visited_copy = visited;
2487         text_alternative = TextFromElements(
2488             true, visited, elements_from_attribute, related_objects);
2489         if (!ids.IsEmpty())
2490           AXObjectCache().UpdateReverseRelations(this, ids);
2491         if (!text_alternative.IsNull()) {
2492           if (name_sources) {
2493             NameSource& source = name_sources->back();
2494             source.type = name_from;
2495             source.related_objects = *related_objects;
2496             source.text = text_alternative;
2497             *found_text_alternative = true;
2498           } else {
2499             *found_text_alternative = true;
2500             return text_alternative;
2501           }
2502         } else if (name_sources) {
2503           name_sources->back().invalid = true;
2504         }
2505       }
2506     }
2507   }
2508 
2509   // Step 2C from: http://www.w3.org/TR/accname-aam-1.1
2510   // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
2511   name_from = ax::mojom::blink::NameFrom::kAttribute;
2512   if (name_sources) {
2513     name_sources->push_back(
2514         NameSource(*found_text_alternative, html_names::kAriaLabelAttr));
2515     name_sources->back().type = name_from;
2516   }
2517   const AtomicString& aria_label =
2518       GetAOMPropertyOrARIAAttribute(AOMStringProperty::kLabel);
2519   if (!aria_label.IsEmpty()) {
2520     text_alternative = aria_label;
2521 
2522     if (name_sources) {
2523       NameSource& source = name_sources->back();
2524       source.text = text_alternative;
2525       source.attribute_value = aria_label;
2526       *found_text_alternative = true;
2527     } else {
2528       *found_text_alternative = true;
2529       return text_alternative;
2530     }
2531   }
2532 
2533   return text_alternative;
2534 }
2535 
TextFromElements(bool in_aria_labelledby_traversal,AXObjectSet & visited,HeapVector<Member<Element>> & elements,AXRelatedObjectVector * related_objects) const2536 String AXObject::TextFromElements(
2537     bool in_aria_labelledby_traversal,
2538     AXObjectSet& visited,
2539     HeapVector<Member<Element>>& elements,
2540     AXRelatedObjectVector* related_objects) const {
2541   StringBuilder accumulated_text;
2542   bool found_valid_element = false;
2543   AXRelatedObjectVector local_related_objects;
2544 
2545   for (const auto& element : elements) {
2546     AXObject* ax_element = AXObjectCache().GetOrCreate(element);
2547     if (ax_element) {
2548       found_valid_element = true;
2549 
2550       String result = RecursiveTextAlternative(
2551           *ax_element, in_aria_labelledby_traversal, visited);
2552       visited.insert(ax_element);
2553       local_related_objects.push_back(
2554           MakeGarbageCollected<NameSourceRelatedObject>(ax_element, result));
2555       if (!result.IsEmpty()) {
2556         if (!accumulated_text.IsEmpty())
2557           accumulated_text.Append(' ');
2558         accumulated_text.Append(result);
2559       }
2560     }
2561   }
2562   if (!found_valid_element)
2563     return String();
2564   if (related_objects)
2565     *related_objects = local_related_objects;
2566   return accumulated_text.ToString();
2567 }
2568 
TokenVectorFromAttribute(Vector<String> & tokens,const QualifiedName & attribute) const2569 void AXObject::TokenVectorFromAttribute(Vector<String>& tokens,
2570                                         const QualifiedName& attribute) const {
2571   Node* node = this->GetNode();
2572   if (!node || !node->IsElementNode())
2573     return;
2574 
2575   String attribute_value = GetAttribute(attribute).GetString();
2576   if (attribute_value.IsEmpty())
2577     return;
2578 
2579   attribute_value = attribute_value.SimplifyWhiteSpace();
2580   attribute_value.Split(' ', tokens);
2581 }
2582 
ElementsFromAttribute(HeapVector<Member<Element>> & elements,const QualifiedName & attribute,Vector<String> & ids) const2583 void AXObject::ElementsFromAttribute(HeapVector<Member<Element>>& elements,
2584                                      const QualifiedName& attribute,
2585                                      Vector<String>& ids) const {
2586   // We compute the attr-associated elements, which are either explicitly set
2587   // element references set via the IDL, or computed from the content attribute.
2588   TokenVectorFromAttribute(ids, attribute);
2589   Element* element = GetElement();
2590   if (!element)
2591     return;
2592 
2593   base::Optional<HeapVector<Member<Element>>> attr_associated_elements =
2594       element->GetElementArrayAttribute(attribute);
2595   if (!attr_associated_elements)
2596     return;
2597 
2598   for (const auto& element : attr_associated_elements.value())
2599     elements.push_back(element);
2600 }
2601 
AriaLabelledbyElementVector(HeapVector<Member<Element>> & elements,Vector<String> & ids) const2602 void AXObject::AriaLabelledbyElementVector(
2603     HeapVector<Member<Element>>& elements,
2604     Vector<String>& ids) const {
2605   // Try both spellings, but prefer aria-labelledby, which is the official spec.
2606   ElementsFromAttribute(elements, html_names::kAriaLabelledbyAttr, ids);
2607   if (!ids.size())
2608     ElementsFromAttribute(elements, html_names::kAriaLabeledbyAttr, ids);
2609 }
2610 
TextFromAriaLabelledby(AXObjectSet & visited,AXRelatedObjectVector * related_objects,Vector<String> & ids) const2611 String AXObject::TextFromAriaLabelledby(AXObjectSet& visited,
2612                                         AXRelatedObjectVector* related_objects,
2613                                         Vector<String>& ids) const {
2614   HeapVector<Member<Element>> elements;
2615   AriaLabelledbyElementVector(elements, ids);
2616   return TextFromElements(true, visited, elements, related_objects);
2617 }
2618 
TextFromAriaDescribedby(AXRelatedObjectVector * related_objects,Vector<String> & ids) const2619 String AXObject::TextFromAriaDescribedby(AXRelatedObjectVector* related_objects,
2620                                          Vector<String>& ids) const {
2621   AXObjectSet visited;
2622   HeapVector<Member<Element>> elements;
2623   ElementsFromAttribute(elements, html_names::kAriaDescribedbyAttr, ids);
2624   return TextFromElements(true, visited, elements, related_objects);
2625 }
2626 
BackgroundColor() const2627 RGBA32 AXObject::BackgroundColor() const {
2628   UpdateCachedAttributeValuesIfNeeded();
2629   return cached_background_color_;
2630 }
2631 
Orientation() const2632 AccessibilityOrientation AXObject::Orientation() const {
2633   // In ARIA 1.1, the default value for aria-orientation changed from
2634   // horizontal to undefined.
2635   return kAccessibilityOrientationUndefined;
2636 }
2637 
LoadInlineTextBoxes()2638 void AXObject::LoadInlineTextBoxes() {}
2639 
NextOnLine() const2640 AXObject* AXObject::NextOnLine() const {
2641   return nullptr;
2642 }
2643 
PreviousOnLine() const2644 AXObject* AXObject::PreviousOnLine() const {
2645   return nullptr;
2646 }
2647 
2648 base::Optional<const DocumentMarker::MarkerType>
GetAriaSpellingOrGrammarMarker() const2649 AXObject::GetAriaSpellingOrGrammarMarker() const {
2650   AtomicString aria_invalid_value;
2651   const AncestorsIterator iter = std::find_if(
2652       UnignoredAncestorsBegin(), UnignoredAncestorsEnd(),
2653       [&aria_invalid_value](const AXObject& ancestor) {
2654         return ancestor.HasAOMPropertyOrARIAAttribute(
2655                    AOMStringProperty::kInvalid, aria_invalid_value) ||
2656                ancestor.IsLineBreakingObject();
2657       });
2658 
2659   if (iter == UnignoredAncestorsEnd())
2660     return base::nullopt;
2661   if (EqualIgnoringASCIICase(aria_invalid_value, "spelling"))
2662     return DocumentMarker::kSpelling;
2663   if (EqualIgnoringASCIICase(aria_invalid_value, "grammar"))
2664     return DocumentMarker::kGrammar;
2665   return base::nullopt;
2666 }
2667 
GetDocumentMarkers(VectorOf<DocumentMarker::MarkerType> * marker_types,VectorOf<AXRange> * marker_ranges) const2668 void AXObject::GetDocumentMarkers(
2669     VectorOf<DocumentMarker::MarkerType>* marker_types,
2670     VectorOf<AXRange>* marker_ranges) const {}
2671 
TextCharacterOffsets(Vector<int> &) const2672 void AXObject::TextCharacterOffsets(Vector<int>&) const {}
2673 
GetWordBoundaries(Vector<int> & word_starts,Vector<int> & word_ends) const2674 void AXObject::GetWordBoundaries(Vector<int>& word_starts,
2675                                  Vector<int>& word_ends) const {}
2676 
TextOffsetInFormattingContext(int offset) const2677 int AXObject::TextOffsetInFormattingContext(int offset) const {
2678   DCHECK_GE(offset, 0);
2679   return offset;
2680 }
2681 
TextOffsetInContainer(int offset) const2682 int AXObject::TextOffsetInContainer(int offset) const {
2683   DCHECK_GE(offset, 0);
2684   return offset;
2685 }
2686 
Action() const2687 ax::mojom::blink::DefaultActionVerb AXObject::Action() const {
2688   Element* action_element = ActionElement();
2689   if (!action_element)
2690     return ax::mojom::blink::DefaultActionVerb::kNone;
2691 
2692   // TODO(dmazzoni): Ensure that combo box text field is handled here.
2693   if (IsTextControl())
2694     return ax::mojom::blink::DefaultActionVerb::kActivate;
2695 
2696   if (IsCheckable()) {
2697     return CheckedState() != ax::mojom::blink::CheckedState::kTrue
2698                ? ax::mojom::blink::DefaultActionVerb::kCheck
2699                : ax::mojom::blink::DefaultActionVerb::kUncheck;
2700   }
2701 
2702   // If this object cannot receive focus and has a button role, use click as
2703   // the default action. On the AuraLinux platform, the press action is a
2704   // signal to users that they can trigger the action using the keyboard, while
2705   // a click action means the user should trigger the action via a simulated
2706   // click. If this object cannot receive focus, it's impossible to trigger it
2707   // with a key press.
2708   if (RoleValue() == ax::mojom::blink::Role::kButton && !CanSetFocusAttribute())
2709     return ax::mojom::blink::DefaultActionVerb::kClick;
2710 
2711   switch (RoleValue()) {
2712     case ax::mojom::blink::Role::kButton:
2713     case ax::mojom::blink::Role::kDisclosureTriangle:
2714     case ax::mojom::blink::Role::kToggleButton:
2715       return ax::mojom::blink::DefaultActionVerb::kPress;
2716     case ax::mojom::blink::Role::kListBoxOption:
2717     case ax::mojom::blink::Role::kMenuItemRadio:
2718     case ax::mojom::blink::Role::kMenuItem:
2719     case ax::mojom::blink::Role::kMenuListOption:
2720       return ax::mojom::blink::DefaultActionVerb::kSelect;
2721     case ax::mojom::blink::Role::kLink:
2722       return ax::mojom::blink::DefaultActionVerb::kJump;
2723     case ax::mojom::blink::Role::kComboBoxMenuButton:
2724     case ax::mojom::blink::Role::kPopUpButton:
2725       return ax::mojom::blink::DefaultActionVerb::kOpen;
2726     default:
2727       if (action_element == GetNode())
2728         return ax::mojom::blink::DefaultActionVerb::kClick;
2729       return ax::mojom::blink::DefaultActionVerb::kClickAncestor;
2730   }
2731 }
2732 
AriaPressedIsPresent() const2733 bool AXObject::AriaPressedIsPresent() const {
2734   AtomicString result;
2735   return HasAOMPropertyOrARIAAttribute(AOMStringProperty::kPressed, result);
2736 }
2737 
AriaCheckedIsPresent() const2738 bool AXObject::AriaCheckedIsPresent() const {
2739   AtomicString result;
2740   return HasAOMPropertyOrARIAAttribute(AOMStringProperty::kChecked, result);
2741 }
2742 
SupportsARIAExpanded() const2743 bool AXObject::SupportsARIAExpanded() const {
2744   switch (RoleValue()) {
2745     case ax::mojom::blink::Role::kApplication:
2746     case ax::mojom::blink::Role::kButton:
2747     case ax::mojom::blink::Role::kCheckBox:
2748     case ax::mojom::blink::Role::kColumnHeader:
2749     case ax::mojom::blink::Role::kComboBoxGrouping:
2750     case ax::mojom::blink::Role::kComboBoxMenuButton:
2751     case ax::mojom::blink::Role::kDisclosureTriangle:
2752     case ax::mojom::blink::Role::kListBox:
2753     case ax::mojom::blink::Role::kLink:
2754     case ax::mojom::blink::Role::kPopUpButton:
2755     case ax::mojom::blink::Role::kMenuItem:
2756     case ax::mojom::blink::Role::kMenuItemCheckBox:
2757     case ax::mojom::blink::Role::kMenuItemRadio:
2758     case ax::mojom::blink::Role::kRow:
2759     case ax::mojom::blink::Role::kRowHeader:
2760     case ax::mojom::blink::Role::kSwitch:
2761     case ax::mojom::blink::Role::kTab:
2762     case ax::mojom::blink::Role::kTextFieldWithComboBox:
2763     case ax::mojom::blink::Role::kTreeItem:
2764       return true;
2765     case ax::mojom::blink::Role::kCell:
2766       // TODO(Accessibility): aria-expanded is supported on grid cells but not
2767       // on cells inside a static table. Consider creating separate internal
2768       // roles so that we can easily distinguish these two types. See also
2769       // IsSubWidget().
2770       return true;
2771     default:
2772       return false;
2773   }
2774 }
2775 
IsGlobalARIAAttribute(const AtomicString & name)2776 bool IsGlobalARIAAttribute(const AtomicString& name) {
2777   if (!name.StartsWith("ARIA"))
2778     return false;
2779   if (name.StartsWith("ARIA-ATOMIC"))
2780     return true;
2781   if (name.StartsWith("ARIA-BUSY"))
2782     return true;
2783   if (name.StartsWith("ARIA-CONTROLS"))
2784     return true;
2785   if (name.StartsWith("ARIA-CURRENT"))
2786     return true;
2787   if (name.StartsWith("ARIA-DESCRIBEDBY"))
2788     return true;
2789   if (name.StartsWith("ARIA-DETAILS"))
2790     return true;
2791   if (name.StartsWith("ARIA-DISABLED"))
2792     return true;
2793   if (name.StartsWith("ARIA-DROPEFFECT"))
2794     return true;
2795   if (name.StartsWith("ARIA-ERRORMESSAGE"))
2796     return true;
2797   if (name.StartsWith("ARIA-FLOWTO"))
2798     return true;
2799   if (name.StartsWith("ARIA-GRABBED"))
2800     return true;
2801   if (name.StartsWith("ARIA-HASPOPUP"))
2802     return true;
2803   if (name.StartsWith("ARIA-HIDDEN"))
2804     return true;
2805   if (name.StartsWith("ARIA-INVALID"))
2806     return true;
2807   if (name.StartsWith("ARIA-KEYSHORTCUTS"))
2808     return true;
2809   if (name.StartsWith("ARIA-LABEL"))
2810     return true;
2811   if (name.StartsWith("ARIA-LABELEDBY"))
2812     return true;
2813   if (name.StartsWith("ARIA-LABELLEDBY"))
2814     return true;
2815   if (name.StartsWith("ARIA-LIVE"))
2816     return true;
2817   if (name.StartsWith("ARIA-OWNS"))
2818     return true;
2819   if (name.StartsWith("ARIA-RELEVANT"))
2820     return true;
2821   if (name.StartsWith("ARIA-ROLEDESCRIPTION"))
2822     return true;
2823   return false;
2824 }
2825 
HasGlobalARIAAttribute() const2826 bool AXObject::HasGlobalARIAAttribute() const {
2827   auto* element = GetElement();
2828   if (!element)
2829     return false;
2830 
2831   AttributeCollection attributes = element->AttributesWithoutUpdate();
2832   for (const Attribute& attr : attributes) {
2833     // Attributes cache their uppercase names.
2834     auto name = attr.GetName().LocalNameUpper();
2835     if (IsGlobalARIAAttribute(name))
2836       return true;
2837   }
2838   if (!element->DidAttachInternals())
2839     return false;
2840   const auto& internals_attributes =
2841       element->EnsureElementInternals().GetAttributes();
2842   for (const QualifiedName& attr : internals_attributes.Keys()) {
2843     if (IsGlobalARIAAttribute(attr.LocalNameUpper()))
2844       return true;
2845   }
2846   return false;
2847 }
2848 
IndexInParent() const2849 int AXObject::IndexInParent() const {
2850   DCHECK(AccessibilityIsIncludedInTree())
2851       << "IndexInParent is only valid when a node is included in the tree";
2852   if (!ParentObjectIncludedInTree())
2853     return 0;
2854 
2855   const AXObjectVector& siblings =
2856       ParentObjectIncludedInTree()->ChildrenIncludingIgnored();
2857   wtf_size_t index = siblings.Find(this);
2858   DCHECK_NE(index, kNotFound);
2859   return (index == kNotFound) ? 0 : static_cast<int>(index);
2860 }
2861 
IsLiveRegionRoot() const2862 bool AXObject::IsLiveRegionRoot() const {
2863   const AtomicString& live_region = LiveRegionStatus();
2864   return !live_region.IsEmpty();
2865 }
2866 
IsActiveLiveRegionRoot() const2867 bool AXObject::IsActiveLiveRegionRoot() const {
2868   const AtomicString& live_region = LiveRegionStatus();
2869   return !live_region.IsEmpty() && !EqualIgnoringASCIICase(live_region, "off");
2870 }
2871 
Restriction() const2872 AXRestriction AXObject::Restriction() const {
2873   // According to ARIA, all elements of the base markup can be disabled.
2874   // According to CORE-AAM, any focusable descendant of aria-disabled
2875   // ancestor is also disabled.
2876   bool is_disabled;
2877   if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kDisabled,
2878                                     is_disabled)) {
2879     // Has aria-disabled, overrides native markup determining disabled.
2880     if (is_disabled)
2881       return kRestrictionDisabled;
2882   } else if (CanSetFocusAttribute() && IsDescendantOfDisabledNode()) {
2883     // aria-disabled on an ancestor propagates to focusable descendants.
2884     return kRestrictionDisabled;
2885   }
2886 
2887   // Check aria-readonly if supported by current role.
2888   bool is_read_only;
2889   if (SupportsARIAReadOnly() &&
2890       HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kReadOnly,
2891                                     is_read_only)) {
2892     // ARIA overrides other readonly state markup.
2893     return is_read_only ? kRestrictionReadOnly : kRestrictionNone;
2894   }
2895 
2896   // This is a node that is not readonly and not disabled.
2897   return kRestrictionNone;
2898 }
2899 
DetermineAccessibilityRole()2900 ax::mojom::blink::Role AXObject::DetermineAccessibilityRole() {
2901   aria_role_ = DetermineAriaRoleAttribute();
2902   return aria_role_;
2903 }
2904 
AriaRoleAttribute() const2905 ax::mojom::blink::Role AXObject::AriaRoleAttribute() const {
2906   return aria_role_;
2907 }
2908 
DetermineAriaRoleAttribute() const2909 ax::mojom::blink::Role AXObject::DetermineAriaRoleAttribute() const {
2910   const AtomicString& aria_role =
2911       GetAOMPropertyOrARIAAttribute(AOMStringProperty::kRole);
2912   if (aria_role.IsNull() || aria_role.IsEmpty())
2913     return ax::mojom::blink::Role::kUnknown;
2914 
2915   ax::mojom::blink::Role role = AriaRoleToWebCoreRole(aria_role);
2916 
2917   // ARIA states if an item can get focus, it should not be presentational.
2918   // It also states user agents should ignore the presentational role if
2919   // the element has global ARIA states and properties.
2920   if ((role == ax::mojom::blink::Role::kNone ||
2921        role == ax::mojom::blink::Role::kPresentational) &&
2922       (CanSetFocusAttribute() || HasGlobalARIAAttribute()))
2923     return ax::mojom::blink::Role::kUnknown;
2924 
2925   if (role == ax::mojom::blink::Role::kButton)
2926     role = ButtonRoleType();
2927 
2928   role = RemapAriaRoleDueToParent(role);
2929 
2930   // Distinguish between different uses of the "combobox" role:
2931   //
2932   // ax::mojom::blink::Role::kComboBoxGrouping:
2933   //   <div role="combobox"><input></div>
2934   // ax::mojom::blink::Role::kTextFieldWithComboBox:
2935   //   <input role="combobox">
2936   // ax::mojom::blink::Role::kComboBoxMenuButton:
2937   //   <div tabindex=0 role="combobox">Select</div>
2938   if (role == ax::mojom::blink::Role::kComboBoxGrouping) {
2939     if (IsNativeTextControl())
2940       role = ax::mojom::blink::Role::kTextFieldWithComboBox;
2941     else if (GetElement() && GetElement()->SupportsFocus())
2942       role = ax::mojom::blink::Role::kComboBoxMenuButton;
2943   }
2944 
2945   if (role != ax::mojom::blink::Role::kUnknown)
2946     return role;
2947 
2948   return ax::mojom::blink::Role::kUnknown;
2949 }
2950 
RemapAriaRoleDueToParent(ax::mojom::blink::Role role) const2951 ax::mojom::blink::Role AXObject::RemapAriaRoleDueToParent(
2952     ax::mojom::blink::Role role) const {
2953   // Some objects change their role based on their parent.
2954   // However, asking for the unignoredParent calls accessibilityIsIgnored(),
2955   // which can trigger a loop.  While inside the call stack of creating an
2956   // element, we need to avoid accessibilityIsIgnored().
2957   // https://bugs.webkit.org/show_bug.cgi?id=65174
2958 
2959   // Don't return table roles unless inside a table-like container.
2960   switch (role) {
2961     case ax::mojom::blink::Role::kRow:
2962     case ax::mojom::blink::Role::kRowGroup:
2963     case ax::mojom::blink::Role::kCell:
2964     case ax::mojom::blink::Role::kRowHeader:
2965     case ax::mojom::blink::Role::kColumnHeader:
2966       for (AXObject* ancestor = ParentObjectUnignored(); ancestor;
2967            ancestor = ancestor->ParentObjectUnignored()) {
2968         ax::mojom::blink::Role ancestor_aria_role =
2969             ancestor->AriaRoleAttribute();
2970         if (ancestor_aria_role == ax::mojom::blink::Role::kCell)
2971           return ax::mojom::blink::Role::kGenericContainer;  // In another cell,
2972                                                              // illegal.
2973         if (ancestor->IsTableLikeRole())
2974           return role;  // Inside a table: ARIA role is legal.
2975       }
2976       return ax::mojom::blink::Role::kGenericContainer;  // Not in a table.
2977     default:
2978       break;
2979   }
2980 
2981   if (role != ax::mojom::blink::Role::kListBoxOption &&
2982       role != ax::mojom::blink::Role::kMenuItem)
2983     return role;
2984 
2985   for (AXObject* parent = ParentObject();
2986        parent && !parent->AccessibilityIsIgnored();
2987        parent = parent->ParentObject()) {
2988     ax::mojom::blink::Role parent_aria_role = parent->AriaRoleAttribute();
2989 
2990     // Selects and listboxes both have options as child roles, but they map to
2991     // different roles within WebCore.
2992     if (role == ax::mojom::blink::Role::kListBoxOption &&
2993         parent_aria_role == ax::mojom::blink::Role::kMenu)
2994       return ax::mojom::blink::Role::kMenuItem;
2995 
2996     // If the parent had a different role, then we don't need to continue
2997     // searching up the chain.
2998     if (parent_aria_role != ax::mojom::blink::Role::kUnknown)
2999       break;
3000   }
3001 
3002   return role;
3003 }
3004 
IsEditableRoot() const3005 bool AXObject::IsEditableRoot() const {
3006   UpdateCachedAttributeValuesIfNeeded();
3007   return cached_is_editable_root_;
3008 }
3009 
LiveRegionRoot() const3010 AXObject* AXObject::LiveRegionRoot() const {
3011   UpdateCachedAttributeValuesIfNeeded();
3012   return cached_live_region_root_;
3013 }
3014 
LiveRegionAtomic() const3015 bool AXObject::LiveRegionAtomic() const {
3016   bool atomic = false;
3017   if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kAtomic, atomic))
3018     return atomic;
3019 
3020   // ARIA roles "alert" and "status" should have an implicit aria-atomic value
3021   // of true.
3022   return RoleValue() == ax::mojom::blink::Role::kAlert ||
3023          RoleValue() == ax::mojom::blink::Role::kStatus;
3024 }
3025 
ContainerLiveRegionStatus() const3026 const AtomicString& AXObject::ContainerLiveRegionStatus() const {
3027   UpdateCachedAttributeValuesIfNeeded();
3028   return cached_live_region_root_ ? cached_live_region_root_->LiveRegionStatus()
3029                                   : g_null_atom;
3030 }
3031 
ContainerLiveRegionRelevant() const3032 const AtomicString& AXObject::ContainerLiveRegionRelevant() const {
3033   UpdateCachedAttributeValuesIfNeeded();
3034   return cached_live_region_root_
3035              ? cached_live_region_root_->LiveRegionRelevant()
3036              : g_null_atom;
3037 }
3038 
ContainerLiveRegionAtomic() const3039 bool AXObject::ContainerLiveRegionAtomic() const {
3040   UpdateCachedAttributeValuesIfNeeded();
3041   return cached_live_region_root_ &&
3042          cached_live_region_root_->LiveRegionAtomic();
3043 }
3044 
ContainerLiveRegionBusy() const3045 bool AXObject::ContainerLiveRegionBusy() const {
3046   UpdateCachedAttributeValuesIfNeeded();
3047   return cached_live_region_root_ &&
3048          cached_live_region_root_->AOMPropertyOrARIAAttributeIsTrue(
3049              AOMBooleanProperty::kBusy);
3050 }
3051 
ElementAccessibilityHitTest(const IntPoint & point) const3052 AXObject* AXObject::ElementAccessibilityHitTest(const IntPoint& point) const {
3053   // Check if there are any mock elements that need to be handled.
3054   for (const auto& child : ChildrenIncludingIgnored()) {
3055     if (child->IsMockObject() &&
3056         child->GetBoundsInFrameCoordinates().Contains(point))
3057       return child->ElementAccessibilityHitTest(point);
3058   }
3059 
3060   return const_cast<AXObject*>(this);
3061 }
3062 
UnignoredAncestorsBegin() const3063 AXObject::AncestorsIterator AXObject::UnignoredAncestorsBegin() const {
3064   AXObject* parent = ParentObjectUnignored();
3065   if (parent)
3066     return AXObject::AncestorsIterator(*parent);
3067   return UnignoredAncestorsEnd();
3068 }
3069 
UnignoredAncestorsEnd() const3070 AXObject::AncestorsIterator AXObject::UnignoredAncestorsEnd() const {
3071   return AXObject::AncestorsIterator();
3072 }
3073 
GetInOrderTraversalIterator()3074 AXObject::InOrderTraversalIterator AXObject::GetInOrderTraversalIterator() {
3075   return InOrderTraversalIterator(*this);
3076 }
3077 
ChildCountIncludingIgnored() const3078 int AXObject::ChildCountIncludingIgnored() const {
3079   return HasIndirectChildren() ? 0 : int{ChildrenIncludingIgnored().size()};
3080 }
3081 
ChildAtIncludingIgnored(int index) const3082 AXObject* AXObject::ChildAtIncludingIgnored(int index) const {
3083   // We need to use "ChildCountIncludingIgnored()" and
3084   // "ChildrenIncludingIgnored()" instead of using the "children_" member
3085   // directly, because we might need to update children and check for the
3086   // presence of indirect children.
3087   if (index < 0 || index >= ChildCountIncludingIgnored())
3088     return nullptr;
3089   return ChildrenIncludingIgnored()[index];
3090 }
3091 
ChildrenIncludingIgnored() const3092 const AXObject::AXObjectVector& AXObject::ChildrenIncludingIgnored() const {
3093   return const_cast<AXObject*>(this)->ChildrenIncludingIgnored();
3094 }
3095 
ChildrenIncludingIgnored()3096 const AXObject::AXObjectVector& AXObject::ChildrenIncludingIgnored() {
3097   UpdateChildrenIfNecessary();
3098   return children_;
3099 }
3100 
UnignoredChildren() const3101 const AXObject::AXObjectVector AXObject::UnignoredChildren() const {
3102   return const_cast<AXObject*>(this)->UnignoredChildren();
3103 }
3104 
UnignoredChildren()3105 const AXObject::AXObjectVector AXObject::UnignoredChildren() {
3106   if (!AccessibilityIsIncludedInTree()) {
3107     NOTREACHED() << "We don't support finding the unignored children of "
3108                     "objects excluded from the accessibility tree.";
3109     return {};
3110   }
3111 
3112   UpdateChildrenIfNecessary();
3113 
3114   // Capture only descendants that are not accessibility ignored, and that are
3115   // one level deeper than the current object after flattening any accessibility
3116   // ignored descendants.
3117   //
3118   // For example :
3119   // ++A
3120   // ++++B
3121   // ++++C IGNORED
3122   // ++++++F
3123   // ++++D
3124   // ++++++G
3125   // ++++E IGNORED
3126   // ++++++H IGNORED
3127   // ++++++++J
3128   // ++++++I
3129   //
3130   // Objects [B, F, D, I, J] will be returned, since after flattening all
3131   // ignored objects ,those are the ones that are one level deep.
3132 
3133   AXObjectVector unignored_children;
3134   AXObject* child = FirstChildIncludingIgnored();
3135   while (child && child != this) {
3136     if (child->AccessibilityIsIgnored()) {
3137       child = child->NextInPreOrderIncludingIgnored(this);
3138       continue;
3139     }
3140 
3141     unignored_children.push_back(child);
3142     for (; child != this; child = child->ParentObjectIncludedInTree()) {
3143       if (AXObject* sibling = child->NextSiblingIncludingIgnored()) {
3144         child = sibling;
3145         break;
3146       }
3147     }
3148   }
3149 
3150   return unignored_children;
3151 }
3152 
FirstChildIncludingIgnored() const3153 AXObject* AXObject::FirstChildIncludingIgnored() const {
3154   return ChildCountIncludingIgnored() ? *ChildrenIncludingIgnored().begin()
3155                                       : nullptr;
3156 }
3157 
LastChildIncludingIgnored() const3158 AXObject* AXObject::LastChildIncludingIgnored() const {
3159   return ChildCountIncludingIgnored() ? *(ChildrenIncludingIgnored().end() - 1)
3160                                       : nullptr;
3161 }
3162 
DeepestFirstChildIncludingIgnored() const3163 AXObject* AXObject::DeepestFirstChildIncludingIgnored() const {
3164   if (!ChildCountIncludingIgnored())
3165     return nullptr;
3166 
3167   AXObject* deepest_child = FirstChildIncludingIgnored();
3168   while (deepest_child->ChildCountIncludingIgnored())
3169     deepest_child = deepest_child->FirstChildIncludingIgnored();
3170 
3171   return deepest_child;
3172 }
3173 
DeepestLastChildIncludingIgnored() const3174 AXObject* AXObject::DeepestLastChildIncludingIgnored() const {
3175   if (!ChildCountIncludingIgnored())
3176     return nullptr;
3177 
3178   AXObject* deepest_child = LastChildIncludingIgnored();
3179   while (deepest_child->ChildCountIncludingIgnored())
3180     deepest_child = deepest_child->LastChildIncludingIgnored();
3181 
3182   return deepest_child;
3183 }
3184 
IsAncestorOf(const AXObject & descendant) const3185 bool AXObject::IsAncestorOf(const AXObject& descendant) const {
3186   return descendant.IsDescendantOf(*this);
3187 }
3188 
IsDescendantOf(const AXObject & ancestor) const3189 bool AXObject::IsDescendantOf(const AXObject& ancestor) const {
3190   const AXObject* parent = ParentObject();
3191   while (parent && parent != &ancestor)
3192     parent = parent->ParentObject();
3193   return !!parent;
3194 }
3195 
NextSiblingIncludingIgnored() const3196 AXObject* AXObject::NextSiblingIncludingIgnored() const {
3197   if (!AccessibilityIsIncludedInTree()) {
3198     NOTREACHED() << "We don't support iterating over objects excluded "
3199                     "from the accessibility tree.";
3200     return nullptr;
3201   }
3202 
3203   const AXObject* parent_in_tree = ParentObjectIncludedInTree();
3204   if (!parent_in_tree)
3205     return nullptr;
3206 
3207   const int index_in_parent = IndexInParent();
3208   if (index_in_parent < parent_in_tree->ChildCountIncludingIgnored() - 1)
3209     return parent_in_tree->ChildAtIncludingIgnored(index_in_parent + 1);
3210   return nullptr;
3211 }
3212 
PreviousSiblingIncludingIgnored() const3213 AXObject* AXObject::PreviousSiblingIncludingIgnored() const {
3214   if (!AccessibilityIsIncludedInTree()) {
3215     NOTREACHED() << "We don't support iterating over objects excluded "
3216                     "from the accessibility tree.";
3217     return nullptr;
3218   }
3219 
3220   const AXObject* parent_in_tree = ParentObjectIncludedInTree();
3221   if (!parent_in_tree)
3222     return nullptr;
3223 
3224   const int index_in_parent = IndexInParent();
3225   if (index_in_parent > 0)
3226     return parent_in_tree->ChildAtIncludingIgnored(index_in_parent - 1);
3227   return nullptr;
3228 }
3229 
NextInPreOrderIncludingIgnored(const AXObject * within) const3230 AXObject* AXObject::NextInPreOrderIncludingIgnored(
3231     const AXObject* within) const {
3232   if (!AccessibilityIsIncludedInTree()) {
3233     NOTREACHED() << "We don't support iterating over objects excluded "
3234                     "from the accessibility tree.";
3235     return nullptr;
3236   }
3237 
3238   if (ChildCountIncludingIgnored())
3239     return FirstChildIncludingIgnored();
3240 
3241   if (within == this)
3242     return nullptr;
3243 
3244   const AXObject* current = this;
3245   AXObject* next = current->NextSiblingIncludingIgnored();
3246   for (; !next; next = current->NextSiblingIncludingIgnored()) {
3247     current = current->ParentObjectIncludedInTree();
3248     if (!current || within == current)
3249       return nullptr;
3250   }
3251   return next;
3252 }
3253 
PreviousInPreOrderIncludingIgnored(const AXObject * within) const3254 AXObject* AXObject::PreviousInPreOrderIncludingIgnored(
3255     const AXObject* within) const {
3256   if (!AccessibilityIsIncludedInTree()) {
3257     NOTREACHED() << "We don't support iterating over objects excluded "
3258                     "from the accessibility tree.";
3259     return nullptr;
3260   }
3261   if (within == this)
3262     return nullptr;
3263 
3264   if (AXObject* sibling = PreviousSiblingIncludingIgnored()) {
3265     if (sibling->ChildCountIncludingIgnored())
3266       return sibling->DeepestLastChildIncludingIgnored();
3267     return sibling;
3268   }
3269 
3270   return ParentObjectIncludedInTree();
3271 }
3272 
PreviousInPostOrderIncludingIgnored(const AXObject * within) const3273 AXObject* AXObject::PreviousInPostOrderIncludingIgnored(
3274     const AXObject* within) const {
3275   if (!AccessibilityIsIncludedInTree()) {
3276     NOTREACHED() << "We don't support iterating over objects excluded "
3277                     "from the accessibility tree.";
3278     return nullptr;
3279   }
3280 
3281   if (ChildCountIncludingIgnored())
3282     return LastChildIncludingIgnored();
3283 
3284   if (within == this)
3285     return nullptr;
3286 
3287   const AXObject* current = this;
3288   AXObject* previous = current->PreviousSiblingIncludingIgnored();
3289   for (; !previous; previous = current->PreviousSiblingIncludingIgnored()) {
3290     current = current->ParentObjectIncludedInTree();
3291     if (!current || within == current)
3292       return nullptr;
3293   }
3294   return previous;
3295 }
3296 
UnignoredChildCount() const3297 int AXObject::UnignoredChildCount() const {
3298   return int{UnignoredChildren().size()};
3299 }
3300 
UnignoredChildAt(int index) const3301 AXObject* AXObject::UnignoredChildAt(int index) const {
3302   const AXObjectVector unignored_children = UnignoredChildren();
3303   if (index < 0 || index >= int{unignored_children.size()})
3304     return nullptr;
3305   return unignored_children[index];
3306 }
3307 
UnignoredNextSibling() const3308 AXObject* AXObject::UnignoredNextSibling() const {
3309   if (AccessibilityIsIgnored()) {
3310     NOTREACHED() << "We don't support finding unignored siblings for ignored "
3311                     "objects because it is not clear whether to search for the "
3312                     "sibling in the unignored tree or in the whole tree.";
3313     return nullptr;
3314   }
3315 
3316   // Find the next sibling for the same unignored parent object,
3317   // flattening accessibility ignored objects.
3318   //
3319   // For example :
3320   // ++A
3321   // ++++B
3322   // ++++C IGNORED
3323   // ++++++E
3324   // ++++D
3325   // Objects [B, E, D] will be siblings since C is ignored.
3326 
3327   const AXObject* unignored_parent = ParentObjectUnignored();
3328   const AXObject* current_obj = this;
3329   while (current_obj) {
3330     AXObject* sibling = current_obj->NextSiblingIncludingIgnored();
3331     if (sibling) {
3332       // If we found an ignored sibling, walk in next pre-order
3333       // until an unignored object is found, flattening the ignored object.
3334       while (sibling && sibling->AccessibilityIsIgnored()) {
3335         sibling = sibling->NextInPreOrderIncludingIgnored(unignored_parent);
3336       }
3337       return sibling;
3338     }
3339 
3340     // If a sibling has not been found, try again with the parent object,
3341     // until the unignored parent is reached.
3342     current_obj = current_obj->ParentObjectIncludedInTree();
3343     if (!current_obj || !current_obj->AccessibilityIsIgnored())
3344       return nullptr;
3345   }
3346   return nullptr;
3347 }
3348 
UnignoredPreviousSibling() const3349 AXObject* AXObject::UnignoredPreviousSibling() const {
3350   if (AccessibilityIsIgnored()) {
3351     NOTREACHED() << "We don't support finding unignored siblings for ignored "
3352                     "objects because it is not clear whether to search for the "
3353                     "sibling in the unignored tree or in the whole tree.";
3354     return nullptr;
3355   }
3356 
3357   // Find the previous sibling for the same unignored parent object,
3358   // flattening accessibility ignored objects.
3359   //
3360   // For example :
3361   // ++A
3362   // ++++B
3363   // ++++C IGNORED
3364   // ++++++E
3365   // ++++D
3366   // Objects [B, E, D] will be siblings since C is ignored.
3367 
3368   const AXObject* current_obj = this;
3369   while (current_obj) {
3370     AXObject* sibling = current_obj->PreviousSiblingIncludingIgnored();
3371     if (sibling) {
3372       const AXObject* unignored_parent = ParentObjectUnignored();
3373       // If we found an ignored sibling, walk in previous post-order
3374       // until an unignored object is found, flattening the ignored object.
3375       while (sibling && sibling->AccessibilityIsIgnored()) {
3376         sibling =
3377             sibling->PreviousInPostOrderIncludingIgnored(unignored_parent);
3378       }
3379       return sibling;
3380     }
3381 
3382     // If a sibling has not been found, try again with the parent object,
3383     // until the unignored parent is reached.
3384     current_obj = current_obj->ParentObjectIncludedInTree();
3385     if (!current_obj || !current_obj->AccessibilityIsIgnored())
3386       return nullptr;
3387   }
3388   return nullptr;
3389 }
3390 
UnignoredNextInPreOrder() const3391 AXObject* AXObject::UnignoredNextInPreOrder() const {
3392   AXObject* next = NextInPreOrderIncludingIgnored();
3393   while (next && next->AccessibilityIsIgnored()) {
3394     next = next->NextInPreOrderIncludingIgnored();
3395   }
3396   return next;
3397 }
3398 
UnignoredPreviousInPreOrder() const3399 AXObject* AXObject::UnignoredPreviousInPreOrder() const {
3400   AXObject* previous = PreviousInPreOrderIncludingIgnored();
3401   while (previous && previous->AccessibilityIsIgnored()) {
3402     previous = previous->PreviousInPreOrderIncludingIgnored();
3403   }
3404   return previous;
3405 }
3406 
ParentObject() const3407 AXObject* AXObject::ParentObject() const {
3408   if (IsDetached())
3409     return nullptr;
3410 
3411   if (parent_)
3412     return parent_;
3413 
3414   if (AXObjectCache().IsAriaOwned(this))
3415     return AXObjectCache().GetAriaOwnedParent(this);
3416 
3417   return ComputeParent();
3418 }
3419 
ParentObjectIfExists() const3420 AXObject* AXObject::ParentObjectIfExists() const {
3421   if (IsDetached())
3422     return nullptr;
3423 
3424   if (parent_)
3425     return parent_;
3426 
3427   return ComputeParentIfExists();
3428 }
3429 
ParentObjectUnignored() const3430 AXObject* AXObject::ParentObjectUnignored() const {
3431   AXObject* parent;
3432   for (parent = ParentObject(); parent && parent->AccessibilityIsIgnored();
3433        parent = parent->ParentObject()) {
3434   }
3435 
3436   return parent;
3437 }
3438 
ParentObjectIncludedInTree() const3439 AXObject* AXObject::ParentObjectIncludedInTree() const {
3440   AXObject* parent;
3441   for (parent = ParentObject();
3442        parent && !parent->AccessibilityIsIncludedInTree();
3443        parent = parent->ParentObject()) {
3444   }
3445 
3446   return parent;
3447 }
3448 
3449 // Container widgets are those that a user tabs into and arrows around
3450 // sub-widgets
IsContainerWidget() const3451 bool AXObject::IsContainerWidget() const {
3452   return ui::IsContainerWithSelectableChildren(RoleValue());
3453 }
3454 
ContainerWidget() const3455 AXObject* AXObject::ContainerWidget() const {
3456   AXObject* ancestor = ParentObjectUnignored();
3457   while (ancestor && !ancestor->IsContainerWidget())
3458     ancestor = ancestor->ParentObjectUnignored();
3459 
3460   return ancestor;
3461 }
3462 
UpdateChildrenIfNecessary()3463 void AXObject::UpdateChildrenIfNecessary() {
3464   if (!HasChildren())
3465     AddChildren();
3466 }
3467 
ClearChildren()3468 void AXObject::ClearChildren() {
3469   // Detach all weak pointers from objects to their parents.
3470   for (const auto& child : children_) {
3471     if (child->parent_ == this)
3472       child->DetachFromParent();
3473   }
3474 
3475   children_.clear();
3476   have_children_ = false;
3477 }
3478 
AddAccessibleNodeChildren()3479 void AXObject::AddAccessibleNodeChildren() {
3480   Element* element = GetElement();
3481   if (!element)
3482     return;
3483 
3484   AccessibleNode* accessible_node = element->ExistingAccessibleNode();
3485   if (!accessible_node)
3486     return;
3487 
3488   for (const auto& child : accessible_node->GetChildren())
3489     children_.push_back(AXObjectCache().GetOrCreate(child));
3490 }
3491 
GetElement() const3492 Element* AXObject::GetElement() const {
3493   return DynamicTo<Element>(GetNode());
3494 }
3495 
GetDocument() const3496 Document* AXObject::GetDocument() const {
3497   LocalFrameView* frame_view = DocumentFrameView();
3498   if (!frame_view)
3499     return nullptr;
3500 
3501   return frame_view->GetFrame().GetDocument();
3502 }
3503 
RootScroller() const3504 AXObject* AXObject::RootScroller() const {
3505   Node* global_root_scroller = GetDocument()
3506                                    ->GetPage()
3507                                    ->GlobalRootScrollerController()
3508                                    .GlobalRootScroller();
3509   if (!global_root_scroller)
3510     return nullptr;
3511 
3512   // Only return the root scroller if it's part of the same document.
3513   if (global_root_scroller->GetDocument() != GetDocument())
3514     return nullptr;
3515 
3516   return AXObjectCache().GetOrCreate(global_root_scroller);
3517 }
3518 
DocumentFrameView() const3519 LocalFrameView* AXObject::DocumentFrameView() const {
3520   const AXObject* object = this;
3521   while (object && !object->IsAXLayoutObject())
3522     object = object->ParentObject();
3523 
3524   if (!object)
3525     return nullptr;
3526 
3527   return object->DocumentFrameView();
3528 }
3529 
Language() const3530 AtomicString AXObject::Language() const {
3531   // This method is used when the style engine is either not available on this
3532   // object, e.g. for canvas fallback content, or is unable to determine the
3533   // document's language. We use the following signals to detect the element's
3534   // language, in decreasing priority:
3535   // 1. The [language of a node] as defined in HTML, if known.
3536   // 2. The list of languages the browser sends in the [Accept-Language] header.
3537   // 3. The browser's default language.
3538 
3539   const AtomicString& lang = GetAttribute(html_names::kLangAttr);
3540   if (!lang.IsEmpty())
3541     return lang;
3542 
3543   // Only fallback for the root node, propagating this value down the tree is
3544   // handled browser side within AXNode::GetLanguage.
3545   //
3546   // TODO(chrishall): Consider moving this to AXNodeObject or AXLayoutObject as
3547   // the kRootWebArea node is currently an AXLayoutObject.
3548   if (RoleValue() == ax::mojom::blink::Role::kRootWebArea) {
3549     const Document* document = GetDocument();
3550     if (document) {
3551       // Fall back to the first content language specified in the meta tag.
3552       // This is not part of what the HTML5 Standard suggests but it still
3553       // appears to be necessary.
3554       if (document->ContentLanguage()) {
3555         const String content_languages = document->ContentLanguage();
3556         Vector<String> languages;
3557         content_languages.Split(',', languages);
3558         if (!languages.IsEmpty())
3559           return AtomicString(languages[0].StripWhiteSpace());
3560       }
3561 
3562       if (document->GetPage()) {
3563         // Use the first accept language preference if present.
3564         const String accept_languages =
3565             document->GetPage()->GetChromeClient().AcceptLanguages();
3566         Vector<String> languages;
3567         accept_languages.Split(',', languages);
3568         if (!languages.IsEmpty())
3569           return AtomicString(languages[0].StripWhiteSpace());
3570       }
3571     }
3572 
3573     // As a last resort, return the default language of the browser's UI.
3574     AtomicString default_language = DefaultLanguage();
3575     return default_language;
3576   }
3577 
3578   return g_null_atom;
3579 }
3580 
3581 //
3582 // Scrollable containers.
3583 //
3584 
IsScrollableContainer() const3585 bool AXObject::IsScrollableContainer() const {
3586   return !!GetScrollableAreaIfScrollable();
3587 }
3588 
IsUserScrollable() const3589 bool AXObject::IsUserScrollable() const {
3590   // TODO(accessibility) Actually expose correct info on whether a doc is
3591   // is scrollable or not. Unfortunately IsScrollableContainer() always returns
3592   // true anyway. For now, just expose as scrollable unless overflow is hidden.
3593   if (IsWebArea()) {
3594     if (!GetScrollableAreaIfScrollable() || !GetLayoutObject())
3595       return false;
3596 
3597     const ComputedStyle* style = GetLayoutObject()->Style();
3598     if (!style)
3599       return false;
3600 
3601     return style->ScrollsOverflowY() || style->ScrollsOverflowX();
3602   }
3603 
3604   return GetLayoutObject() && GetLayoutObject()->IsBox() &&
3605          To<LayoutBox>(GetLayoutObject())->CanBeScrolledAndHasScrollableArea();
3606 }
3607 
GetScrollOffset() const3608 IntPoint AXObject::GetScrollOffset() const {
3609   ScrollableArea* area = GetScrollableAreaIfScrollable();
3610   if (!area)
3611     return IntPoint();
3612 
3613   return IntPoint(area->ScrollOffsetInt().Width(),
3614                   area->ScrollOffsetInt().Height());
3615 }
3616 
MinimumScrollOffset() const3617 IntPoint AXObject::MinimumScrollOffset() const {
3618   ScrollableArea* area = GetScrollableAreaIfScrollable();
3619   if (!area)
3620     return IntPoint();
3621 
3622   return IntPoint(area->MinimumScrollOffsetInt().Width(),
3623                   area->MinimumScrollOffsetInt().Height());
3624 }
3625 
MaximumScrollOffset() const3626 IntPoint AXObject::MaximumScrollOffset() const {
3627   ScrollableArea* area = GetScrollableAreaIfScrollable();
3628   if (!area)
3629     return IntPoint();
3630 
3631   return IntPoint(area->MaximumScrollOffsetInt().Width(),
3632                   area->MaximumScrollOffsetInt().Height());
3633 }
3634 
SetScrollOffset(const IntPoint & offset) const3635 void AXObject::SetScrollOffset(const IntPoint& offset) const {
3636   ScrollableArea* area = GetScrollableAreaIfScrollable();
3637   if (!area)
3638     return;
3639 
3640   // TODO(bokan): This should potentially be a UserScroll.
3641   area->SetScrollOffset(ScrollOffset(offset.X(), offset.Y()),
3642                         mojom::blink::ScrollType::kProgrammatic);
3643 }
3644 
IsTableLikeRole() const3645 bool AXObject::IsTableLikeRole() const {
3646   return ui::IsTableLike(RoleValue()) ||
3647          RoleValue() == ax::mojom::blink::Role::kLayoutTable;
3648 }
3649 
IsTableRowLikeRole() const3650 bool AXObject::IsTableRowLikeRole() const {
3651   return ui::IsTableRow(RoleValue()) ||
3652          RoleValue() == ax::mojom::blink::Role::kLayoutTableRow;
3653 }
3654 
IsTableCellLikeRole() const3655 bool AXObject::IsTableCellLikeRole() const {
3656   return ui::IsCellOrTableHeader(RoleValue()) ||
3657          RoleValue() == ax::mojom::blink::Role::kLayoutTableCell;
3658 }
3659 
ColumnCount() const3660 unsigned AXObject::ColumnCount() const {
3661   if (!IsTableLikeRole())
3662     return 0;
3663 
3664   unsigned max_column_count = 0;
3665   for (const auto& row : TableRowChildren()) {
3666     unsigned column_count = row->TableCellChildren().size();
3667     max_column_count = std::max(column_count, max_column_count);
3668   }
3669 
3670   return max_column_count;
3671 }
3672 
RowCount() const3673 unsigned AXObject::RowCount() const {
3674   if (!IsTableLikeRole())
3675     return 0;
3676 
3677   return TableRowChildren().size();
3678 }
3679 
ColumnHeaders(AXObjectVector & headers) const3680 void AXObject::ColumnHeaders(AXObjectVector& headers) const {
3681   if (!IsTableLikeRole())
3682     return;
3683 
3684   for (const auto& row : TableRowChildren()) {
3685     for (const auto& cell : row->TableCellChildren()) {
3686       if (cell->RoleValue() == ax::mojom::blink::Role::kColumnHeader)
3687         headers.push_back(cell);
3688     }
3689   }
3690 }
3691 
RowHeaders(AXObjectVector & headers) const3692 void AXObject::RowHeaders(AXObjectVector& headers) const {
3693   if (!IsTableLikeRole())
3694     return;
3695 
3696   for (const auto& row : TableRowChildren()) {
3697     for (const auto& cell : row->TableCellChildren()) {
3698       if (cell->RoleValue() == ax::mojom::blink::Role::kRowHeader)
3699         headers.push_back(cell);
3700     }
3701   }
3702 }
3703 
CellForColumnAndRow(unsigned target_column_index,unsigned target_row_index) const3704 AXObject* AXObject::CellForColumnAndRow(unsigned target_column_index,
3705                                         unsigned target_row_index) const {
3706   if (!IsTableLikeRole())
3707     return nullptr;
3708 
3709   // Note that this code is only triggered if this is not a LayoutTable,
3710   // i.e. it's an ARIA grid/table.
3711   //
3712   // TODO(dmazzoni): delete this code or rename it "for testing only"
3713   // since it's only needed for Blink web tests and not for production.
3714   unsigned row_index = 0;
3715   for (const auto& row : TableRowChildren()) {
3716     unsigned column_index = 0;
3717     for (const auto& cell : row->TableCellChildren()) {
3718       if (target_column_index == column_index && target_row_index == row_index)
3719         return cell;
3720       column_index++;
3721     }
3722     row_index++;
3723   }
3724 
3725   return nullptr;
3726 }
3727 
AriaColumnCount() const3728 int AXObject::AriaColumnCount() const {
3729   if (!IsTableLikeRole())
3730     return 0;
3731 
3732   int32_t col_count;
3733   if (!HasAOMPropertyOrARIAAttribute(AOMIntProperty::kColCount, col_count))
3734     return 0;
3735 
3736   if (col_count > static_cast<int>(ColumnCount()))
3737     return col_count;
3738 
3739   // Spec says that if all of the columns are present in the DOM, it
3740   // is not necessary to set this attribute as the user agent can
3741   // automatically calculate the total number of columns.
3742   // It returns 0 in order not to set this attribute.
3743   if (col_count == static_cast<int>(ColumnCount()) || col_count != -1)
3744     return 0;
3745 
3746   return -1;
3747 }
3748 
AriaRowCount() const3749 int AXObject::AriaRowCount() const {
3750   if (!IsTableLikeRole())
3751     return 0;
3752 
3753   int32_t row_count;
3754   if (!HasAOMPropertyOrARIAAttribute(AOMIntProperty::kRowCount, row_count))
3755     return 0;
3756 
3757   if (row_count > int{RowCount()})
3758     return row_count;
3759 
3760   // Spec says that if all of the rows are present in the DOM, it is
3761   // not necessary to set this attribute as the user agent can
3762   // automatically calculate the total number of rows.
3763   // It returns 0 in order not to set this attribute.
3764   if (row_count == int{RowCount()} || row_count != -1)
3765     return 0;
3766 
3767   // In the spec, -1 explicitly means an unknown number of rows.
3768   return -1;
3769 }
3770 
ColumnIndex() const3771 unsigned AXObject::ColumnIndex() const {
3772   return 0;
3773 }
3774 
RowIndex() const3775 unsigned AXObject::RowIndex() const {
3776   return 0;
3777 }
3778 
ColumnSpan() const3779 unsigned AXObject::ColumnSpan() const {
3780   return IsTableCellLikeRole() ? 1 : 0;
3781 }
3782 
RowSpan() const3783 unsigned AXObject::RowSpan() const {
3784   return IsTableCellLikeRole() ? 1 : 0;
3785 }
3786 
AriaColumnIndex() const3787 unsigned AXObject::AriaColumnIndex() const {
3788   UpdateCachedAttributeValuesIfNeeded();
3789   return cached_aria_column_index_;
3790 }
3791 
AriaRowIndex() const3792 unsigned AXObject::AriaRowIndex() const {
3793   UpdateCachedAttributeValuesIfNeeded();
3794   return cached_aria_row_index_;
3795 }
3796 
ComputeAriaColumnIndex() const3797 unsigned AXObject::ComputeAriaColumnIndex() const {
3798   // Return the ARIA column index if it has been set. Otherwise return a default
3799   // value of 0.
3800   uint32_t col_index = 0;
3801   HasAOMPropertyOrARIAAttribute(AOMUIntProperty::kColIndex, col_index);
3802   return col_index;
3803 }
3804 
ComputeAriaRowIndex() const3805 unsigned AXObject::ComputeAriaRowIndex() const {
3806   // Return the ARIA row index if it has been set. Otherwise return a default
3807   // value of 0.
3808   uint32_t row_index = 0;
3809   HasAOMPropertyOrARIAAttribute(AOMUIntProperty::kRowIndex, row_index);
3810   return row_index;
3811 }
3812 
TableRowChildren() const3813 AXObject::AXObjectVector AXObject::TableRowChildren() const {
3814   AXObjectVector result;
3815   for (const auto& child : ChildrenIncludingIgnored()) {
3816     if (child->IsTableRowLikeRole())
3817       result.push_back(child);
3818     else if (child->RoleValue() == ax::mojom::blink::Role::kRowGroup)
3819       result.AppendVector(child->TableRowChildren());
3820   }
3821   return result;
3822 }
3823 
TableCellChildren() const3824 AXObject::AXObjectVector AXObject::TableCellChildren() const {
3825   AXObjectVector result;
3826   for (const auto& child : ChildrenIncludingIgnored()) {
3827     if (child->IsTableCellLikeRole())
3828       result.push_back(child);
3829     else if (child->RoleValue() == ax::mojom::blink::Role::kGenericContainer)
3830       result.AppendVector(child->TableCellChildren());
3831   }
3832   return result;
3833 }
3834 
TableRowParent() const3835 const AXObject* AXObject::TableRowParent() const {
3836   const AXObject* row = ParentObjectUnignored();
3837   while (row && !row->IsTableRowLikeRole() &&
3838          row->RoleValue() == ax::mojom::blink::Role::kGenericContainer)
3839     row = row->ParentObjectUnignored();
3840   return row;
3841 }
3842 
TableParent() const3843 const AXObject* AXObject::TableParent() const {
3844   const AXObject* table = ParentObjectUnignored();
3845   while (table && !table->IsTableLikeRole() &&
3846          table->RoleValue() == ax::mojom::blink::Role::kGenericContainer)
3847     table = table->ParentObjectUnignored();
3848   return table;
3849 }
3850 
GetDOMNodeId() const3851 int AXObject::GetDOMNodeId() const {
3852   Node* node = GetNode();
3853   if (node)
3854     return DOMNodeIds::IdForNode(node);
3855   return 0;
3856 }
3857 
GetRelativeBounds(AXObject ** out_container,FloatRect & out_bounds_in_container,SkMatrix44 & out_container_transform,bool * clips_children) const3858 void AXObject::GetRelativeBounds(AXObject** out_container,
3859                                  FloatRect& out_bounds_in_container,
3860                                  SkMatrix44& out_container_transform,
3861                                  bool* clips_children) const {
3862   *out_container = nullptr;
3863   out_bounds_in_container = FloatRect();
3864   out_container_transform.setIdentity();
3865 
3866   // First check if it has explicit bounds, for example if this element is tied
3867   // to a canvas path. When explicit coordinates are provided, the ID of the
3868   // explicit container element that the coordinates are relative to must be
3869   // provided too.
3870   if (!explicit_element_rect_.IsEmpty()) {
3871     *out_container = AXObjectCache().ObjectFromAXID(explicit_container_id_);
3872     if (*out_container) {
3873       out_bounds_in_container = FloatRect(explicit_element_rect_);
3874       return;
3875     }
3876   }
3877 
3878   LayoutObject* layout_object = LayoutObjectForRelativeBounds();
3879   if (!layout_object)
3880     return;
3881 
3882   if (layout_object->IsFixedPositioned() ||
3883       layout_object->IsStickyPositioned()) {
3884     AXObjectCache().AddToFixedOrStickyNodeList(this);
3885   }
3886 
3887   if (clips_children) {
3888     if (IsWebArea())
3889       *clips_children = true;
3890     else
3891       *clips_children = layout_object->HasNonVisibleOverflow();
3892   }
3893 
3894   if (IsWebArea()) {
3895     if (LocalFrameView* view = layout_object->GetFrame()->View()) {
3896       out_bounds_in_container.SetSize(FloatSize(view->Size()));
3897 
3898       // If it's a popup, account for the popup window's offset.
3899       if (view->GetPage()->GetChromeClient().IsPopup()) {
3900         IntRect frame_rect = view->FrameToScreen(view->FrameRect());
3901         LocalFrameView* root_view =
3902             AXObjectCache().GetDocument().GetFrame()->View();
3903         IntRect root_frame_rect =
3904             root_view->FrameToScreen(root_view->FrameRect());
3905 
3906         // Screen coordinates are in DIP without device scale factor applied.
3907         // Accessibility expects device scale factor applied here which is
3908         // unapplied at the destination AXTree.
3909         float scale_factor =
3910             view->GetPage()->GetChromeClient().WindowToViewportScalar(
3911                 layout_object->GetFrame(), 1.0f);
3912         out_bounds_in_container.SetLocation(
3913             FloatPoint(scale_factor * (frame_rect.X() - root_frame_rect.X()),
3914                        scale_factor * (frame_rect.Y() - root_frame_rect.Y())));
3915       }
3916     }
3917     return;
3918   }
3919 
3920   // First compute the container. The container must be an ancestor in the
3921   // accessibility tree, and its LayoutObject must be an ancestor in the layout
3922   // tree. Get the first such ancestor that's either scrollable or has a paint
3923   // layer.
3924   AXObject* container = ParentObjectUnignored();
3925   LayoutObject* container_layout_object = nullptr;
3926   if (layout_object->IsFixedPositioned()) {
3927     // If it's a fixed position element, the container should simply be the
3928     // root web area.
3929     container = AXObjectCache().GetOrCreate(GetDocument());
3930   } else {
3931     while (container) {
3932       container_layout_object = container->GetLayoutObject();
3933       if (container_layout_object && container_layout_object->IsBox() &&
3934           layout_object->IsDescendantOf(container_layout_object)) {
3935         if (container->IsScrollableContainer() ||
3936             container_layout_object->HasLayer()) {
3937           if (layout_object->IsAbsolutePositioned()) {
3938             // If it's absolutely positioned, the container must be the
3939             // nearest positioned container, or the root.
3940             if (container->IsWebArea())
3941               break;
3942             if (container_layout_object->IsPositioned())
3943               break;
3944           } else {
3945             break;
3946           }
3947         }
3948       }
3949 
3950       container = container->ParentObjectUnignored();
3951     }
3952   }
3953 
3954   if (!container)
3955     return;
3956   *out_container = container;
3957   out_bounds_in_container =
3958       layout_object->LocalBoundingBoxRectForAccessibility();
3959 
3960   // Frames need to take their border and padding into account so the
3961   // child element's computed position will be correct.
3962   if (layout_object->IsBox() && layout_object->GetNode() &&
3963       layout_object->GetNode()->IsFrameOwnerElement()) {
3964     out_bounds_in_container =
3965         FloatRect(To<LayoutBox>(layout_object)->PhysicalContentBoxRect());
3966   }
3967 
3968   // If the container has a scroll offset, subtract that out because we want our
3969   // bounds to be relative to the *unscrolled* position of the container object.
3970   if (auto* scrollable_area = container->GetScrollableAreaIfScrollable())
3971     out_bounds_in_container.Move(scrollable_area->GetScrollOffset());
3972 
3973   // Compute the transform between the container's coordinate space and this
3974   // object.
3975   TransformationMatrix transform = layout_object->LocalToAncestorTransform(
3976       To<LayoutBoxModelObject>(container_layout_object));
3977 
3978   // If the transform is just a simple translation, apply that to the
3979   // bounding box, but if it's a non-trivial transformation like a rotation,
3980   // scaling, etc. then return the full matrix instead.
3981   if (transform.IsIdentityOr2DTranslation()) {
3982     out_bounds_in_container.Move(transform.To2DTranslation());
3983   } else {
3984     out_container_transform = TransformationMatrix::ToSkMatrix44(transform);
3985   }
3986 }
3987 
LocalBoundingBoxRectForAccessibility()3988 FloatRect AXObject::LocalBoundingBoxRectForAccessibility() {
3989   if (!GetLayoutObject())
3990     return FloatRect();
3991   DCHECK(GetLayoutObject()->IsText());
3992   UpdateCachedAttributeValuesIfNeeded();
3993   return cached_local_bounding_box_rect_for_accessibility_;
3994 }
3995 
GetBoundsInFrameCoordinates() const3996 LayoutRect AXObject::GetBoundsInFrameCoordinates() const {
3997   AXObject* container = nullptr;
3998   FloatRect bounds;
3999   SkMatrix44 transform;
4000   GetRelativeBounds(&container, bounds, transform);
4001   FloatRect computed_bounds(0, 0, bounds.Width(), bounds.Height());
4002   while (container && container != this) {
4003     computed_bounds.Move(bounds.X(), bounds.Y());
4004     if (!container->IsWebArea()) {
4005       computed_bounds.Move(-container->GetScrollOffset().X(),
4006                            -container->GetScrollOffset().Y());
4007     }
4008     if (!transform.isIdentity()) {
4009       TransformationMatrix transformation_matrix(transform);
4010       transformation_matrix.MapRect(computed_bounds);
4011     }
4012     container->GetRelativeBounds(&container, bounds, transform);
4013   }
4014   return LayoutRect(computed_bounds);
4015 }
4016 
4017 //
4018 // Modify or take an action on an object.
4019 //
4020 
RequestDecrementAction()4021 bool AXObject::RequestDecrementAction() {
4022   Event* event =
4023       Event::CreateCancelable(event_type_names::kAccessibledecrement);
4024   if (DispatchEventToAOMEventListeners(*event))
4025     return true;
4026 
4027   return OnNativeDecrementAction();
4028 }
4029 
RequestClickAction()4030 bool AXObject::RequestClickAction() {
4031   Event* event = Event::CreateCancelable(event_type_names::kAccessibleclick);
4032   if (DispatchEventToAOMEventListeners(*event))
4033     return true;
4034 
4035   return OnNativeClickAction();
4036 }
4037 
OnNativeClickAction()4038 bool AXObject::OnNativeClickAction() {
4039   Document* document = GetDocument();
4040   if (!document)
4041     return false;
4042 
4043   LocalFrame::NotifyUserActivation(
4044       document->GetFrame(),
4045       mojom::blink::UserActivationNotificationType::kInteraction);
4046 
4047   Element* element = GetElement();
4048   if (!element && GetNode())
4049     element = GetNode()->parentElement();
4050 
4051   if (IsTextControl())
4052     return OnNativeFocusAction();
4053 
4054   if (element) {
4055     // Always set the sequential focus navigation starting point.
4056     // Even if this element isn't focusable, if you press "Tab" it will
4057     // start the search from this element.
4058     GetDocument()->SetSequentialFocusNavigationStartingPoint(element);
4059 
4060     // Explicitly focus the element if it's focusable but not currently
4061     // the focused element, to be consistent with
4062     // EventHandler::HandleMousePressEvent.
4063     if (element->IsMouseFocusable() && !element->IsFocusedElementInDocument()) {
4064       Page* const page = GetDocument()->GetPage();
4065       if (page) {
4066         page->GetFocusController().SetFocusedElement(
4067             element, GetDocument()->GetFrame(),
4068             FocusParams(SelectionBehaviorOnFocus::kNone,
4069                         mojom::blink::FocusType::kMouse, nullptr));
4070       }
4071     }
4072 
4073     // For most elements, AccessKeyAction triggers sending a simulated
4074     // click, including simulating the mousedown, mouseup, and click events.
4075     element->AccessKeyAction(true);
4076     return true;
4077   }
4078 
4079   if (CanSetFocusAttribute())
4080     return OnNativeFocusAction();
4081 
4082   return false;
4083 }
4084 
RequestFocusAction()4085 bool AXObject::RequestFocusAction() {
4086   Event* event = Event::CreateCancelable(event_type_names::kAccessiblefocus);
4087   if (DispatchEventToAOMEventListeners(*event))
4088     return true;
4089 
4090   return OnNativeFocusAction();
4091 }
4092 
RequestIncrementAction()4093 bool AXObject::RequestIncrementAction() {
4094   Event* event =
4095       Event::CreateCancelable(event_type_names::kAccessibleincrement);
4096   if (DispatchEventToAOMEventListeners(*event))
4097     return true;
4098 
4099   return OnNativeIncrementAction();
4100 }
4101 
RequestScrollToGlobalPointAction(const IntPoint & point)4102 bool AXObject::RequestScrollToGlobalPointAction(const IntPoint& point) {
4103   return OnNativeScrollToGlobalPointAction(point);
4104 }
4105 
RequestScrollToMakeVisibleAction()4106 bool AXObject::RequestScrollToMakeVisibleAction() {
4107   Event* event =
4108       Event::CreateCancelable(event_type_names::kAccessiblescrollintoview);
4109   if (DispatchEventToAOMEventListeners(*event))
4110     return true;
4111 
4112   return OnNativeScrollToMakeVisibleAction();
4113 }
4114 
RequestScrollToMakeVisibleWithSubFocusAction(const IntRect & subfocus,blink::mojom::blink::ScrollAlignment horizontal_scroll_alignment,blink::mojom::blink::ScrollAlignment vertical_scroll_alignment)4115 bool AXObject::RequestScrollToMakeVisibleWithSubFocusAction(
4116     const IntRect& subfocus,
4117     blink::mojom::blink::ScrollAlignment horizontal_scroll_alignment,
4118     blink::mojom::blink::ScrollAlignment vertical_scroll_alignment) {
4119   return OnNativeScrollToMakeVisibleWithSubFocusAction(
4120       subfocus, horizontal_scroll_alignment, vertical_scroll_alignment);
4121 }
4122 
RequestSetSelectedAction(bool selected)4123 bool AXObject::RequestSetSelectedAction(bool selected) {
4124   return OnNativeSetSelectedAction(selected);
4125 }
4126 
RequestSetSequentialFocusNavigationStartingPointAction()4127 bool AXObject::RequestSetSequentialFocusNavigationStartingPointAction() {
4128   return OnNativeSetSequentialFocusNavigationStartingPointAction();
4129 }
4130 
RequestSetValueAction(const String & value)4131 bool AXObject::RequestSetValueAction(const String& value) {
4132   return OnNativeSetValueAction(value);
4133 }
4134 
RequestShowContextMenuAction()4135 bool AXObject::RequestShowContextMenuAction() {
4136   Event* event =
4137       Event::CreateCancelable(event_type_names::kAccessiblecontextmenu);
4138   if (DispatchEventToAOMEventListeners(*event))
4139     return true;
4140 
4141   return OnNativeShowContextMenuAction();
4142 }
4143 
InternalClearAccessibilityFocusAction()4144 bool AXObject::InternalClearAccessibilityFocusAction() {
4145   return false;
4146 }
4147 
InternalSetAccessibilityFocusAction()4148 bool AXObject::InternalSetAccessibilityFocusAction() {
4149   return false;
4150 }
4151 
GetLayoutObjectForNativeScrollAction() const4152 LayoutObject* AXObject::GetLayoutObjectForNativeScrollAction() const {
4153   Node* node = GetNode();
4154   if (!node || !node->isConnected())
4155     return nullptr;
4156 
4157   // Node might not have a LayoutObject due to the fact that it is in a locked
4158   // subtree. Force the update to create the LayoutObject (and update position
4159   // information) for this node.
4160   GetDocument()->UpdateStyleAndLayoutForNode(
4161       node, DocumentUpdateReason::kDisplayLock);
4162   return node->GetLayoutObject();
4163 }
4164 
OnNativeScrollToMakeVisibleAction() const4165 bool AXObject::OnNativeScrollToMakeVisibleAction() const {
4166   LayoutObject* layout_object = GetLayoutObjectForNativeScrollAction();
4167   if (!layout_object)
4168     return false;
4169   PhysicalRect target_rect(layout_object->AbsoluteBoundingBoxRect());
4170   layout_object->ScrollRectToVisible(
4171       target_rect,
4172       ScrollAlignment::CreateScrollIntoViewParams(
4173           ScrollAlignment::CenterIfNeeded(), ScrollAlignment::CenterIfNeeded(),
4174           mojom::blink::ScrollType::kProgrammatic, false,
4175           mojom::blink::ScrollBehavior::kAuto));
4176   AXObjectCache().PostNotification(
4177       AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
4178       ax::mojom::blink::Event::kLocationChanged);
4179   return true;
4180 }
4181 
OnNativeScrollToMakeVisibleWithSubFocusAction(const IntRect & rect,blink::mojom::blink::ScrollAlignment horizontal_scroll_alignment,blink::mojom::blink::ScrollAlignment vertical_scroll_alignment) const4182 bool AXObject::OnNativeScrollToMakeVisibleWithSubFocusAction(
4183     const IntRect& rect,
4184     blink::mojom::blink::ScrollAlignment horizontal_scroll_alignment,
4185     blink::mojom::blink::ScrollAlignment vertical_scroll_alignment) const {
4186   LayoutObject* layout_object = GetLayoutObjectForNativeScrollAction();
4187   if (!layout_object)
4188     return false;
4189 
4190   PhysicalRect target_rect =
4191       layout_object->LocalToAbsoluteRect(PhysicalRect(rect));
4192   layout_object->ScrollRectToVisible(
4193       target_rect, ScrollAlignment::CreateScrollIntoViewParams(
4194                        horizontal_scroll_alignment, vertical_scroll_alignment,
4195                        mojom::blink::ScrollType::kProgrammatic,
4196                        false /* make_visible_in_visual_viewport */,
4197                        mojom::blink::ScrollBehavior::kAuto));
4198   AXObjectCache().PostNotification(
4199       AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
4200       ax::mojom::blink::Event::kLocationChanged);
4201   return true;
4202 }
4203 
OnNativeScrollToGlobalPointAction(const IntPoint & global_point) const4204 bool AXObject::OnNativeScrollToGlobalPointAction(
4205     const IntPoint& global_point) const {
4206   LayoutObject* layout_object = GetLayoutObjectForNativeScrollAction();
4207   if (!layout_object)
4208     return false;
4209 
4210   PhysicalRect target_rect(layout_object->AbsoluteBoundingBoxRect());
4211   target_rect.Move(-PhysicalOffset(global_point));
4212   layout_object->ScrollRectToVisible(
4213       target_rect,
4214       ScrollAlignment::CreateScrollIntoViewParams(
4215           ScrollAlignment::LeftAlways(), ScrollAlignment::TopAlways(),
4216           mojom::blink::ScrollType::kProgrammatic, false,
4217           mojom::blink::ScrollBehavior::kAuto));
4218   AXObjectCache().PostNotification(
4219       AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
4220       ax::mojom::blink::Event::kLocationChanged);
4221   return true;
4222 }
4223 
OnNativeSetSequentialFocusNavigationStartingPointAction()4224 bool AXObject::OnNativeSetSequentialFocusNavigationStartingPointAction() {
4225   // Call it on the nearest ancestor that overrides this with a specific
4226   // implementation.
4227   if (ParentObject()) {
4228     return ParentObject()
4229         ->OnNativeSetSequentialFocusNavigationStartingPointAction();
4230   }
4231   return false;
4232 }
4233 
OnNativeDecrementAction()4234 bool AXObject::OnNativeDecrementAction() {
4235   return false;
4236 }
4237 
OnNativeFocusAction()4238 bool AXObject::OnNativeFocusAction() {
4239   return false;
4240 }
4241 
OnNativeIncrementAction()4242 bool AXObject::OnNativeIncrementAction() {
4243   return false;
4244 }
4245 
OnNativeSetValueAction(const String &)4246 bool AXObject::OnNativeSetValueAction(const String&) {
4247   return false;
4248 }
4249 
OnNativeSetSelectedAction(bool)4250 bool AXObject::OnNativeSetSelectedAction(bool) {
4251   return false;
4252 }
4253 
OnNativeShowContextMenuAction()4254 bool AXObject::OnNativeShowContextMenuAction() {
4255   Element* element = GetElement();
4256   if (!element)
4257     element = ParentObject() ? ParentObject()->GetElement() : nullptr;
4258   if (!element)
4259     return false;
4260 
4261   Document* document = GetDocument();
4262   if (!document || !document->GetFrame())
4263     return false;
4264 
4265   ContextMenuAllowedScope scope;
4266   document->GetFrame()->GetEventHandler().ShowNonLocatedContextMenu(
4267       element, kMenuSourceKeyboard);
4268   return true;
4269 }
4270 
SelectionChanged()4271 void AXObject::SelectionChanged() {
4272   if (AXObject* parent = ParentObjectIfExists())
4273     parent->SelectionChanged();
4274 }
4275 
4276 // static
IsARIAControl(ax::mojom::blink::Role aria_role)4277 bool AXObject::IsARIAControl(ax::mojom::blink::Role aria_role) {
4278   return IsARIAInput(aria_role) ||
4279          aria_role == ax::mojom::blink::Role::kButton ||
4280          aria_role == ax::mojom::blink::Role::kComboBoxMenuButton ||
4281          aria_role == ax::mojom::blink::Role::kSlider;
4282 }
4283 
4284 // static
IsARIAInput(ax::mojom::blink::Role aria_role)4285 bool AXObject::IsARIAInput(ax::mojom::blink::Role aria_role) {
4286   return aria_role == ax::mojom::blink::Role::kRadioButton ||
4287          aria_role == ax::mojom::blink::Role::kCheckBox ||
4288          aria_role == ax::mojom::blink::Role::kTextField ||
4289          aria_role == ax::mojom::blink::Role::kSwitch ||
4290          aria_role == ax::mojom::blink::Role::kSearchBox ||
4291          aria_role == ax::mojom::blink::Role::kTextFieldWithComboBox;
4292 }
4293 
4294 // static
HasARIAOwns(Element * element)4295 bool AXObject::HasARIAOwns(Element* element) {
4296   if (!element)
4297     return false;
4298 
4299   // A LayoutObject is not required, because an invisible object can still
4300   // use aria-owns to point to visible children.
4301 
4302   const AtomicString& aria_owns =
4303       element->FastGetAttribute(html_names::kAriaOwnsAttr);
4304 
4305   // TODO: do we need to check !AriaOwnsElements.empty() ? Is that fundamentally
4306   // different from HasExplicitlySetAttrAssociatedElements()? And is an element
4307   // even necessary in the case of virtual nodes?
4308   return !aria_owns.IsEmpty() ||
4309          element->HasExplicitlySetAttrAssociatedElements(
4310              html_names::kAriaOwnsAttr);
4311 }
4312 
AriaRoleToWebCoreRole(const String & value)4313 ax::mojom::blink::Role AXObject::AriaRoleToWebCoreRole(const String& value) {
4314   DCHECK(!value.IsEmpty());
4315 
4316   static const ARIARoleMap* role_map = CreateARIARoleMap();
4317 
4318   Vector<String> role_vector;
4319   value.Split(' ', role_vector);
4320   ax::mojom::blink::Role role = ax::mojom::blink::Role::kUnknown;
4321   for (const auto& child : role_vector) {
4322     role = role_map->at(child);
4323     if (role != ax::mojom::blink::Role::kUnknown)
4324       return role;
4325   }
4326 
4327   return role;
4328 }
4329 
NameFromSelectedOption(bool recursive) const4330 bool AXObject::NameFromSelectedOption(bool recursive) const {
4331   switch (RoleValue()) {
4332     // Step 2E from: http://www.w3.org/TR/accname-aam-1.1
4333     case ax::mojom::blink::Role::kComboBoxGrouping:
4334     case ax::mojom::blink::Role::kComboBoxMenuButton:
4335     case ax::mojom::blink::Role::kListBox:
4336       return recursive;
4337     // This can be either a button widget with a non-false value of
4338     // aria-haspopup or a select element with size of 1.
4339     case ax::mojom::blink::Role::kPopUpButton:
4340       return DynamicTo<HTMLSelectElement>(*GetNode()) ? recursive : false;
4341     default:
4342       return false;
4343   }
4344 }
4345 
SupportsNameFromContents(bool recursive) const4346 bool AXObject::SupportsNameFromContents(bool recursive) const {
4347   // ARIA 1.1, section 5.2.7.5.
4348   bool result = false;
4349 
4350   switch (RoleValue()) {
4351     // ----- NameFrom: contents -------------------------
4352     // Get their own name from contents, or contribute to ancestors
4353     case ax::mojom::blink::Role::kAnchor:
4354     case ax::mojom::blink::Role::kButton:
4355     case ax::mojom::blink::Role::kCell:
4356     case ax::mojom::blink::Role::kCheckBox:
4357     case ax::mojom::blink::Role::kColumnHeader:
4358     case ax::mojom::blink::Role::kDocBackLink:
4359     case ax::mojom::blink::Role::kDocBiblioRef:
4360     case ax::mojom::blink::Role::kDocNoteRef:
4361     case ax::mojom::blink::Role::kDocGlossRef:
4362     case ax::mojom::blink::Role::kDisclosureTriangle:
4363     case ax::mojom::blink::Role::kHeading:
4364     case ax::mojom::blink::Role::kLayoutTableCell:
4365     case ax::mojom::blink::Role::kLineBreak:
4366     case ax::mojom::blink::Role::kLink:
4367     case ax::mojom::blink::Role::kListBoxOption:
4368     case ax::mojom::blink::Role::kMenuItem:
4369     case ax::mojom::blink::Role::kMenuItemCheckBox:
4370     case ax::mojom::blink::Role::kMenuItemRadio:
4371     case ax::mojom::blink::Role::kMenuListOption:
4372     case ax::mojom::blink::Role::kPopUpButton:
4373     case ax::mojom::blink::Role::kPortal:
4374     case ax::mojom::blink::Role::kRadioButton:
4375     case ax::mojom::blink::Role::kRowHeader:
4376     case ax::mojom::blink::Role::kStaticText:
4377     case ax::mojom::blink::Role::kSwitch:
4378     case ax::mojom::blink::Role::kTab:
4379     case ax::mojom::blink::Role::kToggleButton:
4380     case ax::mojom::blink::Role::kTreeItem:
4381     case ax::mojom::blink::Role::kTooltip:
4382       result = true;
4383       break;
4384 
4385     // ----- No name from contents -------------------------
4386     // These never have or contribute a name from contents, as they are
4387     // containers for many subobjects. Superset of nameFrom:author ARIA roles.
4388     case ax::mojom::blink::Role::kAlert:
4389     case ax::mojom::blink::Role::kAlertDialog:
4390     case ax::mojom::blink::Role::kApplication:
4391     case ax::mojom::blink::Role::kAudio:
4392     case ax::mojom::blink::Role::kArticle:
4393     case ax::mojom::blink::Role::kBanner:
4394     case ax::mojom::blink::Role::kBlockquote:
4395     case ax::mojom::blink::Role::kCaret:
4396     case ax::mojom::blink::Role::kClient:
4397     case ax::mojom::blink::Role::kColorWell:
4398     case ax::mojom::blink::Role::kColumn:
4399     case ax::mojom::blink::Role::kComboBoxMenuButton:  // Only value from
4400                                                        // content.
4401     case ax::mojom::blink::Role::kComboBoxGrouping:
4402     case ax::mojom::blink::Role::kComment:
4403     case ax::mojom::blink::Role::kComplementary:
4404     case ax::mojom::blink::Role::kContentInfo:
4405     case ax::mojom::blink::Role::kDate:
4406     case ax::mojom::blink::Role::kDateTime:
4407     case ax::mojom::blink::Role::kDesktop:
4408     case ax::mojom::blink::Role::kDialog:
4409     case ax::mojom::blink::Role::kDirectory:
4410     case ax::mojom::blink::Role::kDocCover:
4411     case ax::mojom::blink::Role::kDocBiblioEntry:
4412     case ax::mojom::blink::Role::kDocEndnote:
4413     case ax::mojom::blink::Role::kDocFootnote:
4414     case ax::mojom::blink::Role::kDocPageBreak:
4415     case ax::mojom::blink::Role::kDocPageFooter:
4416     case ax::mojom::blink::Role::kDocPageHeader:
4417     case ax::mojom::blink::Role::kDocAbstract:
4418     case ax::mojom::blink::Role::kDocAcknowledgments:
4419     case ax::mojom::blink::Role::kDocAfterword:
4420     case ax::mojom::blink::Role::kDocAppendix:
4421     case ax::mojom::blink::Role::kDocBibliography:
4422     case ax::mojom::blink::Role::kDocChapter:
4423     case ax::mojom::blink::Role::kDocColophon:
4424     case ax::mojom::blink::Role::kDocConclusion:
4425     case ax::mojom::blink::Role::kDocCredit:
4426     case ax::mojom::blink::Role::kDocCredits:
4427     case ax::mojom::blink::Role::kDocDedication:
4428     case ax::mojom::blink::Role::kDocEndnotes:
4429     case ax::mojom::blink::Role::kDocEpigraph:
4430     case ax::mojom::blink::Role::kDocEpilogue:
4431     case ax::mojom::blink::Role::kDocErrata:
4432     case ax::mojom::blink::Role::kDocExample:
4433     case ax::mojom::blink::Role::kDocForeword:
4434     case ax::mojom::blink::Role::kDocGlossary:
4435     case ax::mojom::blink::Role::kDocIndex:
4436     case ax::mojom::blink::Role::kDocIntroduction:
4437     case ax::mojom::blink::Role::kDocNotice:
4438     case ax::mojom::blink::Role::kDocPageList:
4439     case ax::mojom::blink::Role::kDocPart:
4440     case ax::mojom::blink::Role::kDocPreface:
4441     case ax::mojom::blink::Role::kDocPrologue:
4442     case ax::mojom::blink::Role::kDocPullquote:
4443     case ax::mojom::blink::Role::kDocQna:
4444     case ax::mojom::blink::Role::kDocSubtitle:
4445     case ax::mojom::blink::Role::kDocTip:
4446     case ax::mojom::blink::Role::kDocToc:
4447     case ax::mojom::blink::Role::kDocument:
4448     case ax::mojom::blink::Role::kEmbeddedObject:
4449     case ax::mojom::blink::Role::kFeed:
4450     case ax::mojom::blink::Role::kFigure:
4451     case ax::mojom::blink::Role::kForm:
4452     case ax::mojom::blink::Role::kGraphicsDocument:
4453     case ax::mojom::blink::Role::kGraphicsObject:
4454     case ax::mojom::blink::Role::kGraphicsSymbol:
4455     case ax::mojom::blink::Role::kGrid:
4456     case ax::mojom::blink::Role::kGroup:
4457     case ax::mojom::blink::Role::kHeader:
4458     case ax::mojom::blink::Role::kIframePresentational:
4459     case ax::mojom::blink::Role::kIframe:
4460     case ax::mojom::blink::Role::kImage:
4461     case ax::mojom::blink::Role::kImeCandidate:  // Internal role, not used on
4462                                                  // the web.
4463     case ax::mojom::blink::Role::kInputTime:
4464     case ax::mojom::blink::Role::kKeyboard:
4465     case ax::mojom::blink::Role::kListBox:
4466     case ax::mojom::blink::Role::kListGrid:
4467     case ax::mojom::blink::Role::kLog:
4468     case ax::mojom::blink::Role::kMain:
4469     case ax::mojom::blink::Role::kMarquee:
4470     case ax::mojom::blink::Role::kMath:
4471     case ax::mojom::blink::Role::kMenuListPopup:
4472     case ax::mojom::blink::Role::kMenu:
4473     case ax::mojom::blink::Role::kMenuBar:
4474     case ax::mojom::blink::Role::kMeter:
4475     case ax::mojom::blink::Role::kNavigation:
4476     case ax::mojom::blink::Role::kNote:
4477     case ax::mojom::blink::Role::kPane:
4478     case ax::mojom::blink::Role::kPluginObject:
4479     case ax::mojom::blink::Role::kProgressIndicator:
4480     case ax::mojom::blink::Role::kRadioGroup:
4481     case ax::mojom::blink::Role::kRowGroup:
4482     case ax::mojom::blink::Role::kScrollBar:
4483     case ax::mojom::blink::Role::kScrollView:
4484     case ax::mojom::blink::Role::kSearch:
4485     case ax::mojom::blink::Role::kSearchBox:
4486     case ax::mojom::blink::Role::kSplitter:
4487     case ax::mojom::blink::Role::kSlider:
4488     case ax::mojom::blink::Role::kSpinButton:
4489     case ax::mojom::blink::Role::kStatus:
4490     case ax::mojom::blink::Role::kSliderThumb:
4491     case ax::mojom::blink::Role::kSuggestion:
4492     case ax::mojom::blink::Role::kSvgRoot:
4493     case ax::mojom::blink::Role::kTable:
4494     case ax::mojom::blink::Role::kTableHeaderContainer:
4495     case ax::mojom::blink::Role::kTabList:
4496     case ax::mojom::blink::Role::kTabPanel:
4497     case ax::mojom::blink::Role::kTerm:
4498     case ax::mojom::blink::Role::kTextField:
4499     case ax::mojom::blink::Role::kTextFieldWithComboBox:
4500     case ax::mojom::blink::Role::kTitleBar:
4501     case ax::mojom::blink::Role::kTimer:
4502     case ax::mojom::blink::Role::kToolbar:
4503     case ax::mojom::blink::Role::kTree:
4504     case ax::mojom::blink::Role::kTreeGrid:
4505     case ax::mojom::blink::Role::kVideo:
4506     case ax::mojom::blink::Role::kWebArea:
4507     case ax::mojom::blink::Role::kWebView:
4508     case ax::mojom::blink::Role::kWindow:
4509       result = false;
4510       break;
4511 
4512     // ----- Conditional: contribute to ancestor only, unless focusable -------
4513     // Some objects can contribute their contents to ancestor names, but
4514     // only have their own name if they are focusable
4515     case ax::mojom::blink::Role::kAbbr:
4516     case ax::mojom::blink::Role::kCanvas:
4517     case ax::mojom::blink::Role::kCaption:
4518     case ax::mojom::blink::Role::kCode:
4519     case ax::mojom::blink::Role::kContentDeletion:
4520     case ax::mojom::blink::Role::kContentInsertion:
4521     case ax::mojom::blink::Role::kDefinition:
4522     case ax::mojom::blink::Role::kDescriptionListDetail:
4523     case ax::mojom::blink::Role::kDescriptionList:
4524     case ax::mojom::blink::Role::kDescriptionListTerm:
4525     case ax::mojom::blink::Role::kDetails:
4526     case ax::mojom::blink::Role::kEmphasis:
4527     case ax::mojom::blink::Role::kFigcaption:
4528     case ax::mojom::blink::Role::kFooter:
4529     case ax::mojom::blink::Role::kFooterAsNonLandmark:
4530     case ax::mojom::blink::Role::kGenericContainer:
4531     case ax::mojom::blink::Role::kHeaderAsNonLandmark:
4532     case ax::mojom::blink::Role::kIgnored:
4533     case ax::mojom::blink::Role::kImageMap:
4534     case ax::mojom::blink::Role::kInlineTextBox:
4535     case ax::mojom::blink::Role::kLabelText:
4536     case ax::mojom::blink::Role::kLayoutTable:
4537     case ax::mojom::blink::Role::kLayoutTableRow:
4538     case ax::mojom::blink::Role::kLegend:
4539     case ax::mojom::blink::Role::kList:
4540     case ax::mojom::blink::Role::kListItem:
4541     case ax::mojom::blink::Role::kListMarker:
4542     case ax::mojom::blink::Role::kMark:
4543     case ax::mojom::blink::Role::kNone:
4544     case ax::mojom::blink::Role::kParagraph:
4545     case ax::mojom::blink::Role::kPre:
4546     case ax::mojom::blink::Role::kPresentational:
4547     case ax::mojom::blink::Role::kRegion:
4548     // Spec says we should always expose the name on rows,
4549     // but for performance reasons we only do it
4550     // if the row might receive focus
4551     case ax::mojom::blink::Role::kRow:
4552     case ax::mojom::blink::Role::kRuby:
4553     case ax::mojom::blink::Role::kRubyAnnotation:
4554     case ax::mojom::blink::Role::kSection:
4555     case ax::mojom::blink::Role::kStrong:
4556     case ax::mojom::blink::Role::kTime:
4557       if (recursive) {
4558         // Use contents if part of a recursive name computation.
4559         result = true;
4560       } else {
4561         // Use contents if focusable, so that there is a name in the case
4562         // where the author mistakenly forgot to provide one.
4563         // Exceptions:
4564         // 1.Elements with contenteditable, where using the contents as a name
4565         //   would cause them to be double-announced.
4566         // 2.Containers with aria-activedescendant, where the focus is being
4567         //   forwarded somewhere else.
4568         // TODO(accessibility) Scrollables are currently allowed here in order
4569         // to keep the current behavior. In the future, this can be removed
4570         // because this code will be handled in IsFocusable(), once
4571         // KeyboardFocusableScrollersEnabled is permanently enabled.
4572         // Note: this uses the same scrollable check that element.cc uses.
4573         bool is_focusable_scrollable =
4574             RuntimeEnabledFeatures::KeyboardFocusableScrollersEnabled() &&
4575             IsUserScrollable();
4576         bool is_focusable = is_focusable_scrollable || CanSetFocusAttribute();
4577         result = is_focusable && !IsEditable() &&
4578                  !GetAOMPropertyOrARIAAttribute(
4579                      AOMRelationProperty::kActiveDescendant);
4580       }
4581       break;
4582 
4583     case ax::mojom::blink::Role::kPdfActionableHighlight:
4584       LOG(ERROR) << "PDF specific highlight role, Blink shouldn't generate "
4585                     "this role type";
4586       NOTREACHED();
4587       break;
4588 
4589     // A root web area normally only computes its name from the document title,
4590     // but a root web area inside a portal's main frame should compute its name
4591     // from its contents. This name is used by the portal element that hosts
4592     // this portal.
4593     case ax::mojom::blink::Role::kRootWebArea: {
4594       DCHECK(GetNode());
4595       const Document& document = GetNode()->GetDocument();
4596       bool is_main_frame =
4597           document.GetFrame() && document.GetFrame()->IsMainFrame();
4598       bool is_inside_portal =
4599           document.GetPage() && document.GetPage()->InsidePortal();
4600       return is_inside_portal && is_main_frame;
4601     }
4602 
4603     case ax::mojom::blink::Role::kUnknown:
4604       LOG(ERROR) << "ax::mojom::blink::Role::kUnknown for " << GetNode();
4605       NOTREACHED();
4606       break;
4607   }
4608 
4609   return result;
4610 }
4611 
SupportsARIAReadOnly() const4612 bool AXObject::SupportsARIAReadOnly() const {
4613   if (ui::IsReadOnlySupported(RoleValue()))
4614     return true;
4615 
4616   if (ui::IsCellOrTableHeader(RoleValue())) {
4617     // For cells and row/column headers, readonly is supported within a grid.
4618     return std::any_of(
4619         UnignoredAncestorsBegin(), UnignoredAncestorsEnd(),
4620         [](const AXObject& ancestor) {
4621           return ancestor.RoleValue() == ax::mojom::blink::Role::kGrid ||
4622                  ancestor.RoleValue() == ax::mojom::blink::Role::kTreeGrid;
4623         });
4624   }
4625 
4626   return false;
4627 }
4628 
ButtonRoleType() const4629 ax::mojom::blink::Role AXObject::ButtonRoleType() const {
4630   // If aria-pressed is present, then it should be exposed as a toggle button.
4631   // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
4632   if (AriaPressedIsPresent())
4633     return ax::mojom::blink::Role::kToggleButton;
4634   if (HasPopup() != ax::mojom::blink::HasPopup::kFalse)
4635     return ax::mojom::blink::Role::kPopUpButton;
4636   // We don't contemplate RadioButtonRole, as it depends on the input
4637   // type.
4638 
4639   return ax::mojom::blink::Role::kButton;
4640 }
4641 
4642 // static
RoleName(ax::mojom::blink::Role role)4643 const AtomicString& AXObject::RoleName(ax::mojom::blink::Role role) {
4644   static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector();
4645 
4646   return role_name_vector->at(static_cast<wtf_size_t>(role));
4647 }
4648 
4649 // static
InternalRoleName(ax::mojom::blink::Role role)4650 const AtomicString& AXObject::InternalRoleName(ax::mojom::blink::Role role) {
4651   static const Vector<AtomicString>* internal_role_name_vector =
4652       CreateInternalRoleNameVector();
4653 
4654   return internal_role_name_vector->at(static_cast<wtf_size_t>(role));
4655 }
4656 
4657 // static
LowestCommonAncestor(const AXObject & first,const AXObject & second,int * index_in_ancestor1,int * index_in_ancestor2)4658 const AXObject* AXObject::LowestCommonAncestor(const AXObject& first,
4659                                                const AXObject& second,
4660                                                int* index_in_ancestor1,
4661                                                int* index_in_ancestor2) {
4662   *index_in_ancestor1 = -1;
4663   *index_in_ancestor2 = -1;
4664 
4665   if (first.IsDetached() || second.IsDetached())
4666     return nullptr;
4667 
4668   if (first == second)
4669     return &first;
4670 
4671   HeapVector<Member<const AXObject>> ancestors1;
4672   ancestors1.push_back(&first);
4673   while (ancestors1.back())
4674     ancestors1.push_back(ancestors1.back()->ParentObjectIncludedInTree());
4675 
4676   HeapVector<Member<const AXObject>> ancestors2;
4677   ancestors2.push_back(&second);
4678   while (ancestors2.back())
4679     ancestors2.push_back(ancestors2.back()->ParentObjectIncludedInTree());
4680 
4681   const AXObject* common_ancestor = nullptr;
4682   while (!ancestors1.IsEmpty() && !ancestors2.IsEmpty() &&
4683          ancestors1.back() == ancestors2.back()) {
4684     common_ancestor = ancestors1.back();
4685     ancestors1.pop_back();
4686     ancestors2.pop_back();
4687   }
4688 
4689   if (common_ancestor) {
4690     if (!ancestors1.IsEmpty())
4691       *index_in_ancestor1 = ancestors1.back()->IndexInParent();
4692     if (!ancestors2.IsEmpty())
4693       *index_in_ancestor2 = ancestors2.back()->IndexInParent();
4694   }
4695 
4696   return common_ancestor;
4697 }
4698 
ToString(bool verbose) const4699 String AXObject::ToString(bool verbose) const {
4700   // Build a friendly name for debugging the object.
4701   // If verbose, build a longer name name in the form of:
4702   // CheckBox axid#28 <input.someClass#cbox1> name="checkbox"
4703   String string_builder =
4704       AXObject::InternalRoleName(RoleValue()).GetString().EncodeForDebugging();
4705 
4706   if (verbose) {
4707     string_builder = string_builder + " axid#" + String::Number(AXObjectID());
4708     // Add useful HTML element info, like <div.myClass#myId>.
4709     if (GetElement()) {
4710       string_builder =
4711           string_builder + " <" + GetElement()->tagName().LowerASCII();
4712       if (GetElement()->FastHasAttribute(html_names::kClassAttr)) {
4713         string_builder = string_builder + "." +
4714                          GetElement()->FastGetAttribute(html_names::kClassAttr);
4715       }
4716       if (GetElement()->FastHasAttribute(html_names::kIdAttr)) {
4717         string_builder = string_builder + "#" +
4718                          GetElement()->FastGetAttribute(html_names::kIdAttr);
4719       }
4720       string_builder = string_builder + ">";
4721     }
4722 
4723     // Add properties of interest that often contribute to errors:
4724     if (HasARIAOwns(GetElement()))
4725       string_builder = string_builder + " @aria-owns";
4726     if (GetAOMPropertyOrARIAAttribute(AOMRelationProperty::kActiveDescendant))
4727       string_builder = string_builder + " @aria-activedescendant";
4728     if (IsFocused())
4729       string_builder = string_builder + " focused";
4730     if (AXObjectCache().IsAriaOwned(this))
4731       string_builder = string_builder + " isAriaOwned";
4732     if (AccessibilityIsIgnored()) {
4733       string_builder = string_builder + " isIgnored";
4734       if (!AccessibilityIsIncludedInTree())
4735         string_builder = string_builder + " isRemovedFromTree";
4736     }
4737     if (GetNode() &&
4738         DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock(
4739             *GetNode(), DisplayLockActivationReason::kAccessibility)) {
4740       string_builder = string_builder + " isDisplayLocked";
4741     }
4742     if (AriaHiddenRoot())
4743       string_builder = string_builder + " isAriaHidden";
4744     if (IsHiddenViaStyle())
4745       string_builder = string_builder + " isHiddenViaCSS";
4746     if (GetNode() && GetNode()->IsInert())
4747       string_builder = string_builder + " isInert";
4748 
4749     string_builder = string_builder + " name=";
4750   } else {
4751     string_builder = string_builder + ": ";
4752   }
4753 
4754   // Append name last, in case it is long.
4755   return string_builder + ComputedName().EncodeForDebugging();
4756 }
4757 
operator ==(const AXObject & first,const AXObject & second)4758 bool operator==(const AXObject& first, const AXObject& second) {
4759   if (first.IsDetached() || second.IsDetached())
4760     return false;
4761   if (&first == &second) {
4762     DCHECK_EQ(first.AXObjectID(), second.AXObjectID());
4763     return true;
4764   }
4765   return false;
4766 }
4767 
operator !=(const AXObject & first,const AXObject & second)4768 bool operator!=(const AXObject& first, const AXObject& second) {
4769   return !(first == second);
4770 }
4771 
operator <(const AXObject & first,const AXObject & second)4772 bool operator<(const AXObject& first, const AXObject& second) {
4773   if (first.IsDetached() || second.IsDetached())
4774     return false;
4775 
4776   int index_in_ancestor1, index_in_ancestor2;
4777   const AXObject* ancestor = AXObject::LowestCommonAncestor(
4778       first, second, &index_in_ancestor1, &index_in_ancestor2);
4779   DCHECK_GE(index_in_ancestor1, -1);
4780   DCHECK_GE(index_in_ancestor2, -1);
4781   if (!ancestor)
4782     return false;
4783   return index_in_ancestor1 < index_in_ancestor2;
4784 }
4785 
operator <=(const AXObject & first,const AXObject & second)4786 bool operator<=(const AXObject& first, const AXObject& second) {
4787   return first == second || first < second;
4788 }
4789 
operator >(const AXObject & first,const AXObject & second)4790 bool operator>(const AXObject& first, const AXObject& second) {
4791   if (first.IsDetached() || second.IsDetached())
4792     return false;
4793 
4794   int index_in_ancestor1, index_in_ancestor2;
4795   const AXObject* ancestor = AXObject::LowestCommonAncestor(
4796       first, second, &index_in_ancestor1, &index_in_ancestor2);
4797   DCHECK_GE(index_in_ancestor1, -1);
4798   DCHECK_GE(index_in_ancestor2, -1);
4799   if (!ancestor)
4800     return false;
4801   return index_in_ancestor1 > index_in_ancestor2;
4802 }
4803 
operator >=(const AXObject & first,const AXObject & second)4804 bool operator>=(const AXObject& first, const AXObject& second) {
4805   return first == second || first > second;
4806 }
4807 
operator <<(std::ostream & stream,const AXObject & obj)4808 std::ostream& operator<<(std::ostream& stream, const AXObject& obj) {
4809   return stream << obj.ToString(true).Utf8();
4810 }
4811 
Trace(Visitor * visitor) const4812 void AXObject::Trace(Visitor* visitor) const {
4813   visitor->Trace(children_);
4814   visitor->Trace(parent_);
4815   visitor->Trace(cached_live_region_root_);
4816   visitor->Trace(ax_object_cache_);
4817 }
4818 
4819 }  // namespace blink
4820