OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "base/string_number_conversions.h" | 7 #include "base/string_number_conversions.h" |
8 #include "base/string_util.h" | 8 #include "base/string_util.h" |
9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
10 #include "base/win/scoped_comptr.h" | 10 #include "base/win/scoped_comptr.h" |
11 #include "content/browser/accessibility/browser_accessibility_manager_win.h" | 11 #include "content/browser/accessibility/browser_accessibility_manager_win.h" |
12 #include "content/common/view_messages.h" | 12 #include "content/common/view_messages.h" |
13 #include "net/base/escape.h" | 13 #include "net/base/escape.h" |
14 #include "ui/base/accessibility/accessible_text_utils.h" | 14 #include "ui/base/accessibility/accessible_text_utils.h" |
15 | 15 |
16 using webkit_glue::WebAccessibility; | 16 using webkit_glue::WebAccessibility; |
17 | 17 |
18 // The GUID for the ISimpleDOM service is not defined in the IDL files. | 18 // The GUID for the ISimpleDOM service is not defined in the IDL files. |
19 // This is taken directly from the Mozilla sources | 19 // This is taken directly from the Mozilla sources |
20 // (accessible/src/msaa/nsAccessNodeWrap.cpp) and it's also documented at: | 20 // (accessible/src/msaa/nsAccessNodeWrap.cpp) and it's also documented at: |
21 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/
MSAA | 21 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/
MSAA |
22 | 22 |
23 const GUID GUID_ISimpleDOM = { | 23 const GUID GUID_ISimpleDOM = { |
24 0x0c539790, 0x12e4, 0x11cf, | 24 0x0c539790, 0x12e4, 0x11cf, |
25 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; | 25 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; |
26 | 26 |
| 27 const char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc"; |
| 28 |
27 // | 29 // |
28 // BrowserAccessibilityRelation | 30 // BrowserAccessibilityRelation |
29 // | 31 // |
30 // A simple implementation of IAccessibleRelation, used to represent | 32 // A simple implementation of IAccessibleRelation, used to represent |
31 // a relationship between two accessible nodes in the tree. | 33 // a relationship between two accessible nodes in the tree. |
32 // | 34 // |
33 | 35 |
34 class BrowserAccessibilityRelation | 36 class BrowserAccessibilityRelation |
35 : public CComObjectRootEx<CComMultiThreadModel>, | 37 : public CComObjectRootEx<CComMultiThreadModel>, |
36 public IAccessibleRelation { | 38 public IAccessibleRelation { |
(...skipping 1557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1594 // IAccessibleText methods. | 1596 // IAccessibleText methods. |
1595 // | 1597 // |
1596 | 1598 |
1597 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { | 1599 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { |
1598 if (!instance_active_) | 1600 if (!instance_active_) |
1599 return E_FAIL; | 1601 return E_FAIL; |
1600 | 1602 |
1601 if (!n_characters) | 1603 if (!n_characters) |
1602 return E_INVALIDARG; | 1604 return E_INVALIDARG; |
1603 | 1605 |
1604 if (role_ == WebAccessibility::ROLE_TEXT_FIELD || | 1606 *n_characters = TextForIAccessibleText().length(); |
1605 role_ == WebAccessibility::ROLE_TEXTAREA) { | |
1606 *n_characters = value_.length(); | |
1607 } else { | |
1608 *n_characters = name_.length(); | |
1609 } | |
1610 | |
1611 return S_OK; | 1607 return S_OK; |
1612 } | 1608 } |
1613 | 1609 |
1614 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { | 1610 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { |
1615 if (!instance_active_) | 1611 if (!instance_active_) |
1616 return E_FAIL; | 1612 return E_FAIL; |
1617 | 1613 |
1618 if (!offset) | 1614 if (!offset) |
1619 return E_INVALIDARG; | 1615 return E_INVALIDARG; |
1620 | 1616 |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1847 return E_INVALIDARG; | 1843 return E_INVALIDARG; |
1848 | 1844 |
1849 // TODO(dmazzoni): implement this. We're returning S_OK for now so that | 1845 // TODO(dmazzoni): implement this. We're returning S_OK for now so that |
1850 // screen readers still return partially accurate results rather than | 1846 // screen readers still return partially accurate results rather than |
1851 // completely failing. | 1847 // completely failing. |
1852 *offset = 0; | 1848 *offset = 0; |
1853 return S_OK; | 1849 return S_OK; |
1854 } | 1850 } |
1855 | 1851 |
1856 // | 1852 // |
| 1853 // IAccessibleHypertext methods. |
| 1854 // |
| 1855 |
| 1856 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) { |
| 1857 if (!instance_active_) |
| 1858 return E_FAIL; |
| 1859 |
| 1860 if (!hyperlink_count) |
| 1861 return E_INVALIDARG; |
| 1862 |
| 1863 *hyperlink_count = hyperlink_offset_to_index_.size(); |
| 1864 return S_OK; |
| 1865 } |
| 1866 |
| 1867 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( |
| 1868 long index, |
| 1869 IAccessibleHyperlink** hyperlink) { |
| 1870 if (!instance_active_) |
| 1871 return E_FAIL; |
| 1872 |
| 1873 if (!hyperlink || |
| 1874 index < 0 || |
| 1875 index >= static_cast<long>(hyperlinks_.size())) { |
| 1876 return E_INVALIDARG; |
| 1877 } |
| 1878 |
| 1879 BrowserAccessibilityWin* child = |
| 1880 children_[hyperlinks_[index]]->toBrowserAccessibilityWin(); |
| 1881 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); |
| 1882 return S_OK; |
| 1883 } |
| 1884 |
| 1885 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( |
| 1886 long char_index, |
| 1887 long* hyperlink_index) { |
| 1888 if (!instance_active_) |
| 1889 return E_FAIL; |
| 1890 |
| 1891 if (!hyperlink_index) |
| 1892 return E_INVALIDARG; |
| 1893 |
| 1894 *hyperlink_index = -1; |
| 1895 |
| 1896 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size())) |
| 1897 return E_INVALIDARG; |
| 1898 |
| 1899 std::map<int32, int32>::iterator it = |
| 1900 hyperlink_offset_to_index_.find(char_index); |
| 1901 if (it == hyperlink_offset_to_index_.end()) |
| 1902 return E_FAIL; |
| 1903 |
| 1904 *hyperlink_index = it->second; |
| 1905 return S_OK; |
| 1906 } |
| 1907 |
| 1908 // |
1857 // IAccessibleValue methods. | 1909 // IAccessibleValue methods. |
1858 // | 1910 // |
1859 | 1911 |
1860 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { | 1912 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { |
1861 if (!instance_active_) | 1913 if (!instance_active_) |
1862 return E_FAIL; | 1914 return E_FAIL; |
1863 | 1915 |
1864 if (!value) | 1916 if (!value) |
1865 return E_INVALIDARG; | 1917 return E_INVALIDARG; |
1866 | 1918 |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2240 // IServiceProvider methods. | 2292 // IServiceProvider methods. |
2241 // | 2293 // |
2242 | 2294 |
2243 STDMETHODIMP BrowserAccessibilityWin::QueryService( | 2295 STDMETHODIMP BrowserAccessibilityWin::QueryService( |
2244 REFGUID guidService, REFIID riid, void** object) { | 2296 REFGUID guidService, REFIID riid, void** object) { |
2245 if (!instance_active_) | 2297 if (!instance_active_) |
2246 return E_FAIL; | 2298 return E_FAIL; |
2247 | 2299 |
2248 if (guidService == IID_IAccessible || | 2300 if (guidService == IID_IAccessible || |
2249 guidService == IID_IAccessible2 || | 2301 guidService == IID_IAccessible2 || |
| 2302 guidService == IID_IAccessibleAction || |
| 2303 guidService == IID_IAccessibleHyperlink || |
| 2304 guidService == IID_IAccessibleHypertext || |
2250 guidService == IID_IAccessibleImage || | 2305 guidService == IID_IAccessibleImage || |
2251 guidService == IID_IAccessibleTable || | 2306 guidService == IID_IAccessibleTable || |
2252 guidService == IID_IAccessibleTable2 || | 2307 guidService == IID_IAccessibleTable2 || |
2253 guidService == IID_IAccessibleTableCell || | 2308 guidService == IID_IAccessibleTableCell || |
2254 guidService == IID_IAccessibleText || | 2309 guidService == IID_IAccessibleText || |
2255 guidService == IID_IAccessibleValue || | 2310 guidService == IID_IAccessibleValue || |
2256 guidService == IID_ISimpleDOMDocument || | 2311 guidService == IID_ISimpleDOMDocument || |
2257 guidService == IID_ISimpleDOMNode || | 2312 guidService == IID_ISimpleDOMNode || |
2258 guidService == IID_ISimpleDOMText || | 2313 guidService == IID_ISimpleDOMText || |
2259 guidService == GUID_ISimpleDOM) { | 2314 guidService == GUID_ISimpleDOM) { |
2260 return QueryInterface(riid, object); | 2315 return QueryInterface(riid, object); |
2261 } | 2316 } |
2262 | 2317 |
2263 *object = NULL; | 2318 *object = NULL; |
2264 return E_FAIL; | 2319 return E_FAIL; |
2265 } | 2320 } |
2266 | 2321 |
2267 // | 2322 // |
2268 // CComObjectRootEx methods. | 2323 // CComObjectRootEx methods. |
2269 // | 2324 // |
2270 | 2325 |
2271 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( | 2326 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( |
2272 void* this_ptr, | 2327 void* this_ptr, |
2273 const _ATL_INTMAP_ENTRY* entries, | 2328 const _ATL_INTMAP_ENTRY* entries, |
2274 REFIID iid, | 2329 REFIID iid, |
2275 void** object) { | 2330 void** object) { |
2276 if (iid == IID_IAccessibleText) { | 2331 if (iid == IID_IAccessibleImage) { |
2277 if (ia_role_ != ROLE_SYSTEM_LINK && ia_role_ != ROLE_SYSTEM_TEXT) { | |
2278 *object = NULL; | |
2279 return E_NOINTERFACE; | |
2280 } | |
2281 } else if (iid == IID_IAccessibleImage) { | |
2282 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) { | 2332 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) { |
2283 *object = NULL; | 2333 *object = NULL; |
2284 return E_NOINTERFACE; | 2334 return E_NOINTERFACE; |
2285 } | 2335 } |
2286 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { | 2336 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { |
2287 if (ia_role_ != ROLE_SYSTEM_TABLE) { | 2337 if (ia_role_ != ROLE_SYSTEM_TABLE) { |
2288 *object = NULL; | 2338 *object = NULL; |
2289 return E_NOINTERFACE; | 2339 return E_NOINTERFACE; |
2290 } | 2340 } |
2291 } else if (iid == IID_IAccessibleTableCell) { | 2341 } else if (iid == IID_IAccessibleTableCell) { |
(...skipping 17 matching lines...) Expand all Loading... |
2309 | 2359 |
2310 return CComObjectRootBase::InternalQueryInterface( | 2360 return CComObjectRootBase::InternalQueryInterface( |
2311 this_ptr, entries, iid, object); | 2361 this_ptr, entries, iid, object); |
2312 } | 2362 } |
2313 | 2363 |
2314 // | 2364 // |
2315 // Private methods. | 2365 // Private methods. |
2316 // | 2366 // |
2317 | 2367 |
2318 // Initialize this object and mark it as active. | 2368 // Initialize this object and mark it as active. |
2319 void BrowserAccessibilityWin::Initialize() { | 2369 void BrowserAccessibilityWin::PreInitialize() { |
2320 BrowserAccessibility::Initialize(); | 2370 BrowserAccessibility::PreInitialize(); |
2321 | 2371 |
2322 InitRoleAndState(); | 2372 InitRoleAndState(); |
2323 | 2373 |
2324 // Expose headings levels with the "level" attribute. | 2374 // Expose headings levels with the "level" attribute. |
2325 if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 && | 2375 if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 && |
2326 IsAsciiDigit(role_name_[1])) { | 2376 IsAsciiDigit(role_name_[1])) { |
2327 ia2_attributes_.push_back(string16(L"level:") + role_name_.substr(1)); | 2377 ia2_attributes_.push_back(string16(L"level:") + role_name_.substr(1)); |
2328 } | 2378 } |
2329 | 2379 |
2330 // Expose the "display" and "tag" attributes. | 2380 // Expose the "display" and "tag" attributes. |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2417 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( | 2467 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( |
2418 &relation); | 2468 &relation); |
2419 DCHECK(SUCCEEDED(hr)); | 2469 DCHECK(SUCCEEDED(hr)); |
2420 relation->AddRef(); | 2470 relation->AddRef(); |
2421 relation->Initialize(this, IA2_RELATION_LABELLED_BY); | 2471 relation->Initialize(this, IA2_RELATION_LABELLED_BY); |
2422 relation->AddTarget(title_elem_id); | 2472 relation->AddTarget(title_elem_id); |
2423 relations_.push_back(relation); | 2473 relations_.push_back(relation); |
2424 } | 2474 } |
2425 } | 2475 } |
2426 | 2476 |
2427 void BrowserAccessibilityWin::SendNodeUpdateEvents() { | 2477 void BrowserAccessibilityWin::PostInitialize() { |
| 2478 BrowserAccessibility::PostInitialize(); |
| 2479 |
| 2480 // Construct the hypertext for this node. |
| 2481 hyperlink_offset_to_index_.clear(); |
| 2482 hyperlinks_.clear(); |
| 2483 hypertext_.clear(); |
| 2484 for (unsigned int i = 0; i < children().size(); ++i) { |
| 2485 BrowserAccessibility* child = children()[i]; |
| 2486 if (child->role() == WebAccessibility::ROLE_STATIC_TEXT) { |
| 2487 hypertext_ += child->name(); |
| 2488 } else { |
| 2489 hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size(); |
| 2490 hypertext_ += kEmbeddedCharacter; |
| 2491 hyperlinks_.push_back(i); |
| 2492 } |
| 2493 } |
| 2494 DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size()); |
| 2495 |
2428 // Fire an event when an alert first appears. | 2496 // Fire an event when an alert first appears. |
2429 if (role_ == WebAccessibility::ROLE_ALERT && first_time_) | 2497 if (role_ == WebAccessibility::ROLE_ALERT && first_time_) |
2430 manager_->NotifyAccessibilityEvent(ViewHostMsg_AccEvent::ALERT, this); | 2498 manager_->NotifyAccessibilityEvent(ViewHostMsg_AccEvent::ALERT, this); |
2431 | 2499 |
2432 // Fire events if text has changed. | 2500 // Fire events if text has changed. |
2433 string16 text = TextForIAccessibleText(); | 2501 string16 text = TextForIAccessibleText(); |
2434 if (previous_text_ != text) { | 2502 if (previous_text_ != text) { |
2435 if (!previous_text_.empty() && !text.empty()) { | 2503 if (!previous_text_.empty() && !text.empty()) { |
2436 manager_->NotifyAccessibilityEvent( | 2504 manager_->NotifyAccessibilityEvent( |
2437 ViewHostMsg_AccEvent::OBJECT_SHOW, this); | 2505 ViewHostMsg_AccEvent::OBJECT_SHOW, this); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2514 base::IntToString16(value)); | 2582 base::IntToString16(value)); |
2515 } | 2583 } |
2516 | 2584 |
2517 string16 BrowserAccessibilityWin::Escape(const string16& str) { | 2585 string16 BrowserAccessibilityWin::Escape(const string16& str) { |
2518 return net::EscapeQueryParamValueUTF8(str, false); | 2586 return net::EscapeQueryParamValueUTF8(str, false); |
2519 } | 2587 } |
2520 | 2588 |
2521 const string16& BrowserAccessibilityWin::TextForIAccessibleText() { | 2589 const string16& BrowserAccessibilityWin::TextForIAccessibleText() { |
2522 if (IsEditableText()) { | 2590 if (IsEditableText()) { |
2523 return value_; | 2591 return value_; |
| 2592 } else if (role_ == WebAccessibility::ROLE_STATIC_TEXT) { |
| 2593 return name_; |
2524 } else { | 2594 } else { |
2525 return name_; | 2595 return hypertext_; |
2526 } | 2596 } |
2527 } | 2597 } |
2528 | 2598 |
2529 void BrowserAccessibilityWin::HandleSpecialTextOffset( | 2599 void BrowserAccessibilityWin::HandleSpecialTextOffset( |
2530 const string16& text, LONG* offset) { | 2600 const string16& text, LONG* offset) { |
2531 if (*offset == IA2_TEXT_OFFSET_LENGTH) { | 2601 if (*offset == IA2_TEXT_OFFSET_LENGTH) { |
2532 *offset = static_cast<LONG>(text.size()); | 2602 *offset = static_cast<LONG>(text.size()); |
2533 } else if (*offset == IA2_TEXT_OFFSET_CARET) { | 2603 } else if (*offset == IA2_TEXT_OFFSET_CARET) { |
2534 get_caretOffset(offset); | 2604 get_caretOffset(offset); |
2535 } | 2605 } |
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2966 } | 3036 } |
2967 | 3037 |
2968 // The role should always be set. | 3038 // The role should always be set. |
2969 DCHECK(!role_name_.empty() || ia_role_); | 3039 DCHECK(!role_name_.empty() || ia_role_); |
2970 | 3040 |
2971 // If we didn't explicitly set the IAccessible2 role, make it the same | 3041 // If we didn't explicitly set the IAccessible2 role, make it the same |
2972 // as the MSAA role. | 3042 // as the MSAA role. |
2973 if (!ia2_role_) | 3043 if (!ia2_role_) |
2974 ia2_role_ = ia_role_; | 3044 ia2_role_ = ia_role_; |
2975 } | 3045 } |
OLD | NEW |