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