OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/accessibility/browser_accessibility_win.h" | 5 #include "content/browser/accessibility/browser_accessibility_win.h" |
6 | 6 |
7 #include <UIAutomationClient.h> | |
8 #include <UIAutomationCoreApi.h> | |
9 | |
10 #include <algorithm> | |
11 #include <iterator> | |
12 #include <utility> | |
13 | |
14 #include "base/metrics/histogram_macros.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #include "base/strings/string_split.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "base/win/enum_variant.h" | |
20 #include "base/win/scoped_comptr.h" | |
21 #include "base/win/windows_version.h" | |
22 #include "content/browser/accessibility/browser_accessibility_event_win.h" | |
23 #include "content/browser/accessibility/browser_accessibility_manager_win.h" | |
24 #include "content/browser/accessibility/browser_accessibility_state_impl.h" | |
25 #include "content/common/accessibility_messages.h" | |
26 #include "content/common/accessibility_mode.h" | |
27 #include "content/public/common/content_client.h" | |
28 #include "third_party/skia/include/core/SkColor.h" | |
29 #include "ui/accessibility/ax_text_utils.h" | |
30 #include "ui/base/win/accessibility_ids_win.h" | |
31 #include "ui/base/win/accessibility_misc_utils.h" | |
32 #include "ui/base/win/atl_module.h" | 7 #include "ui/base/win/atl_module.h" |
33 | 8 |
34 namespace { | |
35 | |
36 // IMPORTANT! | |
37 // These values are written to logs. Do not renumber or delete | |
38 // existing items; add new entries to the end of the list. | |
39 enum { | |
40 UMA_API_ACC_DO_DEFAULT_ACTION = 0, | |
41 UMA_API_ACC_HIT_TEST = 1, | |
42 UMA_API_ACC_LOCATION = 2, | |
43 UMA_API_ACC_NAVIGATE = 3, | |
44 UMA_API_ACC_SELECT = 4, | |
45 UMA_API_ADD_SELECTION = 5, | |
46 UMA_API_CONVERT_RETURNED_ELEMENT = 6, | |
47 UMA_API_DO_ACTION = 7, | |
48 UMA_API_GET_ACCESSIBLE_AT = 8, | |
49 UMA_API_GET_ACC_CHILD = 9, | |
50 UMA_API_GET_ACC_CHILD_COUNT = 10, | |
51 UMA_API_GET_ACC_DEFAULT_ACTION = 11, | |
52 UMA_API_GET_ACC_DESCRIPTION = 12, | |
53 UMA_API_GET_ACC_FOCUS = 13, | |
54 UMA_API_GET_ACC_HELP = 14, | |
55 UMA_API_GET_ACC_HELP_TOPIC = 15, | |
56 UMA_API_GET_ACC_KEYBOARD_SHORTCUT = 16, | |
57 UMA_API_GET_ACC_NAME = 17, | |
58 UMA_API_GET_ACC_PARENT = 18, | |
59 UMA_API_GET_ACC_ROLE = 19, | |
60 UMA_API_GET_ACC_SELECTION = 20, | |
61 UMA_API_GET_ACC_STATE = 21, | |
62 UMA_API_GET_ACC_VALUE = 22, | |
63 UMA_API_GET_ANCHOR = 23, | |
64 UMA_API_GET_ANCHOR_TARGET = 24, | |
65 UMA_API_GET_APP_NAME = 25, | |
66 UMA_API_GET_APP_VERSION = 26, | |
67 UMA_API_GET_ATTRIBUTES_FOR_NAMES = 27, | |
68 UMA_API_GET_CAPTION = 28, | |
69 UMA_API_GET_CARET_OFFSET = 29, | |
70 UMA_API_GET_CELL_AT = 30, | |
71 UMA_API_GET_CHARACTER_EXTENTS = 31, | |
72 UMA_API_GET_CHILD_AT = 32, | |
73 UMA_API_GET_CHILD_INDEX = 33, | |
74 UMA_API_GET_CLIPPED_SUBSTRING_BOUNDS = 34, | |
75 UMA_API_GET_COLUMN_DESCRIPTION = 35, | |
76 UMA_API_GET_COLUMN_EXTENT = 36, | |
77 UMA_API_GET_COLUMN_EXTENT_AT = 37, | |
78 UMA_API_GET_COLUMN_HEADER = 38, | |
79 UMA_API_GET_COLUMN_HEADER_CELLS = 39, | |
80 UMA_API_GET_COLUMN_INDEX = 40, | |
81 UMA_API_GET_COMPUTED_STYLE = 41, | |
82 UMA_API_GET_COMPUTED_STYLE_FOR_PROPERTIES = 42, | |
83 UMA_API_GET_CURRENT_VALUE = 43, | |
84 UMA_API_GET_DESCRIPTION = 44, | |
85 UMA_API_GET_DOC_TYPE = 45, | |
86 UMA_API_GET_DOM_TEXT = 46, | |
87 UMA_API_GET_END_INDEX = 47, | |
88 UMA_API_GET_EXTENDED_ROLE = 48, | |
89 UMA_API_GET_EXTENDED_STATES = 49, | |
90 UMA_API_GET_FIRST_CHILD = 50, | |
91 UMA_API_GET_FONT_FAMILY = 51, | |
92 UMA_API_GET_GROUP_POSITION = 52, | |
93 UMA_API_GET_HOST_RAW_ELEMENT_PROVIDER = 53, | |
94 UMA_API_GET_HYPERLINK = 54, | |
95 UMA_API_GET_HYPERLINK_INDEX = 55, | |
96 UMA_API_GET_IACCESSIBLE_PAIR = 56, | |
97 UMA_API_GET_IMAGE_POSITION = 57, | |
98 UMA_API_GET_IMAGE_SIZE = 58, | |
99 UMA_API_GET_INDEX_IN_PARENT = 59, | |
100 UMA_API_GET_INNER_HTML = 60, | |
101 UMA_API_GET_IS_COLUMN_SELECTED = 61, | |
102 UMA_API_GET_IS_ROW_SELECTED = 62, | |
103 UMA_API_GET_IS_SELECTED = 63, | |
104 UMA_API_GET_KEY_BINDING = 64, | |
105 UMA_API_GET_LANGUAGE = 65, | |
106 UMA_API_GET_LAST_CHILD = 66, | |
107 UMA_API_GET_LOCALE = 67, | |
108 UMA_API_GET_LOCALIZED_EXTENDED_ROLE = 68, | |
109 UMA_API_GET_LOCALIZED_EXTENDED_STATES = 69, | |
110 UMA_API_GET_LOCALIZED_NAME = 70, | |
111 UMA_API_GET_LOCAL_INTERFACE = 71, | |
112 UMA_API_GET_MAXIMUM_VALUE = 72, | |
113 UMA_API_GET_MIME_TYPE = 73, | |
114 UMA_API_GET_MINIMUM_VALUE = 74, | |
115 UMA_API_GET_NAME = 75, | |
116 UMA_API_GET_NAMESPACE_URI_FOR_ID = 76, | |
117 UMA_API_GET_NEW_TEXT = 77, | |
118 UMA_API_GET_NEXT_SIBLING = 78, | |
119 UMA_API_GET_NODE_INFO = 79, | |
120 UMA_API_GET_N_CHARACTERS = 80, | |
121 UMA_API_GET_N_COLUMNS = 81, | |
122 UMA_API_GET_N_EXTENDED_STATES = 82, | |
123 UMA_API_GET_N_HYPERLINKS = 83, | |
124 UMA_API_GET_N_RELATIONS = 84, | |
125 UMA_API_GET_N_ROWS = 85, | |
126 UMA_API_GET_N_SELECTED_CELLS = 86, | |
127 UMA_API_GET_N_SELECTED_CHILDREN = 87, | |
128 UMA_API_GET_N_SELECTED_COLUMNS = 88, | |
129 UMA_API_GET_N_SELECTED_ROWS = 89, | |
130 UMA_API_GET_N_SELECTIONS = 90, | |
131 UMA_API_GET_OBJECT_FOR_CHILD = 91, | |
132 UMA_API_GET_OFFSET_AT_POINT = 92, | |
133 UMA_API_GET_OLD_TEXT = 93, | |
134 UMA_API_GET_PARENT_NODE = 94, | |
135 UMA_API_GET_PATTERN_PROVIDER = 95, | |
136 UMA_API_GET_PREVIOUS_SIBLING = 96, | |
137 UMA_API_GET_PROPERTY_VALUE = 97, | |
138 UMA_API_GET_PROVIDER_OPTIONS = 98, | |
139 UMA_API_GET_RELATION = 99, | |
140 UMA_API_GET_RELATIONS = 100, | |
141 UMA_API_GET_ROW_COLUMN_EXTENTS = 101, | |
142 UMA_API_GET_ROW_COLUMN_EXTENTS_AT_INDEX = 102, | |
143 UMA_API_GET_ROW_DESCRIPTION = 103, | |
144 UMA_API_GET_ROW_EXTENT = 104, | |
145 UMA_API_GET_ROW_EXTENT_AT = 105, | |
146 UMA_API_GET_ROW_HEADER = 106, | |
147 UMA_API_GET_ROW_HEADER_CELLS = 107, | |
148 UMA_API_GET_ROW_INDEX = 108, | |
149 UMA_API_GET_RUNTIME_ID = 109, | |
150 UMA_API_GET_SELECTED_CELLS = 110, | |
151 UMA_API_GET_SELECTED_CHILDREN = 111, | |
152 UMA_API_GET_SELECTED_COLUMNS = 112, | |
153 UMA_API_GET_SELECTED_ROWS = 113, | |
154 UMA_API_GET_SELECTION = 114, | |
155 UMA_API_GET_START_INDEX = 115, | |
156 UMA_API_GET_STATES = 116, | |
157 UMA_API_GET_SUMMARY = 117, | |
158 UMA_API_GET_TABLE = 118, | |
159 UMA_API_GET_TEXT = 119, | |
160 UMA_API_GET_TEXT_AFTER_OFFSET = 120, | |
161 UMA_API_GET_TEXT_AT_OFFSET = 121, | |
162 UMA_API_GET_TEXT_BEFORE_OFFSET = 122, | |
163 UMA_API_GET_TITLE = 123, | |
164 UMA_API_GET_TOOLKIT_NAME = 124, | |
165 UMA_API_GET_TOOLKIT_VERSION = 125, | |
166 UMA_API_GET_UNCLIPPED_SUBSTRING_BOUNDS = 126, | |
167 UMA_API_GET_UNIQUE_ID = 127, | |
168 UMA_API_GET_URL = 128, | |
169 UMA_API_GET_VALID = 129, | |
170 UMA_API_GET_WINDOW_HANDLE = 130, | |
171 UMA_API_IA2_GET_ATTRIBUTES = 131, | |
172 UMA_API_IA2_SCROLL_TO = 132, | |
173 UMA_API_IAACTION_GET_DESCRIPTION = 133, | |
174 UMA_API_IATEXT_GET_ATTRIBUTES = 134, | |
175 UMA_API_ISIMPLEDOMNODE_GET_ATTRIBUTES = 135, | |
176 UMA_API_ISIMPLEDOMNODE_SCROLL_TO = 136, | |
177 UMA_API_N_ACTIONS = 137, | |
178 UMA_API_PUT_ALTERNATE_VIEW_MEDIA_TYPES = 138, | |
179 UMA_API_QUERY_SERVICE = 139, | |
180 UMA_API_REMOVE_SELECTION = 140, | |
181 UMA_API_ROLE = 141, | |
182 UMA_API_SCROLL_SUBSTRING_TO = 142, | |
183 UMA_API_SCROLL_SUBSTRING_TO_POINT = 143, | |
184 UMA_API_SCROLL_TO_POINT = 144, | |
185 UMA_API_SCROLL_TO_SUBSTRING = 145, | |
186 UMA_API_SELECT_COLUMN = 146, | |
187 UMA_API_SELECT_ROW = 147, | |
188 UMA_API_SET_CARET_OFFSET = 148, | |
189 UMA_API_SET_CURRENT_VALUE = 149, | |
190 UMA_API_SET_SELECTION = 150, | |
191 UMA_API_TABLE2_GET_SELECTED_COLUMNS = 151, | |
192 UMA_API_TABLE2_GET_SELECTED_ROWS = 152, | |
193 UMA_API_TABLECELL_GET_COLUMN_INDEX = 153, | |
194 UMA_API_TABLECELL_GET_IS_SELECTED = 154, | |
195 UMA_API_TABLECELL_GET_ROW_INDEX = 155, | |
196 UMA_API_UNSELECT_COLUMN = 156, | |
197 UMA_API_UNSELECT_ROW = 157, | |
198 | |
199 // This must always be the last enum. It's okay for its value to | |
200 // increase, but none of the other enum values may change. | |
201 UMA_API_MAX | |
202 }; | |
203 | |
204 #define WIN_ACCESSIBILITY_API_HISTOGRAM(enum_value) \ | |
205 UMA_HISTOGRAM_ENUMERATION("Accessibility.WinAPIs", enum_value, UMA_API_MAX) | |
206 | |
207 // There is no easy way to decouple |kScreenReader| and |kHTML| accessibility | |
208 // modes when Windows screen readers are used. For example, certain roles use | |
209 // the HTML tag name. Input fields require their type attribute to be exposed. | |
210 const uint32_t kScreenReaderAndHTMLAccessibilityModes = | |
211 content::AccessibilityMode::kScreenReader | | |
212 content::AccessibilityMode::kHTML; | |
213 | |
214 const WCHAR *const IA2_RELATION_DETAILS = L"details"; | |
215 const WCHAR *const IA2_RELATION_DETAILS_FOR = L"detailsFor"; | |
216 const WCHAR *const IA2_RELATION_ERROR_MESSAGE = L"errorMessage"; | |
217 | |
218 } // namespace | |
219 | |
220 namespace content { | 9 namespace content { |
221 | 10 |
222 // These nonstandard GUIDs are taken directly from the Mozilla sources | 11 // static |
223 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here: | 12 BrowserAccessibility* BrowserAccessibility::Create() { |
224 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/
MSAA | 13 return new BrowserAccessibilityWin(); |
225 const GUID GUID_ISimpleDOM = {0x0c539790, | |
226 0x12e4, | |
227 0x11cf, | |
228 {0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}}; | |
229 const GUID GUID_IAccessibleContentDocument = { | |
230 0xa5d8e1f3, | |
231 0x3571, | |
232 0x4d8f, | |
233 {0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e}}; | |
234 | |
235 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter = L'\xfffc'; | |
236 | |
237 void AddAccessibilityModeFlags(AccessibilityMode mode_flags) { | |
238 BrowserAccessibilityStateImpl::GetInstance()->AddAccessibilityModeFlags( | |
239 mode_flags); | |
240 } | 14 } |
241 | 15 |
242 // | 16 BrowserAccessibilityWin::BrowserAccessibilityWin() { |
243 // BrowserAccessibilityRelation | 17 ui::win::CreateATLModuleIfNeeded(); |
244 // | 18 HRESULT hr = CComObject<BrowserAccessibilityComWin>::CreateInstance( |
245 // A simple implementation of IAccessibleRelation, used to represent | 19 &browser_accessibility_com_); |
246 // a relationship between two accessible nodes in the tree. | 20 DCHECK(SUCCEEDED(hr)); |
247 // | |
248 | 21 |
249 class BrowserAccessibilityRelation | 22 browser_accessibility_com_->AddRef(); |
250 : public CComObjectRootEx<CComMultiThreadModel>, | 23 browser_accessibility_com_->SetOwner(this); |
251 public IAccessibleRelation { | |
252 BEGIN_COM_MAP(BrowserAccessibilityRelation) | |
253 COM_INTERFACE_ENTRY(IAccessibleRelation) | |
254 END_COM_MAP() | |
255 | 24 |
256 CONTENT_EXPORT BrowserAccessibilityRelation() {} | 25 // Set the delegate to us |
257 CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {} | 26 browser_accessibility_com_->Init(this); |
258 | |
259 CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner, | |
260 const base::string16& type); | |
261 CONTENT_EXPORT void AddTarget(int target_id); | |
262 CONTENT_EXPORT void RemoveTarget(int target_id); | |
263 | |
264 // Accessors. | |
265 const base::string16& get_type() const { return type_; } | |
266 const std::vector<int>& get_target_ids() const { return target_ids_; } | |
267 | |
268 // IAccessibleRelation methods. | |
269 CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type) override; | |
270 CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets) override; | |
271 CONTENT_EXPORT STDMETHODIMP | |
272 get_target(long target_index, IUnknown** target) override; | |
273 CONTENT_EXPORT STDMETHODIMP | |
274 get_targets(long max_targets, IUnknown** targets, long* n_targets) override; | |
275 | |
276 // IAccessibleRelation methods not implemented. | |
277 CONTENT_EXPORT STDMETHODIMP | |
278 get_localizedRelationType(BSTR* relation_type) override { | |
279 return E_NOTIMPL; | |
280 } | |
281 | |
282 private: | |
283 base::string16 type_; | |
284 base::win::ScopedComPtr<BrowserAccessibilityWin> owner_; | |
285 std::vector<int> target_ids_; | |
286 }; | |
287 | |
288 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner, | |
289 const base::string16& type) { | |
290 owner_ = owner; | |
291 type_ = type; | |
292 } | 27 } |
293 | 28 |
294 void BrowserAccessibilityRelation::AddTarget(int target_id) { | 29 BrowserAccessibilityWin::~BrowserAccessibilityWin() {} |
295 target_ids_.push_back(target_id); | 30 |
| 31 void BrowserAccessibilityWin::UpdatePlatformAttributes() { |
| 32 GetCOM()->UpdateStep1ComputeWinAttributes(); |
| 33 GetCOM()->UpdateStep2ComputeHypertext(); |
| 34 GetCOM()->UpdateStep3FireEvents(false); |
296 } | 35 } |
297 | 36 |
298 void BrowserAccessibilityRelation::RemoveTarget(int target_id) { | 37 void BrowserAccessibilityWin::Destroy() { |
299 target_ids_.erase( | 38 if (browser_accessibility_com_) { |
300 std::remove(target_ids_.begin(), target_ids_.end(), target_id), | 39 browser_accessibility_com_->SetOwner(nullptr); |
301 target_ids_.end()); | 40 // TODO(dougt) AXPlatformNode::Reset |
302 } | 41 browser_accessibility_com_->Init(nullptr); |
303 | 42 browser_accessibility_com_->Release(); |
304 STDMETHODIMP BrowserAccessibilityRelation::get_relationType( | 43 browser_accessibility_com_ = nullptr; |
305 BSTR* relation_type) { | |
306 if (!relation_type) | |
307 return E_INVALIDARG; | |
308 | |
309 if (!owner_->instance_active()) | |
310 return E_FAIL; | |
311 | |
312 *relation_type = SysAllocString(type_.c_str()); | |
313 DCHECK(*relation_type); | |
314 return S_OK; | |
315 } | |
316 | |
317 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) { | |
318 if (!n_targets) | |
319 return E_INVALIDARG; | |
320 | |
321 if (!owner_->instance_active()) | |
322 return E_FAIL; | |
323 | |
324 *n_targets = static_cast<long>(target_ids_.size()); | |
325 | |
326 for (long i = *n_targets - 1; i >= 0; --i) { | |
327 BrowserAccessibilityWin* result = owner_->GetFromID(target_ids_[i]); | |
328 if (!result || !result->instance_active()) { | |
329 *n_targets = 0; | |
330 break; | |
331 } | |
332 } | 44 } |
333 return S_OK; | 45 BrowserAccessibility::Destroy(); |
334 } | |
335 | |
336 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index, | |
337 IUnknown** target) { | |
338 if (!target) | |
339 return E_INVALIDARG; | |
340 | |
341 if (!owner_->instance_active()) | |
342 return E_FAIL; | |
343 | |
344 if (target_index < 0 || | |
345 target_index >= static_cast<long>(target_ids_.size())) { | |
346 return E_INVALIDARG; | |
347 } | |
348 | |
349 BrowserAccessibility* result = owner_->GetFromID(target_ids_[target_index]); | |
350 if (!result || !result->instance_active()) | |
351 return E_FAIL; | |
352 | |
353 *target = static_cast<IAccessible*>( | |
354 ToBrowserAccessibilityWin(result)->NewReference()); | |
355 return S_OK; | |
356 } | |
357 | |
358 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets, | |
359 IUnknown** targets, | |
360 long* n_targets) { | |
361 if (!targets || !n_targets) | |
362 return E_INVALIDARG; | |
363 | |
364 if (!owner_->instance_active()) | |
365 return E_FAIL; | |
366 | |
367 long count = static_cast<long>(target_ids_.size()); | |
368 if (count > max_targets) | |
369 count = max_targets; | |
370 | |
371 *n_targets = count; | |
372 if (count == 0) | |
373 return S_FALSE; | |
374 | |
375 for (long i = 0; i < count; ++i) { | |
376 HRESULT result = get_target(i, &targets[i]); | |
377 if (result != S_OK) | |
378 return result; | |
379 } | |
380 | |
381 return S_OK; | |
382 } | |
383 | |
384 // | |
385 // BrowserAccessibilityWin::WinAttributes | |
386 // | |
387 | |
388 BrowserAccessibilityWin::WinAttributes::WinAttributes() | |
389 : ia_role(0), | |
390 ia_state(0), | |
391 ia2_role(0), | |
392 ia2_state(0) { | |
393 } | |
394 | |
395 BrowserAccessibilityWin::WinAttributes::~WinAttributes() { | |
396 } | |
397 | |
398 // | |
399 // BrowserAccessibilityWin | |
400 // | |
401 | |
402 // static | |
403 BrowserAccessibility* BrowserAccessibility::Create() { | |
404 ui::win::CreateATLModuleIfNeeded(); | |
405 CComObject<BrowserAccessibilityWin>* instance; | |
406 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance); | |
407 DCHECK(SUCCEEDED(hr)); | |
408 return instance->NewReference(); | |
409 } | |
410 | |
411 BrowserAccessibilityWin::BrowserAccessibilityWin() | |
412 : win_attributes_(new WinAttributes()), | |
413 previous_scroll_x_(0), | |
414 previous_scroll_y_(0) { | |
415 } | |
416 | |
417 BrowserAccessibilityWin::~BrowserAccessibilityWin() { | |
418 for (BrowserAccessibilityRelation* relation : relations_) | |
419 relation->Release(); | |
420 } | |
421 | |
422 // | |
423 // IAccessible methods. | |
424 // | |
425 // Conventions: | |
426 // * Always test for instance_active() first and return E_FAIL if it's false. | |
427 // * Always check for invalid arguments first, even if they're unused. | |
428 // * Return S_FALSE if the only output is a string argument and it's empty. | |
429 // | |
430 | |
431 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) { | |
432 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ACC_DO_DEFAULT_ACTION); | |
433 if (!instance_active()) | |
434 return E_FAIL; | |
435 | |
436 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
437 if (!target) | |
438 return E_INVALIDARG; | |
439 | |
440 // Return an error if it's not clickable. | |
441 if (!target->HasIntAttribute(ui::AX_ATTR_ACTION)) | |
442 return DISP_E_MEMBERNOTFOUND; | |
443 | |
444 manager_->DoDefaultAction(*target); | |
445 return S_OK; | |
446 } | |
447 | |
448 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, | |
449 LONG y_top, | |
450 VARIANT* child) { | |
451 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ACC_HIT_TEST); | |
452 if (!instance_active()) | |
453 return E_FAIL; | |
454 | |
455 if (!child) | |
456 return E_INVALIDARG; | |
457 | |
458 gfx::Point point(x_left, y_top); | |
459 if (!GetScreenBoundsRect().Contains(point)) { | |
460 // Return S_FALSE and VT_EMPTY when outside the object's boundaries. | |
461 child->vt = VT_EMPTY; | |
462 return S_FALSE; | |
463 } | |
464 | |
465 BrowserAccessibility* result = manager_->CachingAsyncHitTest(point); | |
466 if (result == this) { | |
467 // Point is within this object. | |
468 child->vt = VT_I4; | |
469 child->lVal = CHILDID_SELF; | |
470 } else { | |
471 child->vt = VT_DISPATCH; | |
472 child->pdispVal = ToBrowserAccessibilityWin(result)->NewReference(); | |
473 } | |
474 return S_OK; | |
475 } | |
476 | |
477 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, | |
478 LONG* y_top, | |
479 LONG* width, | |
480 LONG* height, | |
481 VARIANT var_id) { | |
482 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ACC_LOCATION); | |
483 if (!instance_active()) | |
484 return E_FAIL; | |
485 | |
486 return GetPlatformNodeWin()->accLocation(x_left, y_top, width, height, | |
487 var_id); | |
488 } | |
489 | |
490 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir, | |
491 VARIANT start, | |
492 VARIANT* end) { | |
493 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ACC_NAVIGATE); | |
494 | |
495 // Forward all directions but NAVDIR_ to the platform node implementation. | |
496 if (nav_dir != NAVDIR_DOWN && nav_dir != NAVDIR_UP && | |
497 nav_dir != NAVDIR_LEFT && nav_dir != NAVDIR_RIGHT) { | |
498 return GetPlatformNodeWin()->accNavigate(nav_dir, start, end); | |
499 } | |
500 | |
501 BrowserAccessibilityWin* target = GetTargetFromChildID(start); | |
502 if (!target) | |
503 return E_INVALIDARG; | |
504 | |
505 BrowserAccessibility* result = nullptr; | |
506 switch (nav_dir) { | |
507 case NAVDIR_DOWN: | |
508 result = target->GetTableCell(GetTableRow() + GetTableRowSpan(), | |
509 GetTableColumn()); | |
510 break; | |
511 case NAVDIR_UP: | |
512 result = target->GetTableCell(GetTableRow() - 1, GetTableColumn()); | |
513 break; | |
514 case NAVDIR_LEFT: | |
515 result = target->GetTableCell(GetTableRow(), GetTableColumn() - 1); | |
516 break; | |
517 case NAVDIR_RIGHT: | |
518 result = target->GetTableCell(GetTableRow(), | |
519 GetTableColumn() + GetTableColumnSpan()); | |
520 break; | |
521 } | |
522 | |
523 if (!result) { | |
524 end->vt = VT_EMPTY; | |
525 return S_FALSE; | |
526 } | |
527 | |
528 end->vt = VT_DISPATCH; | |
529 end->pdispVal = ToBrowserAccessibilityWin(result)->NewReference(); | |
530 return S_OK; | |
531 } | |
532 | |
533 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child, | |
534 IDispatch** disp_child) { | |
535 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_CHILD); | |
536 if (!instance_active()) | |
537 return E_FAIL; | |
538 | |
539 if (!disp_child) | |
540 return E_INVALIDARG; | |
541 | |
542 *disp_child = NULL; | |
543 | |
544 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child); | |
545 if (!target) | |
546 return E_INVALIDARG; | |
547 | |
548 (*disp_child) = target->NewReference(); | |
549 return S_OK; | |
550 } | |
551 | |
552 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) { | |
553 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_CHILD_COUNT); | |
554 if (!instance_active()) | |
555 return E_FAIL; | |
556 | |
557 if (!child_count) | |
558 return E_INVALIDARG; | |
559 | |
560 *child_count = PlatformChildCount(); | |
561 | |
562 return S_OK; | |
563 } | |
564 | |
565 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id, | |
566 BSTR* def_action) { | |
567 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_DEFAULT_ACTION); | |
568 if (!instance_active()) | |
569 return E_FAIL; | |
570 | |
571 if (!def_action) | |
572 return E_INVALIDARG; | |
573 | |
574 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
575 if (!target) | |
576 return E_INVALIDARG; | |
577 | |
578 return target->get_localizedName(0, def_action); | |
579 } | |
580 | |
581 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id, | |
582 BSTR* desc) { | |
583 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_DESCRIPTION); | |
584 if (!instance_active()) | |
585 return E_FAIL; | |
586 | |
587 if (!desc) | |
588 return E_INVALIDARG; | |
589 | |
590 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
591 if (!target) | |
592 return E_INVALIDARG; | |
593 | |
594 base::string16 description_str = target->description(); | |
595 if (description_str.empty()) | |
596 return S_FALSE; | |
597 | |
598 *desc = SysAllocString(description_str.c_str()); | |
599 | |
600 DCHECK(*desc); | |
601 return S_OK; | |
602 } | |
603 | |
604 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) { | |
605 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_FOCUS); | |
606 if (!instance_active()) | |
607 return E_FAIL; | |
608 | |
609 if (!focus_child) | |
610 return E_INVALIDARG; | |
611 | |
612 BrowserAccessibilityWin* focus = | |
613 static_cast<BrowserAccessibilityWin*>(manager_->GetFocus()); | |
614 if (focus == this) { | |
615 focus_child->vt = VT_I4; | |
616 focus_child->lVal = CHILDID_SELF; | |
617 } else if (focus == NULL) { | |
618 focus_child->vt = VT_EMPTY; | |
619 } else { | |
620 focus_child->vt = VT_DISPATCH; | |
621 focus_child->pdispVal = focus->NewReference(); | |
622 } | |
623 | |
624 return S_OK; | |
625 } | |
626 | |
627 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) { | |
628 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_HELP); | |
629 if (!instance_active()) | |
630 return E_FAIL; | |
631 | |
632 return GetPlatformNodeWin()->get_accHelp(var_id, help); | |
633 } | |
634 | |
635 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id, | |
636 BSTR* acc_key) { | |
637 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_KEYBOARD_SHORTCUT); | |
638 if (!instance_active()) | |
639 return E_FAIL; | |
640 | |
641 if (!acc_key) | |
642 return E_INVALIDARG; | |
643 | |
644 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
645 if (!target) | |
646 return E_INVALIDARG; | |
647 | |
648 if (target->HasStringAttribute(ui::AX_ATTR_KEY_SHORTCUTS)) { | |
649 return target->GetStringAttributeAsBstr( | |
650 ui::AX_ATTR_KEY_SHORTCUTS, acc_key); | |
651 } | |
652 | |
653 return target->GetStringAttributeAsBstr( | |
654 ui::AX_ATTR_SHORTCUT, acc_key); | |
655 } | |
656 | |
657 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { | |
658 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_NAME); | |
659 if (!instance_active()) | |
660 return E_FAIL; | |
661 | |
662 return GetPlatformNodeWin()->get_accName(var_id, name); | |
663 } | |
664 | |
665 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) { | |
666 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_PARENT); | |
667 if (!instance_active()) | |
668 return E_FAIL; | |
669 | |
670 if (!disp_parent) | |
671 return E_INVALIDARG; | |
672 | |
673 IAccessible* parent_obj = ToBrowserAccessibilityWin(PlatformGetParent()); | |
674 if (parent_obj == NULL) { | |
675 // This happens if we're the root of the tree; | |
676 // return the IAccessible for the window. | |
677 parent_obj = | |
678 manager_->ToBrowserAccessibilityManagerWin()->GetParentIAccessible(); | |
679 // |parent| can only be NULL if the manager was created before the parent | |
680 // IAccessible was known and it wasn't subsequently set before a client | |
681 // requested it. This has been fixed. |parent| may also be NULL during | |
682 // destruction. Possible cases where this could occur include tabs being | |
683 // dragged to a new window, etc. | |
684 if (!parent_obj) { | |
685 DVLOG(1) << "In Function: " << __func__ | |
686 << ". Parent IAccessible interface is NULL. Returning failure"; | |
687 return E_FAIL; | |
688 } | |
689 } | |
690 parent_obj->AddRef(); | |
691 *disp_parent = parent_obj; | |
692 return S_OK; | |
693 } | |
694 | |
695 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id, | |
696 VARIANT* role) { | |
697 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_ROLE); | |
698 if (!instance_active()) | |
699 return E_FAIL; | |
700 | |
701 if (!role) | |
702 return E_INVALIDARG; | |
703 | |
704 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
705 if (!target) | |
706 return E_INVALIDARG; | |
707 | |
708 if (!target->role_name().empty()) { | |
709 role->vt = VT_BSTR; | |
710 role->bstrVal = SysAllocString(target->role_name().c_str()); | |
711 } else { | |
712 role->vt = VT_I4; | |
713 role->lVal = target->ia_role(); | |
714 } | |
715 return S_OK; | |
716 } | |
717 | |
718 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id, | |
719 VARIANT* state) { | |
720 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_STATE); | |
721 if (!instance_active()) | |
722 return E_FAIL; | |
723 | |
724 if (!state) | |
725 return E_INVALIDARG; | |
726 | |
727 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
728 if (!target) | |
729 return E_INVALIDARG; | |
730 | |
731 state->vt = VT_I4; | |
732 state->lVal = target->ia_state(); | |
733 if (manager_->GetFocus() == this) | |
734 state->lVal |= STATE_SYSTEM_FOCUSED; | |
735 | |
736 return S_OK; | |
737 } | |
738 | |
739 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id, | |
740 BSTR* value) { | |
741 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_VALUE); | |
742 if (!instance_active()) | |
743 return E_FAIL; | |
744 | |
745 if (!value) | |
746 return E_INVALIDARG; | |
747 | |
748 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | |
749 if (!target) | |
750 return E_INVALIDARG; | |
751 | |
752 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR || | |
753 target->ia_role() == ROLE_SYSTEM_SCROLLBAR || | |
754 target->ia_role() == ROLE_SYSTEM_SLIDER) { | |
755 base::string16 value_text = target->GetValueText(); | |
756 *value = SysAllocString(value_text.c_str()); | |
757 DCHECK(*value); | |
758 return S_OK; | |
759 } | |
760 | |
761 // Expose color well value. | |
762 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) { | |
763 unsigned int color = static_cast<unsigned int>( | |
764 target->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); | |
765 unsigned int red = SkColorGetR(color); | |
766 unsigned int green = SkColorGetG(color); | |
767 unsigned int blue = SkColorGetB(color); | |
768 base::string16 value_text; | |
769 value_text = base::UintToString16(red * 100 / 255) + L"% red " + | |
770 base::UintToString16(green * 100 / 255) + L"% green " + | |
771 base::UintToString16(blue * 100 / 255) + L"% blue"; | |
772 *value = SysAllocString(value_text.c_str()); | |
773 DCHECK(*value); | |
774 return S_OK; | |
775 } | |
776 | |
777 *value = SysAllocString(target->value().c_str()); | |
778 DCHECK(*value); | |
779 return S_OK; | |
780 } | |
781 | |
782 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file, | |
783 VARIANT var_id, | |
784 LONG* topic_id) { | |
785 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_HELP_TOPIC); | |
786 return E_NOTIMPL; | |
787 } | |
788 | |
789 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { | |
790 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_SELECTION); | |
791 if (!instance_active()) | |
792 return E_FAIL; | |
793 | |
794 if (GetRole() != ui::AX_ROLE_LIST_BOX) | |
795 return E_NOTIMPL; | |
796 | |
797 unsigned long selected_count = 0; | |
798 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
799 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) | |
800 ++selected_count; | |
801 } | |
802 | |
803 if (selected_count == 0) { | |
804 selected->vt = VT_EMPTY; | |
805 return S_OK; | |
806 } | |
807 | |
808 if (selected_count == 1) { | |
809 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
810 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { | |
811 selected->vt = VT_DISPATCH; | |
812 selected->pdispVal = | |
813 ToBrowserAccessibilityWin(InternalGetChild(i))->NewReference(); | |
814 return S_OK; | |
815 } | |
816 } | |
817 } | |
818 | |
819 // Multiple items are selected. | |
820 base::win::EnumVariant* enum_variant = | |
821 new base::win::EnumVariant(selected_count); | |
822 enum_variant->AddRef(); | |
823 unsigned long index = 0; | |
824 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
825 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { | |
826 enum_variant->ItemAt(index)->vt = VT_DISPATCH; | |
827 enum_variant->ItemAt(index)->pdispVal = | |
828 ToBrowserAccessibilityWin(InternalGetChild(i))->NewReference(); | |
829 ++index; | |
830 } | |
831 } | |
832 selected->vt = VT_UNKNOWN; | |
833 selected->punkVal = static_cast<IUnknown*>( | |
834 static_cast<base::win::IUnknownImpl*>(enum_variant)); | |
835 return S_OK; | |
836 } | |
837 | |
838 STDMETHODIMP BrowserAccessibilityWin::accSelect( | |
839 LONG flags_sel, VARIANT var_id) { | |
840 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ACC_SELECT); | |
841 if (!instance_active()) | |
842 return E_FAIL; | |
843 | |
844 if (flags_sel & SELFLAG_TAKEFOCUS) { | |
845 manager_->SetFocus(*this); | |
846 return S_OK; | |
847 } | |
848 | |
849 return S_FALSE; | |
850 } | |
851 | |
852 STDMETHODIMP | |
853 BrowserAccessibilityWin::put_accName(VARIANT var_id, BSTR put_name) { | |
854 return E_NOTIMPL; | |
855 } | |
856 STDMETHODIMP | |
857 BrowserAccessibilityWin::put_accValue(VARIANT var_id, BSTR put_val) { | |
858 return E_NOTIMPL; | |
859 } | |
860 | |
861 // | |
862 // IAccessible2 methods. | |
863 // | |
864 | |
865 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) { | |
866 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ROLE); | |
867 if (!instance_active()) | |
868 return E_FAIL; | |
869 | |
870 if (!role) | |
871 return E_INVALIDARG; | |
872 | |
873 *role = ia2_role(); | |
874 return S_OK; | |
875 } | |
876 | |
877 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) { | |
878 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_IA2_GET_ATTRIBUTES); | |
879 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
880 if (!attributes) | |
881 return E_INVALIDARG; | |
882 *attributes = nullptr; | |
883 | |
884 if (!instance_active()) | |
885 return E_FAIL; | |
886 | |
887 base::string16 str; | |
888 for (const base::string16& attribute : ia2_attributes()) | |
889 str += attribute + L';'; | |
890 | |
891 if (str.empty()) | |
892 return S_FALSE; | |
893 | |
894 *attributes = SysAllocString(str.c_str()); | |
895 DCHECK(*attributes); | |
896 return S_OK; | |
897 } | |
898 | |
899 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) { | |
900 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_STATES); | |
901 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
902 if (!instance_active()) | |
903 return E_FAIL; | |
904 | |
905 if (!states) | |
906 return E_INVALIDARG; | |
907 | |
908 *states = ia2_state(); | |
909 | |
910 return S_OK; | |
911 } | |
912 | |
913 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) { | |
914 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_UNIQUE_ID); | |
915 if (!instance_active()) | |
916 return E_FAIL; | |
917 | |
918 if (!unique_id) | |
919 return E_INVALIDARG; | |
920 | |
921 *unique_id = -this->unique_id(); | |
922 return S_OK; | |
923 } | |
924 | |
925 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) { | |
926 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_WINDOW_HANDLE); | |
927 if (!instance_active()) | |
928 return E_FAIL; | |
929 | |
930 if (!window_handle) | |
931 return E_INVALIDARG; | |
932 | |
933 *window_handle = | |
934 manager_->ToBrowserAccessibilityManagerWin()->GetParentHWND(); | |
935 if (!*window_handle) | |
936 return E_FAIL; | |
937 | |
938 return S_OK; | |
939 } | |
940 | |
941 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) { | |
942 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_INDEX_IN_PARENT); | |
943 if (!instance_active()) | |
944 return E_FAIL; | |
945 | |
946 if (!index_in_parent) | |
947 return E_INVALIDARG; | |
948 | |
949 *index_in_parent = this->GetIndexInParent(); | |
950 return S_OK; | |
951 } | |
952 | |
953 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) { | |
954 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_RELATIONS); | |
955 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
956 if (!instance_active()) | |
957 return E_FAIL; | |
958 | |
959 if (!n_relations) | |
960 return E_INVALIDARG; | |
961 | |
962 *n_relations = relations_.size(); | |
963 return S_OK; | |
964 } | |
965 | |
966 STDMETHODIMP BrowserAccessibilityWin::get_relation( | |
967 LONG relation_index, | |
968 IAccessibleRelation** relation) { | |
969 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_RELATION); | |
970 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
971 if (!instance_active()) | |
972 return E_FAIL; | |
973 | |
974 if (relation_index < 0 || | |
975 relation_index >= static_cast<long>(relations_.size())) { | |
976 return E_INVALIDARG; | |
977 } | |
978 | |
979 if (!relation) | |
980 return E_INVALIDARG; | |
981 | |
982 relations_[relation_index]->AddRef(); | |
983 *relation = relations_[relation_index]; | |
984 return S_OK; | |
985 } | |
986 | |
987 STDMETHODIMP BrowserAccessibilityWin::get_relations( | |
988 LONG max_relations, | |
989 IAccessibleRelation** relations, | |
990 LONG* n_relations) { | |
991 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_RELATIONS); | |
992 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
993 if (!instance_active()) | |
994 return E_FAIL; | |
995 | |
996 if (!relations || !n_relations) | |
997 return E_INVALIDARG; | |
998 | |
999 long count = static_cast<long>(relations_.size()); | |
1000 *n_relations = count; | |
1001 if (count == 0) | |
1002 return S_FALSE; | |
1003 | |
1004 for (long i = 0; i < count; ++i) { | |
1005 relations_[i]->AddRef(); | |
1006 relations[i] = relations_[i]; | |
1007 } | |
1008 | |
1009 return S_OK; | |
1010 } | |
1011 | |
1012 STDMETHODIMP BrowserAccessibilityWin::scrollTo(IA2ScrollType scroll_type) { | |
1013 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_IA2_SCROLL_TO); | |
1014 if (!instance_active()) | |
1015 return E_FAIL; | |
1016 | |
1017 gfx::Rect r = GetFrameBoundsRect(); | |
1018 switch(scroll_type) { | |
1019 case IA2_SCROLL_TYPE_TOP_LEFT: | |
1020 manager_->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0)); | |
1021 break; | |
1022 case IA2_SCROLL_TYPE_BOTTOM_RIGHT: | |
1023 manager_->ScrollToMakeVisible(*this, | |
1024 gfx::Rect(r.right(), r.bottom(), 0, 0)); | |
1025 break; | |
1026 case IA2_SCROLL_TYPE_TOP_EDGE: | |
1027 manager_->ScrollToMakeVisible(*this, | |
1028 gfx::Rect(r.x(), r.y(), r.width(), 0)); | |
1029 break; | |
1030 case IA2_SCROLL_TYPE_BOTTOM_EDGE: | |
1031 manager_->ScrollToMakeVisible(*this, | |
1032 gfx::Rect(r.x(), r.bottom(), r.width(), 0)); | |
1033 break; | |
1034 case IA2_SCROLL_TYPE_LEFT_EDGE: | |
1035 manager_->ScrollToMakeVisible(*this, | |
1036 gfx::Rect(r.x(), r.y(), 0, r.height())); | |
1037 break; | |
1038 case IA2_SCROLL_TYPE_RIGHT_EDGE: | |
1039 manager_->ScrollToMakeVisible(*this, | |
1040 gfx::Rect(r.right(), r.y(), 0, r.height())); | |
1041 break; | |
1042 case IA2_SCROLL_TYPE_ANYWHERE: | |
1043 default: | |
1044 manager_->ScrollToMakeVisible(*this, r); | |
1045 break; | |
1046 } | |
1047 | |
1048 return S_OK; | |
1049 } | |
1050 | |
1051 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint( | |
1052 IA2CoordinateType coordinate_type, | |
1053 LONG x, | |
1054 LONG y) { | |
1055 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SCROLL_TO_POINT); | |
1056 if (!instance_active()) | |
1057 return E_FAIL; | |
1058 | |
1059 gfx::Point scroll_to(x, y); | |
1060 | |
1061 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { | |
1062 scroll_to -= manager_->GetViewBounds().OffsetFromOrigin(); | |
1063 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { | |
1064 if (PlatformGetParent()) | |
1065 scroll_to += PlatformGetParent()->GetFrameBoundsRect().OffsetFromOrigin(); | |
1066 } else { | |
1067 return E_INVALIDARG; | |
1068 } | |
1069 | |
1070 manager_->ScrollToPoint(*this, scroll_to); | |
1071 | |
1072 return S_OK; | |
1073 } | |
1074 | |
1075 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition( | |
1076 LONG* group_level, | |
1077 LONG* similar_items_in_group, | |
1078 LONG* position_in_group) { | |
1079 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_GROUP_POSITION); | |
1080 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1081 if (!instance_active()) | |
1082 return E_FAIL; | |
1083 | |
1084 if (!group_level || !similar_items_in_group || !position_in_group) | |
1085 return E_INVALIDARG; | |
1086 | |
1087 *group_level = GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL); | |
1088 *similar_items_in_group = GetIntAttribute(ui::AX_ATTR_SET_SIZE); | |
1089 *position_in_group = GetIntAttribute(ui::AX_ATTR_POS_IN_SET); | |
1090 | |
1091 if (*group_level == *similar_items_in_group == *position_in_group == 0) | |
1092 return S_FALSE; | |
1093 return S_OK; | |
1094 } | |
1095 | |
1096 STDMETHODIMP | |
1097 BrowserAccessibilityWin::get_localizedExtendedRole( | |
1098 BSTR* localized_extended_role) { | |
1099 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALIZED_EXTENDED_ROLE); | |
1100 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1101 | |
1102 if (!instance_active()) | |
1103 return E_FAIL; | |
1104 | |
1105 if (!localized_extended_role) | |
1106 return E_INVALIDARG; | |
1107 | |
1108 return GetStringAttributeAsBstr( | |
1109 ui::AX_ATTR_ROLE_DESCRIPTION, localized_extended_role); | |
1110 } | |
1111 | |
1112 // | |
1113 // IAccessible2 methods not implemented. | |
1114 // | |
1115 | |
1116 STDMETHODIMP BrowserAccessibilityWin::get_extendedRole(BSTR* extended_role) { | |
1117 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_EXTENDED_ROLE); | |
1118 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1119 return E_NOTIMPL; | |
1120 } | |
1121 STDMETHODIMP | |
1122 BrowserAccessibilityWin::get_nExtendedStates(LONG* n_extended_states) { | |
1123 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_EXTENDED_STATES); | |
1124 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1125 return E_NOTIMPL; | |
1126 } | |
1127 STDMETHODIMP | |
1128 BrowserAccessibilityWin::get_extendedStates(LONG max_extended_states, | |
1129 BSTR** extended_states, | |
1130 LONG* n_extended_states) { | |
1131 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_EXTENDED_STATES); | |
1132 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1133 return E_NOTIMPL; | |
1134 } | |
1135 STDMETHODIMP | |
1136 BrowserAccessibilityWin::get_localizedExtendedStates( | |
1137 LONG max_localized_extended_states, | |
1138 BSTR** localized_extended_states, | |
1139 LONG* n_localized_extended_states) { | |
1140 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALIZED_EXTENDED_STATES); | |
1141 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1142 return E_NOTIMPL; | |
1143 } | |
1144 STDMETHODIMP BrowserAccessibilityWin::get_locale(IA2Locale* locale) { | |
1145 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALE); | |
1146 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1147 return E_NOTIMPL; | |
1148 } | |
1149 | |
1150 // | |
1151 // IAccessibleApplication methods. | |
1152 // | |
1153 | |
1154 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) { | |
1155 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_APP_NAME); | |
1156 // No need to check |instance_active()| because this interface is | |
1157 // global, and doesn't depend on any local state. | |
1158 | |
1159 if (!app_name) | |
1160 return E_INVALIDARG; | |
1161 | |
1162 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out | |
1163 // the part before the "/". | |
1164 std::vector<std::string> product_components = base::SplitString( | |
1165 GetContentClient()->GetProduct(), "/", | |
1166 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | |
1167 DCHECK_EQ(2U, product_components.size()); | |
1168 if (product_components.size() != 2) | |
1169 return E_FAIL; | |
1170 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str()); | |
1171 DCHECK(*app_name); | |
1172 return *app_name ? S_OK : E_FAIL; | |
1173 } | |
1174 | |
1175 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) { | |
1176 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_APP_VERSION); | |
1177 // No need to check |instance_active()| because this interface is | |
1178 // global, and doesn't depend on any local state. | |
1179 | |
1180 if (!app_version) | |
1181 return E_INVALIDARG; | |
1182 | |
1183 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out | |
1184 // the part after the "/". | |
1185 std::vector<std::string> product_components = base::SplitString( | |
1186 GetContentClient()->GetProduct(), "/", | |
1187 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | |
1188 DCHECK_EQ(2U, product_components.size()); | |
1189 if (product_components.size() != 2) | |
1190 return E_FAIL; | |
1191 *app_version = | |
1192 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str()); | |
1193 DCHECK(*app_version); | |
1194 return *app_version ? S_OK : E_FAIL; | |
1195 } | |
1196 | |
1197 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) { | |
1198 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TOOLKIT_NAME); | |
1199 // No need to check |instance_active()| because this interface is | |
1200 // global, and doesn't depend on any local state. | |
1201 | |
1202 if (!toolkit_name) | |
1203 return E_INVALIDARG; | |
1204 | |
1205 // This is hard-coded; all products based on the Chromium engine | |
1206 // will have the same toolkit name, so that assistive technology can | |
1207 // detect any Chrome-based product. | |
1208 *toolkit_name = SysAllocString(L"Chrome"); | |
1209 DCHECK(*toolkit_name); | |
1210 return *toolkit_name ? S_OK : E_FAIL; | |
1211 } | |
1212 | |
1213 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion( | |
1214 BSTR* toolkit_version) { | |
1215 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TOOLKIT_VERSION); | |
1216 // No need to check |instance_active()| because this interface is | |
1217 // global, and doesn't depend on any local state. | |
1218 | |
1219 if (!toolkit_version) | |
1220 return E_INVALIDARG; | |
1221 | |
1222 std::string user_agent = GetContentClient()->GetUserAgent(); | |
1223 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str()); | |
1224 DCHECK(*toolkit_version); | |
1225 return *toolkit_version ? S_OK : E_FAIL; | |
1226 } | |
1227 | |
1228 // | |
1229 // IAccessibleImage methods. | |
1230 // | |
1231 | |
1232 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) { | |
1233 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_DESCRIPTION); | |
1234 if (!instance_active()) | |
1235 return E_FAIL; | |
1236 | |
1237 if (!desc) | |
1238 return E_INVALIDARG; | |
1239 | |
1240 if (description().empty()) | |
1241 return S_FALSE; | |
1242 | |
1243 *desc = SysAllocString(description().c_str()); | |
1244 | |
1245 DCHECK(*desc); | |
1246 return S_OK; | |
1247 } | |
1248 | |
1249 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition( | |
1250 IA2CoordinateType coordinate_type, | |
1251 LONG* x, | |
1252 LONG* y) { | |
1253 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IMAGE_POSITION); | |
1254 if (!instance_active()) | |
1255 return E_FAIL; | |
1256 | |
1257 if (!x || !y) | |
1258 return E_INVALIDARG; | |
1259 | |
1260 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { | |
1261 gfx::Rect bounds = GetScreenBoundsRect(); | |
1262 *x = bounds.x(); | |
1263 *y = bounds.y(); | |
1264 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { | |
1265 gfx::Rect bounds = GetPageBoundsRect(); | |
1266 gfx::Rect parent_bounds = PlatformGetParent() | |
1267 ? PlatformGetParent()->GetPageBoundsRect() | |
1268 : gfx::Rect(); | |
1269 *x = bounds.x() - parent_bounds.x(); | |
1270 *y = bounds.y() - parent_bounds.y(); | |
1271 } else { | |
1272 return E_INVALIDARG; | |
1273 } | |
1274 | |
1275 return S_OK; | |
1276 } | |
1277 | |
1278 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) { | |
1279 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IMAGE_SIZE); | |
1280 if (!instance_active()) | |
1281 return E_FAIL; | |
1282 | |
1283 if (!height || !width) | |
1284 return E_INVALIDARG; | |
1285 | |
1286 *height = GetPageBoundsRect().height(); | |
1287 *width = GetPageBoundsRect().width(); | |
1288 return S_OK; | |
1289 } | |
1290 | |
1291 // | |
1292 // IAccessibleTable methods. | |
1293 // | |
1294 | |
1295 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt( | |
1296 long row, | |
1297 long column, | |
1298 IUnknown** accessible) { | |
1299 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACCESSIBLE_AT); | |
1300 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1301 if (!instance_active()) | |
1302 return E_FAIL; | |
1303 | |
1304 if (!accessible) | |
1305 return E_INVALIDARG; | |
1306 | |
1307 BrowserAccessibility* cell = | |
1308 GetTableCell(static_cast<int>(row), static_cast<int>(column)); | |
1309 if (cell && ToBrowserAccessibilityWin(cell)) { | |
1310 *accessible = static_cast<IAccessible*>( | |
1311 ToBrowserAccessibilityWin(cell)->NewReference()); | |
1312 return S_OK; | |
1313 } | |
1314 | |
1315 *accessible = nullptr; | |
1316 return E_INVALIDARG; | |
1317 } | |
1318 | |
1319 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) { | |
1320 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CAPTION); | |
1321 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1322 if (!instance_active()) | |
1323 return E_FAIL; | |
1324 | |
1325 if (!accessible) | |
1326 return E_INVALIDARG; | |
1327 | |
1328 // TODO(dmazzoni): implement | |
1329 *accessible = nullptr; | |
1330 return S_FALSE; | |
1331 } | |
1332 | |
1333 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row, | |
1334 long column, | |
1335 long* cell_index) { | |
1336 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CHILD_INDEX); | |
1337 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1338 if (!instance_active()) | |
1339 return E_FAIL; | |
1340 | |
1341 if (!cell_index) | |
1342 return E_INVALIDARG; | |
1343 | |
1344 BrowserAccessibility* cell = | |
1345 GetTableCell(static_cast<int>(row), static_cast<int>(column)); | |
1346 if (cell) { | |
1347 *cell_index = static_cast<LONG>(cell->GetTableCellIndex()); | |
1348 return S_OK; | |
1349 } | |
1350 | |
1351 *cell_index = 0; | |
1352 return E_INVALIDARG; | |
1353 } | |
1354 | |
1355 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column, | |
1356 BSTR* description) { | |
1357 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_DESCRIPTION); | |
1358 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1359 if (!instance_active()) | |
1360 return E_FAIL; | |
1361 | |
1362 if (!description) | |
1363 return E_INVALIDARG; | |
1364 | |
1365 int columns = GetTableColumnCount(); | |
1366 if (column < 0 || column >= columns) | |
1367 return E_INVALIDARG; | |
1368 | |
1369 int rows = GetTableRowCount(); | |
1370 if (rows <= 0) { | |
1371 *description = nullptr; | |
1372 return S_FALSE; | |
1373 } | |
1374 | |
1375 for (int i = 0; i < rows; ++i) { | |
1376 const BrowserAccessibility* cell = GetTableCell(i, column); | |
1377 if (ToBrowserAccessibilityWin(cell) && | |
1378 cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { | |
1379 base::string16 cell_name = cell->GetString16Attribute( | |
1380 ui::AX_ATTR_NAME); | |
1381 if (cell_name.size() > 0) { | |
1382 *description = SysAllocString(cell_name.c_str()); | |
1383 return S_OK; | |
1384 } | |
1385 | |
1386 if (ToBrowserAccessibilityWin(cell)->description().size() > 0) { | |
1387 *description = SysAllocString( | |
1388 ToBrowserAccessibilityWin(cell)->description().c_str()); | |
1389 return S_OK; | |
1390 } | |
1391 } | |
1392 } | |
1393 | |
1394 *description = nullptr; | |
1395 return S_FALSE; | |
1396 } | |
1397 | |
1398 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt( | |
1399 long row, | |
1400 long column, | |
1401 long* n_columns_spanned) { | |
1402 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_EXTENT_AT); | |
1403 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1404 if (!instance_active()) | |
1405 return E_FAIL; | |
1406 | |
1407 if (!n_columns_spanned) | |
1408 return E_INVALIDARG; | |
1409 | |
1410 BrowserAccessibility* cell = | |
1411 GetTableCell(static_cast<int>(row), static_cast<int>(column)); | |
1412 if (!cell) | |
1413 return E_INVALIDARG; | |
1414 | |
1415 *n_columns_spanned = cell->GetTableColumnSpan(); | |
1416 return S_OK; | |
1417 } | |
1418 | |
1419 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader( | |
1420 IAccessibleTable** accessible_table, | |
1421 long* starting_row_index) { | |
1422 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_HEADER); | |
1423 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1424 // TODO(dmazzoni): implement | |
1425 return E_NOTIMPL; | |
1426 } | |
1427 | |
1428 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index, | |
1429 long* column_index) { | |
1430 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_INDEX); | |
1431 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1432 if (!instance_active()) | |
1433 return E_FAIL; | |
1434 | |
1435 if (!column_index) | |
1436 return E_INVALIDARG; | |
1437 | |
1438 BrowserAccessibility* cell = GetTableCell(cell_index); | |
1439 if (!cell) | |
1440 return E_INVALIDARG; | |
1441 *column_index = cell->GetTableColumn(); | |
1442 return S_OK; | |
1443 } | |
1444 | |
1445 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) { | |
1446 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_COLUMNS); | |
1447 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1448 if (!instance_active()) | |
1449 return E_FAIL; | |
1450 | |
1451 if (!column_count) | |
1452 return E_INVALIDARG; | |
1453 | |
1454 *column_count = GetTableColumnCount(); | |
1455 return S_OK; | |
1456 } | |
1457 | |
1458 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) { | |
1459 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_ROWS); | |
1460 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1461 if (!instance_active()) | |
1462 return E_FAIL; | |
1463 | |
1464 if (!row_count) | |
1465 return E_INVALIDARG; | |
1466 | |
1467 *row_count = GetTableRowCount(); | |
1468 return S_OK; | |
1469 } | |
1470 | |
1471 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) { | |
1472 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_CHILDREN); | |
1473 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1474 if (!instance_active()) | |
1475 return E_FAIL; | |
1476 | |
1477 if (!cell_count) | |
1478 return E_INVALIDARG; | |
1479 | |
1480 // TODO(dmazzoni): add support for selected cells/rows/columns in tables. | |
1481 *cell_count = 0; | |
1482 return S_FALSE; | |
1483 } | |
1484 | |
1485 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) { | |
1486 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_COLUMNS); | |
1487 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1488 if (!instance_active()) | |
1489 return E_FAIL; | |
1490 | |
1491 if (!column_count) | |
1492 return E_INVALIDARG; | |
1493 | |
1494 *column_count = 0; | |
1495 return S_FALSE; | |
1496 } | |
1497 | |
1498 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) { | |
1499 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_ROWS); | |
1500 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1501 if (!instance_active()) | |
1502 return E_FAIL; | |
1503 | |
1504 if (!row_count) | |
1505 return E_INVALIDARG; | |
1506 | |
1507 *row_count = 0; | |
1508 return S_FALSE; | |
1509 } | |
1510 | |
1511 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row, | |
1512 BSTR* description) { | |
1513 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_DESCRIPTION); | |
1514 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1515 if (!instance_active()) | |
1516 return E_FAIL; | |
1517 | |
1518 if (!description) | |
1519 return E_INVALIDARG; | |
1520 | |
1521 if (row < 0 || row >= GetTableRowCount()) | |
1522 return E_INVALIDARG; | |
1523 | |
1524 int columns = GetTableColumnCount(); | |
1525 if (columns <= 0) { | |
1526 *description = nullptr; | |
1527 return S_FALSE; | |
1528 } | |
1529 | |
1530 for (int i = 0; i < columns; ++i) { | |
1531 const BrowserAccessibility* cell = GetTableCell(row, i); | |
1532 if (ToBrowserAccessibilityWin(cell) && | |
1533 cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { | |
1534 base::string16 cell_name = cell->GetString16Attribute( | |
1535 ui::AX_ATTR_NAME); | |
1536 if (cell_name.size() > 0) { | |
1537 *description = SysAllocString(cell_name.c_str()); | |
1538 return S_OK; | |
1539 } | |
1540 | |
1541 if (ToBrowserAccessibilityWin(cell)->description().size() > 0) { | |
1542 *description = SysAllocString( | |
1543 ToBrowserAccessibilityWin(cell)->description().c_str()); | |
1544 return S_OK; | |
1545 } | |
1546 } | |
1547 } | |
1548 | |
1549 *description = nullptr; | |
1550 return S_FALSE; | |
1551 } | |
1552 | |
1553 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row, | |
1554 long column, | |
1555 long* n_rows_spanned) { | |
1556 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_EXTENT_AT); | |
1557 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1558 if (!instance_active()) | |
1559 return E_FAIL; | |
1560 | |
1561 if (!n_rows_spanned) | |
1562 return E_INVALIDARG; | |
1563 | |
1564 BrowserAccessibility* cell = GetTableCell(row, column); | |
1565 if (!cell) | |
1566 return E_INVALIDARG; | |
1567 | |
1568 *n_rows_spanned = GetTableRowSpan(); | |
1569 return S_OK; | |
1570 } | |
1571 | |
1572 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader( | |
1573 IAccessibleTable** accessible_table, | |
1574 long* starting_column_index) { | |
1575 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_HEADER); | |
1576 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1577 // TODO(dmazzoni): implement | |
1578 return E_NOTIMPL; | |
1579 } | |
1580 | |
1581 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index, | |
1582 long* row_index) { | |
1583 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_INDEX); | |
1584 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1585 if (!instance_active()) | |
1586 return E_FAIL; | |
1587 | |
1588 if (!row_index) | |
1589 return E_INVALIDARG; | |
1590 | |
1591 BrowserAccessibility* cell = GetTableCell(cell_index); | |
1592 if (!cell) | |
1593 return E_INVALIDARG; | |
1594 | |
1595 *row_index = cell->GetTableRow(); | |
1596 return S_OK; | |
1597 } | |
1598 | |
1599 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children, | |
1600 long** children, | |
1601 long* n_children) { | |
1602 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SELECTED_CHILDREN); | |
1603 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1604 if (!instance_active()) | |
1605 return E_FAIL; | |
1606 | |
1607 if (!children || !n_children) | |
1608 return E_INVALIDARG; | |
1609 | |
1610 // TODO(dmazzoni): Implement this. | |
1611 *n_children = 0; | |
1612 return S_FALSE; | |
1613 } | |
1614 | |
1615 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns, | |
1616 long** columns, | |
1617 long* n_columns) { | |
1618 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SELECTED_COLUMNS); | |
1619 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1620 if (!instance_active()) | |
1621 return E_FAIL; | |
1622 | |
1623 if (!columns || !n_columns) | |
1624 return E_INVALIDARG; | |
1625 | |
1626 // TODO(dmazzoni): Implement this. | |
1627 *n_columns = 0; | |
1628 return S_FALSE; | |
1629 } | |
1630 | |
1631 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows, | |
1632 long** rows, | |
1633 long* n_rows) { | |
1634 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SELECTED_ROWS); | |
1635 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1636 if (!instance_active()) | |
1637 return E_FAIL; | |
1638 | |
1639 if (!rows || !n_rows) | |
1640 return E_INVALIDARG; | |
1641 | |
1642 // TODO(dmazzoni): Implement this. | |
1643 *n_rows = 0; | |
1644 return S_FALSE; | |
1645 } | |
1646 | |
1647 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) { | |
1648 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SUMMARY); | |
1649 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1650 if (!instance_active()) | |
1651 return E_FAIL; | |
1652 | |
1653 if (!accessible) | |
1654 return E_INVALIDARG; | |
1655 | |
1656 // TODO(dmazzoni): implement | |
1657 *accessible = nullptr; | |
1658 return S_FALSE; | |
1659 } | |
1660 | |
1661 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected( | |
1662 long column, | |
1663 boolean* is_selected) { | |
1664 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IS_COLUMN_SELECTED); | |
1665 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1666 if (!instance_active()) | |
1667 return E_FAIL; | |
1668 | |
1669 if (!is_selected) | |
1670 return E_INVALIDARG; | |
1671 | |
1672 // TODO(dmazzoni): Implement this. | |
1673 *is_selected = false; | |
1674 return S_OK; | |
1675 } | |
1676 | |
1677 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row, | |
1678 boolean* is_selected) { | |
1679 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IS_ROW_SELECTED); | |
1680 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1681 if (!instance_active()) | |
1682 return E_FAIL; | |
1683 | |
1684 if (!is_selected) | |
1685 return E_INVALIDARG; | |
1686 | |
1687 // TODO(dmazzoni): Implement this. | |
1688 *is_selected = false; | |
1689 return S_OK; | |
1690 } | |
1691 | |
1692 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row, | |
1693 long column, | |
1694 boolean* is_selected) { | |
1695 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IS_SELECTED); | |
1696 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1697 if (!instance_active()) | |
1698 return E_FAIL; | |
1699 | |
1700 if (!is_selected) | |
1701 return E_INVALIDARG; | |
1702 | |
1703 // TODO(dmazzoni): Implement this. | |
1704 *is_selected = false; | |
1705 return S_OK; | |
1706 } | |
1707 | |
1708 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( | |
1709 long index, | |
1710 long* row, | |
1711 long* column, | |
1712 long* row_extents, | |
1713 long* column_extents, | |
1714 boolean* is_selected) { | |
1715 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_COLUMN_EXTENTS_AT_INDEX); | |
1716 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1717 if (!instance_active()) | |
1718 return E_FAIL; | |
1719 | |
1720 if (!row || !column || !row_extents || !column_extents || !is_selected) | |
1721 return E_INVALIDARG; | |
1722 | |
1723 BrowserAccessibility* cell = GetTableCell(index); | |
1724 if (!cell) | |
1725 return E_INVALIDARG; | |
1726 | |
1727 *row = cell->GetTableRow(); | |
1728 *column = cell->GetTableColumn(); | |
1729 *row_extents = GetTableRowSpan(); | |
1730 *column_extents = GetTableColumnSpan(); | |
1731 *is_selected = false; // Not supported. | |
1732 | |
1733 return S_OK; | |
1734 } | |
1735 | |
1736 STDMETHODIMP BrowserAccessibilityWin::selectRow(long row) { | |
1737 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SELECT_ROW); | |
1738 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1739 return E_NOTIMPL; | |
1740 } | |
1741 | |
1742 STDMETHODIMP BrowserAccessibilityWin::selectColumn(long column) { | |
1743 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SELECT_COLUMN); | |
1744 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1745 return E_NOTIMPL; | |
1746 } | |
1747 | |
1748 STDMETHODIMP BrowserAccessibilityWin::unselectRow(long row) { | |
1749 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_UNSELECT_ROW); | |
1750 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1751 return E_NOTIMPL; | |
1752 } | |
1753 | |
1754 STDMETHODIMP BrowserAccessibilityWin::unselectColumn(long column) { | |
1755 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_UNSELECT_COLUMN); | |
1756 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1757 return E_NOTIMPL; | |
1758 } | |
1759 | |
1760 STDMETHODIMP | |
1761 BrowserAccessibilityWin::get_modelChange(IA2TableModelChange* model_change) { | |
1762 return E_NOTIMPL; | |
1763 } | |
1764 | |
1765 // | |
1766 // IAccessibleTable2 methods. | |
1767 // | |
1768 | |
1769 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row, | |
1770 long column, | |
1771 IUnknown** cell) { | |
1772 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CELL_AT); | |
1773 AddAccessibilityModeFlags(AccessibilityMode::kScreenReader); | |
1774 if (!instance_active()) | |
1775 return E_FAIL; | |
1776 | |
1777 if (!cell) | |
1778 return E_INVALIDARG; | |
1779 | |
1780 BrowserAccessibility* table_cell = | |
1781 GetTableCell(static_cast<int>(row), static_cast<int>(column)); | |
1782 if (ToBrowserAccessibilityWin(table_cell)) { | |
1783 return ToBrowserAccessibilityWin(table_cell) | |
1784 ->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(cell)); | |
1785 } | |
1786 | |
1787 *cell = nullptr; | |
1788 return E_INVALIDARG; | |
1789 } | |
1790 | |
1791 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) { | |
1792 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_CELLS); | |
1793 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1794 return get_nSelectedChildren(cell_count); | |
1795 } | |
1796 | |
1797 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells( | |
1798 IUnknown*** cells, | |
1799 long* n_selected_cells) { | |
1800 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SELECTED_CELLS); | |
1801 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1802 if (!instance_active()) | |
1803 return E_FAIL; | |
1804 | |
1805 if (!cells || !n_selected_cells) | |
1806 return E_INVALIDARG; | |
1807 | |
1808 // TODO(dmazzoni): Implement this. | |
1809 *n_selected_cells = 0; | |
1810 return S_OK; | |
1811 } | |
1812 | |
1813 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns, | |
1814 long* n_columns) { | |
1815 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLE2_GET_SELECTED_COLUMNS); | |
1816 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1817 if (!instance_active()) | |
1818 return E_FAIL; | |
1819 | |
1820 if (!columns || !n_columns) | |
1821 return E_INVALIDARG; | |
1822 | |
1823 // TODO(dmazzoni): Implement this. | |
1824 *n_columns = 0; | |
1825 return S_OK; | |
1826 } | |
1827 | |
1828 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows, | |
1829 long* n_rows) { | |
1830 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLE2_GET_SELECTED_ROWS); | |
1831 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1832 if (!instance_active()) | |
1833 return E_FAIL; | |
1834 | |
1835 if (!rows || !n_rows) | |
1836 return E_INVALIDARG; | |
1837 | |
1838 // TODO(dmazzoni): Implement this. | |
1839 *n_rows = 0; | |
1840 return S_OK; | |
1841 } | |
1842 | |
1843 | |
1844 // | |
1845 // IAccessibleTableCell methods. | |
1846 // | |
1847 | |
1848 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent( | |
1849 long* n_columns_spanned) { | |
1850 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_EXTENT); | |
1851 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1852 if (!instance_active()) | |
1853 return E_FAIL; | |
1854 | |
1855 if (!n_columns_spanned) | |
1856 return E_INVALIDARG; | |
1857 | |
1858 *n_columns_spanned = GetTableColumnSpan(); | |
1859 return S_OK; | |
1860 } | |
1861 | |
1862 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( | |
1863 IUnknown*** cell_accessibles, | |
1864 long* n_column_header_cells) { | |
1865 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_HEADER_CELLS); | |
1866 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1867 if (!instance_active()) | |
1868 return E_FAIL; | |
1869 | |
1870 if (!cell_accessibles || !n_column_header_cells) | |
1871 return E_INVALIDARG; | |
1872 | |
1873 *n_column_header_cells = 0; | |
1874 BrowserAccessibility* table = GetTable(); | |
1875 if (!table) { | |
1876 NOTREACHED(); | |
1877 return S_FALSE; | |
1878 } | |
1879 | |
1880 int column = GetTableColumn(); | |
1881 int columns = GetTableColumnCount(); | |
1882 int rows = GetTableRowCount(); | |
1883 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns) | |
1884 return S_FALSE; | |
1885 | |
1886 for (int i = 0; i < rows; ++i) { | |
1887 BrowserAccessibility* cell = GetTableCell(i, column); | |
1888 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) | |
1889 (*n_column_header_cells)++; | |
1890 } | |
1891 | |
1892 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc( | |
1893 (*n_column_header_cells) * sizeof(cell_accessibles[0]))); | |
1894 int index = 0; | |
1895 for (int i = 0; i < rows; ++i) { | |
1896 BrowserAccessibility* cell = GetTableCell(i, column); | |
1897 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { | |
1898 (*cell_accessibles)[index] = static_cast<IAccessible*>( | |
1899 ToBrowserAccessibilityWin(cell)->NewReference()); | |
1900 ++index; | |
1901 } | |
1902 } | |
1903 | |
1904 return S_OK; | |
1905 } | |
1906 | |
1907 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) { | |
1908 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLECELL_GET_COLUMN_INDEX); | |
1909 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1910 if (!instance_active()) | |
1911 return E_FAIL; | |
1912 | |
1913 if (!column_index) | |
1914 return E_INVALIDARG; | |
1915 | |
1916 *column_index = GetTableColumn(); | |
1917 return S_OK; | |
1918 } | |
1919 | |
1920 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) { | |
1921 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_EXTENT); | |
1922 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1923 if (!instance_active()) | |
1924 return E_FAIL; | |
1925 | |
1926 if (!n_rows_spanned) | |
1927 return E_INVALIDARG; | |
1928 | |
1929 *n_rows_spanned = GetTableRowSpan(); | |
1930 return S_OK; | |
1931 } | |
1932 | |
1933 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( | |
1934 IUnknown*** cell_accessibles, | |
1935 long* n_row_header_cells) { | |
1936 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_HEADER_CELLS); | |
1937 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1938 if (!instance_active()) | |
1939 return E_FAIL; | |
1940 | |
1941 if (!cell_accessibles || !n_row_header_cells) | |
1942 return E_INVALIDARG; | |
1943 | |
1944 *n_row_header_cells = 0; | |
1945 BrowserAccessibility* table = GetTable(); | |
1946 if (!table) { | |
1947 NOTREACHED(); | |
1948 return S_FALSE; | |
1949 } | |
1950 | |
1951 int row = GetTableRow(); | |
1952 int columns = GetTableColumnCount(); | |
1953 int rows = GetTableRowCount(); | |
1954 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows) | |
1955 return S_FALSE; | |
1956 | |
1957 for (int i = 0; i < columns; ++i) { | |
1958 BrowserAccessibility* cell = GetTableCell(row, i); | |
1959 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) | |
1960 (*n_row_header_cells)++; | |
1961 } | |
1962 | |
1963 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc( | |
1964 (*n_row_header_cells) * sizeof(cell_accessibles[0]))); | |
1965 int index = 0; | |
1966 for (int i = 0; i < columns; ++i) { | |
1967 BrowserAccessibility* cell = GetTableCell(row, i); | |
1968 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { | |
1969 (*cell_accessibles)[index] = static_cast<IAccessible*>( | |
1970 ToBrowserAccessibilityWin(cell)->NewReference()); | |
1971 ++index; | |
1972 } | |
1973 } | |
1974 | |
1975 return S_OK; | |
1976 } | |
1977 | |
1978 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) { | |
1979 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLECELL_GET_ROW_INDEX); | |
1980 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1981 if (!instance_active()) | |
1982 return E_FAIL; | |
1983 | |
1984 if (!row_index) | |
1985 return E_INVALIDARG; | |
1986 | |
1987 *row_index = GetTableRow(); | |
1988 return S_OK; | |
1989 } | |
1990 | |
1991 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) { | |
1992 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLECELL_GET_IS_SELECTED); | |
1993 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
1994 if (!instance_active()) | |
1995 return E_FAIL; | |
1996 | |
1997 if (!is_selected) | |
1998 return E_INVALIDARG; | |
1999 | |
2000 *is_selected = false; | |
2001 return S_OK; | |
2002 } | |
2003 | |
2004 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents( | |
2005 long* row_index, | |
2006 long* column_index, | |
2007 long* row_extents, | |
2008 long* column_extents, | |
2009 boolean* is_selected) { | |
2010 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_COLUMN_EXTENTS); | |
2011 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2012 if (!instance_active()) | |
2013 return E_FAIL; | |
2014 | |
2015 if (!row_index || | |
2016 !column_index || | |
2017 !row_extents || | |
2018 !column_extents || | |
2019 !is_selected) { | |
2020 return E_INVALIDARG; | |
2021 } | |
2022 | |
2023 *row_index = GetTableRow(); | |
2024 *column_index = GetTableColumn(); | |
2025 *row_extents = GetTableRowSpan(); | |
2026 *column_extents = GetTableColumnSpan(); | |
2027 *is_selected = false; // Not supported. | |
2028 | |
2029 return S_OK; | |
2030 } | |
2031 | |
2032 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) { | |
2033 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TABLE); | |
2034 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2035 if (!instance_active()) | |
2036 return E_FAIL; | |
2037 | |
2038 if (!table) | |
2039 return E_INVALIDARG; | |
2040 | |
2041 BrowserAccessibility* find_table = GetTable(); | |
2042 if (!find_table || !ToBrowserAccessibilityWin(find_table)) { | |
2043 *table = nullptr; | |
2044 return S_FALSE; | |
2045 } | |
2046 | |
2047 *table = static_cast<IAccessibleTable*>( | |
2048 ToBrowserAccessibilityWin(find_table)->NewReference()); | |
2049 return S_OK; | |
2050 } | |
2051 | |
2052 // | |
2053 // IAccessibleText methods. | |
2054 // | |
2055 | |
2056 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { | |
2057 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_CHARACTERS); | |
2058 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2059 AccessibilityMode::kInlineTextBoxes); | |
2060 if (!instance_active()) | |
2061 return E_FAIL; | |
2062 | |
2063 if (!n_characters) | |
2064 return E_INVALIDARG; | |
2065 | |
2066 *n_characters = static_cast<LONG>(GetText().size()); | |
2067 return S_OK; | |
2068 } | |
2069 | |
2070 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { | |
2071 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CARET_OFFSET); | |
2072 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2073 if (!instance_active()) | |
2074 return E_FAIL; | |
2075 | |
2076 if (!offset) | |
2077 return E_INVALIDARG; | |
2078 | |
2079 if (!HasCaret()) | |
2080 return S_FALSE; | |
2081 | |
2082 int selection_start, selection_end; | |
2083 GetSelectionOffsets(&selection_start, &selection_end); | |
2084 // The caret is always at the end of the selection. | |
2085 *offset = selection_end; | |
2086 if (*offset < 0) | |
2087 return S_FALSE; | |
2088 | |
2089 return S_OK; | |
2090 } | |
2091 | |
2092 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents( | |
2093 LONG offset, | |
2094 IA2CoordinateType coordinate_type, | |
2095 LONG* out_x, | |
2096 LONG* out_y, | |
2097 LONG* out_width, | |
2098 LONG* out_height) { | |
2099 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CHARACTER_EXTENTS); | |
2100 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2101 AccessibilityMode::kInlineTextBoxes); | |
2102 if (!instance_active()) | |
2103 return E_FAIL; | |
2104 | |
2105 if (!out_x || !out_y || !out_width || !out_height) | |
2106 return E_INVALIDARG; | |
2107 | |
2108 const base::string16& text_str = GetText(); | |
2109 HandleSpecialTextOffset(&offset); | |
2110 if (offset < 0 || offset > static_cast<LONG>(text_str.size())) | |
2111 return E_INVALIDARG; | |
2112 | |
2113 gfx::Rect character_bounds; | |
2114 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { | |
2115 character_bounds = GetScreenBoundsForRange(offset, 1); | |
2116 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { | |
2117 character_bounds = GetPageBoundsForRange(offset, 1); | |
2118 if (PlatformGetParent()) | |
2119 character_bounds -= | |
2120 PlatformGetParent()->GetPageBoundsRect().OffsetFromOrigin(); | |
2121 } else { | |
2122 return E_INVALIDARG; | |
2123 } | |
2124 | |
2125 *out_x = character_bounds.x(); | |
2126 *out_y = character_bounds.y(); | |
2127 *out_width = character_bounds.width(); | |
2128 *out_height = character_bounds.height(); | |
2129 | |
2130 return S_OK; | |
2131 } | |
2132 | |
2133 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { | |
2134 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTIONS); | |
2135 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2136 if (!instance_active()) | |
2137 return E_FAIL; | |
2138 | |
2139 if (!n_selections) | |
2140 return E_INVALIDARG; | |
2141 | |
2142 *n_selections = 0; | |
2143 int selection_start, selection_end; | |
2144 GetSelectionOffsets(&selection_start, &selection_end); | |
2145 if (selection_start >= 0 && selection_end >= 0 && | |
2146 selection_start != selection_end) { | |
2147 *n_selections = 1; | |
2148 } | |
2149 | |
2150 return S_OK; | |
2151 } | |
2152 | |
2153 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, | |
2154 LONG* start_offset, | |
2155 LONG* end_offset) { | |
2156 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_SELECTION); | |
2157 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2158 if (!instance_active()) | |
2159 return E_FAIL; | |
2160 | |
2161 if (!start_offset || !end_offset || selection_index != 0) | |
2162 return E_INVALIDARG; | |
2163 | |
2164 *start_offset = 0; | |
2165 *end_offset = 0; | |
2166 int selection_start, selection_end; | |
2167 GetSelectionOffsets(&selection_start, &selection_end); | |
2168 if (selection_start >= 0 && selection_end >= 0 && | |
2169 selection_start != selection_end) { | |
2170 // We should ignore the direction of the selection when exposing start and | |
2171 // end offsets. According to the IA2 Spec the end offset is always increased | |
2172 // by one past the end of the selection. This wouldn't make sense if | |
2173 // end < start. | |
2174 if (selection_end < selection_start) | |
2175 std::swap(selection_start, selection_end); | |
2176 | |
2177 *start_offset = selection_start; | |
2178 *end_offset = selection_end; | |
2179 return S_OK; | |
2180 } | |
2181 | |
2182 return E_INVALIDARG; | |
2183 } | |
2184 | |
2185 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset, | |
2186 LONG end_offset, | |
2187 BSTR* text) { | |
2188 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TEXT); | |
2189 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2190 if (!instance_active()) | |
2191 return E_FAIL; | |
2192 | |
2193 if (!text) | |
2194 return E_INVALIDARG; | |
2195 | |
2196 const base::string16& text_str = GetText(); | |
2197 HandleSpecialTextOffset(&start_offset); | |
2198 HandleSpecialTextOffset(&end_offset); | |
2199 | |
2200 // The spec allows the arguments to be reversed. | |
2201 if (start_offset > end_offset) { | |
2202 LONG tmp = start_offset; | |
2203 start_offset = end_offset; | |
2204 end_offset = tmp; | |
2205 } | |
2206 | |
2207 // The spec does not allow the start or end offsets to be out or range; | |
2208 // we must return an error if so. | |
2209 LONG len = text_str.length(); | |
2210 if (start_offset < 0) | |
2211 return E_INVALIDARG; | |
2212 if (end_offset > len) | |
2213 return E_INVALIDARG; | |
2214 | |
2215 base::string16 substr = text_str.substr(start_offset, | |
2216 end_offset - start_offset); | |
2217 | |
2218 if (substr.empty()) | |
2219 return S_FALSE; | |
2220 | |
2221 *text = SysAllocString(substr.c_str()); | |
2222 DCHECK(*text); | |
2223 return S_OK; | |
2224 } | |
2225 | |
2226 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset( | |
2227 LONG offset, | |
2228 IA2TextBoundaryType boundary_type, | |
2229 LONG* start_offset, | |
2230 LONG* end_offset, | |
2231 BSTR* text) { | |
2232 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TEXT_AT_OFFSET); | |
2233 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2234 AccessibilityMode::kInlineTextBoxes); | |
2235 if (!instance_active()) | |
2236 return E_FAIL; | |
2237 | |
2238 if (!start_offset || !end_offset || !text) | |
2239 return E_INVALIDARG; | |
2240 | |
2241 HandleSpecialTextOffset(&offset); | |
2242 if (offset < 0) | |
2243 return E_INVALIDARG; | |
2244 | |
2245 const base::string16& text_str = GetText(); | |
2246 LONG text_len = text_str.length(); | |
2247 if (offset > text_len) | |
2248 return E_INVALIDARG; | |
2249 | |
2250 // The IAccessible2 spec says we don't have to implement the "sentence" | |
2251 // boundary type, we can just let the screenreader handle it. | |
2252 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
2253 *start_offset = 0; | |
2254 *end_offset = 0; | |
2255 *text = NULL; | |
2256 return S_FALSE; | |
2257 } | |
2258 | |
2259 // According to the IA2 Spec, only line boundaries should succeed when | |
2260 // the offset is one past the end of the text. | |
2261 if (offset == text_len) { | |
2262 if (boundary_type == IA2_TEXT_BOUNDARY_LINE) { | |
2263 --offset; | |
2264 } else { | |
2265 *start_offset = 0; | |
2266 *end_offset = 0; | |
2267 *text = nullptr; | |
2268 return S_FALSE; | |
2269 } | |
2270 } | |
2271 | |
2272 *start_offset = FindBoundary( | |
2273 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); | |
2274 *end_offset = FindBoundary( | |
2275 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); | |
2276 return get_text(*start_offset, *end_offset, text); | |
2277 } | |
2278 | |
2279 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset( | |
2280 LONG offset, | |
2281 IA2TextBoundaryType boundary_type, | |
2282 LONG* start_offset, | |
2283 LONG* end_offset, | |
2284 BSTR* text) { | |
2285 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TEXT_BEFORE_OFFSET); | |
2286 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2287 AccessibilityMode::kInlineTextBoxes); | |
2288 if (!instance_active()) | |
2289 return E_FAIL; | |
2290 | |
2291 if (!start_offset || !end_offset || !text) | |
2292 return E_INVALIDARG; | |
2293 | |
2294 // The IAccessible2 spec says we don't have to implement the "sentence" | |
2295 // boundary type, we can just let the screenreader handle it. | |
2296 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
2297 *start_offset = 0; | |
2298 *end_offset = 0; | |
2299 *text = NULL; | |
2300 return S_FALSE; | |
2301 } | |
2302 | |
2303 const base::string16& text_str = GetText(); | |
2304 | |
2305 *start_offset = FindBoundary( | |
2306 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); | |
2307 *end_offset = offset; | |
2308 return get_text(*start_offset, *end_offset, text); | |
2309 } | |
2310 | |
2311 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset( | |
2312 LONG offset, | |
2313 IA2TextBoundaryType boundary_type, | |
2314 LONG* start_offset, | |
2315 LONG* end_offset, | |
2316 BSTR* text) { | |
2317 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TEXT_AFTER_OFFSET); | |
2318 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2319 AccessibilityMode::kInlineTextBoxes); | |
2320 if (!instance_active()) | |
2321 return E_FAIL; | |
2322 | |
2323 if (!start_offset || !end_offset || !text) | |
2324 return E_INVALIDARG; | |
2325 | |
2326 // The IAccessible2 spec says we don't have to implement the "sentence" | |
2327 // boundary type, we can just let the screenreader handle it. | |
2328 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
2329 *start_offset = 0; | |
2330 *end_offset = 0; | |
2331 *text = NULL; | |
2332 return S_FALSE; | |
2333 } | |
2334 | |
2335 const base::string16& text_str = GetText(); | |
2336 | |
2337 *start_offset = offset; | |
2338 *end_offset = FindBoundary( | |
2339 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); | |
2340 return get_text(*start_offset, *end_offset, text); | |
2341 } | |
2342 | |
2343 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) { | |
2344 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_NEW_TEXT); | |
2345 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2346 if (!instance_active()) | |
2347 return E_FAIL; | |
2348 | |
2349 if (!new_text) | |
2350 return E_INVALIDARG; | |
2351 | |
2352 if (!old_win_attributes_) | |
2353 return E_FAIL; | |
2354 | |
2355 int start, old_len, new_len; | |
2356 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); | |
2357 if (new_len == 0) | |
2358 return E_FAIL; | |
2359 | |
2360 base::string16 substr = GetText().substr(start, new_len); | |
2361 new_text->text = SysAllocString(substr.c_str()); | |
2362 new_text->start = static_cast<long>(start); | |
2363 new_text->end = static_cast<long>(start + new_len); | |
2364 return S_OK; | |
2365 } | |
2366 | |
2367 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) { | |
2368 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_OLD_TEXT); | |
2369 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2370 if (!instance_active()) | |
2371 return E_FAIL; | |
2372 | |
2373 if (!old_text) | |
2374 return E_INVALIDARG; | |
2375 | |
2376 if (!old_win_attributes_) | |
2377 return E_FAIL; | |
2378 | |
2379 int start, old_len, new_len; | |
2380 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); | |
2381 if (old_len == 0) | |
2382 return E_FAIL; | |
2383 | |
2384 base::string16 old_hypertext = old_win_attributes_->hypertext; | |
2385 base::string16 substr = old_hypertext.substr(start, old_len); | |
2386 old_text->text = SysAllocString(substr.c_str()); | |
2387 old_text->start = static_cast<long>(start); | |
2388 old_text->end = static_cast<long>(start + old_len); | |
2389 return S_OK; | |
2390 } | |
2391 | |
2392 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint( | |
2393 LONG x, | |
2394 LONG y, | |
2395 IA2CoordinateType coord_type, | |
2396 LONG* offset) { | |
2397 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_OFFSET_AT_POINT); | |
2398 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2399 AccessibilityMode::kInlineTextBoxes); | |
2400 if (!instance_active()) | |
2401 return E_FAIL; | |
2402 | |
2403 if (!offset) | |
2404 return E_INVALIDARG; | |
2405 | |
2406 // TODO(dmazzoni): implement this. We're returning S_OK for now so that | |
2407 // screen readers still return partially accurate results rather than | |
2408 // completely failing. | |
2409 *offset = 0; | |
2410 return S_OK; | |
2411 } | |
2412 | |
2413 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo( | |
2414 LONG start_index, | |
2415 LONG end_index, | |
2416 IA2ScrollType scroll_type) { | |
2417 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SCROLL_SUBSTRING_TO); | |
2418 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2419 AccessibilityMode::kInlineTextBoxes); | |
2420 // TODO(dmazzoni): adjust this for the start and end index, too. | |
2421 return scrollTo(scroll_type); | |
2422 } | |
2423 | |
2424 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint( | |
2425 LONG start_index, | |
2426 LONG end_index, | |
2427 IA2CoordinateType coordinate_type, | |
2428 LONG x, | |
2429 LONG y) { | |
2430 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SCROLL_SUBSTRING_TO_POINT); | |
2431 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
2432 AccessibilityMode::kInlineTextBoxes); | |
2433 if (start_index > end_index) | |
2434 std::swap(start_index, end_index); | |
2435 LONG length = end_index - start_index + 1; | |
2436 DCHECK_GE(length, 0); | |
2437 | |
2438 gfx::Rect string_bounds = GetPageBoundsForRange(start_index, length); | |
2439 string_bounds -= GetPageBoundsRect().OffsetFromOrigin(); | |
2440 x -= string_bounds.x(); | |
2441 y -= string_bounds.y(); | |
2442 | |
2443 return scrollToPoint(coordinate_type, x, y); | |
2444 } | |
2445 | |
2446 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset, | |
2447 LONG end_offset) { | |
2448 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ADD_SELECTION); | |
2449 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2450 if (!instance_active()) | |
2451 return E_FAIL; | |
2452 | |
2453 // We only support one selection. | |
2454 SetIA2HypertextSelection(start_offset, end_offset); | |
2455 return S_OK; | |
2456 } | |
2457 | |
2458 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) { | |
2459 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_REMOVE_SELECTION); | |
2460 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2461 if (!instance_active()) | |
2462 return E_FAIL; | |
2463 | |
2464 if (selection_index != 0) | |
2465 return E_INVALIDARG; | |
2466 | |
2467 // Simply collapse the selection to the position of the caret if a caret is | |
2468 // visible, otherwise set the selection to 0. | |
2469 LONG caret_offset = 0; | |
2470 int selection_start, selection_end; | |
2471 GetSelectionOffsets(&selection_start, &selection_end); | |
2472 if (HasCaret() && selection_end >= 0) | |
2473 caret_offset = selection_end; | |
2474 SetIA2HypertextSelection(caret_offset, caret_offset); | |
2475 return S_OK; | |
2476 } | |
2477 | |
2478 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) { | |
2479 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SET_CARET_OFFSET); | |
2480 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2481 if (!instance_active()) | |
2482 return E_FAIL; | |
2483 SetIA2HypertextSelection(offset, offset); | |
2484 return S_OK; | |
2485 } | |
2486 | |
2487 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index, | |
2488 LONG start_offset, | |
2489 LONG end_offset) { | |
2490 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SET_SELECTION); | |
2491 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2492 if (!instance_active()) | |
2493 return E_FAIL; | |
2494 if (selection_index != 0) | |
2495 return E_INVALIDARG; | |
2496 SetIA2HypertextSelection(start_offset, end_offset); | |
2497 return S_OK; | |
2498 } | |
2499 | |
2500 STDMETHODIMP BrowserAccessibilityWin::get_attributes(LONG offset, | |
2501 LONG* start_offset, | |
2502 LONG* end_offset, | |
2503 BSTR* text_attributes) { | |
2504 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_IATEXT_GET_ATTRIBUTES); | |
2505 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2506 if (!start_offset || !end_offset || !text_attributes) | |
2507 return E_INVALIDARG; | |
2508 | |
2509 *start_offset = *end_offset = 0; | |
2510 *text_attributes = nullptr; | |
2511 if (!instance_active()) | |
2512 return E_FAIL; | |
2513 | |
2514 const base::string16 text = GetText(); | |
2515 HandleSpecialTextOffset(&offset); | |
2516 if (offset < 0 || offset > static_cast<LONG>(text.size())) | |
2517 return E_INVALIDARG; | |
2518 | |
2519 ComputeStylesIfNeeded(); | |
2520 *start_offset = FindStartOfStyle(offset, ui::BACKWARDS_DIRECTION); | |
2521 *end_offset = FindStartOfStyle(offset, ui::FORWARDS_DIRECTION); | |
2522 | |
2523 base::string16 attributes_str; | |
2524 const std::vector<base::string16>& attributes = | |
2525 offset_to_text_attributes().find(*start_offset)->second; | |
2526 for (const base::string16& attribute : attributes) { | |
2527 attributes_str += attribute + L';'; | |
2528 } | |
2529 | |
2530 if (attributes.empty()) | |
2531 return S_FALSE; | |
2532 | |
2533 *text_attributes = SysAllocString(attributes_str.c_str()); | |
2534 DCHECK(*text_attributes); | |
2535 return S_OK; | |
2536 } | |
2537 | |
2538 // | |
2539 // IAccessibleHypertext methods. | |
2540 // | |
2541 | |
2542 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) { | |
2543 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_HYPERLINKS); | |
2544 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2545 if (!instance_active()) | |
2546 return E_FAIL; | |
2547 | |
2548 if (!hyperlink_count) | |
2549 return E_INVALIDARG; | |
2550 | |
2551 *hyperlink_count = hyperlink_offset_to_index().size(); | |
2552 return S_OK; | |
2553 } | |
2554 | |
2555 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( | |
2556 long index, | |
2557 IAccessibleHyperlink** hyperlink) { | |
2558 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_HYPERLINK); | |
2559 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2560 if (!instance_active()) | |
2561 return E_FAIL; | |
2562 | |
2563 if (!hyperlink || | |
2564 index < 0 || | |
2565 index >= static_cast<long>(hyperlinks().size())) { | |
2566 return E_INVALIDARG; | |
2567 } | |
2568 | |
2569 int32_t id = hyperlinks()[index]; | |
2570 BrowserAccessibilityWin* link = | |
2571 ToBrowserAccessibilityWin(GetFromUniqueID(id)); | |
2572 if (!link) | |
2573 return E_FAIL; | |
2574 | |
2575 *hyperlink = static_cast<IAccessibleHyperlink*>(link->NewReference()); | |
2576 return S_OK; | |
2577 } | |
2578 | |
2579 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( | |
2580 long char_index, | |
2581 long* hyperlink_index) { | |
2582 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_HYPERLINK_INDEX); | |
2583 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2584 if (!instance_active()) | |
2585 return E_FAIL; | |
2586 | |
2587 if (!hyperlink_index) | |
2588 return E_INVALIDARG; | |
2589 | |
2590 if (char_index < 0 || char_index >= static_cast<long>(GetText().size())) { | |
2591 return E_INVALIDARG; | |
2592 } | |
2593 | |
2594 std::map<int32_t, int32_t>::iterator it = | |
2595 hyperlink_offset_to_index().find(char_index); | |
2596 if (it == hyperlink_offset_to_index().end()) { | |
2597 *hyperlink_index = -1; | |
2598 return S_FALSE; | |
2599 } | |
2600 | |
2601 *hyperlink_index = it->second; | |
2602 return S_OK; | |
2603 } | |
2604 | |
2605 // | |
2606 // IAccessibleHyperlink methods. | |
2607 // | |
2608 | |
2609 // Currently, only text links are supported. | |
2610 STDMETHODIMP BrowserAccessibilityWin::get_anchor(long index, VARIANT* anchor) { | |
2611 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ANCHOR); | |
2612 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2613 if (!instance_active() || !IsHyperlink()) | |
2614 return E_FAIL; | |
2615 | |
2616 // IA2 text links can have only one anchor, that is the text inside them. | |
2617 if (index != 0 || !anchor) | |
2618 return E_INVALIDARG; | |
2619 | |
2620 BSTR ia2_hypertext = SysAllocString(GetText().c_str()); | |
2621 DCHECK(ia2_hypertext); | |
2622 anchor->vt = VT_BSTR; | |
2623 anchor->bstrVal = ia2_hypertext; | |
2624 | |
2625 // Returning S_FALSE is not mentioned in the IA2 Spec, but it might have been | |
2626 // an oversight. | |
2627 if (!SysStringLen(ia2_hypertext)) | |
2628 return S_FALSE; | |
2629 | |
2630 return S_OK; | |
2631 } | |
2632 | |
2633 // Currently, only text links are supported. | |
2634 STDMETHODIMP BrowserAccessibilityWin::get_anchorTarget(long index, | |
2635 VARIANT* anchor_target) { | |
2636 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ANCHOR_TARGET); | |
2637 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2638 if (!instance_active() || !IsHyperlink()) | |
2639 return E_FAIL; | |
2640 | |
2641 // IA2 text links can have at most one target, that is when they represent an | |
2642 // HTML hyperlink, i.e. an <a> element with a "href" attribute. | |
2643 if (index != 0 || !anchor_target) | |
2644 return E_INVALIDARG; | |
2645 | |
2646 BSTR target; | |
2647 if (!(ia_state() & STATE_SYSTEM_LINKED) || | |
2648 FAILED(GetStringAttributeAsBstr(ui::AX_ATTR_URL, &target))) { | |
2649 target = SysAllocString(L""); | |
2650 } | |
2651 DCHECK(target); | |
2652 anchor_target->vt = VT_BSTR; | |
2653 anchor_target->bstrVal = target; | |
2654 | |
2655 // Returning S_FALSE is not mentioned in the IA2 Spec, but it might have been | |
2656 // an oversight. | |
2657 if (!SysStringLen(target)) | |
2658 return S_FALSE; | |
2659 | |
2660 return S_OK; | |
2661 } | |
2662 | |
2663 STDMETHODIMP BrowserAccessibilityWin::get_startIndex(long* index) { | |
2664 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_START_INDEX); | |
2665 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2666 if (!instance_active() || !IsHyperlink()) | |
2667 return E_FAIL; | |
2668 | |
2669 if (!index) | |
2670 return E_INVALIDARG; | |
2671 | |
2672 int32_t hypertext_offset = 0; | |
2673 auto* parent = PlatformGetParent(); | |
2674 if (parent) { | |
2675 hypertext_offset = | |
2676 ToBrowserAccessibilityWin(parent)->GetHypertextOffsetFromChild(*this); | |
2677 } | |
2678 *index = static_cast<LONG>(hypertext_offset); | |
2679 return S_OK; | |
2680 } | |
2681 | |
2682 STDMETHODIMP BrowserAccessibilityWin::get_endIndex(long* index) { | |
2683 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_END_INDEX); | |
2684 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2685 LONG start_index; | |
2686 HRESULT hr = get_startIndex(&start_index); | |
2687 if (hr == S_OK) | |
2688 *index = start_index + 1; | |
2689 return hr; | |
2690 } | |
2691 | |
2692 // This method is deprecated in the IA2 Spec. | |
2693 STDMETHODIMP BrowserAccessibilityWin::get_valid(boolean* valid) { | |
2694 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_VALID); | |
2695 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2696 return E_NOTIMPL; | |
2697 } | |
2698 | |
2699 // | |
2700 // IAccessibleAction partly implemented. | |
2701 // | |
2702 | |
2703 STDMETHODIMP BrowserAccessibilityWin::nActions(long* n_actions) { | |
2704 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_N_ACTIONS); | |
2705 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2706 if (!instance_active()) | |
2707 return E_FAIL; | |
2708 | |
2709 if (!n_actions) | |
2710 return E_INVALIDARG; | |
2711 | |
2712 // |IsHyperlink| is required for |IAccessibleHyperlink::anchor/anchorTarget| | |
2713 // to work properly because the |IAccessibleHyperlink| interface inherits from | |
2714 // |IAccessibleAction|. | |
2715 if (IsHyperlink() || HasIntAttribute(ui::AX_ATTR_ACTION)) { | |
2716 *n_actions = 1; | |
2717 } else { | |
2718 *n_actions = 0; | |
2719 } | |
2720 | |
2721 return S_OK; | |
2722 } | |
2723 | |
2724 STDMETHODIMP BrowserAccessibilityWin::doAction(long action_index) { | |
2725 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_DO_ACTION); | |
2726 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2727 if (!instance_active()) | |
2728 return E_FAIL; | |
2729 | |
2730 if (!HasIntAttribute(ui::AX_ATTR_ACTION) || action_index != 0) | |
2731 return E_INVALIDARG; | |
2732 | |
2733 manager_->DoDefaultAction(*this); | |
2734 return S_OK; | |
2735 } | |
2736 | |
2737 STDMETHODIMP | |
2738 BrowserAccessibilityWin::get_description(long action_index, BSTR* description) { | |
2739 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_IAACTION_GET_DESCRIPTION); | |
2740 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2741 return E_NOTIMPL; | |
2742 } | |
2743 | |
2744 STDMETHODIMP BrowserAccessibilityWin::get_keyBinding(long action_index, | |
2745 long n_max_bindings, | |
2746 BSTR** key_bindings, | |
2747 long* n_bindings) { | |
2748 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_KEY_BINDING); | |
2749 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2750 return E_NOTIMPL; | |
2751 } | |
2752 | |
2753 STDMETHODIMP BrowserAccessibilityWin::get_name(long action_index, BSTR* name) { | |
2754 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_NAME); | |
2755 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2756 if (!instance_active()) | |
2757 return E_FAIL; | |
2758 | |
2759 if (!name) | |
2760 return E_INVALIDARG; | |
2761 | |
2762 int action; | |
2763 if (!GetIntAttribute(ui::AX_ATTR_ACTION, &action) || action_index != 0) { | |
2764 *name = nullptr; | |
2765 return E_INVALIDARG; | |
2766 } | |
2767 | |
2768 base::string16 action_verb = | |
2769 ui::ActionToUnlocalizedString(static_cast<ui::AXSupportedAction>(action)); | |
2770 if (action_verb.empty() || action_verb == L"none") { | |
2771 *name = nullptr; | |
2772 return S_FALSE; | |
2773 } | |
2774 | |
2775 *name = SysAllocString(action_verb.c_str()); | |
2776 DCHECK(name); | |
2777 return S_OK; | |
2778 } | |
2779 | |
2780 STDMETHODIMP | |
2781 BrowserAccessibilityWin::get_localizedName(long action_index, | |
2782 BSTR* localized_name) { | |
2783 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALIZED_NAME); | |
2784 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2785 if (!instance_active()) | |
2786 return E_FAIL; | |
2787 | |
2788 if (!localized_name) | |
2789 return E_INVALIDARG; | |
2790 | |
2791 int action; | |
2792 if (!GetIntAttribute(ui::AX_ATTR_ACTION, &action) || action_index != 0) { | |
2793 *localized_name = nullptr; | |
2794 return E_INVALIDARG; | |
2795 } | |
2796 | |
2797 base::string16 action_verb = | |
2798 ui::ActionToString(static_cast<ui::AXSupportedAction>(action)); | |
2799 if (action_verb.empty()) { | |
2800 *localized_name = nullptr; | |
2801 return S_FALSE; | |
2802 } | |
2803 | |
2804 *localized_name = SysAllocString(action_verb.c_str()); | |
2805 DCHECK(localized_name); | |
2806 return S_OK; | |
2807 } | |
2808 | |
2809 // | |
2810 // IAccessibleValue methods. | |
2811 // | |
2812 | |
2813 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { | |
2814 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CURRENT_VALUE); | |
2815 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2816 if (!instance_active()) | |
2817 return E_FAIL; | |
2818 | |
2819 if (!value) | |
2820 return E_INVALIDARG; | |
2821 | |
2822 float float_val; | |
2823 if (GetFloatAttribute( | |
2824 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) { | |
2825 value->vt = VT_R8; | |
2826 value->dblVal = float_val; | |
2827 return S_OK; | |
2828 } | |
2829 | |
2830 value->vt = VT_EMPTY; | |
2831 return S_FALSE; | |
2832 } | |
2833 | |
2834 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) { | |
2835 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_MINIMUM_VALUE); | |
2836 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2837 if (!instance_active()) | |
2838 return E_FAIL; | |
2839 | |
2840 if (!value) | |
2841 return E_INVALIDARG; | |
2842 | |
2843 float float_val; | |
2844 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE, | |
2845 &float_val)) { | |
2846 value->vt = VT_R8; | |
2847 value->dblVal = float_val; | |
2848 return S_OK; | |
2849 } | |
2850 | |
2851 value->vt = VT_EMPTY; | |
2852 return S_FALSE; | |
2853 } | |
2854 | |
2855 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) { | |
2856 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_MAXIMUM_VALUE); | |
2857 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2858 if (!instance_active()) | |
2859 return E_FAIL; | |
2860 | |
2861 if (!value) | |
2862 return E_INVALIDARG; | |
2863 | |
2864 float float_val; | |
2865 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE, | |
2866 &float_val)) { | |
2867 value->vt = VT_R8; | |
2868 value->dblVal = float_val; | |
2869 return S_OK; | |
2870 } | |
2871 | |
2872 value->vt = VT_EMPTY; | |
2873 return S_FALSE; | |
2874 } | |
2875 | |
2876 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) { | |
2877 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SET_CURRENT_VALUE); | |
2878 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2879 // TODO(dmazzoni): Implement this. | |
2880 return E_NOTIMPL; | |
2881 } | |
2882 | |
2883 // | |
2884 // ISimpleDOMDocument methods. | |
2885 // | |
2886 | |
2887 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) { | |
2888 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_URL); | |
2889 if (!instance_active()) | |
2890 return E_FAIL; | |
2891 | |
2892 if (!url) | |
2893 return E_INVALIDARG; | |
2894 | |
2895 if (this != manager_->GetRoot()) | |
2896 return E_FAIL; | |
2897 | |
2898 std::string str = manager_->GetTreeData().url; | |
2899 if (str.empty()) | |
2900 return S_FALSE; | |
2901 | |
2902 *url = SysAllocString(base::UTF8ToUTF16(str).c_str()); | |
2903 DCHECK(*url); | |
2904 | |
2905 return S_OK; | |
2906 } | |
2907 | |
2908 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) { | |
2909 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_TITLE); | |
2910 if (!instance_active()) | |
2911 return E_FAIL; | |
2912 | |
2913 if (!title) | |
2914 return E_INVALIDARG; | |
2915 | |
2916 std::string str = manager_->GetTreeData().title; | |
2917 if (str.empty()) | |
2918 return S_FALSE; | |
2919 | |
2920 *title = SysAllocString(base::UTF8ToUTF16(str).c_str()); | |
2921 DCHECK(*title); | |
2922 | |
2923 return S_OK; | |
2924 } | |
2925 | |
2926 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) { | |
2927 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_MIME_TYPE); | |
2928 if (!instance_active()) | |
2929 return E_FAIL; | |
2930 | |
2931 if (!mime_type) | |
2932 return E_INVALIDARG; | |
2933 | |
2934 std::string str = manager_->GetTreeData().mimetype; | |
2935 if (str.empty()) | |
2936 return S_FALSE; | |
2937 | |
2938 *mime_type = SysAllocString(base::UTF8ToUTF16(str).c_str()); | |
2939 DCHECK(*mime_type); | |
2940 | |
2941 return S_OK; | |
2942 } | |
2943 | |
2944 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) { | |
2945 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_DOC_TYPE); | |
2946 if (!instance_active()) | |
2947 return E_FAIL; | |
2948 | |
2949 if (!doc_type) | |
2950 return E_INVALIDARG; | |
2951 | |
2952 std::string str = manager_->GetTreeData().doctype; | |
2953 if (str.empty()) | |
2954 return S_FALSE; | |
2955 | |
2956 *doc_type = SysAllocString(base::UTF8ToUTF16(str).c_str()); | |
2957 DCHECK(*doc_type); | |
2958 | |
2959 return S_OK; | |
2960 } | |
2961 | |
2962 STDMETHODIMP | |
2963 BrowserAccessibilityWin::get_nameSpaceURIForID(short name_space_id, | |
2964 BSTR* name_space_uri) { | |
2965 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_NAMESPACE_URI_FOR_ID); | |
2966 return E_NOTIMPL; | |
2967 } | |
2968 | |
2969 STDMETHODIMP | |
2970 BrowserAccessibilityWin::put_alternateViewMediaTypes( | |
2971 BSTR* comma_separated_media_types) { | |
2972 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_PUT_ALTERNATE_VIEW_MEDIA_TYPES); | |
2973 return E_NOTIMPL; | |
2974 } | |
2975 | |
2976 // | |
2977 // ISimpleDOMNode methods. | |
2978 // | |
2979 | |
2980 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo( | |
2981 BSTR* node_name, | |
2982 short* name_space_id, | |
2983 BSTR* node_value, | |
2984 unsigned int* num_children, | |
2985 unsigned int* unique_id, | |
2986 unsigned short* node_type) { | |
2987 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_NODE_INFO); | |
2988 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
2989 if (!instance_active()) | |
2990 return E_FAIL; | |
2991 | |
2992 if (!node_name || !name_space_id || !node_value || !num_children || | |
2993 !unique_id || !node_type) { | |
2994 return E_INVALIDARG; | |
2995 } | |
2996 | |
2997 base::string16 tag; | |
2998 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag)) | |
2999 *node_name = SysAllocString(tag.c_str()); | |
3000 else | |
3001 *node_name = nullptr; | |
3002 | |
3003 *name_space_id = 0; | |
3004 *node_value = SysAllocString(value().c_str()); | |
3005 *num_children = PlatformChildCount(); | |
3006 *unique_id = -this->unique_id(); | |
3007 | |
3008 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || | |
3009 GetRole() == ui::AX_ROLE_WEB_AREA) { | |
3010 *node_type = NODETYPE_DOCUMENT; | |
3011 } else if (IsTextOnlyObject()) { | |
3012 *node_type = NODETYPE_TEXT; | |
3013 } else { | |
3014 *node_type = NODETYPE_ELEMENT; | |
3015 } | |
3016 | |
3017 return S_OK; | |
3018 } | |
3019 | |
3020 STDMETHODIMP BrowserAccessibilityWin::get_attributes( | |
3021 unsigned short max_attribs, | |
3022 BSTR* attrib_names, | |
3023 short* name_space_id, | |
3024 BSTR* attrib_values, | |
3025 unsigned short* num_attribs) { | |
3026 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ISIMPLEDOMNODE_GET_ATTRIBUTES); | |
3027 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3028 if (!instance_active()) | |
3029 return E_FAIL; | |
3030 | |
3031 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs) | |
3032 return E_INVALIDARG; | |
3033 | |
3034 *num_attribs = max_attribs; | |
3035 if (*num_attribs > GetHtmlAttributes().size()) | |
3036 *num_attribs = GetHtmlAttributes().size(); | |
3037 | |
3038 for (unsigned short i = 0; i < *num_attribs; ++i) { | |
3039 attrib_names[i] = SysAllocString( | |
3040 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str()); | |
3041 name_space_id[i] = 0; | |
3042 attrib_values[i] = SysAllocString( | |
3043 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str()); | |
3044 } | |
3045 return S_OK; | |
3046 } | |
3047 | |
3048 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames( | |
3049 unsigned short num_attribs, | |
3050 BSTR* attrib_names, | |
3051 short* name_space_id, | |
3052 BSTR* attrib_values) { | |
3053 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ATTRIBUTES_FOR_NAMES); | |
3054 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3055 if (!instance_active()) | |
3056 return E_FAIL; | |
3057 | |
3058 if (!attrib_names || !name_space_id || !attrib_values) | |
3059 return E_INVALIDARG; | |
3060 | |
3061 for (unsigned short i = 0; i < num_attribs; ++i) { | |
3062 name_space_id[i] = 0; | |
3063 bool found = false; | |
3064 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]); | |
3065 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) { | |
3066 if (GetHtmlAttributes()[j].first == name) { | |
3067 attrib_values[i] = SysAllocString( | |
3068 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str()); | |
3069 found = true; | |
3070 break; | |
3071 } | |
3072 } | |
3073 if (!found) { | |
3074 attrib_values[i] = NULL; | |
3075 } | |
3076 } | |
3077 return S_OK; | |
3078 } | |
3079 | |
3080 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle( | |
3081 unsigned short max_style_properties, | |
3082 boolean use_alternate_view, | |
3083 BSTR* style_properties, | |
3084 BSTR* style_values, | |
3085 unsigned short *num_style_properties) { | |
3086 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COMPUTED_STYLE); | |
3087 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3088 if (!instance_active()) | |
3089 return E_FAIL; | |
3090 | |
3091 if (!style_properties || !style_values) | |
3092 return E_INVALIDARG; | |
3093 | |
3094 // We only cache a single style property for now: DISPLAY | |
3095 | |
3096 base::string16 display; | |
3097 if (max_style_properties == 0 || | |
3098 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) { | |
3099 *num_style_properties = 0; | |
3100 return S_OK; | |
3101 } | |
3102 | |
3103 *num_style_properties = 1; | |
3104 style_properties[0] = SysAllocString(L"display"); | |
3105 style_values[0] = SysAllocString(display.c_str()); | |
3106 | |
3107 return S_OK; | |
3108 } | |
3109 | |
3110 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties( | |
3111 unsigned short num_style_properties, | |
3112 boolean use_alternate_view, | |
3113 BSTR* style_properties, | |
3114 BSTR* style_values) { | |
3115 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COMPUTED_STYLE_FOR_PROPERTIES); | |
3116 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3117 if (!instance_active()) | |
3118 return E_FAIL; | |
3119 | |
3120 if (!style_properties || !style_values) | |
3121 return E_INVALIDARG; | |
3122 | |
3123 // We only cache a single style property for now: DISPLAY | |
3124 | |
3125 for (unsigned short i = 0; i < num_style_properties; ++i) { | |
3126 base::string16 name = base::ToLowerASCII( | |
3127 reinterpret_cast<const base::char16*>(style_properties[i])); | |
3128 if (name == L"display") { | |
3129 base::string16 display = GetString16Attribute( | |
3130 ui::AX_ATTR_DISPLAY); | |
3131 style_values[i] = SysAllocString(display.c_str()); | |
3132 } else { | |
3133 style_values[i] = NULL; | |
3134 } | |
3135 } | |
3136 | |
3137 return S_OK; | |
3138 } | |
3139 | |
3140 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) { | |
3141 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ISIMPLEDOMNODE_SCROLL_TO); | |
3142 return scrollTo(placeTopLeft ? | |
3143 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE); | |
3144 } | |
3145 | |
3146 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) { | |
3147 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PARENT_NODE); | |
3148 if (!instance_active()) | |
3149 return E_FAIL; | |
3150 | |
3151 if (!node) | |
3152 return E_INVALIDARG; | |
3153 | |
3154 *node = ToBrowserAccessibilityWin(PlatformGetParent())->NewReference(); | |
3155 return S_OK; | |
3156 } | |
3157 | |
3158 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) { | |
3159 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_FIRST_CHILD); | |
3160 if (!instance_active()) | |
3161 return E_FAIL; | |
3162 | |
3163 if (!node) | |
3164 return E_INVALIDARG; | |
3165 | |
3166 if (PlatformChildCount() == 0) { | |
3167 *node = NULL; | |
3168 return S_FALSE; | |
3169 } | |
3170 | |
3171 *node = ToBrowserAccessibilityWin(PlatformGetChild(0))->NewReference(); | |
3172 return S_OK; | |
3173 } | |
3174 | |
3175 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) { | |
3176 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LAST_CHILD); | |
3177 if (!instance_active()) | |
3178 return E_FAIL; | |
3179 | |
3180 if (!node) | |
3181 return E_INVALIDARG; | |
3182 | |
3183 if (PlatformChildCount() == 0) { | |
3184 *node = NULL; | |
3185 return S_FALSE; | |
3186 } | |
3187 | |
3188 *node = ToBrowserAccessibilityWin( | |
3189 PlatformGetChild(PlatformChildCount() - 1))->NewReference(); | |
3190 return S_OK; | |
3191 } | |
3192 | |
3193 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling( | |
3194 ISimpleDOMNode** node) { | |
3195 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PREVIOUS_SIBLING); | |
3196 if (!instance_active()) | |
3197 return E_FAIL; | |
3198 | |
3199 if (!node) | |
3200 return E_INVALIDARG; | |
3201 | |
3202 if (!PlatformGetParent() || GetIndexInParent() <= 0) { | |
3203 *node = NULL; | |
3204 return S_FALSE; | |
3205 } | |
3206 | |
3207 *node = ToBrowserAccessibilityWin( | |
3208 PlatformGetParent()->InternalGetChild(GetIndexInParent() - 1)) | |
3209 ->NewReference(); | |
3210 return S_OK; | |
3211 } | |
3212 | |
3213 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) { | |
3214 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_NEXT_SIBLING); | |
3215 if (!instance_active()) | |
3216 return E_FAIL; | |
3217 | |
3218 if (!node) | |
3219 return E_INVALIDARG; | |
3220 | |
3221 if (!PlatformGetParent() || GetIndexInParent() < 0 || | |
3222 GetIndexInParent() >= | |
3223 static_cast<int>(PlatformGetParent()->InternalChildCount()) - 1) { | |
3224 *node = NULL; | |
3225 return S_FALSE; | |
3226 } | |
3227 | |
3228 *node = ToBrowserAccessibilityWin( | |
3229 PlatformGetParent()->InternalGetChild(GetIndexInParent() + 1)) | |
3230 ->NewReference(); | |
3231 return S_OK; | |
3232 } | |
3233 | |
3234 STDMETHODIMP BrowserAccessibilityWin::get_childAt( | |
3235 unsigned int child_index, | |
3236 ISimpleDOMNode** node) { | |
3237 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CHILD_AT); | |
3238 if (!instance_active()) | |
3239 return E_FAIL; | |
3240 | |
3241 if (!node) | |
3242 return E_INVALIDARG; | |
3243 | |
3244 if (child_index >= PlatformChildCount()) | |
3245 return E_INVALIDARG; | |
3246 | |
3247 BrowserAccessibility* child = PlatformGetChild(child_index); | |
3248 if (!child) { | |
3249 *node = NULL; | |
3250 return S_FALSE; | |
3251 } | |
3252 | |
3253 *node = ToBrowserAccessibilityWin(child)->NewReference(); | |
3254 return S_OK; | |
3255 } | |
3256 | |
3257 // We only support this method for retrieving MathML content. | |
3258 STDMETHODIMP BrowserAccessibilityWin::get_innerHTML(BSTR* innerHTML) { | |
3259 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_INNER_HTML); | |
3260 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3261 if (GetRole() != ui::AX_ROLE_MATH) | |
3262 return E_NOTIMPL; | |
3263 if (!instance_active()) | |
3264 return E_FAIL; | |
3265 | |
3266 base::string16 inner_html = GetString16Attribute(ui::AX_ATTR_INNER_HTML); | |
3267 *innerHTML = SysAllocString(inner_html.c_str()); | |
3268 DCHECK(*innerHTML); | |
3269 return S_OK; | |
3270 } | |
3271 | |
3272 STDMETHODIMP | |
3273 BrowserAccessibilityWin::get_localInterface(void** local_interface) { | |
3274 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCAL_INTERFACE); | |
3275 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3276 return E_NOTIMPL; | |
3277 } | |
3278 | |
3279 STDMETHODIMP BrowserAccessibilityWin::get_language(BSTR* language) { | |
3280 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LANGUAGE); | |
3281 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3282 if (!language) | |
3283 return E_INVALIDARG; | |
3284 *language = nullptr; | |
3285 | |
3286 if (!instance_active()) | |
3287 return E_FAIL; | |
3288 | |
3289 base::string16 lang = GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE); | |
3290 if (lang.empty()) | |
3291 lang = L"en-US"; | |
3292 | |
3293 *language = SysAllocString(lang.c_str()); | |
3294 DCHECK(*language); | |
3295 return S_OK; | |
3296 } | |
3297 | |
3298 // | |
3299 // ISimpleDOMText methods. | |
3300 // | |
3301 | |
3302 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) { | |
3303 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_DOM_TEXT); | |
3304 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3305 if (!instance_active()) | |
3306 return E_FAIL; | |
3307 | |
3308 if (!dom_text) | |
3309 return E_INVALIDARG; | |
3310 | |
3311 return GetStringAttributeAsBstr( | |
3312 ui::AX_ATTR_NAME, dom_text); | |
3313 } | |
3314 | |
3315 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds( | |
3316 unsigned int start_index, | |
3317 unsigned int end_index, | |
3318 int* out_x, | |
3319 int* out_y, | |
3320 int* out_width, | |
3321 int* out_height) { | |
3322 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CLIPPED_SUBSTRING_BOUNDS); | |
3323 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
3324 AccessibilityMode::kInlineTextBoxes); | |
3325 // TODO(dmazzoni): fully support this API by intersecting the | |
3326 // rect with the container's rect. | |
3327 return get_unclippedSubstringBounds( | |
3328 start_index, end_index, out_x, out_y, out_width, out_height); | |
3329 } | |
3330 | |
3331 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds( | |
3332 unsigned int start_index, | |
3333 unsigned int end_index, | |
3334 int* out_x, | |
3335 int* out_y, | |
3336 int* out_width, | |
3337 int* out_height) { | |
3338 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_UNCLIPPED_SUBSTRING_BOUNDS); | |
3339 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
3340 AccessibilityMode::kInlineTextBoxes); | |
3341 if (!instance_active()) | |
3342 return E_FAIL; | |
3343 | |
3344 if (!out_x || !out_y || !out_width || !out_height) | |
3345 return E_INVALIDARG; | |
3346 | |
3347 unsigned int text_length = static_cast<unsigned int>(GetText().size()); | |
3348 if (start_index > text_length || end_index > text_length || | |
3349 start_index > end_index) { | |
3350 return E_INVALIDARG; | |
3351 } | |
3352 | |
3353 gfx::Rect bounds = GetScreenBoundsForRange( | |
3354 start_index, end_index - start_index); | |
3355 *out_x = bounds.x(); | |
3356 *out_y = bounds.y(); | |
3357 *out_width = bounds.width(); | |
3358 *out_height = bounds.height(); | |
3359 return S_OK; | |
3360 } | |
3361 | |
3362 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring( | |
3363 unsigned int start_index, | |
3364 unsigned int end_index) { | |
3365 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SCROLL_TO_SUBSTRING); | |
3366 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes | | |
3367 AccessibilityMode::kInlineTextBoxes); | |
3368 if (!instance_active()) | |
3369 return E_FAIL; | |
3370 | |
3371 unsigned int text_length = static_cast<unsigned int>(GetText().size()); | |
3372 if (start_index > text_length || end_index > text_length || | |
3373 start_index > end_index) { | |
3374 return E_INVALIDARG; | |
3375 } | |
3376 | |
3377 manager_->ScrollToMakeVisible( | |
3378 *this, GetPageBoundsForRange(start_index, end_index - start_index)); | |
3379 | |
3380 return S_OK; | |
3381 } | |
3382 | |
3383 STDMETHODIMP BrowserAccessibilityWin::get_fontFamily(BSTR* font_family) { | |
3384 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_FONT_FAMILY); | |
3385 AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes); | |
3386 if (!font_family) | |
3387 return E_INVALIDARG; | |
3388 *font_family = nullptr; | |
3389 | |
3390 if (!instance_active()) | |
3391 return E_FAIL; | |
3392 | |
3393 base::string16 family = | |
3394 GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY); | |
3395 if (family.empty()) | |
3396 return S_FALSE; | |
3397 | |
3398 *font_family = SysAllocString(family.c_str()); | |
3399 DCHECK(*font_family); | |
3400 return S_OK; | |
3401 } | |
3402 | |
3403 // | |
3404 // IServiceProvider methods. | |
3405 // | |
3406 | |
3407 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guid_service, | |
3408 REFIID riid, | |
3409 void** object) { | |
3410 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_QUERY_SERVICE); | |
3411 if (!instance_active()) | |
3412 return E_FAIL; | |
3413 | |
3414 if (guid_service == GUID_IAccessibleContentDocument) { | |
3415 // Special Mozilla extension: return the accessible for the root document. | |
3416 // Screen readers use this to distinguish between a document loaded event | |
3417 // on the root document vs on an iframe. | |
3418 BrowserAccessibility* node = this; | |
3419 while (node->PlatformGetParent()) | |
3420 node = node->PlatformGetParent()->manager()->GetRoot(); | |
3421 return ToBrowserAccessibilityWin(node)->QueryInterface( | |
3422 IID_IAccessible2, object); | |
3423 } | |
3424 | |
3425 if (guid_service == IID_IAccessible || | |
3426 guid_service == IID_IAccessible2 || | |
3427 guid_service == IID_IAccessibleAction || | |
3428 guid_service == IID_IAccessibleApplication || | |
3429 guid_service == IID_IAccessibleHyperlink || | |
3430 guid_service == IID_IAccessibleHypertext || | |
3431 guid_service == IID_IAccessibleImage || | |
3432 guid_service == IID_IAccessibleTable || | |
3433 guid_service == IID_IAccessibleTable2 || | |
3434 guid_service == IID_IAccessibleTableCell || | |
3435 guid_service == IID_IAccessibleText || | |
3436 guid_service == IID_IAccessibleValue || | |
3437 guid_service == IID_ISimpleDOMDocument || | |
3438 guid_service == IID_ISimpleDOMNode || | |
3439 guid_service == IID_ISimpleDOMText || | |
3440 guid_service == GUID_ISimpleDOM) { | |
3441 return QueryInterface(riid, object); | |
3442 } | |
3443 | |
3444 // We only support the IAccessibleEx interface on Windows 8 and above. This | |
3445 // is needed for the on-screen Keyboard to show up in metro mode, when the | |
3446 // user taps an editable portion on the page. | |
3447 // All methods in the IAccessibleEx interface are unimplemented. | |
3448 if (riid == IID_IAccessibleEx && | |
3449 base::win::GetVersion() >= base::win::VERSION_WIN8) { | |
3450 return QueryInterface(riid, object); | |
3451 } | |
3452 | |
3453 *object = NULL; | |
3454 return E_FAIL; | |
3455 } | |
3456 | |
3457 STDMETHODIMP | |
3458 BrowserAccessibilityWin::GetObjectForChild(long child_id, IAccessibleEx** ret) { | |
3459 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_OBJECT_FOR_CHILD); | |
3460 return E_NOTIMPL; | |
3461 } | |
3462 | |
3463 STDMETHODIMP | |
3464 BrowserAccessibilityWin::GetIAccessiblePair(IAccessible** acc, long* child_id) { | |
3465 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_IACCESSIBLE_PAIR); | |
3466 return E_NOTIMPL; | |
3467 } | |
3468 | |
3469 STDMETHODIMP BrowserAccessibilityWin::GetRuntimeId(SAFEARRAY** runtime_id) { | |
3470 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_RUNTIME_ID); | |
3471 return E_NOTIMPL; | |
3472 } | |
3473 | |
3474 STDMETHODIMP | |
3475 BrowserAccessibilityWin::ConvertReturnedElement( | |
3476 IRawElementProviderSimple* element, | |
3477 IAccessibleEx** acc) { | |
3478 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_CONVERT_RETURNED_ELEMENT); | |
3479 return E_NOTIMPL; | |
3480 } | |
3481 | |
3482 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id, | |
3483 IUnknown** provider) { | |
3484 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PATTERN_PROVIDER); | |
3485 DVLOG(1) << "In Function: " << __func__ << " for pattern id: " << id; | |
3486 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) { | |
3487 if (HasState(ui::AX_STATE_EDITABLE)) { | |
3488 DVLOG(1) << "Returning UIA text provider"; | |
3489 base::win::UIATextProvider::CreateTextProvider( | |
3490 GetValueText(), true, provider); | |
3491 return S_OK; | |
3492 } | |
3493 } | |
3494 return E_NOTIMPL; | |
3495 } | |
3496 | |
3497 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id, | |
3498 VARIANT* ret) { | |
3499 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PROPERTY_VALUE); | |
3500 DVLOG(1) << "In Function: " << __func__ << " for property id: " << id; | |
3501 V_VT(ret) = VT_EMPTY; | |
3502 if (id == UIA_ControlTypePropertyId) { | |
3503 if (HasState(ui::AX_STATE_EDITABLE)) { | |
3504 V_VT(ret) = VT_I4; | |
3505 ret->lVal = UIA_EditControlTypeId; | |
3506 DVLOG(1) << "Returning Edit control type"; | |
3507 } else { | |
3508 DVLOG(1) << "Returning empty control type"; | |
3509 } | |
3510 } | |
3511 return S_OK; | |
3512 } | |
3513 | |
3514 STDMETHODIMP BrowserAccessibilityWin::get_ProviderOptions( | |
3515 ProviderOptions* ret) { | |
3516 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PROVIDER_OPTIONS); | |
3517 return E_NOTIMPL; | |
3518 } | |
3519 | |
3520 STDMETHODIMP BrowserAccessibilityWin::get_HostRawElementProvider( | |
3521 IRawElementProviderSimple** provider) { | |
3522 WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_HOST_RAW_ELEMENT_PROVIDER); | |
3523 return E_NOTIMPL; | |
3524 } | |
3525 | |
3526 // | |
3527 // CComObjectRootEx methods. | |
3528 // | |
3529 | |
3530 // static | |
3531 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( | |
3532 void* this_ptr, | |
3533 const _ATL_INTMAP_ENTRY* entries, | |
3534 REFIID iid, | |
3535 void** object) { | |
3536 BrowserAccessibilityWin* accessibility = | |
3537 reinterpret_cast<BrowserAccessibilityWin*>(this_ptr); | |
3538 int32_t ia_role = accessibility->ia_role(); | |
3539 if (iid == IID_IAccessibleImage) { | |
3540 if (ia_role != ROLE_SYSTEM_GRAPHIC) { | |
3541 *object = NULL; | |
3542 return E_NOINTERFACE; | |
3543 } | |
3544 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { | |
3545 if (ia_role != ROLE_SYSTEM_TABLE) { | |
3546 *object = NULL; | |
3547 return E_NOINTERFACE; | |
3548 } | |
3549 } else if (iid == IID_IAccessibleTableCell) { | |
3550 if (!accessibility->IsCellOrTableHeaderRole()) { | |
3551 *object = NULL; | |
3552 return E_NOINTERFACE; | |
3553 } | |
3554 } else if (iid == IID_IAccessibleValue) { | |
3555 if (ia_role != ROLE_SYSTEM_PROGRESSBAR && | |
3556 ia_role != ROLE_SYSTEM_SCROLLBAR && | |
3557 ia_role != ROLE_SYSTEM_SLIDER) { | |
3558 *object = NULL; | |
3559 return E_NOINTERFACE; | |
3560 } | |
3561 } else if (iid == IID_ISimpleDOMDocument) { | |
3562 if (ia_role != ROLE_SYSTEM_DOCUMENT) { | |
3563 *object = NULL; | |
3564 return E_NOINTERFACE; | |
3565 } | |
3566 } else if (iid == IID_IAccessibleHyperlink) { | |
3567 auto* ax_object = | |
3568 reinterpret_cast<const BrowserAccessibilityWin*>(this_ptr); | |
3569 if (!ax_object || !ax_object->IsHyperlink()) { | |
3570 *object = nullptr; | |
3571 return E_NOINTERFACE; | |
3572 } | |
3573 } | |
3574 | |
3575 return CComObjectRootBase::InternalQueryInterface( | |
3576 this_ptr, entries, iid, object); | |
3577 } | |
3578 | |
3579 void BrowserAccessibilityWin::ComputeStylesIfNeeded() { | |
3580 if (!offset_to_text_attributes().empty()) | |
3581 return; | |
3582 | |
3583 std::map<int, std::vector<base::string16>> attributes_map; | |
3584 if (PlatformIsLeaf() || IsSimpleTextControl()) { | |
3585 attributes_map[0] = ComputeTextAttributes(); | |
3586 std::map<int, std::vector<base::string16>> spelling_attributes = | |
3587 GetSpellingAttributes(); | |
3588 for (auto& spelling_attribute : spelling_attributes) { | |
3589 auto attributes_iterator = attributes_map.find(spelling_attribute.first); | |
3590 if (attributes_iterator == attributes_map.end()) { | |
3591 attributes_map[spelling_attribute.first] = | |
3592 std::move(spelling_attribute.second); | |
3593 } else { | |
3594 std::vector<base::string16>& existing_attributes = | |
3595 attributes_iterator->second; | |
3596 | |
3597 // There might be a spelling attribute already in the list of text | |
3598 // attributes, originating from "aria-invalid". | |
3599 auto existing_spelling_attribute = | |
3600 std::find(existing_attributes.begin(), existing_attributes.end(), | |
3601 L"invalid:false"); | |
3602 if (existing_spelling_attribute != existing_attributes.end()) | |
3603 existing_attributes.erase(existing_spelling_attribute); | |
3604 | |
3605 existing_attributes.insert(existing_attributes.end(), | |
3606 spelling_attribute.second.begin(), | |
3607 spelling_attribute.second.end()); | |
3608 } | |
3609 } | |
3610 win_attributes_->offset_to_text_attributes.swap(attributes_map); | |
3611 return; | |
3612 } | |
3613 | |
3614 int start_offset = 0; | |
3615 for (size_t i = 0; i < PlatformChildCount(); ++i) { | |
3616 auto* child = ToBrowserAccessibilityWin(PlatformGetChild(i)); | |
3617 DCHECK(child); | |
3618 std::vector<base::string16> attributes(child->ComputeTextAttributes()); | |
3619 | |
3620 if (attributes_map.empty()) { | |
3621 attributes_map[start_offset] = attributes; | |
3622 } else { | |
3623 // Only add the attributes for this child if we are at the start of a new | |
3624 // style span. | |
3625 std::vector<base::string16> previous_attributes = | |
3626 attributes_map.rbegin()->second; | |
3627 if (!std::equal(attributes.begin(), attributes.end(), | |
3628 previous_attributes.begin())) { | |
3629 attributes_map[start_offset] = attributes; | |
3630 } | |
3631 } | |
3632 | |
3633 if (child->IsTextOnlyObject()) | |
3634 start_offset += child->GetText().length(); | |
3635 else | |
3636 start_offset += 1; | |
3637 } | |
3638 | |
3639 win_attributes_->offset_to_text_attributes.swap(attributes_map); | |
3640 } | |
3641 | |
3642 // |offset| could either be a text character or a child index in case of | |
3643 // non-text objects. | |
3644 // TODO(nektar): Remove this function once selection bugs are fixed in Blink. | |
3645 BrowserAccessibilityWin::AXPlatformPositionInstance | |
3646 BrowserAccessibilityWin::CreatePositionForSelectionAt(int offset) const { | |
3647 if (!IsNativeTextControl() && !IsTextOnlyObject()) { | |
3648 DCHECK(manager_); | |
3649 const BrowserAccessibilityWin* child = this; | |
3650 // TODO(nektar): Make parents of text-only objects not include the text of | |
3651 // children in their hypertext. | |
3652 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
3653 int new_offset = offset; | |
3654 child = ToBrowserAccessibilityWin(InternalGetChild(i)); | |
3655 DCHECK(child); | |
3656 if (child->IsTextOnlyObject()) { | |
3657 new_offset -= child->GetText().length(); | |
3658 } else { | |
3659 new_offset -= 1; | |
3660 } | |
3661 if (new_offset <= 0) | |
3662 break; | |
3663 offset = new_offset; | |
3664 } | |
3665 AXPlatformPositionInstance position = | |
3666 AXPlatformPosition::CreateTextPosition(manager_->ax_tree_id(), | |
3667 child->GetId(), offset, | |
3668 ui::AX_TEXT_AFFINITY_DOWNSTREAM) | |
3669 ->AsLeafTextPosition(); | |
3670 if (position->GetAnchor() && | |
3671 position->GetAnchor()->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) { | |
3672 return position->CreateParentPosition(); | |
3673 } | |
3674 return position; | |
3675 } | |
3676 return CreatePositionAt(offset); | |
3677 } | |
3678 | |
3679 base::string16 BrowserAccessibilityWin::GetText() const { | |
3680 if (PlatformIsChildOfLeaf()) | |
3681 return BrowserAccessibility::GetText(); | |
3682 return win_attributes_->hypertext; | |
3683 } | |
3684 | |
3685 // | |
3686 // Private methods. | |
3687 // | |
3688 | |
3689 void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() { | |
3690 // Swap win_attributes_ to old_win_attributes_, allowing us to see | |
3691 // exactly what changed and fire appropriate events. Note that | |
3692 // old_win_attributes_ is cleared at the end of UpdateStep3FireEvents. | |
3693 old_win_attributes_.swap(win_attributes_); | |
3694 win_attributes_.reset(new WinAttributes()); | |
3695 | |
3696 InitRoleAndState(); | |
3697 | |
3698 win_attributes_->ia2_attributes.clear(); | |
3699 | |
3700 // Expose some HTLM and ARIA attributes in the IAccessible2 attributes string. | |
3701 // "display", "tag", and "xml-roles" have somewhat unusual names for | |
3702 // historical reasons. Aside from that virtually every ARIA attribute | |
3703 // is exposed in a really straightforward way, i.e. "aria-foo" is exposed | |
3704 // as "foo". | |
3705 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display"); | |
3706 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag"); | |
3707 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles"); | |
3708 | |
3709 StringAttributeToIA2(ui::AX_ATTR_AUTO_COMPLETE, "autocomplete"); | |
3710 StringAttributeToIA2(ui::AX_ATTR_ROLE_DESCRIPTION, "roledescription"); | |
3711 StringAttributeToIA2(ui::AX_ATTR_KEY_SHORTCUTS, "keyshortcuts"); | |
3712 | |
3713 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level"); | |
3714 IntAttributeToIA2(ui::AX_ATTR_SET_SIZE, "setsize"); | |
3715 IntAttributeToIA2(ui::AX_ATTR_POS_IN_SET, "posinset"); | |
3716 | |
3717 if (ia_role() == ROLE_SYSTEM_CHECKBUTTON || | |
3718 ia_role() == ROLE_SYSTEM_RADIOBUTTON || | |
3719 ia2_role() == IA2_ROLE_CHECK_MENU_ITEM || | |
3720 ia2_role() == IA2_ROLE_RADIO_MENU_ITEM || | |
3721 ia2_role() == IA2_ROLE_TOGGLE_BUTTON) { | |
3722 win_attributes_->ia2_attributes.push_back(L"checkable:true"); | |
3723 } | |
3724 | |
3725 // Expose live region attributes. | |
3726 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live"); | |
3727 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant"); | |
3728 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic"); | |
3729 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy"); | |
3730 | |
3731 // Expose container live region attributes. | |
3732 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS, | |
3733 "container-live"); | |
3734 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT, | |
3735 "container-relevant"); | |
3736 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC, | |
3737 "container-atomic"); | |
3738 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY, | |
3739 "container-busy"); | |
3740 | |
3741 // Expose the non-standard explicit-name IA2 attribute. | |
3742 int name_from; | |
3743 if (GetIntAttribute(ui::AX_ATTR_NAME_FROM, &name_from) && | |
3744 name_from != ui::AX_NAME_FROM_CONTENTS) { | |
3745 win_attributes_->ia2_attributes.push_back(L"explicit-name:true"); | |
3746 } | |
3747 | |
3748 // Expose the aria-current attribute. | |
3749 int32_t aria_current_state; | |
3750 if (GetIntAttribute(ui::AX_ATTR_ARIA_CURRENT_STATE, &aria_current_state)) { | |
3751 switch (static_cast<ui::AXAriaCurrentState>(aria_current_state)) { | |
3752 case ui::AX_ARIA_CURRENT_STATE_NONE: | |
3753 break; | |
3754 case ui::AX_ARIA_CURRENT_STATE_FALSE: | |
3755 win_attributes_->ia2_attributes.push_back(L"current:false"); | |
3756 break; | |
3757 case ui::AX_ARIA_CURRENT_STATE_TRUE: | |
3758 win_attributes_->ia2_attributes.push_back(L"current:true"); | |
3759 break; | |
3760 case ui::AX_ARIA_CURRENT_STATE_PAGE: | |
3761 win_attributes_->ia2_attributes.push_back(L"current:page"); | |
3762 break; | |
3763 case ui::AX_ARIA_CURRENT_STATE_STEP: | |
3764 win_attributes_->ia2_attributes.push_back(L"current:step"); | |
3765 break; | |
3766 case ui::AX_ARIA_CURRENT_STATE_LOCATION: | |
3767 win_attributes_->ia2_attributes.push_back(L"current:location"); | |
3768 break; | |
3769 case ui::AX_ARIA_CURRENT_STATE_DATE: | |
3770 win_attributes_->ia2_attributes.push_back(L"current:date"); | |
3771 break; | |
3772 case ui::AX_ARIA_CURRENT_STATE_TIME: | |
3773 win_attributes_->ia2_attributes.push_back(L"current:time"); | |
3774 break; | |
3775 } | |
3776 } | |
3777 | |
3778 // Expose table cell index. | |
3779 if (IsCellOrTableHeaderRole()) { | |
3780 BrowserAccessibility* table = PlatformGetParent(); | |
3781 while (table && !table->IsTableLikeRole()) | |
3782 table = table->PlatformGetParent(); | |
3783 if (table) { | |
3784 const std::vector<int32_t>& unique_cell_ids = | |
3785 table->GetIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS); | |
3786 for (size_t i = 0; i < unique_cell_ids.size(); ++i) { | |
3787 if (unique_cell_ids[i] == GetId()) { | |
3788 win_attributes_->ia2_attributes.push_back( | |
3789 base::string16(L"table-cell-index:") + base::IntToString16(i)); | |
3790 } | |
3791 } | |
3792 } | |
3793 } | |
3794 | |
3795 // Expose aria-colcount and aria-rowcount in a table, grid or treegrid. | |
3796 if (IsTableLikeRole()) { | |
3797 IntAttributeToIA2(ui::AX_ATTR_ARIA_COLUMN_COUNT, "colcount"); | |
3798 IntAttributeToIA2(ui::AX_ATTR_ARIA_ROW_COUNT, "rowcount"); | |
3799 } | |
3800 | |
3801 // Expose aria-colindex and aria-rowindex in a cell or row. | |
3802 if (IsCellOrTableHeaderRole() || GetRole() == ui::AX_ROLE_ROW) { | |
3803 if (GetRole() != ui::AX_ROLE_ROW) | |
3804 IntAttributeToIA2(ui::AX_ATTR_ARIA_CELL_COLUMN_INDEX, "colindex"); | |
3805 IntAttributeToIA2(ui::AX_ATTR_ARIA_CELL_ROW_INDEX, "rowindex"); | |
3806 } | |
3807 | |
3808 // Expose row or column header sort direction. | |
3809 int32_t sort_direction; | |
3810 if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER || | |
3811 ia_role() == ROLE_SYSTEM_ROWHEADER) && | |
3812 GetIntAttribute(ui::AX_ATTR_SORT_DIRECTION, &sort_direction)) { | |
3813 switch (static_cast<ui::AXSortDirection>(sort_direction)) { | |
3814 case ui::AX_SORT_DIRECTION_NONE: | |
3815 break; | |
3816 case ui::AX_SORT_DIRECTION_UNSORTED: | |
3817 win_attributes_->ia2_attributes.push_back(L"sort:none"); | |
3818 break; | |
3819 case ui::AX_SORT_DIRECTION_ASCENDING: | |
3820 win_attributes_->ia2_attributes.push_back(L"sort:ascending"); | |
3821 break; | |
3822 case ui::AX_SORT_DIRECTION_DESCENDING: | |
3823 win_attributes_->ia2_attributes.push_back(L"sort:descending"); | |
3824 break; | |
3825 case ui::AX_SORT_DIRECTION_OTHER: | |
3826 win_attributes_->ia2_attributes.push_back(L"sort:other"); | |
3827 break; | |
3828 } | |
3829 } | |
3830 | |
3831 win_attributes_->name = GetString16Attribute(ui::AX_ATTR_NAME); | |
3832 win_attributes_->description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); | |
3833 StringAttributeToIA2(ui::AX_ATTR_PLACEHOLDER, "placeholder"); | |
3834 | |
3835 base::string16 value = GetValue(); | |
3836 // On Windows, the value of a document should be its url. | |
3837 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || | |
3838 GetRole() == ui::AX_ROLE_WEB_AREA) { | |
3839 value = base::UTF8ToUTF16(manager_->GetTreeData().url); | |
3840 } | |
3841 // If this doesn't have a value and is linked then set its value to the url | |
3842 // attribute. This allows screen readers to read an empty link's destination. | |
3843 if (value.empty() && (ia_state() & STATE_SYSTEM_LINKED)) | |
3844 value = GetString16Attribute(ui::AX_ATTR_URL); | |
3845 win_attributes_->value = value; | |
3846 | |
3847 ClearOwnRelations(); | |
3848 AddBidirectionalRelations(IA2_RELATION_CONTROLLER_FOR, | |
3849 IA2_RELATION_CONTROLLED_BY, | |
3850 ui::AX_ATTR_CONTROLS_IDS); | |
3851 AddBidirectionalRelations(IA2_RELATION_DESCRIBED_BY, | |
3852 IA2_RELATION_DESCRIPTION_FOR, | |
3853 ui::AX_ATTR_DESCRIBEDBY_IDS); | |
3854 AddBidirectionalRelations(IA2_RELATION_FLOWS_TO, IA2_RELATION_FLOWS_FROM, | |
3855 ui::AX_ATTR_FLOWTO_IDS); | |
3856 AddBidirectionalRelations(IA2_RELATION_LABELLED_BY, IA2_RELATION_LABEL_FOR, | |
3857 ui::AX_ATTR_LABELLEDBY_IDS); | |
3858 AddBidirectionalRelations(IA2_RELATION_DETAILS, IA2_RELATION_DETAILS_FOR, | |
3859 ui::AX_ATTR_DETAILS_IDS); | |
3860 | |
3861 int member_of_id; | |
3862 if (GetIntAttribute(ui::AX_ATTR_MEMBER_OF_ID, &member_of_id)) | |
3863 AddRelation(IA2_RELATION_MEMBER_OF, member_of_id); | |
3864 | |
3865 int error_message_id; | |
3866 if (GetIntAttribute(ui::AX_ATTR_ERRORMESSAGE_ID, &error_message_id)) | |
3867 AddRelation(IA2_RELATION_ERROR_MESSAGE, error_message_id); | |
3868 | |
3869 // Expose slider value. | |
3870 if (ia_role() == ROLE_SYSTEM_PROGRESSBAR || | |
3871 ia_role() == ROLE_SYSTEM_SCROLLBAR || | |
3872 ia_role() == ROLE_SYSTEM_SLIDER) { | |
3873 base::string16 value_text = GetValueText(); | |
3874 SanitizeStringAttributeForIA2(value_text, &value_text); | |
3875 win_attributes_->ia2_attributes.push_back(L"valuetext:" + value_text); | |
3876 } | |
3877 | |
3878 UpdateRequiredAttributes(); | |
3879 // If this is a web area for a presentational iframe, give it a role of | |
3880 // something other than DOCUMENT so that the fact that it's a separate doc | |
3881 // is not exposed to AT. | |
3882 if (IsWebAreaForPresentationalIframe()) { | |
3883 win_attributes_->ia_role = ROLE_SYSTEM_GROUPING; | |
3884 win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING; | |
3885 } | |
3886 } | |
3887 | |
3888 void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() { | |
3889 if (IsSimpleTextControl()) { | |
3890 win_attributes_->hypertext = value(); | |
3891 return; | |
3892 } | |
3893 | |
3894 if (!PlatformChildCount()) { | |
3895 if (IsRichTextControl()) { | |
3896 // We don't want to expose any associated label in IA2 Hypertext. | |
3897 return; | |
3898 } | |
3899 win_attributes_->hypertext = name(); | |
3900 return; | |
3901 } | |
3902 | |
3903 // Construct the hypertext for this node, which contains the concatenation | |
3904 // of all of the static text and widespace of this node's children and an | |
3905 // embedded object character for all the other children. Build up a map from | |
3906 // the character index of each embedded object character to the id of the | |
3907 // child object it points to. | |
3908 for (unsigned int i = 0; i < PlatformChildCount(); ++i) { | |
3909 auto* child = ToBrowserAccessibilityWin(PlatformGetChild(i)); | |
3910 DCHECK(child); | |
3911 // Similar to Firefox, we don't expose text-only objects in IA2 hypertext. | |
3912 if (child->IsTextOnlyObject()) { | |
3913 win_attributes_->hypertext += child->name(); | |
3914 } else { | |
3915 int32_t char_offset = static_cast<int32_t>(GetText().size()); | |
3916 int32_t child_unique_id = child->unique_id(); | |
3917 int32_t index = hyperlinks().size(); | |
3918 win_attributes_->hyperlink_offset_to_index[char_offset] = index; | |
3919 win_attributes_->hyperlinks.push_back(child_unique_id); | |
3920 win_attributes_->hypertext += kEmbeddedCharacter; | |
3921 } | |
3922 } | |
3923 } | |
3924 | |
3925 void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation) { | |
3926 // Fire an event when a new subtree is created. | |
3927 if (is_subtree_creation) | |
3928 FireNativeEvent(EVENT_OBJECT_SHOW); | |
3929 | |
3930 // The rest of the events only fire on changes, not on new objects. | |
3931 if (old_win_attributes_->ia_role != 0 || | |
3932 !old_win_attributes_->role_name.empty()) { | |
3933 // Fire an event if the name, description, help, or value changes. | |
3934 if (name() != old_win_attributes_->name) | |
3935 FireNativeEvent(EVENT_OBJECT_NAMECHANGE); | |
3936 if (description() != old_win_attributes_->description) | |
3937 FireNativeEvent(EVENT_OBJECT_DESCRIPTIONCHANGE); | |
3938 if (value() != old_win_attributes_->value) | |
3939 FireNativeEvent(EVENT_OBJECT_VALUECHANGE); | |
3940 if (ia_state() != old_win_attributes_->ia_state) | |
3941 FireNativeEvent(EVENT_OBJECT_STATECHANGE); | |
3942 | |
3943 // Handle selection being added or removed. | |
3944 bool is_selected_now = (ia_state() & STATE_SYSTEM_SELECTED) != 0; | |
3945 bool was_selected_before = | |
3946 (old_win_attributes_->ia_state & STATE_SYSTEM_SELECTED) != 0; | |
3947 if (is_selected_now || was_selected_before) { | |
3948 bool multiselect = false; | |
3949 if (PlatformGetParent() && | |
3950 PlatformGetParent()->HasState(ui::AX_STATE_MULTISELECTABLE)) | |
3951 multiselect = true; | |
3952 | |
3953 if (multiselect) { | |
3954 // In a multi-select box, fire SELECTIONADD and SELECTIONREMOVE events. | |
3955 if (is_selected_now && !was_selected_before) { | |
3956 FireNativeEvent(EVENT_OBJECT_SELECTIONADD); | |
3957 } else if (!is_selected_now && was_selected_before) { | |
3958 FireNativeEvent(EVENT_OBJECT_SELECTIONREMOVE); | |
3959 } | |
3960 } else if (is_selected_now && !was_selected_before) { | |
3961 // In a single-select box, only fire SELECTION events. | |
3962 FireNativeEvent(EVENT_OBJECT_SELECTION); | |
3963 } | |
3964 } | |
3965 | |
3966 // Fire an event if this container object has scrolled. | |
3967 int sx = 0; | |
3968 int sy = 0; | |
3969 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) && | |
3970 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) { | |
3971 if (sx != previous_scroll_x_ || sy != previous_scroll_y_) | |
3972 FireNativeEvent(EVENT_SYSTEM_SCROLLINGEND); | |
3973 previous_scroll_x_ = sx; | |
3974 previous_scroll_y_ = sy; | |
3975 } | |
3976 | |
3977 // Fire hypertext-related events. | |
3978 int start, old_len, new_len; | |
3979 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); | |
3980 if (old_len > 0) { | |
3981 // In-process screen readers may call IAccessibleText::get_oldText | |
3982 // in reaction to this event to retrieve the text that was removed. | |
3983 FireNativeEvent(IA2_EVENT_TEXT_REMOVED); | |
3984 } | |
3985 if (new_len > 0) { | |
3986 // In-process screen readers may call IAccessibleText::get_newText | |
3987 // in reaction to this event to retrieve the text that was inserted. | |
3988 FireNativeEvent(IA2_EVENT_TEXT_INSERTED); | |
3989 } | |
3990 | |
3991 // Changing a static text node can affect the IAccessibleText hypertext | |
3992 // of the parent node, so force an update on the parent. | |
3993 BrowserAccessibilityWin* parent = | |
3994 ToBrowserAccessibilityWin(PlatformGetParent()); | |
3995 if (parent && IsTextOnlyObject() && | |
3996 name() != old_win_attributes_->name) { | |
3997 parent->UpdatePlatformAttributes(); | |
3998 } | |
3999 } | |
4000 | |
4001 old_win_attributes_.reset(nullptr); | |
4002 } | |
4003 | |
4004 void BrowserAccessibilityWin::UpdatePlatformAttributes() { | |
4005 UpdateStep1ComputeWinAttributes(); | |
4006 UpdateStep2ComputeHypertext(); | |
4007 UpdateStep3FireEvents(false); | |
4008 } | 46 } |
4009 | 47 |
4010 void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() { | 48 void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() { |
4011 FireNativeEvent(EVENT_OBJECT_HIDE); | 49 GetCOM()->FireNativeEvent(EVENT_OBJECT_HIDE); |
4012 } | |
4013 | |
4014 void BrowserAccessibilityWin::NativeAddReference() { | |
4015 AddRef(); | |
4016 } | |
4017 | |
4018 void BrowserAccessibilityWin::NativeReleaseReference() { | |
4019 Release(); | |
4020 } | 50 } |
4021 | 51 |
4022 bool BrowserAccessibilityWin::IsNative() const { | 52 bool BrowserAccessibilityWin::IsNative() const { |
4023 return true; | 53 return true; |
4024 } | 54 } |
4025 | 55 |
4026 void BrowserAccessibilityWin::OnLocationChanged() { | 56 void BrowserAccessibilityWin::OnLocationChanged() { |
4027 FireNativeEvent(EVENT_OBJECT_LOCATIONCHANGE); | 57 GetCOM()->FireNativeEvent(EVENT_OBJECT_LOCATIONCHANGE); |
4028 } | 58 } |
4029 | 59 |
4030 std::vector<base::string16> BrowserAccessibilityWin::ComputeTextAttributes() | 60 base::string16 BrowserAccessibilityWin::GetText() const { |
4031 const { | 61 if (PlatformIsChildOfLeaf()) |
4032 std::vector<base::string16> attributes; | 62 return BrowserAccessibility::GetText(); |
4033 | 63 return GetCOM()->win_attributes_->hypertext; |
4034 // We include list markers for now, but there might be other objects that are | |
4035 // auto generated. | |
4036 // TODO(nektar): Compute what objects are auto-generated in Blink. | |
4037 if (GetRole() == ui::AX_ROLE_LIST_MARKER) | |
4038 attributes.push_back(L"auto-generated:true"); | |
4039 else | |
4040 attributes.push_back(L"auto-generated:false"); | |
4041 | |
4042 int color; | |
4043 base::string16 color_value(L"transparent"); | |
4044 if (GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, &color)) { | |
4045 unsigned int alpha = SkColorGetA(color); | |
4046 unsigned int red = SkColorGetR(color); | |
4047 unsigned int green = SkColorGetG(color); | |
4048 unsigned int blue = SkColorGetB(color); | |
4049 if (alpha) { | |
4050 color_value = L"rgb(" + base::UintToString16(red) + L',' + | |
4051 base::UintToString16(green) + L',' + | |
4052 base::UintToString16(blue) + L')'; | |
4053 } | |
4054 } | |
4055 SanitizeStringAttributeForIA2(color_value, &color_value); | |
4056 attributes.push_back(L"background-color:" + color_value); | |
4057 | |
4058 if (GetIntAttribute(ui::AX_ATTR_COLOR, &color)) { | |
4059 unsigned int red = SkColorGetR(color); | |
4060 unsigned int green = SkColorGetG(color); | |
4061 unsigned int blue = SkColorGetB(color); | |
4062 color_value = L"rgb(" + base::UintToString16(red) + L',' + | |
4063 base::UintToString16(green) + L',' + | |
4064 base::UintToString16(blue) + L')'; | |
4065 } else { | |
4066 color_value = L"rgb(0,0,0)"; | |
4067 } | |
4068 SanitizeStringAttributeForIA2(color_value, &color_value); | |
4069 attributes.push_back(L"color:" + color_value); | |
4070 | |
4071 base::string16 font_family( | |
4072 GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY)); | |
4073 // Attribute has no default value. | |
4074 if (!font_family.empty()) { | |
4075 SanitizeStringAttributeForIA2(font_family, &font_family); | |
4076 attributes.push_back(L"font-family:" + font_family); | |
4077 } | |
4078 | |
4079 float font_size; | |
4080 // Attribute has no default value. | |
4081 if (GetFloatAttribute(ui::AX_ATTR_FONT_SIZE, &font_size)) { | |
4082 // The IA2 Spec requires the value to be in pt, not in pixels. | |
4083 // There are 72 points per inch. | |
4084 // We assume that there are 96 pixels per inch on a standard display. | |
4085 // TODO(nektar): Figure out the current value of pixels per inch. | |
4086 float points = font_size * 72.0 / 96.0; | |
4087 attributes.push_back(L"font-size:" + | |
4088 base::UTF8ToUTF16(base::DoubleToString(points)) + | |
4089 L"pt"); | |
4090 } | |
4091 | |
4092 auto text_style = | |
4093 static_cast<ui::AXTextStyle>(GetIntAttribute(ui::AX_ATTR_TEXT_STYLE)); | |
4094 if (text_style == ui::AX_TEXT_STYLE_NONE) { | |
4095 attributes.push_back(L"font-style:normal"); | |
4096 attributes.push_back(L"font-weight:normal"); | |
4097 } else { | |
4098 if (text_style & ui::AX_TEXT_STYLE_ITALIC) { | |
4099 attributes.push_back(L"font-style:italic"); | |
4100 } else { | |
4101 attributes.push_back(L"font-style:normal"); | |
4102 } | |
4103 | |
4104 if (text_style & ui::AX_TEXT_STYLE_BOLD) { | |
4105 attributes.push_back(L"font-weight:bold"); | |
4106 } else { | |
4107 attributes.push_back(L"font-weight:normal"); | |
4108 } | |
4109 } | |
4110 | |
4111 auto invalid_state = static_cast<ui::AXInvalidState>( | |
4112 GetIntAttribute(ui::AX_ATTR_INVALID_STATE)); | |
4113 switch (invalid_state) { | |
4114 case ui::AX_INVALID_STATE_NONE: | |
4115 case ui::AX_INVALID_STATE_FALSE: | |
4116 attributes.push_back(L"invalid:false"); | |
4117 break; | |
4118 case ui::AX_INVALID_STATE_TRUE: | |
4119 attributes.push_back(L"invalid:true"); | |
4120 break; | |
4121 case ui::AX_INVALID_STATE_SPELLING: | |
4122 case ui::AX_INVALID_STATE_GRAMMAR: { | |
4123 base::string16 spelling_grammar_value; | |
4124 if (invalid_state & ui::AX_INVALID_STATE_SPELLING) | |
4125 spelling_grammar_value = L"spelling"; | |
4126 else if (invalid_state & ui::AX_INVALID_STATE_GRAMMAR) | |
4127 spelling_grammar_value = L"grammar"; | |
4128 else | |
4129 spelling_grammar_value = L"spelling,grammar"; | |
4130 attributes.push_back(L"invalid:" + spelling_grammar_value); | |
4131 break; | |
4132 } | |
4133 case ui::AX_INVALID_STATE_OTHER: { | |
4134 base::string16 aria_invalid_value; | |
4135 if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE, | |
4136 &aria_invalid_value)) { | |
4137 SanitizeStringAttributeForIA2(aria_invalid_value, &aria_invalid_value); | |
4138 attributes.push_back(L"invalid:" + aria_invalid_value); | |
4139 } else { | |
4140 // Set the attribute to L"true", since we cannot be more specific. | |
4141 attributes.push_back(L"invalid:true"); | |
4142 } | |
4143 break; | |
4144 } | |
4145 } | |
4146 | |
4147 base::string16 language(GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); | |
4148 // Default value should be L"en-US". | |
4149 if (language.empty()) { | |
4150 attributes.push_back(L"language:en-US"); | |
4151 } else { | |
4152 SanitizeStringAttributeForIA2(language, &language); | |
4153 attributes.push_back(L"language:" + language); | |
4154 } | |
4155 | |
4156 // TODO(nektar): Add Blink support for the following attributes. | |
4157 // Currently set to their default values as dictated by the IA2 Spec. | |
4158 attributes.push_back(L"text-line-through-mode:continuous"); | |
4159 if (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) { | |
4160 // TODO(nektar): Figure out a more specific value. | |
4161 attributes.push_back(L"text-line-through-style:solid"); | |
4162 } else { | |
4163 attributes.push_back(L"text-line-through-style:none"); | |
4164 } | |
4165 // Default value must be the empty string. | |
4166 attributes.push_back(L"text-line-through-text:"); | |
4167 if (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) { | |
4168 // TODO(nektar): Figure out a more specific value. | |
4169 attributes.push_back(L"text-line-through-type:single"); | |
4170 } else { | |
4171 attributes.push_back(L"text-line-through-type:none"); | |
4172 } | |
4173 attributes.push_back(L"text-line-through-width:auto"); | |
4174 attributes.push_back(L"text-outline:false"); | |
4175 attributes.push_back(L"text-position:baseline"); | |
4176 attributes.push_back(L"text-shadow:none"); | |
4177 attributes.push_back(L"text-underline-mode:continuous"); | |
4178 if (text_style & ui::AX_TEXT_STYLE_UNDERLINE) { | |
4179 // TODO(nektar): Figure out a more specific value. | |
4180 attributes.push_back(L"text-underline-style:solid"); | |
4181 attributes.push_back(L"text-underline-type:single"); | |
4182 } else { | |
4183 attributes.push_back(L"text-underline-style:none"); | |
4184 attributes.push_back(L"text-underline-type:none"); | |
4185 } | |
4186 attributes.push_back(L"text-underline-width:auto"); | |
4187 | |
4188 auto text_direction = static_cast<ui::AXTextDirection>( | |
4189 GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)); | |
4190 switch (text_direction) { | |
4191 case ui::AX_TEXT_DIRECTION_NONE: | |
4192 case ui::AX_TEXT_DIRECTION_LTR: | |
4193 attributes.push_back(L"writing-mode:lr"); | |
4194 break; | |
4195 case ui::AX_TEXT_DIRECTION_RTL: | |
4196 attributes.push_back(L"writing-mode:rl"); | |
4197 break; | |
4198 case ui::AX_TEXT_DIRECTION_TTB: | |
4199 attributes.push_back(L"writing-mode:tb"); | |
4200 break; | |
4201 case ui::AX_TEXT_DIRECTION_BTT: | |
4202 // Not listed in the IA2 Spec. | |
4203 attributes.push_back(L"writing-mode:bt"); | |
4204 break; | |
4205 } | |
4206 | |
4207 return attributes; | |
4208 } | 64 } |
4209 | 65 |
4210 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() { | 66 BrowserAccessibilityComWin* BrowserAccessibilityWin::GetCOM() const { |
4211 AddRef(); | 67 DCHECK(browser_accessibility_com_); |
4212 return this; | 68 return browser_accessibility_com_; |
4213 } | |
4214 | |
4215 std::map<int, std::vector<base::string16>> | |
4216 BrowserAccessibilityWin::GetSpellingAttributes() const { | |
4217 std::map<int, std::vector<base::string16>> spelling_attributes; | |
4218 if (IsTextOnlyObject()) { | |
4219 const std::vector<int32_t>& marker_types = | |
4220 GetIntListAttribute(ui::AX_ATTR_MARKER_TYPES); | |
4221 const std::vector<int>& marker_starts = | |
4222 GetIntListAttribute(ui::AX_ATTR_MARKER_STARTS); | |
4223 const std::vector<int>& marker_ends = | |
4224 GetIntListAttribute(ui::AX_ATTR_MARKER_ENDS); | |
4225 for (size_t i = 0; i < marker_types.size(); ++i) { | |
4226 if (!(static_cast<ui::AXMarkerType>(marker_types[i]) & | |
4227 ui::AX_MARKER_TYPE_SPELLING)) | |
4228 continue; | |
4229 int start_offset = marker_starts[i]; | |
4230 int end_offset = marker_ends[i]; | |
4231 std::vector<base::string16> start_attributes; | |
4232 start_attributes.push_back(L"invalid:spelling"); | |
4233 std::vector<base::string16> end_attributes; | |
4234 end_attributes.push_back(L"invalid:false"); | |
4235 spelling_attributes[start_offset] = start_attributes; | |
4236 spelling_attributes[end_offset] = end_attributes; | |
4237 } | |
4238 } | |
4239 if (IsSimpleTextControl()) { | |
4240 int start_offset = 0; | |
4241 for (const BrowserAccessibility* static_text = | |
4242 BrowserAccessibilityManager::NextTextOnlyObject( | |
4243 InternalGetChild(0)); | |
4244 static_text; static_text = static_text->GetNextSibling()) { | |
4245 auto* text_win = ToBrowserAccessibilityWin(static_text); | |
4246 if (text_win) { | |
4247 std::map<int, std::vector<base::string16>> text_spelling_attributes = | |
4248 text_win->GetSpellingAttributes(); | |
4249 for (auto& attribute : text_spelling_attributes) { | |
4250 spelling_attributes[start_offset + attribute.first] = | |
4251 std::move(attribute.second); | |
4252 } | |
4253 start_offset += static_cast<int>(text_win->GetText().length()); | |
4254 } | |
4255 } | |
4256 } | |
4257 return spelling_attributes; | |
4258 } | |
4259 | |
4260 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID( | |
4261 const VARIANT& var_id) { | |
4262 if (var_id.vt != VT_I4) | |
4263 return nullptr; | |
4264 | |
4265 LONG child_id = var_id.lVal; | |
4266 if (child_id == CHILDID_SELF) | |
4267 return this; | |
4268 | |
4269 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount())) | |
4270 return ToBrowserAccessibilityWin(PlatformGetChild(child_id - 1)); | |
4271 | |
4272 BrowserAccessibilityWin* child = ToBrowserAccessibilityWin( | |
4273 BrowserAccessibility::GetFromUniqueID(-child_id)); | |
4274 if (child && child->IsDescendantOf(this)) | |
4275 return child; | |
4276 | |
4277 return nullptr; | |
4278 } | |
4279 | |
4280 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr( | |
4281 ui::AXStringAttribute attribute, | |
4282 BSTR* value_bstr) { | |
4283 base::string16 str; | |
4284 | |
4285 if (!GetString16Attribute(attribute, &str)) | |
4286 return S_FALSE; | |
4287 | |
4288 *value_bstr = SysAllocString(str.c_str()); | |
4289 DCHECK(*value_bstr); | |
4290 | |
4291 return S_OK; | |
4292 } | |
4293 | |
4294 // Static | |
4295 void BrowserAccessibilityWin::SanitizeStringAttributeForIA2( | |
4296 const base::string16& input, | |
4297 base::string16* output) { | |
4298 DCHECK(output); | |
4299 // According to the IA2 Spec, these characters need to be escaped with a | |
4300 // backslash: backslash, colon, comma, equals and semicolon. | |
4301 // Note that backslash must be replaced first. | |
4302 base::ReplaceChars(input, L"\\", L"\\\\", output); | |
4303 base::ReplaceChars(*output, L":", L"\\:", output); | |
4304 base::ReplaceChars(*output, L",", L"\\,", output); | |
4305 base::ReplaceChars(*output, L"=", L"\\=", output); | |
4306 base::ReplaceChars(*output, L";", L"\\;", output); | |
4307 } | |
4308 | |
4309 void BrowserAccessibilityWin::SetIA2HypertextSelection(LONG start_offset, | |
4310 LONG end_offset) { | |
4311 HandleSpecialTextOffset(&start_offset); | |
4312 HandleSpecialTextOffset(&end_offset); | |
4313 AXPlatformPositionInstance start_position = | |
4314 CreatePositionForSelectionAt(static_cast<int>(start_offset)); | |
4315 AXPlatformPositionInstance end_position = | |
4316 CreatePositionForSelectionAt(static_cast<int>(end_offset)); | |
4317 manager_->SetSelection(AXPlatformRange(start_position->AsTextPosition(), | |
4318 end_position->AsTextPosition())); | |
4319 } | |
4320 | |
4321 void BrowserAccessibilityWin::StringAttributeToIA2( | |
4322 ui::AXStringAttribute attribute, | |
4323 const char* ia2_attr) { | |
4324 base::string16 value; | |
4325 if (GetString16Attribute(attribute, &value)) { | |
4326 SanitizeStringAttributeForIA2(value, &value); | |
4327 win_attributes_->ia2_attributes.push_back( | |
4328 base::ASCIIToUTF16(ia2_attr) + L":" + value); | |
4329 } | |
4330 } | |
4331 | |
4332 void BrowserAccessibilityWin::BoolAttributeToIA2( | |
4333 ui::AXBoolAttribute attribute, | |
4334 const char* ia2_attr) { | |
4335 bool value; | |
4336 if (GetBoolAttribute(attribute, &value)) { | |
4337 win_attributes_->ia2_attributes.push_back( | |
4338 (base::ASCIIToUTF16(ia2_attr) + L":") + | |
4339 (value ? L"true" : L"false")); | |
4340 } | |
4341 } | |
4342 | |
4343 void BrowserAccessibilityWin::IntAttributeToIA2( | |
4344 ui::AXIntAttribute attribute, | |
4345 const char* ia2_attr) { | |
4346 int value; | |
4347 if (GetIntAttribute(attribute, &value)) { | |
4348 win_attributes_->ia2_attributes.push_back( | |
4349 base::ASCIIToUTF16(ia2_attr) + L":" + | |
4350 base::IntToString16(value)); | |
4351 } | |
4352 } | |
4353 | |
4354 bool BrowserAccessibilityWin::IsHyperlink() const { | |
4355 int32_t hyperlink_index = -1; | |
4356 auto* parent = PlatformGetParent(); | |
4357 if (parent) { | |
4358 hyperlink_index = | |
4359 ToBrowserAccessibilityWin(parent)->GetHyperlinkIndexFromChild(*this); | |
4360 } | |
4361 | |
4362 if (hyperlink_index >= 0) | |
4363 return true; | |
4364 return false; | |
4365 } | |
4366 | |
4367 BrowserAccessibilityWin* | |
4368 BrowserAccessibilityWin::GetHyperlinkFromHypertextOffset(int offset) const { | |
4369 std::map<int32_t, int32_t>::iterator iterator = | |
4370 hyperlink_offset_to_index().find(offset); | |
4371 if (iterator == hyperlink_offset_to_index().end()) | |
4372 return nullptr; | |
4373 | |
4374 int32_t index = iterator->second; | |
4375 DCHECK_GE(index, 0); | |
4376 DCHECK_LT(index, static_cast<int32_t>(hyperlinks().size())); | |
4377 int32_t id = hyperlinks()[index]; | |
4378 BrowserAccessibilityWin* hyperlink = | |
4379 ToBrowserAccessibilityWin(GetFromUniqueID(id)); | |
4380 if (!hyperlink) | |
4381 return nullptr; | |
4382 return hyperlink; | |
4383 } | |
4384 | |
4385 int32_t BrowserAccessibilityWin::GetHyperlinkIndexFromChild( | |
4386 const BrowserAccessibilityWin& child) const { | |
4387 if (hyperlinks().empty()) | |
4388 return -1; | |
4389 | |
4390 auto iterator = | |
4391 std::find(hyperlinks().begin(), hyperlinks().end(), child.unique_id()); | |
4392 if (iterator == hyperlinks().end()) | |
4393 return -1; | |
4394 | |
4395 return static_cast<int32_t>(iterator - hyperlinks().begin()); | |
4396 } | |
4397 | |
4398 int32_t BrowserAccessibilityWin::GetHypertextOffsetFromHyperlinkIndex( | |
4399 int32_t hyperlink_index) const { | |
4400 for (auto& offset_index : hyperlink_offset_to_index()) { | |
4401 if (offset_index.second == hyperlink_index) | |
4402 return offset_index.first; | |
4403 } | |
4404 | |
4405 return -1; | |
4406 } | |
4407 | |
4408 int32_t BrowserAccessibilityWin::GetHypertextOffsetFromChild( | |
4409 const BrowserAccessibilityWin& child) const { | |
4410 DCHECK(child.PlatformGetParent() == this); | |
4411 | |
4412 // Handle the case when we are dealing with a direct text-only child. | |
4413 // (Note that this object might be a platform leaf, e.g. an ARIA searchbox, | |
4414 // and so |InternalChild...| functions need to be used. Also, direct text-only | |
4415 // children should not be present at tree roots and so no cross-tree traversal | |
4416 // is necessary.) | |
4417 if (child.IsTextOnlyObject()) { | |
4418 int32_t hypertextOffset = 0; | |
4419 int32_t index_in_parent = child.GetIndexInParent(); | |
4420 DCHECK_GE(index_in_parent, 0); | |
4421 DCHECK_LT(index_in_parent, static_cast<int32_t>(InternalChildCount())); | |
4422 for (uint32_t i = 0; i < static_cast<uint32_t>(index_in_parent); ++i) { | |
4423 const BrowserAccessibilityWin* sibling = | |
4424 ToBrowserAccessibilityWin(InternalGetChild(i)); | |
4425 DCHECK(sibling); | |
4426 if (sibling->IsTextOnlyObject()) | |
4427 hypertextOffset += sibling->GetText().size(); | |
4428 else | |
4429 ++hypertextOffset; | |
4430 } | |
4431 return hypertextOffset; | |
4432 } | |
4433 | |
4434 int32_t hyperlink_index = GetHyperlinkIndexFromChild(child); | |
4435 if (hyperlink_index < 0) | |
4436 return -1; | |
4437 | |
4438 return GetHypertextOffsetFromHyperlinkIndex(hyperlink_index); | |
4439 } | |
4440 | |
4441 int32_t BrowserAccessibilityWin::GetHypertextOffsetFromDescendant( | |
4442 const BrowserAccessibilityWin& descendant) const { | |
4443 auto* parent_object = | |
4444 ToBrowserAccessibilityWin(descendant.PlatformGetParent()); | |
4445 auto* current_object = const_cast<BrowserAccessibilityWin*>(&descendant); | |
4446 while (parent_object && parent_object != this) { | |
4447 current_object = parent_object; | |
4448 parent_object = | |
4449 ToBrowserAccessibilityWin(current_object->PlatformGetParent()); | |
4450 } | |
4451 if (!parent_object) | |
4452 return -1; | |
4453 | |
4454 return parent_object->GetHypertextOffsetFromChild(*current_object); | |
4455 } | |
4456 | |
4457 int BrowserAccessibilityWin::GetHypertextOffsetFromEndpoint( | |
4458 const BrowserAccessibilityWin& endpoint_object, | |
4459 int endpoint_offset) const { | |
4460 // There are three cases: | |
4461 // 1. Either the selection endpoint is inside this object or is an ancestor of | |
4462 // of this object. endpoint_offset should be returned. | |
4463 // 2. The selection endpoint is a pure descendant of this object. The offset | |
4464 // of the character corresponding to the subtree in which the endpoint is | |
4465 // located should be returned. | |
4466 // 3. The selection endpoint is in a completely different part of the tree. | |
4467 // Either 0 or text_length should be returned depending on the direction that | |
4468 // one needs to travel to find the endpoint. | |
4469 | |
4470 // Case 1. | |
4471 // | |
4472 // IsDescendantOf includes the case when endpoint_object == this. | |
4473 if (IsDescendantOf(&endpoint_object)) | |
4474 return endpoint_offset; | |
4475 | |
4476 const BrowserAccessibility* common_parent = this; | |
4477 int32_t index_in_common_parent = GetIndexInParent(); | |
4478 while (common_parent && !endpoint_object.IsDescendantOf(common_parent)) { | |
4479 index_in_common_parent = common_parent->GetIndexInParent(); | |
4480 common_parent = common_parent->PlatformGetParent(); | |
4481 } | |
4482 if (!common_parent) | |
4483 return -1; | |
4484 | |
4485 DCHECK_GE(index_in_common_parent, 0); | |
4486 DCHECK(!(common_parent->IsTextOnlyObject())); | |
4487 | |
4488 // Case 2. | |
4489 // | |
4490 // We already checked in case 1 if our endpoint is inside this object. | |
4491 // We can safely assume that it is a descendant or in a completely different | |
4492 // part of the tree. | |
4493 if (common_parent == this) { | |
4494 int32_t hypertext_offset = | |
4495 GetHypertextOffsetFromDescendant(endpoint_object); | |
4496 if (endpoint_object.PlatformGetParent() == this && | |
4497 endpoint_object.IsTextOnlyObject()) { | |
4498 hypertext_offset += endpoint_offset; | |
4499 } | |
4500 | |
4501 return hypertext_offset; | |
4502 } | |
4503 | |
4504 // Case 3. | |
4505 // | |
4506 // We can safely assume that the endpoint is in another part of the tree or | |
4507 // at common parent, and that this object is a descendant of common parent. | |
4508 int32_t endpoint_index_in_common_parent = -1; | |
4509 for (uint32_t i = 0; i < common_parent->InternalChildCount(); ++i) { | |
4510 const BrowserAccessibility* child = common_parent->InternalGetChild(i); | |
4511 DCHECK(child); | |
4512 if (endpoint_object.IsDescendantOf(child)) { | |
4513 endpoint_index_in_common_parent = child->GetIndexInParent(); | |
4514 break; | |
4515 } | |
4516 } | |
4517 DCHECK_GE(endpoint_index_in_common_parent, 0); | |
4518 | |
4519 if (endpoint_index_in_common_parent < index_in_common_parent) | |
4520 return 0; | |
4521 if (endpoint_index_in_common_parent > index_in_common_parent) | |
4522 return GetText().size(); | |
4523 | |
4524 NOTREACHED(); | |
4525 return -1; | |
4526 } | |
4527 | |
4528 int BrowserAccessibilityWin::GetSelectionAnchor() const { | |
4529 int32_t anchor_id = manager_->GetTreeData().sel_anchor_object_id; | |
4530 const BrowserAccessibilityWin* anchor_object = GetFromID(anchor_id); | |
4531 if (!anchor_object) | |
4532 return -1; | |
4533 | |
4534 int anchor_offset = manager_->GetTreeData().sel_anchor_offset; | |
4535 return GetHypertextOffsetFromEndpoint(*anchor_object, anchor_offset); | |
4536 } | |
4537 | |
4538 int BrowserAccessibilityWin::GetSelectionFocus() const { | |
4539 int32_t focus_id = manager_->GetTreeData().sel_focus_object_id; | |
4540 const BrowserAccessibilityWin* focus_object = GetFromID(focus_id); | |
4541 if (!focus_object) | |
4542 return -1; | |
4543 | |
4544 int focus_offset = manager_->GetTreeData().sel_focus_offset; | |
4545 return GetHypertextOffsetFromEndpoint(*focus_object, focus_offset); | |
4546 } | |
4547 | |
4548 void BrowserAccessibilityWin::GetSelectionOffsets( | |
4549 int* selection_start, int* selection_end) const { | |
4550 DCHECK(selection_start && selection_end); | |
4551 | |
4552 if (IsSimpleTextControl() && | |
4553 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && | |
4554 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { | |
4555 return; | |
4556 } | |
4557 | |
4558 *selection_start = GetSelectionAnchor(); | |
4559 *selection_end = GetSelectionFocus(); | |
4560 if (*selection_start < 0 || *selection_end < 0) | |
4561 return; | |
4562 | |
4563 // There are three cases when a selection would start and end on the same | |
4564 // character: | |
4565 // 1. Anchor and focus are both in a subtree that is to the right of this | |
4566 // object. | |
4567 // 2. Anchor and focus are both in a subtree that is to the left of this | |
4568 // object. | |
4569 // 3. Anchor and focus are in a subtree represented by a single embedded | |
4570 // object character. | |
4571 // Only case 3 refers to a valid selection because cases 1 and 2 fall | |
4572 // outside this object in their entirety. | |
4573 // Selections that span more than one character are by definition inside this | |
4574 // object, so checking them is not necessary. | |
4575 if (*selection_start == *selection_end && !HasCaret()) { | |
4576 *selection_start = -1; | |
4577 *selection_end = -1; | |
4578 return; | |
4579 } | |
4580 | |
4581 // The IA2 Spec says that if the largest of the two offsets falls on an | |
4582 // embedded object character and if there is a selection in that embedded | |
4583 // object, it should be incremented by one so that it points after the | |
4584 // embedded object character. | |
4585 // This is a signal to AT software that the embedded object is also part of | |
4586 // the selection. | |
4587 int* largest_offset = | |
4588 (*selection_start <= *selection_end) ? selection_end : selection_start; | |
4589 BrowserAccessibilityWin* hyperlink = | |
4590 GetHyperlinkFromHypertextOffset(*largest_offset); | |
4591 if (!hyperlink) | |
4592 return; | |
4593 | |
4594 LONG n_selections = 0; | |
4595 HRESULT hr = hyperlink->get_nSelections(&n_selections); | |
4596 DCHECK(SUCCEEDED(hr)); | |
4597 if (n_selections > 0) | |
4598 ++(*largest_offset); | |
4599 } | |
4600 | |
4601 base::string16 BrowserAccessibilityWin::GetValueText() { | |
4602 float fval; | |
4603 base::string16 value = this->value(); | |
4604 | |
4605 if (value.empty() && | |
4606 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { | |
4607 value = base::UTF8ToUTF16(base::DoubleToString(fval)); | |
4608 } | |
4609 return value; | |
4610 } | |
4611 | |
4612 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, | |
4613 size_t new_char_index) { | |
4614 CHECK(old_win_attributes_); | |
4615 | |
4616 // For anything other than the "embedded character", we just compare the | |
4617 // characters directly. | |
4618 base::char16 old_ch = old_win_attributes_->hypertext[old_char_index]; | |
4619 base::char16 new_ch = win_attributes_->hypertext[new_char_index]; | |
4620 if (old_ch != new_ch) | |
4621 return false; | |
4622 if (old_ch == new_ch && new_ch != kEmbeddedCharacter) | |
4623 return true; | |
4624 | |
4625 // If it's an embedded character, they're only identical if the child id | |
4626 // the hyperlink points to is the same. | |
4627 std::map<int32_t, int32_t>& old_offset_to_index = | |
4628 old_win_attributes_->hyperlink_offset_to_index; | |
4629 std::vector<int32_t>& old_hyperlinks = old_win_attributes_->hyperlinks; | |
4630 int32_t old_hyperlinks_count = static_cast<int32_t>(old_hyperlinks.size()); | |
4631 std::map<int32_t, int32_t>::iterator iter; | |
4632 iter = old_offset_to_index.find(old_char_index); | |
4633 int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1; | |
4634 int old_child_id = (old_index >= 0 && old_index < old_hyperlinks_count) ? | |
4635 old_hyperlinks[old_index] : -1; | |
4636 | |
4637 std::map<int32_t, int32_t>& new_offset_to_index = | |
4638 win_attributes_->hyperlink_offset_to_index; | |
4639 std::vector<int32_t>& new_hyperlinks = win_attributes_->hyperlinks; | |
4640 int32_t new_hyperlinks_count = static_cast<int32_t>(new_hyperlinks.size()); | |
4641 iter = new_offset_to_index.find(new_char_index); | |
4642 int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1; | |
4643 int new_child_id = (new_index >= 0 && new_index < new_hyperlinks_count) ? | |
4644 new_hyperlinks[new_index] : -1; | |
4645 | |
4646 return old_child_id == new_child_id; | |
4647 } | |
4648 | |
4649 void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted( | |
4650 int* start, int* old_len, int* new_len) { | |
4651 CHECK(old_win_attributes_); | |
4652 | |
4653 *start = 0; | |
4654 *old_len = 0; | |
4655 *new_len = 0; | |
4656 | |
4657 const base::string16& old_text = old_win_attributes_->hypertext; | |
4658 const base::string16& new_text = GetText(); | |
4659 | |
4660 size_t common_prefix = 0; | |
4661 while (common_prefix < old_text.size() && | |
4662 common_prefix < new_text.size() && | |
4663 IsSameHypertextCharacter(common_prefix, common_prefix)) { | |
4664 ++common_prefix; | |
4665 } | |
4666 | |
4667 size_t common_suffix = 0; | |
4668 while (common_prefix + common_suffix < old_text.size() && | |
4669 common_prefix + common_suffix < new_text.size() && | |
4670 IsSameHypertextCharacter( | |
4671 old_text.size() - common_suffix - 1, | |
4672 new_text.size() - common_suffix - 1)) { | |
4673 ++common_suffix; | |
4674 } | |
4675 | |
4676 *start = common_prefix; | |
4677 *old_len = old_text.size() - common_prefix - common_suffix; | |
4678 *new_len = new_text.size() - common_prefix - common_suffix; | |
4679 } | |
4680 | |
4681 void BrowserAccessibilityWin::HandleSpecialTextOffset(LONG* offset) { | |
4682 if (*offset == IA2_TEXT_OFFSET_LENGTH) { | |
4683 *offset = static_cast<LONG>(GetText().length()); | |
4684 } else if (*offset == IA2_TEXT_OFFSET_CARET) { | |
4685 // We shouldn't call |get_caretOffset| here as it affects UMA counts. | |
4686 int selection_start, selection_end; | |
4687 GetSelectionOffsets(&selection_start, &selection_end); | |
4688 *offset = selection_end; | |
4689 } | |
4690 } | |
4691 | |
4692 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary( | |
4693 IA2TextBoundaryType ia2_boundary) { | |
4694 switch(ia2_boundary) { | |
4695 case IA2_TEXT_BOUNDARY_CHAR: | |
4696 return ui::CHAR_BOUNDARY; | |
4697 case IA2_TEXT_BOUNDARY_WORD: | |
4698 return ui::WORD_BOUNDARY; | |
4699 case IA2_TEXT_BOUNDARY_LINE: | |
4700 return ui::LINE_BOUNDARY; | |
4701 case IA2_TEXT_BOUNDARY_SENTENCE: | |
4702 return ui::SENTENCE_BOUNDARY; | |
4703 case IA2_TEXT_BOUNDARY_PARAGRAPH: | |
4704 return ui::PARAGRAPH_BOUNDARY; | |
4705 case IA2_TEXT_BOUNDARY_ALL: | |
4706 return ui::ALL_BOUNDARY; | |
4707 } | |
4708 NOTREACHED(); | |
4709 return ui::CHAR_BOUNDARY; | |
4710 } | |
4711 | |
4712 LONG BrowserAccessibilityWin::FindBoundary( | |
4713 const base::string16& text, | |
4714 IA2TextBoundaryType ia2_boundary, | |
4715 LONG start_offset, | |
4716 ui::TextBoundaryDirection direction) { | |
4717 // If the boundary is relative to the caret, use the selection | |
4718 // affinity, otherwise default to downstream affinity. | |
4719 ui::AXTextAffinity affinity = start_offset == IA2_TEXT_OFFSET_CARET | |
4720 ? manager_->GetTreeData().sel_focus_affinity | |
4721 : ui::AX_TEXT_AFFINITY_DOWNSTREAM; | |
4722 | |
4723 HandleSpecialTextOffset(&start_offset); | |
4724 if (ia2_boundary == IA2_TEXT_BOUNDARY_WORD) { | |
4725 switch (direction) { | |
4726 case ui::FORWARDS_DIRECTION: { | |
4727 AXPlatformPositionInstance position = | |
4728 CreatePositionAt(static_cast<int>(start_offset), affinity); | |
4729 AXPlatformPositionInstance next_word = | |
4730 position->CreateNextWordStartPosition(); | |
4731 if (next_word->anchor_id() != GetId()) | |
4732 next_word = position->CreatePositionAtEndOfAnchor(); | |
4733 return next_word->text_offset(); | |
4734 } | |
4735 case ui::BACKWARDS_DIRECTION: { | |
4736 AXPlatformPositionInstance position = | |
4737 CreatePositionAt(static_cast<int>(start_offset), affinity); | |
4738 AXPlatformPositionInstance previous_word; | |
4739 if (!position->AtStartOfWord()) { | |
4740 previous_word = position->CreatePreviousWordStartPosition(); | |
4741 if (previous_word->anchor_id() != GetId()) | |
4742 previous_word = position->CreatePositionAtStartOfAnchor(); | |
4743 } else { | |
4744 previous_word = std::move(position); | |
4745 } | |
4746 return previous_word->text_offset(); | |
4747 } | |
4748 } | |
4749 } | |
4750 | |
4751 if (ia2_boundary == IA2_TEXT_BOUNDARY_LINE) { | |
4752 switch (direction) { | |
4753 case ui::FORWARDS_DIRECTION: { | |
4754 AXPlatformPositionInstance position = | |
4755 CreatePositionAt(static_cast<int>(start_offset), affinity); | |
4756 AXPlatformPositionInstance next_line = | |
4757 position->CreateNextLineStartPosition(); | |
4758 if (next_line->anchor_id() != GetId()) | |
4759 next_line = position->CreatePositionAtEndOfAnchor(); | |
4760 return next_line->text_offset(); | |
4761 } | |
4762 case ui::BACKWARDS_DIRECTION: { | |
4763 AXPlatformPositionInstance position = | |
4764 CreatePositionAt(static_cast<int>(start_offset), affinity); | |
4765 AXPlatformPositionInstance previous_line; | |
4766 if (!position->AtStartOfLine()) { | |
4767 previous_line = position->CreatePreviousLineStartPosition(); | |
4768 if (previous_line->anchor_id() != GetId()) | |
4769 previous_line = position->CreatePositionAtStartOfAnchor(); | |
4770 } else { | |
4771 previous_line = std::move(position); | |
4772 } | |
4773 return previous_line->text_offset(); | |
4774 } | |
4775 } | |
4776 } | |
4777 | |
4778 // TODO(nektar): |AXPosition| can handle other types of boundaries as well. | |
4779 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); | |
4780 return ui::FindAccessibleTextBoundary(text, GetLineStartOffsets(), boundary, | |
4781 start_offset, direction, affinity); | |
4782 } | |
4783 | |
4784 LONG BrowserAccessibilityWin::FindStartOfStyle( | |
4785 LONG start_offset, | |
4786 ui::TextBoundaryDirection direction) const { | |
4787 LONG text_length = static_cast<LONG>(GetText().length()); | |
4788 DCHECK_GE(start_offset, 0); | |
4789 DCHECK_LE(start_offset, text_length); | |
4790 | |
4791 switch (direction) { | |
4792 case ui::BACKWARDS_DIRECTION: { | |
4793 if (offset_to_text_attributes().empty()) | |
4794 return 0; | |
4795 | |
4796 auto iterator = offset_to_text_attributes().upper_bound(start_offset); | |
4797 --iterator; | |
4798 return static_cast<LONG>(iterator->first); | |
4799 } | |
4800 case ui::FORWARDS_DIRECTION: { | |
4801 const auto iterator = | |
4802 offset_to_text_attributes().upper_bound(start_offset); | |
4803 if (iterator == offset_to_text_attributes().end()) | |
4804 return text_length; | |
4805 return static_cast<LONG>(iterator->first); | |
4806 } | |
4807 } | |
4808 | |
4809 NOTREACHED(); | |
4810 return start_offset; | |
4811 } | |
4812 | |
4813 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32_t id) const { | |
4814 if (!instance_active()) | |
4815 return nullptr; | |
4816 return ToBrowserAccessibilityWin(manager_->GetFromID(id)); | |
4817 } | |
4818 | |
4819 bool BrowserAccessibilityWin::IsListBoxOptionOrMenuListOption() { | |
4820 if (!PlatformGetParent()) | |
4821 return false; | |
4822 | |
4823 int32_t role = GetRole(); | |
4824 int32_t parent_role = PlatformGetParent()->GetRole(); | |
4825 | |
4826 if (role == ui::AX_ROLE_LIST_BOX_OPTION && | |
4827 parent_role == ui::AX_ROLE_LIST_BOX) { | |
4828 return true; | |
4829 } | |
4830 | |
4831 if (role == ui::AX_ROLE_MENU_LIST_OPTION && | |
4832 parent_role == ui::AX_ROLE_MENU_LIST_POPUP) { | |
4833 return true; | |
4834 } | |
4835 | |
4836 return false; | |
4837 } | |
4838 | |
4839 void BrowserAccessibilityWin::AddRelation(const base::string16& relation_type, | |
4840 int target_id) { | |
4841 // Reflexive relations don't need to be exposed through IA2. | |
4842 if (target_id == GetId()) | |
4843 return; | |
4844 | |
4845 CComObject<BrowserAccessibilityRelation>* relation; | |
4846 HRESULT hr = | |
4847 CComObject<BrowserAccessibilityRelation>::CreateInstance(&relation); | |
4848 DCHECK(SUCCEEDED(hr)); | |
4849 relation->AddRef(); | |
4850 relation->Initialize(this, relation_type); | |
4851 relation->AddTarget(target_id); | |
4852 relations_.push_back(relation); | |
4853 } | |
4854 | |
4855 void BrowserAccessibilityWin::AddBidirectionalRelations( | |
4856 const base::string16& relation_type, | |
4857 const base::string16& reverse_relation_type, | |
4858 ui::AXIntListAttribute attribute) { | |
4859 if (!HasIntListAttribute(attribute)) | |
4860 return; | |
4861 | |
4862 const std::vector<int32_t>& target_ids = GetIntListAttribute(attribute); | |
4863 // Reflexive relations don't need to be exposed through IA2. | |
4864 std::vector<int32_t> filtered_target_ids; | |
4865 int32_t current_id = GetId(); | |
4866 std::copy_if(target_ids.begin(), target_ids.end(), | |
4867 std::back_inserter(filtered_target_ids), | |
4868 [current_id](int32_t id) { return id != current_id; }); | |
4869 if (filtered_target_ids.empty()) | |
4870 return; | |
4871 | |
4872 CComObject<BrowserAccessibilityRelation>* relation; | |
4873 HRESULT hr = | |
4874 CComObject<BrowserAccessibilityRelation>::CreateInstance(&relation); | |
4875 DCHECK(SUCCEEDED(hr)); | |
4876 relation->AddRef(); | |
4877 relation->Initialize(this, relation_type); | |
4878 | |
4879 for (int target_id : filtered_target_ids) { | |
4880 BrowserAccessibilityWin* target = | |
4881 GetFromID(static_cast<int32_t>(target_id)); | |
4882 if (!target || !target->instance_active()) | |
4883 continue; | |
4884 relation->AddTarget(target_id); | |
4885 target->AddRelation(reverse_relation_type, GetId()); | |
4886 } | |
4887 | |
4888 relations_.push_back(relation); | |
4889 } | |
4890 | |
4891 // Clears all the forward relations from this object to any other object and the | |
4892 // associated reverse relations on the other objects, but leaves any reverse | |
4893 // relations on this object alone. | |
4894 void BrowserAccessibilityWin::ClearOwnRelations() { | |
4895 RemoveBidirectionalRelationsOfType(IA2_RELATION_CONTROLLER_FOR, | |
4896 IA2_RELATION_CONTROLLED_BY); | |
4897 RemoveBidirectionalRelationsOfType(IA2_RELATION_DESCRIBED_BY, | |
4898 IA2_RELATION_DESCRIPTION_FOR); | |
4899 RemoveBidirectionalRelationsOfType(IA2_RELATION_FLOWS_TO, | |
4900 IA2_RELATION_FLOWS_FROM); | |
4901 RemoveBidirectionalRelationsOfType(IA2_RELATION_LABELLED_BY, | |
4902 IA2_RELATION_LABEL_FOR); | |
4903 | |
4904 relations_.erase( | |
4905 std::remove_if(relations_.begin(), relations_.end(), | |
4906 [](BrowserAccessibilityRelation* relation) { | |
4907 if (relation->get_type() == IA2_RELATION_MEMBER_OF) { | |
4908 relation->Release(); | |
4909 return true; | |
4910 } | |
4911 return false; | |
4912 }), | |
4913 relations_.end()); | |
4914 } | |
4915 | |
4916 void BrowserAccessibilityWin::RemoveBidirectionalRelationsOfType( | |
4917 const base::string16& relation_type, | |
4918 const base::string16& reverse_relation_type) { | |
4919 for (auto iter = relations_.begin(); iter != relations_.end();) { | |
4920 BrowserAccessibilityRelation* relation = *iter; | |
4921 DCHECK(relation); | |
4922 if (relation->get_type() == relation_type) { | |
4923 for (int target_id : relation->get_target_ids()) { | |
4924 BrowserAccessibilityWin* target = | |
4925 GetFromID(static_cast<int32_t>(target_id)); | |
4926 if (!target || !target->instance_active()) | |
4927 continue; | |
4928 DCHECK_NE(target, this); | |
4929 target->RemoveTargetFromRelation(reverse_relation_type, GetId()); | |
4930 } | |
4931 iter = relations_.erase(iter); | |
4932 relation->Release(); | |
4933 } else { | |
4934 ++iter; | |
4935 } | |
4936 } | |
4937 } | |
4938 | |
4939 void BrowserAccessibilityWin::RemoveTargetFromRelation( | |
4940 const base::string16& relation_type, | |
4941 int target_id) { | |
4942 for (auto iter = relations_.begin(); iter != relations_.end();) { | |
4943 BrowserAccessibilityRelation* relation = *iter; | |
4944 DCHECK(relation); | |
4945 if (relation->get_type() == relation_type) { | |
4946 // If |target_id| is not present, |RemoveTarget| will do nothing. | |
4947 relation->RemoveTarget(target_id); | |
4948 } | |
4949 if (relation->get_target_ids().empty()) { | |
4950 iter = relations_.erase(iter); | |
4951 relation->Release(); | |
4952 } else { | |
4953 ++iter; | |
4954 } | |
4955 } | |
4956 } | |
4957 | |
4958 void BrowserAccessibilityWin::UpdateRequiredAttributes() { | |
4959 if (IsCellOrTableHeaderRole()) { | |
4960 // Expose colspan attribute. | |
4961 base::string16 colspan; | |
4962 if (GetHtmlAttribute("aria-colspan", &colspan)) { | |
4963 SanitizeStringAttributeForIA2(colspan, &colspan); | |
4964 win_attributes_->ia2_attributes.push_back(L"colspan:" + colspan); | |
4965 } | |
4966 // Expose rowspan attribute. | |
4967 base::string16 rowspan; | |
4968 if (GetHtmlAttribute("aria-rowspan", &rowspan)) { | |
4969 SanitizeStringAttributeForIA2(rowspan, &rowspan); | |
4970 win_attributes_->ia2_attributes.push_back(L"rowspan:" + rowspan); | |
4971 } | |
4972 } | |
4973 | |
4974 // Expose dropeffect attribute. | |
4975 base::string16 drop_effect; | |
4976 if (GetHtmlAttribute("aria-dropeffect", &drop_effect)) { | |
4977 SanitizeStringAttributeForIA2(drop_effect, &drop_effect); | |
4978 win_attributes_->ia2_attributes.push_back(L"dropeffect:" + drop_effect); | |
4979 } | |
4980 | |
4981 // Expose grabbed attribute. | |
4982 base::string16 grabbed; | |
4983 if (GetHtmlAttribute("aria-grabbed", &grabbed)) { | |
4984 SanitizeStringAttributeForIA2(grabbed, &grabbed); | |
4985 win_attributes_->ia2_attributes.push_back(L"grabbed:" + grabbed); | |
4986 } | |
4987 | |
4988 // Expose class attribute. | |
4989 base::string16 class_attr; | |
4990 if (GetHtmlAttribute("class", &class_attr)) { | |
4991 SanitizeStringAttributeForIA2(class_attr, &class_attr); | |
4992 win_attributes_->ia2_attributes.push_back(L"class:" + class_attr); | |
4993 } | |
4994 | |
4995 // Expose datetime attribute. | |
4996 base::string16 datetime; | |
4997 if (GetRole() == ui::AX_ROLE_TIME && | |
4998 GetHtmlAttribute("datetime", &datetime)) { | |
4999 SanitizeStringAttributeForIA2(datetime, &datetime); | |
5000 win_attributes_->ia2_attributes.push_back(L"datetime:" + datetime); | |
5001 } | |
5002 | |
5003 // Expose id attribute. | |
5004 base::string16 id; | |
5005 if (GetHtmlAttribute("id", &id)) { | |
5006 SanitizeStringAttributeForIA2(id, &id); | |
5007 win_attributes_->ia2_attributes.push_back(L"id:" + id); | |
5008 } | |
5009 | |
5010 // Expose src attribute. | |
5011 base::string16 src; | |
5012 if (GetRole() == ui::AX_ROLE_IMAGE && GetHtmlAttribute("src", &src)) { | |
5013 SanitizeStringAttributeForIA2(src, &src); | |
5014 win_attributes_->ia2_attributes.push_back(L"src:" + src); | |
5015 } | |
5016 | |
5017 // Expose input-text type attribute. | |
5018 base::string16 type; | |
5019 base::string16 html_tag = GetString16Attribute(ui::AX_ATTR_HTML_TAG); | |
5020 if (IsSimpleTextControl() && html_tag == L"input" && | |
5021 GetHtmlAttribute("type", &type)) { | |
5022 SanitizeStringAttributeForIA2(type, &type); | |
5023 win_attributes_->ia2_attributes.push_back(L"text-input-type:" + type); | |
5024 } | |
5025 } | |
5026 | |
5027 void BrowserAccessibilityWin::FireNativeEvent(LONG win_event_type) const { | |
5028 (new BrowserAccessibilityEventWin( | |
5029 BrowserAccessibilityEvent::FromTreeChange, | |
5030 ui::AX_EVENT_NONE, | |
5031 win_event_type, | |
5032 this))->Fire(); | |
5033 } | |
5034 | |
5035 ui::AXPlatformNodeWin* BrowserAccessibilityWin::GetPlatformNodeWin() const { | |
5036 DCHECK(platform_node_); | |
5037 return static_cast<ui::AXPlatformNodeWin*>(platform_node_); | |
5038 } | |
5039 | |
5040 void BrowserAccessibilityWin::InitRoleAndState() { | |
5041 int32_t ia_role = 0; | |
5042 int32_t ia_state = 0; | |
5043 base::string16 role_name; | |
5044 int32_t ia2_role = 0; | |
5045 int32_t ia2_state = IA2_STATE_OPAQUE; | |
5046 | |
5047 if (HasState(ui::AX_STATE_BUSY)) | |
5048 ia_state |= STATE_SYSTEM_BUSY; | |
5049 | |
5050 const auto checked_state = static_cast<ui::AXCheckedState>( | |
5051 GetIntAttribute(ui::AX_ATTR_CHECKED_STATE)); | |
5052 switch (checked_state) { | |
5053 case ui::AX_CHECKED_STATE_TRUE: | |
5054 ia_state |= STATE_SYSTEM_CHECKED; | |
5055 break; | |
5056 case ui::AX_CHECKED_STATE_MIXED: | |
5057 ia_state |= STATE_SYSTEM_MIXED; | |
5058 break; | |
5059 default: | |
5060 break; | |
5061 } | |
5062 | |
5063 if (HasState(ui::AX_STATE_COLLAPSED)) | |
5064 ia_state |= STATE_SYSTEM_COLLAPSED; | |
5065 if (HasState(ui::AX_STATE_EXPANDED)) | |
5066 ia_state |= STATE_SYSTEM_EXPANDED; | |
5067 if (HasState(ui::AX_STATE_FOCUSABLE)) | |
5068 ia_state |= STATE_SYSTEM_FOCUSABLE; | |
5069 if (HasState(ui::AX_STATE_HASPOPUP)) | |
5070 ia_state |= STATE_SYSTEM_HASPOPUP; | |
5071 if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) && | |
5072 GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE) | |
5073 ia2_state |= IA2_STATE_INVALID_ENTRY; | |
5074 if (HasState(ui::AX_STATE_INVISIBLE)) | |
5075 ia_state |= STATE_SYSTEM_INVISIBLE; | |
5076 if (HasState(ui::AX_STATE_LINKED)) | |
5077 ia_state |= STATE_SYSTEM_LINKED; | |
5078 if (HasState(ui::AX_STATE_MULTISELECTABLE)) { | |
5079 ia_state |= STATE_SYSTEM_EXTSELECTABLE; | |
5080 ia_state |= STATE_SYSTEM_MULTISELECTABLE; | |
5081 } | |
5082 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. | |
5083 if (HasState(ui::AX_STATE_OFFSCREEN)) | |
5084 ia_state |= STATE_SYSTEM_OFFSCREEN; | |
5085 if (HasState(ui::AX_STATE_PRESSED)) | |
5086 ia_state |= STATE_SYSTEM_PRESSED; | |
5087 if (HasState(ui::AX_STATE_PROTECTED)) | |
5088 ia_state |= STATE_SYSTEM_PROTECTED; | |
5089 if (HasState(ui::AX_STATE_REQUIRED)) | |
5090 ia2_state |= IA2_STATE_REQUIRED; | |
5091 if (HasState(ui::AX_STATE_SELECTABLE)) | |
5092 ia_state |= STATE_SYSTEM_SELECTABLE; | |
5093 if (HasState(ui::AX_STATE_SELECTED)) | |
5094 ia_state |= STATE_SYSTEM_SELECTED; | |
5095 if (HasState(ui::AX_STATE_VISITED)) | |
5096 ia_state |= STATE_SYSTEM_TRAVERSED; | |
5097 if (HasState(ui::AX_STATE_DISABLED)) | |
5098 ia_state |= STATE_SYSTEM_UNAVAILABLE; | |
5099 if (HasState(ui::AX_STATE_VERTICAL)) | |
5100 ia2_state |= IA2_STATE_VERTICAL; | |
5101 if (HasState(ui::AX_STATE_HORIZONTAL)) | |
5102 ia2_state |= IA2_STATE_HORIZONTAL; | |
5103 if (HasState(ui::AX_STATE_VISITED)) | |
5104 ia_state |= STATE_SYSTEM_TRAVERSED; | |
5105 | |
5106 // Expose whether or not the mouse is over an element, but suppress | |
5107 // this for tests because it can make the test results flaky depending | |
5108 // on the position of the mouse. | |
5109 BrowserAccessibilityStateImpl* accessibility_state = | |
5110 BrowserAccessibilityStateImpl::GetInstance(); | |
5111 if (!accessibility_state->disable_hot_tracking_for_testing()) { | |
5112 if (HasState(ui::AX_STATE_HOVERED)) | |
5113 ia_state |= STATE_SYSTEM_HOTTRACKED; | |
5114 } | |
5115 | |
5116 if (HasState(ui::AX_STATE_EDITABLE)) | |
5117 ia2_state |= IA2_STATE_EDITABLE; | |
5118 | |
5119 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE)) | |
5120 ia2_state |= IA2_STATE_EDITABLE; | |
5121 | |
5122 if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty()) | |
5123 ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; | |
5124 | |
5125 if (GetBoolAttribute(ui::AX_ATTR_MODAL)) | |
5126 ia2_state |= IA2_STATE_MODAL; | |
5127 | |
5128 base::string16 html_tag = GetString16Attribute( | |
5129 ui::AX_ATTR_HTML_TAG); | |
5130 switch (GetRole()) { | |
5131 case ui::AX_ROLE_ALERT: | |
5132 ia_role = ROLE_SYSTEM_ALERT; | |
5133 break; | |
5134 case ui::AX_ROLE_ALERT_DIALOG: | |
5135 ia_role = ROLE_SYSTEM_DIALOG; | |
5136 break; | |
5137 case ui::AX_ROLE_ANCHOR: | |
5138 ia_role = ROLE_SYSTEM_LINK; | |
5139 break; | |
5140 case ui::AX_ROLE_APPLICATION: | |
5141 ia_role = ROLE_SYSTEM_APPLICATION; | |
5142 break; | |
5143 case ui::AX_ROLE_ARTICLE: | |
5144 ia_role = ROLE_SYSTEM_DOCUMENT; | |
5145 ia_state |= STATE_SYSTEM_READONLY; | |
5146 break; | |
5147 case ui::AX_ROLE_AUDIO: | |
5148 ia_role = ROLE_SYSTEM_GROUPING; | |
5149 break; | |
5150 case ui::AX_ROLE_BANNER: | |
5151 ia_role = ROLE_SYSTEM_GROUPING; | |
5152 ia2_role = IA2_ROLE_HEADER; | |
5153 break; | |
5154 case ui::AX_ROLE_BLOCKQUOTE: | |
5155 role_name = html_tag; | |
5156 ia2_role = IA2_ROLE_SECTION; | |
5157 break; | |
5158 case ui::AX_ROLE_BUSY_INDICATOR: | |
5159 ia_role = ROLE_SYSTEM_ANIMATION; | |
5160 ia_state |= STATE_SYSTEM_READONLY; | |
5161 break; | |
5162 case ui::AX_ROLE_BUTTON: | |
5163 ia_role = ROLE_SYSTEM_PUSHBUTTON; | |
5164 break; | |
5165 case ui::AX_ROLE_CANVAS: | |
5166 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) { | |
5167 role_name = L"canvas"; | |
5168 ia2_role = IA2_ROLE_CANVAS; | |
5169 } else { | |
5170 ia_role = ROLE_SYSTEM_GRAPHIC; | |
5171 } | |
5172 break; | |
5173 case ui::AX_ROLE_CAPTION: | |
5174 ia_role = ROLE_SYSTEM_TEXT; | |
5175 ia2_role = IA2_ROLE_CAPTION; | |
5176 break; | |
5177 case ui::AX_ROLE_CELL: | |
5178 ia_role = ROLE_SYSTEM_CELL; | |
5179 break; | |
5180 case ui::AX_ROLE_CHECK_BOX: | |
5181 ia_role = ROLE_SYSTEM_CHECKBUTTON; | |
5182 ia2_state |= IA2_STATE_CHECKABLE; | |
5183 break; | |
5184 case ui::AX_ROLE_COLOR_WELL: | |
5185 ia_role = ROLE_SYSTEM_TEXT; | |
5186 ia2_role = IA2_ROLE_COLOR_CHOOSER; | |
5187 break; | |
5188 case ui::AX_ROLE_COLUMN: | |
5189 ia_role = ROLE_SYSTEM_COLUMN; | |
5190 break; | |
5191 case ui::AX_ROLE_COLUMN_HEADER: | |
5192 ia_role = ROLE_SYSTEM_COLUMNHEADER; | |
5193 break; | |
5194 case ui::AX_ROLE_COMBO_BOX: | |
5195 ia_role = ROLE_SYSTEM_COMBOBOX; | |
5196 break; | |
5197 case ui::AX_ROLE_COMPLEMENTARY: | |
5198 ia_role = ROLE_SYSTEM_GROUPING; | |
5199 ia2_role = IA2_ROLE_NOTE; | |
5200 break; | |
5201 case ui::AX_ROLE_CONTENT_INFO: | |
5202 ia_role = ROLE_SYSTEM_TEXT; | |
5203 ia2_role = IA2_ROLE_PARAGRAPH; | |
5204 break; | |
5205 case ui::AX_ROLE_DATE: | |
5206 case ui::AX_ROLE_DATE_TIME: | |
5207 ia_role = ROLE_SYSTEM_DROPLIST; | |
5208 ia2_role = IA2_ROLE_DATE_EDITOR; | |
5209 break; | |
5210 case ui::AX_ROLE_DIV: | |
5211 role_name = L"div"; | |
5212 ia_role = ROLE_SYSTEM_GROUPING; | |
5213 ia2_role = IA2_ROLE_SECTION; | |
5214 break; | |
5215 case ui::AX_ROLE_DEFINITION: | |
5216 role_name = html_tag; | |
5217 ia2_role = IA2_ROLE_PARAGRAPH; | |
5218 ia_state |= STATE_SYSTEM_READONLY; | |
5219 break; | |
5220 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: | |
5221 role_name = html_tag; | |
5222 ia_role = ROLE_SYSTEM_TEXT; | |
5223 ia2_role = IA2_ROLE_PARAGRAPH; | |
5224 break; | |
5225 case ui::AX_ROLE_DESCRIPTION_LIST: | |
5226 role_name = html_tag; | |
5227 ia_role = ROLE_SYSTEM_LIST; | |
5228 ia_state |= STATE_SYSTEM_READONLY; | |
5229 break; | |
5230 case ui::AX_ROLE_DESCRIPTION_LIST_TERM: | |
5231 ia_role = ROLE_SYSTEM_LISTITEM; | |
5232 ia_state |= STATE_SYSTEM_READONLY; | |
5233 break; | |
5234 case ui::AX_ROLE_DETAILS: | |
5235 role_name = html_tag; | |
5236 ia_role = ROLE_SYSTEM_GROUPING; | |
5237 break; | |
5238 case ui::AX_ROLE_DIALOG: | |
5239 ia_role = ROLE_SYSTEM_DIALOG; | |
5240 break; | |
5241 case ui::AX_ROLE_DISCLOSURE_TRIANGLE: | |
5242 ia_role = ROLE_SYSTEM_PUSHBUTTON; | |
5243 break; | |
5244 case ui::AX_ROLE_DOCUMENT: | |
5245 case ui::AX_ROLE_ROOT_WEB_AREA: | |
5246 case ui::AX_ROLE_WEB_AREA: | |
5247 ia_role = ROLE_SYSTEM_DOCUMENT; | |
5248 ia_state |= STATE_SYSTEM_READONLY; | |
5249 ia_state |= STATE_SYSTEM_FOCUSABLE; | |
5250 break; | |
5251 case ui::AX_ROLE_EMBEDDED_OBJECT: | |
5252 if (PlatformChildCount()) { | |
5253 // Windows screen readers assume that IA2_ROLE_EMBEDDED_OBJECT | |
5254 // doesn't have any children, but it may be something like a | |
5255 // browser plugin that has a document inside. | |
5256 ia_role = ROLE_SYSTEM_GROUPING; | |
5257 } else { | |
5258 ia_role = ROLE_SYSTEM_CLIENT; | |
5259 ia2_role = IA2_ROLE_EMBEDDED_OBJECT; | |
5260 } | |
5261 break; | |
5262 case ui::AX_ROLE_FIGCAPTION: | |
5263 role_name = html_tag; | |
5264 ia2_role = IA2_ROLE_CAPTION; | |
5265 break; | |
5266 case ui::AX_ROLE_FIGURE: | |
5267 ia_role = ROLE_SYSTEM_GROUPING; | |
5268 break; | |
5269 case ui::AX_ROLE_FEED: | |
5270 ia_role = ROLE_SYSTEM_GROUPING; | |
5271 break; | |
5272 case ui::AX_ROLE_FORM: | |
5273 role_name = L"form"; | |
5274 ia2_role = IA2_ROLE_FORM; | |
5275 break; | |
5276 case ui::AX_ROLE_FOOTER: | |
5277 ia_role = ROLE_SYSTEM_GROUPING; | |
5278 ia2_role = IA2_ROLE_FOOTER; | |
5279 break; | |
5280 case ui::AX_ROLE_GRID: | |
5281 ia_role = ROLE_SYSTEM_TABLE; | |
5282 // TODO(aleventhal) this changed between ARIA 1.0 and 1.1, | |
5283 // need to determine whether grids/treegrids should really be readonly | |
5284 // or editable by default | |
5285 // ia_state |= STATE_SYSTEM_READONLY; | |
5286 break; | |
5287 case ui::AX_ROLE_GROUP: { | |
5288 base::string16 aria_role = GetString16Attribute( | |
5289 ui::AX_ATTR_ROLE); | |
5290 if (aria_role == L"group" || html_tag == L"fieldset") { | |
5291 ia_role = ROLE_SYSTEM_GROUPING; | |
5292 } else if (html_tag == L"li") { | |
5293 ia_role = ROLE_SYSTEM_LISTITEM; | |
5294 ia_state |= STATE_SYSTEM_READONLY; | |
5295 } else { | |
5296 if (html_tag.empty()) | |
5297 role_name = L"div"; | |
5298 else | |
5299 role_name = html_tag; | |
5300 ia2_role = IA2_ROLE_SECTION; | |
5301 } | |
5302 break; | |
5303 } | |
5304 case ui::AX_ROLE_HEADING: | |
5305 role_name = html_tag; | |
5306 if (html_tag.empty()) | |
5307 ia_role = ROLE_SYSTEM_GROUPING; | |
5308 ia2_role = IA2_ROLE_HEADING; | |
5309 break; | |
5310 case ui::AX_ROLE_IFRAME: | |
5311 ia_role = ROLE_SYSTEM_DOCUMENT; | |
5312 ia2_role = IA2_ROLE_INTERNAL_FRAME; | |
5313 ia_state = STATE_SYSTEM_READONLY; | |
5314 break; | |
5315 case ui::AX_ROLE_IFRAME_PRESENTATIONAL: | |
5316 ia_role = ROLE_SYSTEM_GROUPING; | |
5317 break; | |
5318 case ui::AX_ROLE_IMAGE: | |
5319 ia_role = ROLE_SYSTEM_GRAPHIC; | |
5320 ia_state |= STATE_SYSTEM_READONLY; | |
5321 break; | |
5322 case ui::AX_ROLE_IMAGE_MAP: | |
5323 role_name = html_tag; | |
5324 ia2_role = IA2_ROLE_IMAGE_MAP; | |
5325 ia_state |= STATE_SYSTEM_READONLY; | |
5326 break; | |
5327 case ui::AX_ROLE_IMAGE_MAP_LINK: | |
5328 ia_role = ROLE_SYSTEM_LINK; | |
5329 ia_state |= STATE_SYSTEM_LINKED; | |
5330 ia_state |= STATE_SYSTEM_READONLY; | |
5331 break; | |
5332 case ui::AX_ROLE_INPUT_TIME: | |
5333 ia_role = ROLE_SYSTEM_GROUPING; | |
5334 break; | |
5335 case ui::AX_ROLE_LABEL_TEXT: | |
5336 case ui::AX_ROLE_LEGEND: | |
5337 ia_role = ROLE_SYSTEM_TEXT; | |
5338 ia2_role = IA2_ROLE_LABEL; | |
5339 break; | |
5340 case ui::AX_ROLE_LINK: | |
5341 ia_role = ROLE_SYSTEM_LINK; | |
5342 ia_state |= STATE_SYSTEM_LINKED; | |
5343 break; | |
5344 case ui::AX_ROLE_LIST: | |
5345 ia_role = ROLE_SYSTEM_LIST; | |
5346 ia_state |= STATE_SYSTEM_READONLY; | |
5347 break; | |
5348 case ui::AX_ROLE_LIST_BOX: | |
5349 ia_role = ROLE_SYSTEM_LIST; | |
5350 break; | |
5351 case ui::AX_ROLE_LIST_BOX_OPTION: | |
5352 ia_role = ROLE_SYSTEM_LISTITEM; | |
5353 if (ia_state & STATE_SYSTEM_SELECTABLE) { | |
5354 ia_state |= STATE_SYSTEM_FOCUSABLE; | |
5355 } | |
5356 break; | |
5357 case ui::AX_ROLE_LIST_ITEM: | |
5358 ia_role = ROLE_SYSTEM_LISTITEM; | |
5359 ia_state |= STATE_SYSTEM_READONLY; | |
5360 break; | |
5361 case ui::AX_ROLE_MAIN: | |
5362 ia_role = ROLE_SYSTEM_GROUPING; | |
5363 ia2_role = IA2_ROLE_PARAGRAPH; | |
5364 break; | |
5365 case ui::AX_ROLE_MARK: | |
5366 ia_role = ROLE_SYSTEM_TEXT; | |
5367 ia2_role = IA2_ROLE_TEXT_FRAME; | |
5368 break; | |
5369 case ui::AX_ROLE_MARQUEE: | |
5370 ia_role = ROLE_SYSTEM_ANIMATION; | |
5371 break; | |
5372 case ui::AX_ROLE_MATH: | |
5373 ia_role = ROLE_SYSTEM_EQUATION; | |
5374 break; | |
5375 case ui::AX_ROLE_MENU: | |
5376 case ui::AX_ROLE_MENU_BUTTON: | |
5377 ia_role = ROLE_SYSTEM_MENUPOPUP; | |
5378 break; | |
5379 case ui::AX_ROLE_MENU_BAR: | |
5380 ia_role = ROLE_SYSTEM_MENUBAR; | |
5381 break; | |
5382 case ui::AX_ROLE_MENU_ITEM: | |
5383 ia_role = ROLE_SYSTEM_MENUITEM; | |
5384 break; | |
5385 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: | |
5386 ia_role = ROLE_SYSTEM_MENUITEM; | |
5387 ia2_role = IA2_ROLE_CHECK_MENU_ITEM; | |
5388 ia2_state |= IA2_STATE_CHECKABLE; | |
5389 break; | |
5390 case ui::AX_ROLE_MENU_ITEM_RADIO: | |
5391 ia_role = ROLE_SYSTEM_MENUITEM; | |
5392 ia2_role = IA2_ROLE_RADIO_MENU_ITEM; | |
5393 break; | |
5394 case ui::AX_ROLE_MENU_LIST_POPUP: | |
5395 ia_role = ROLE_SYSTEM_LIST; | |
5396 ia2_state &= ~(IA2_STATE_EDITABLE); | |
5397 break; | |
5398 case ui::AX_ROLE_MENU_LIST_OPTION: | |
5399 ia_role = ROLE_SYSTEM_LISTITEM; | |
5400 ia2_state &= ~(IA2_STATE_EDITABLE); | |
5401 if (ia_state & STATE_SYSTEM_SELECTABLE) { | |
5402 ia_state |= STATE_SYSTEM_FOCUSABLE; | |
5403 } | |
5404 break; | |
5405 case ui::AX_ROLE_METER: | |
5406 role_name = html_tag; | |
5407 ia_role = ROLE_SYSTEM_PROGRESSBAR; | |
5408 break; | |
5409 case ui::AX_ROLE_NAVIGATION: | |
5410 ia_role = ROLE_SYSTEM_GROUPING; | |
5411 ia2_role = IA2_ROLE_SECTION; | |
5412 break; | |
5413 case ui::AX_ROLE_NOTE: | |
5414 ia_role = ROLE_SYSTEM_GROUPING; | |
5415 ia2_role = IA2_ROLE_NOTE; | |
5416 break; | |
5417 case ui::AX_ROLE_OUTLINE: | |
5418 ia_role = ROLE_SYSTEM_OUTLINE; | |
5419 break; | |
5420 case ui::AX_ROLE_PARAGRAPH: | |
5421 role_name = L"P"; | |
5422 ia2_role = IA2_ROLE_PARAGRAPH; | |
5423 break; | |
5424 case ui::AX_ROLE_POP_UP_BUTTON: | |
5425 if (html_tag == L"select") { | |
5426 ia_role = ROLE_SYSTEM_COMBOBOX; | |
5427 } else { | |
5428 ia_role = ROLE_SYSTEM_BUTTONMENU; | |
5429 } | |
5430 break; | |
5431 case ui::AX_ROLE_PRE: | |
5432 role_name = html_tag; | |
5433 ia_role = ROLE_SYSTEM_TEXT; | |
5434 ia2_role = IA2_ROLE_PARAGRAPH; | |
5435 break; | |
5436 case ui::AX_ROLE_PROGRESS_INDICATOR: | |
5437 ia_role = ROLE_SYSTEM_PROGRESSBAR; | |
5438 ia_state |= STATE_SYSTEM_READONLY; | |
5439 break; | |
5440 case ui::AX_ROLE_RADIO_BUTTON: | |
5441 ia_role = ROLE_SYSTEM_RADIOBUTTON; | |
5442 ia2_state = IA2_STATE_CHECKABLE; | |
5443 break; | |
5444 case ui::AX_ROLE_RADIO_GROUP: | |
5445 ia_role = ROLE_SYSTEM_GROUPING; | |
5446 break; | |
5447 case ui::AX_ROLE_REGION: | |
5448 if (html_tag == L"section") { | |
5449 ia_role = ROLE_SYSTEM_GROUPING; | |
5450 ia2_role = IA2_ROLE_SECTION; | |
5451 } else { | |
5452 ia_role = ROLE_SYSTEM_PANE; | |
5453 } | |
5454 break; | |
5455 case ui::AX_ROLE_ROW: { | |
5456 // Role changes depending on whether row is inside a treegrid | |
5457 // https://www.w3.org/TR/core-aam-1.1/#role-map-row | |
5458 ia_role = IsInTreeGrid(this) ? ROLE_SYSTEM_OUTLINEITEM : ROLE_SYSTEM_ROW; | |
5459 break; | |
5460 } | |
5461 case ui::AX_ROLE_ROW_HEADER: | |
5462 ia_role = ROLE_SYSTEM_ROWHEADER; | |
5463 break; | |
5464 case ui::AX_ROLE_RUBY: | |
5465 ia_role = ROLE_SYSTEM_TEXT; | |
5466 ia2_role = IA2_ROLE_TEXT_FRAME; | |
5467 break; | |
5468 case ui::AX_ROLE_RULER: | |
5469 ia_role = ROLE_SYSTEM_CLIENT; | |
5470 ia2_role = IA2_ROLE_RULER; | |
5471 ia_state |= STATE_SYSTEM_READONLY; | |
5472 break; | |
5473 case ui::AX_ROLE_SCROLL_AREA: | |
5474 ia_role = ROLE_SYSTEM_CLIENT; | |
5475 ia2_role = IA2_ROLE_SCROLL_PANE; | |
5476 ia_state |= STATE_SYSTEM_READONLY; | |
5477 ia2_state &= ~(IA2_STATE_EDITABLE); | |
5478 break; | |
5479 case ui::AX_ROLE_SCROLL_BAR: | |
5480 ia_role = ROLE_SYSTEM_SCROLLBAR; | |
5481 break; | |
5482 case ui::AX_ROLE_SEARCH: | |
5483 ia_role = ROLE_SYSTEM_GROUPING; | |
5484 ia2_role = IA2_ROLE_SECTION; | |
5485 break; | |
5486 case ui::AX_ROLE_SLIDER: | |
5487 ia_role = ROLE_SYSTEM_SLIDER; | |
5488 break; | |
5489 case ui::AX_ROLE_SPIN_BUTTON: | |
5490 ia_role = ROLE_SYSTEM_SPINBUTTON; | |
5491 break; | |
5492 case ui::AX_ROLE_SPIN_BUTTON_PART: | |
5493 ia_role = ROLE_SYSTEM_PUSHBUTTON; | |
5494 break; | |
5495 case ui::AX_ROLE_ANNOTATION: | |
5496 case ui::AX_ROLE_LIST_MARKER: | |
5497 case ui::AX_ROLE_STATIC_TEXT: | |
5498 ia_role = ROLE_SYSTEM_STATICTEXT; | |
5499 break; | |
5500 case ui::AX_ROLE_STATUS: | |
5501 ia_role = ROLE_SYSTEM_STATUSBAR; | |
5502 break; | |
5503 case ui::AX_ROLE_SPLITTER: | |
5504 ia_role = ROLE_SYSTEM_SEPARATOR; | |
5505 break; | |
5506 case ui::AX_ROLE_SVG_ROOT: | |
5507 ia_role = ROLE_SYSTEM_GRAPHIC; | |
5508 break; | |
5509 case ui::AX_ROLE_SWITCH: | |
5510 role_name = L"switch"; | |
5511 ia2_role = IA2_ROLE_TOGGLE_BUTTON; | |
5512 break; | |
5513 case ui::AX_ROLE_TAB: | |
5514 ia_role = ROLE_SYSTEM_PAGETAB; | |
5515 break; | |
5516 case ui::AX_ROLE_TABLE: | |
5517 ia_role = ROLE_SYSTEM_TABLE; | |
5518 break; | |
5519 case ui::AX_ROLE_TABLE_HEADER_CONTAINER: | |
5520 ia_role = ROLE_SYSTEM_GROUPING; | |
5521 ia2_role = IA2_ROLE_SECTION; | |
5522 ia_state |= STATE_SYSTEM_READONLY; | |
5523 break; | |
5524 case ui::AX_ROLE_TAB_LIST: | |
5525 ia_role = ROLE_SYSTEM_PAGETABLIST; | |
5526 break; | |
5527 case ui::AX_ROLE_TAB_PANEL: | |
5528 ia_role = ROLE_SYSTEM_PROPERTYPAGE; | |
5529 break; | |
5530 case ui::AX_ROLE_TERM: | |
5531 ia_role = ROLE_SYSTEM_LISTITEM; | |
5532 ia_state |= STATE_SYSTEM_READONLY; | |
5533 break; | |
5534 case ui::AX_ROLE_TOGGLE_BUTTON: | |
5535 ia_role = ROLE_SYSTEM_PUSHBUTTON; | |
5536 ia2_role = IA2_ROLE_TOGGLE_BUTTON; | |
5537 break; | |
5538 case ui::AX_ROLE_TEXT_FIELD: | |
5539 case ui::AX_ROLE_SEARCH_BOX: | |
5540 ia_role = ROLE_SYSTEM_TEXT; | |
5541 if (HasState(ui::AX_STATE_MULTILINE)) { | |
5542 ia2_state |= IA2_STATE_MULTI_LINE; | |
5543 } else { | |
5544 ia2_state |= IA2_STATE_SINGLE_LINE; | |
5545 } | |
5546 if (HasState(ui::AX_STATE_READ_ONLY)) | |
5547 ia_state |= STATE_SYSTEM_READONLY; | |
5548 ia2_state |= IA2_STATE_SELECTABLE_TEXT; | |
5549 break; | |
5550 case ui::AX_ROLE_ABBR: | |
5551 case ui::AX_ROLE_TIME: | |
5552 role_name = html_tag; | |
5553 ia_role = ROLE_SYSTEM_TEXT; | |
5554 ia2_role = IA2_ROLE_TEXT_FRAME; | |
5555 break; | |
5556 case ui::AX_ROLE_TIMER: | |
5557 ia_role = ROLE_SYSTEM_CLOCK; | |
5558 ia_state |= STATE_SYSTEM_READONLY; | |
5559 break; | |
5560 case ui::AX_ROLE_TOOLBAR: | |
5561 ia_role = ROLE_SYSTEM_TOOLBAR; | |
5562 ia_state |= STATE_SYSTEM_READONLY; | |
5563 break; | |
5564 case ui::AX_ROLE_TOOLTIP: | |
5565 ia_role = ROLE_SYSTEM_TOOLTIP; | |
5566 ia_state |= STATE_SYSTEM_READONLY; | |
5567 break; | |
5568 case ui::AX_ROLE_TREE: | |
5569 ia_role = ROLE_SYSTEM_OUTLINE; | |
5570 break; | |
5571 case ui::AX_ROLE_TREE_GRID: | |
5572 ia_role = ROLE_SYSTEM_OUTLINE; | |
5573 break; | |
5574 case ui::AX_ROLE_TREE_ITEM: | |
5575 ia_role = ROLE_SYSTEM_OUTLINEITEM; | |
5576 break; | |
5577 case ui::AX_ROLE_LINE_BREAK: | |
5578 ia_role = ROLE_SYSTEM_WHITESPACE; | |
5579 break; | |
5580 case ui::AX_ROLE_VIDEO: | |
5581 ia_role = ROLE_SYSTEM_GROUPING; | |
5582 break; | |
5583 case ui::AX_ROLE_WINDOW: | |
5584 ia_role = ROLE_SYSTEM_WINDOW; | |
5585 break; | |
5586 | |
5587 // TODO(dmazzoni): figure out the proper MSAA role for all of these. | |
5588 case ui::AX_ROLE_DIRECTORY: | |
5589 case ui::AX_ROLE_IGNORED: | |
5590 case ui::AX_ROLE_LOG: | |
5591 case ui::AX_ROLE_NONE: | |
5592 case ui::AX_ROLE_PRESENTATIONAL: | |
5593 case ui::AX_ROLE_SLIDER_THUMB: | |
5594 default: | |
5595 ia_role = ROLE_SYSTEM_CLIENT; | |
5596 break; | |
5597 } | |
5598 | |
5599 // Compute the final value of READONLY for MSAA. | |
5600 // | |
5601 // We always set the READONLY state for elements that have the | |
5602 // aria-readonly attribute and for a few roles (in the switch above), | |
5603 // including read-only text fields. | |
5604 // The majority of focusable controls should not have the read-only state set. | |
5605 if (HasState(ui::AX_STATE_FOCUSABLE) && ia_role != ROLE_SYSTEM_DOCUMENT && | |
5606 ia_role != ROLE_SYSTEM_TEXT) { | |
5607 ia_state &= ~(STATE_SYSTEM_READONLY); | |
5608 } | |
5609 if (!HasState(ui::AX_STATE_READ_ONLY)) | |
5610 ia_state &= ~(STATE_SYSTEM_READONLY); | |
5611 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY)) | |
5612 ia_state |= STATE_SYSTEM_READONLY; | |
5613 | |
5614 // The role should always be set. | |
5615 DCHECK(!role_name.empty() || ia_role); | |
5616 | |
5617 // If we didn't explicitly set the IAccessible2 role, make it the same | |
5618 // as the MSAA role. | |
5619 if (!ia2_role) | |
5620 ia2_role = ia_role; | |
5621 | |
5622 win_attributes_->ia_role = ia_role; | |
5623 win_attributes_->ia_state = ia_state; | |
5624 win_attributes_->role_name = role_name; | |
5625 win_attributes_->ia2_role = ia2_role; | |
5626 win_attributes_->ia2_state = ia2_state; | |
5627 } | |
5628 | |
5629 bool BrowserAccessibilityWin::IsInTreeGrid(const BrowserAccessibility* item) { | |
5630 BrowserAccessibility* container = item->PlatformGetParent(); | |
5631 if (container && container->GetRole() == ui::AX_ROLE_GROUP) { | |
5632 // If parent was a rowgroup, we need to look at the grandparent | |
5633 container = container->PlatformGetParent(); | |
5634 } | |
5635 | |
5636 if (!container) { | |
5637 return false; | |
5638 } | |
5639 | |
5640 return container->GetRole() == ui::AX_ROLE_TREE_GRID; | |
5641 } | 69 } |
5642 | 70 |
5643 BrowserAccessibilityWin* ToBrowserAccessibilityWin(BrowserAccessibility* obj) { | 71 BrowserAccessibilityWin* ToBrowserAccessibilityWin(BrowserAccessibility* obj) { |
5644 DCHECK(!obj || obj->IsNative()); | 72 DCHECK(!obj || obj->IsNative()); |
5645 return static_cast<BrowserAccessibilityWin*>(obj); | 73 return static_cast<BrowserAccessibilityWin*>(obj); |
5646 } | 74 } |
5647 | 75 |
5648 const BrowserAccessibilityWin* | 76 const BrowserAccessibilityWin* |
5649 ToBrowserAccessibilityWin(const BrowserAccessibility* obj) { | 77 ToBrowserAccessibilityWin(const BrowserAccessibility* obj) { |
5650 DCHECK(!obj || obj->IsNative()); | 78 DCHECK(!obj || obj->IsNative()); |
5651 return static_cast<const BrowserAccessibilityWin*>(obj); | 79 return static_cast<const BrowserAccessibilityWin*>(obj); |
5652 } | 80 } |
5653 | 81 |
5654 } // namespace content | 82 } // namespace content |
OLD | NEW |