Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / browser_accessibility_manager_win.cc
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
3 // found in the LICENSE file.
4
5 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
6
7 #include "base/command_line.h"
8 #include "base/win/scoped_comptr.h"
9 #include "base/win/windows_version.h"
10 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
11 #include "content/browser/accessibility/browser_accessibility_win.h"
12 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
13 #include "content/common/accessibility_messages.h"
14 #include "ui/base/win/atl_module.h"
15
16 namespace content {
17
18 // static
19 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
20     const ui::AXTreeUpdate& initial_tree,
21     BrowserAccessibilityDelegate* delegate,
22     BrowserAccessibilityFactory* factory) {
23   return new BrowserAccessibilityManagerWin(initial_tree, delegate, factory);
24 }
25
26 BrowserAccessibilityManagerWin*
27 BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
28   return static_cast<BrowserAccessibilityManagerWin*>(this);
29 }
30
31 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
32     const ui::AXTreeUpdate& initial_tree,
33     BrowserAccessibilityDelegate* delegate,
34     BrowserAccessibilityFactory* factory)
35     : BrowserAccessibilityManager(initial_tree, delegate, factory),
36       tracked_scroll_object_(NULL),
37       focus_event_on_root_needed_(false) {
38   ui::win::CreateATLModuleIfNeeded();
39 }
40
41 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
42   if (tracked_scroll_object_) {
43     tracked_scroll_object_->Release();
44     tracked_scroll_object_ = NULL;
45   }
46 }
47
48 // static
49 ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
50   ui::AXNodeData empty_document;
51   empty_document.id = 0;
52   empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
53   empty_document.state =
54       (1 << ui::AX_STATE_ENABLED) |
55       (1 << ui::AX_STATE_READ_ONLY) |
56       (1 << ui::AX_STATE_BUSY);
57
58   ui::AXTreeUpdate update;
59   update.nodes.push_back(empty_document);
60   return update;
61 }
62
63 HWND BrowserAccessibilityManagerWin::GetParentHWND() {
64   if (!delegate_)
65     return NULL;
66   return delegate_->AccessibilityGetAcceleratedWidget();
67 }
68
69 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() {
70   if (!delegate_)
71     return NULL;
72   return delegate_->AccessibilityGetNativeViewAccessible();
73 }
74
75 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
76                                                              LONG child_id) {
77   if (!delegate_)
78     return;
79
80   HWND hwnd = delegate_->AccessibilityGetAcceleratedWidget();
81   if (!hwnd)
82     return;
83
84   ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id);
85 }
86
87
88 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) {
89   BrowserAccessibilityManager::OnNodeCreated(node);
90   BrowserAccessibility* obj = GetFromAXNode(node);
91   LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
92   unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
93 }
94
95 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) {
96   BrowserAccessibilityManager::OnNodeWillBeDeleted(node);
97   BrowserAccessibility* obj = GetFromAXNode(node);
98   if (!obj)
99     return;
100   unique_id_to_ax_id_map_.erase(
101       obj->ToBrowserAccessibilityWin()->unique_id_win());
102   if (obj == tracked_scroll_object_) {
103     tracked_scroll_object_->Release();
104     tracked_scroll_object_ = NULL;
105   }
106 }
107
108 void BrowserAccessibilityManagerWin::OnWindowFocused() {
109   // This is called either when this web frame gets focused, or when
110   // the root of the accessibility tree changes. In both cases, we need
111   // to fire a focus event on the root and then on the focused element
112   // within the page, if different.
113
114   // Set this flag so that we'll keep trying to fire these focus events
115   // if they're not successful this time.
116   focus_event_on_root_needed_ = true;
117
118   if (!delegate_ || !delegate_->AccessibilityViewHasFocus())
119     return;
120
121   // Try to fire a focus event on the root first and then the focused node.
122   // This will clear focus_event_on_root_needed_ if successful.
123   if (focus_ != tree_->GetRoot())
124     NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
125   BrowserAccessibilityManager::OnWindowFocused();
126 }
127
128 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
129     ui::AXEvent event_type,
130     BrowserAccessibility* node) {
131   if (!delegate_ || !delegate_->AccessibilityGetAcceleratedWidget())
132     return;
133
134   // Inline text boxes are an internal implementation detail, we don't
135   // expose them to Windows.
136   if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
137     return;
138
139   // Don't fire focus, blur, or load complete notifications if the
140   // window isn't focused, because that can confuse screen readers into
141   // entering their "browse" mode.
142   if ((event_type == ui::AX_EVENT_FOCUS ||
143        event_type == ui::AX_EVENT_BLUR ||
144        event_type == ui::AX_EVENT_LOAD_COMPLETE) &&
145       (!delegate_ || !delegate_->AccessibilityViewHasFocus())) {
146     return;
147   }
148
149   // NVDA gets confused if we focus the main document element when it hasn't
150   // finished loading and it has no children at all, so suppress that event.
151   if (event_type == ui::AX_EVENT_FOCUS &&
152       node == GetRoot() &&
153       node->PlatformChildCount() == 0 &&
154       !node->HasState(ui::AX_STATE_BUSY) &&
155       !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) {
156     return;
157   }
158
159   // If a focus event is needed on the root, fire that first before
160   // this event.
161   if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot())
162     focus_event_on_root_needed_ = false;
163   else if (focus_event_on_root_needed_)
164     OnWindowFocused();
165
166   LONG event_id = EVENT_MIN;
167   switch (event_type) {
168     case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
169       event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
170       break;
171     case ui::AX_EVENT_ALERT:
172       event_id = EVENT_SYSTEM_ALERT;
173       break;
174     case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
175       event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
176       break;
177     case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
178       event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
179       break;
180     case ui::AX_EVENT_BLUR:
181       // Equivalent to focus on the root.
182       event_id = EVENT_OBJECT_FOCUS;
183       node = GetRoot();
184       break;
185     case ui::AX_EVENT_CHECKED_STATE_CHANGED:
186       event_id = EVENT_OBJECT_STATECHANGE;
187       break;
188     case ui::AX_EVENT_CHILDREN_CHANGED:
189       event_id = EVENT_OBJECT_REORDER;
190       break;
191     case ui::AX_EVENT_FOCUS:
192       event_id = EVENT_OBJECT_FOCUS;
193       break;
194     case ui::AX_EVENT_INVALID_STATUS_CHANGED:
195       event_id = EVENT_OBJECT_STATECHANGE;
196       break;
197     case ui::AX_EVENT_LIVE_REGION_CHANGED:
198       if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
199         return;
200       event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
201       break;
202     case ui::AX_EVENT_LOAD_COMPLETE:
203       event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
204       break;
205     case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
206       event_id = EVENT_OBJECT_FOCUS;
207       break;
208     case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
209       event_id = EVENT_OBJECT_VALUECHANGE;
210       break;
211     case ui::AX_EVENT_HIDE:
212       event_id = EVENT_OBJECT_HIDE;
213       break;
214     case ui::AX_EVENT_SHOW:
215       event_id = EVENT_OBJECT_SHOW;
216       break;
217     case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
218       event_id = EVENT_SYSTEM_SCROLLINGEND;
219       break;
220     case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
221       event_id = EVENT_SYSTEM_SCROLLINGSTART;
222       break;
223     case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
224       event_id = EVENT_OBJECT_SELECTIONWITHIN;
225       break;
226     case ui::AX_EVENT_TEXT_CHANGED:
227       event_id = EVENT_OBJECT_NAMECHANGE;
228       break;
229     case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
230       event_id = IA2_EVENT_TEXT_CARET_MOVED;
231       break;
232     case ui::AX_EVENT_VALUE_CHANGED:
233       event_id = EVENT_OBJECT_VALUECHANGE;
234       break;
235     default:
236       // Not all WebKit accessibility events result in a Windows
237       // accessibility notification.
238       break;
239   }
240
241   if (event_id != EVENT_MIN) {
242     // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
243     // the AT client will then call get_accChild on the HWND's accessibility
244     // object and pass it that same id, which we can use to retrieve the
245     // IAccessible for this node.
246     LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
247     MaybeCallNotifyWinEvent(event_id, child_id);
248   }
249
250   // If this is a layout complete notification (sent when a container scrolls)
251   // and there is a descendant tracked object, send a notification on it.
252   // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
253   if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
254       tracked_scroll_object_ &&
255       tracked_scroll_object_->IsDescendantOf(node)) {
256     MaybeCallNotifyWinEvent(
257         IA2_EVENT_VISIBLE_DATA_CHANGED,
258         tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
259     tracked_scroll_object_->Release();
260     tracked_scroll_object_ = NULL;
261   }
262 }
263
264 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) {
265   // In order to make screen readers aware of the new accessibility root,
266   // we need to fire a focus event on it.
267   OnWindowFocused();
268 }
269
270 void BrowserAccessibilityManagerWin::TrackScrollingObject(
271     BrowserAccessibilityWin* node) {
272   if (tracked_scroll_object_)
273     tracked_scroll_object_->Release();
274   tracked_scroll_object_ = node;
275   tracked_scroll_object_->AddRef();
276 }
277
278 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
279     LONG unique_id_win) {
280   base::hash_map<LONG, int32>::iterator iter =
281       unique_id_to_ax_id_map_.find(unique_id_win);
282   if (iter != unique_id_to_ax_id_map_.end()) {
283     BrowserAccessibility* result = GetFromID(iter->second);
284     if (result)
285       return result->ToBrowserAccessibilityWin();
286   }
287   return NULL;
288 }
289
290 }  // namespace content