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.
5 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
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"
18 BrowserAccessibilityManager
* BrowserAccessibilityManager::Create(
19 const ui::AXTreeUpdate
& initial_tree
,
20 BrowserAccessibilityDelegate
* delegate
,
21 BrowserAccessibilityFactory
* factory
) {
22 return new BrowserAccessibilityManagerWin(
23 content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()).get(),
24 NULL
, initial_tree
, delegate
, factory
);
27 BrowserAccessibilityManagerWin
*
28 BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
29 return static_cast<BrowserAccessibilityManagerWin
*>(this);
32 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
33 LegacyRenderWidgetHostHWND
* accessible_hwnd
,
34 IAccessible
* parent_iaccessible
,
35 const ui::AXTreeUpdate
& initial_tree
,
36 BrowserAccessibilityDelegate
* delegate
,
37 BrowserAccessibilityFactory
* factory
)
38 : BrowserAccessibilityManager(initial_tree
, delegate
, factory
),
39 parent_hwnd_(accessible_hwnd
->GetParent()),
40 parent_iaccessible_(parent_iaccessible
),
41 tracked_scroll_object_(NULL
),
42 accessible_hwnd_(accessible_hwnd
) {
43 accessible_hwnd_
->set_browser_accessibility_manager(this);
46 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
47 if (tracked_scroll_object_
) {
48 tracked_scroll_object_
->Release();
49 tracked_scroll_object_
= NULL
;
52 accessible_hwnd_
->OnManagerDeleted();
56 ui::AXTreeUpdate
BrowserAccessibilityManagerWin::GetEmptyDocument() {
57 ui::AXNodeData empty_document
;
58 empty_document
.id
= 0;
59 empty_document
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
60 empty_document
.state
=
61 (1 << ui::AX_STATE_ENABLED
) |
62 (1 << ui::AX_STATE_READ_ONLY
) |
63 (1 << ui::AX_STATE_BUSY
);
65 ui::AXTreeUpdate update
;
66 update
.nodes
.push_back(empty_document
);
70 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event
,
72 // Don't fire events if this view isn't hooked up to its parent.
73 if (!parent_iaccessible())
76 // If on Win 7 and complete accessibility is enabled, use the fake child HWND
77 // to use as the root of the accessibility tree. See comments above
78 // LegacyRenderWidgetHostHWND for details.
79 if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) {
80 DCHECK(accessible_hwnd_
);
81 parent_hwnd_
= accessible_hwnd_
->hwnd();
82 parent_iaccessible_
= accessible_hwnd_
->window_accessible();
84 ::NotifyWinEvent(event
, parent_hwnd(), OBJID_CLIENT
, child_id
);
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();
95 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode
* node
) {
96 BrowserAccessibilityManager::OnNodeWillBeDeleted(node
);
97 BrowserAccessibility
* obj
= GetFromAXNode(node
);
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
;
108 void BrowserAccessibilityManagerWin::OnWindowFocused() {
109 // Fire a focus event on the root first and then the focused node.
110 if (focus_
!= tree_
->GetRoot())
111 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetRoot());
112 BrowserAccessibilityManager::OnWindowFocused();
115 void BrowserAccessibilityManagerWin::OnWindowBlurred() {
116 // Fire a blur event on the focused node first and then the root.
117 BrowserAccessibilityManager::OnWindowBlurred();
118 if (focus_
!= tree_
->GetRoot())
119 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR
, GetRoot());
122 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
123 ui::AXEvent event_type
,
124 BrowserAccessibility
* node
) {
125 if (node
->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX
)
128 LONG event_id
= EVENT_MIN
;
129 switch (event_type
) {
130 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED
:
131 event_id
= IA2_EVENT_ACTIVE_DESCENDANT_CHANGED
;
133 case ui::AX_EVENT_ALERT
:
134 event_id
= EVENT_SYSTEM_ALERT
;
136 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED
:
137 event_id
= IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED
;
139 case ui::AX_EVENT_AUTOCORRECTION_OCCURED
:
140 event_id
= IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED
;
142 case ui::AX_EVENT_BLUR
:
143 // Equivalent to focus on the root.
144 event_id
= EVENT_OBJECT_FOCUS
;
147 case ui::AX_EVENT_CHECKED_STATE_CHANGED
:
148 event_id
= EVENT_OBJECT_STATECHANGE
;
150 case ui::AX_EVENT_CHILDREN_CHANGED
:
151 event_id
= EVENT_OBJECT_REORDER
;
153 case ui::AX_EVENT_FOCUS
:
154 event_id
= EVENT_OBJECT_FOCUS
;
156 case ui::AX_EVENT_INVALID_STATUS_CHANGED
:
157 event_id
= EVENT_OBJECT_STATECHANGE
;
159 case ui::AX_EVENT_LIVE_REGION_CHANGED
:
160 if (node
->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY
))
162 event_id
= EVENT_OBJECT_LIVEREGIONCHANGED
;
164 case ui::AX_EVENT_LOAD_COMPLETE
:
165 event_id
= IA2_EVENT_DOCUMENT_LOAD_COMPLETE
;
167 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED
:
168 event_id
= EVENT_OBJECT_FOCUS
;
170 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED
:
171 event_id
= EVENT_OBJECT_VALUECHANGE
;
173 case ui::AX_EVENT_HIDE
:
174 event_id
= EVENT_OBJECT_HIDE
;
176 case ui::AX_EVENT_SHOW
:
177 event_id
= EVENT_OBJECT_SHOW
;
179 case ui::AX_EVENT_SCROLL_POSITION_CHANGED
:
180 event_id
= EVENT_SYSTEM_SCROLLINGEND
;
182 case ui::AX_EVENT_SCROLLED_TO_ANCHOR
:
183 event_id
= EVENT_SYSTEM_SCROLLINGSTART
;
185 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED
:
186 event_id
= EVENT_OBJECT_SELECTIONWITHIN
;
188 case ui::AX_EVENT_SELECTED_TEXT_CHANGED
:
189 event_id
= IA2_EVENT_TEXT_CARET_MOVED
;
191 case ui::AX_EVENT_TEXT_CHANGED
:
192 event_id
= EVENT_OBJECT_NAMECHANGE
;
194 case ui::AX_EVENT_TEXT_INSERTED
:
195 event_id
= IA2_EVENT_TEXT_INSERTED
;
197 case ui::AX_EVENT_TEXT_REMOVED
:
198 event_id
= IA2_EVENT_TEXT_REMOVED
;
200 case ui::AX_EVENT_VALUE_CHANGED
:
201 event_id
= EVENT_OBJECT_VALUECHANGE
;
204 // Not all WebKit accessibility events result in a Windows
205 // accessibility notification.
209 if (event_id
!= EVENT_MIN
) {
210 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
211 // the AT client will then call get_accChild on the HWND's accessibility
212 // object and pass it that same id, which we can use to retrieve the
213 // IAccessible for this node.
214 LONG child_id
= node
->ToBrowserAccessibilityWin()->unique_id_win();
215 MaybeCallNotifyWinEvent(event_id
, child_id
);
218 // If this is a layout complete notification (sent when a container scrolls)
219 // and there is a descendant tracked object, send a notification on it.
220 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
221 if (event_type
== ui::AX_EVENT_LAYOUT_COMPLETE
&&
222 tracked_scroll_object_
&&
223 tracked_scroll_object_
->IsDescendantOf(node
)) {
224 MaybeCallNotifyWinEvent(
225 IA2_EVENT_VISIBLE_DATA_CHANGED
,
226 tracked_scroll_object_
->ToBrowserAccessibilityWin()->unique_id_win());
227 tracked_scroll_object_
->Release();
228 tracked_scroll_object_
= NULL
;
232 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode
* new_root
) {
233 if (delegate_
&& delegate_
->AccessibilityViewHasFocus())
234 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetRoot());
237 void BrowserAccessibilityManagerWin::TrackScrollingObject(
238 BrowserAccessibilityWin
* node
) {
239 if (tracked_scroll_object_
)
240 tracked_scroll_object_
->Release();
241 tracked_scroll_object_
= node
;
242 tracked_scroll_object_
->AddRef();
245 BrowserAccessibilityWin
* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
246 LONG unique_id_win
) {
247 base::hash_map
<LONG
, int32
>::iterator iter
=
248 unique_id_to_ax_id_map_
.find(unique_id_win
);
249 if (iter
!= unique_id_to_ax_id_map_
.end()) {
250 BrowserAccessibility
* result
= GetFromID(iter
->second
);
252 return result
->ToBrowserAccessibilityWin();
257 void BrowserAccessibilityManagerWin::OnAccessibleHwndDeleted() {
258 // If the AccessibleHWND is deleted, |parent_hwnd_| and
259 // |parent_iaccessible_| are no longer valid either, since they were
260 // derived from AccessibleHWND. We don't have to restore them to
261 // previous values, though, because this should only happen
262 // during the destruct sequence for this window.
263 accessible_hwnd_
= NULL
;
265 parent_iaccessible_
= NULL
;
268 } // namespace content