IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_win.cc
blob39922be861480ce844f0c069032bc1cc80f1033b
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 <atlbase.h>
8 #include <atlapp.h>
9 #include <atlcom.h>
10 #include <atlcrack.h>
11 #include <oleacc.h>
13 #include "base/command_line.h"
14 #include "base/win/scoped_comptr.h"
15 #include "base/win/windows_version.h"
16 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
17 #include "content/browser/accessibility/browser_accessibility_win.h"
18 #include "content/common/accessibility_messages.h"
20 namespace content {
22 // Some screen readers expect every tab / every unique web content container
23 // to be in its own HWND, like it was before Aura, but with Aura there's just
24 // one main HWND for a frame, or even for the whole desktop. So, we need a
25 // fake HWND as the root of the accessibility tree for each tab.
26 // We should get rid of this code when the latest two versions of all
27 // supported screen readers no longer make this assumption.
29 // This class implements a child HWND with zero size, that delegates its
30 // accessibility implementation to the root of the BrowserAccessibilityManager
31 // tree. This HWND is hooked up as the parent of the root object in the
32 // BrowserAccessibilityManager tree, so when any accessibility client
33 // calls ::WindowFromAccessibleObject, they get this HWND instead of the
34 // DesktopWindowTreeHostWin.
35 class AccessibleHWND
36 : public ATL::CWindowImpl<AccessibleHWND,
37 ATL::CWindow,
38 ATL::CWinTraits<WS_CHILD> > {
39 public:
40 // Unfortunately, some screen readers look for this exact window class
41 // to enable certain features. It'd be great to remove this.
42 DECLARE_WND_CLASS_EX(L"Chrome_RenderWidgetHostHWND", CS_DBLCLKS, 0);
44 BEGIN_MSG_MAP_EX(AccessibleHWND)
45 MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
46 END_MSG_MAP()
48 AccessibleHWND(HWND parent, BrowserAccessibilityManagerWin* manager)
49 : manager_(manager) {
50 Create(parent);
51 ShowWindow(true);
52 MoveWindow(0, 0, 0, 0);
54 HRESULT hr = ::CreateStdAccessibleObject(
55 hwnd(), OBJID_WINDOW, IID_IAccessible,
56 reinterpret_cast<void **>(window_accessible_.Receive()));
57 DCHECK(SUCCEEDED(hr));
60 HWND hwnd() {
61 DCHECK(::IsWindow(m_hWnd));
62 return m_hWnd;
65 IAccessible* window_accessible() { return window_accessible_; }
67 void OnManagerDeleted() {
68 manager_ = NULL;
71 protected:
72 virtual void OnFinalMessage(HWND hwnd) OVERRIDE {
73 if (manager_)
74 manager_->OnAccessibleHwndDeleted();
75 delete this;
78 private:
79 LRESULT OnGetObject(UINT message,
80 WPARAM w_param,
81 LPARAM l_param) {
82 if (OBJID_CLIENT != l_param || !manager_)
83 return static_cast<LRESULT>(0L);
85 base::win::ScopedComPtr<IAccessible> root(
86 manager_->GetRoot()->ToBrowserAccessibilityWin());
87 return LresultFromObject(IID_IAccessible, w_param,
88 static_cast<IAccessible*>(root.Detach()));
91 BrowserAccessibilityManagerWin* manager_;
92 base::win::ScopedComPtr<IAccessible> window_accessible_;
94 DISALLOW_COPY_AND_ASSIGN(AccessibleHWND);
98 // static
99 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
100 const ui::AXNodeData& src,
101 BrowserAccessibilityDelegate* delegate,
102 BrowserAccessibilityFactory* factory) {
103 return new BrowserAccessibilityManagerWin(
104 GetDesktopWindow(), NULL, src, delegate, factory);
107 BrowserAccessibilityManagerWin*
108 BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
109 return static_cast<BrowserAccessibilityManagerWin*>(this);
112 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
113 HWND parent_hwnd,
114 IAccessible* parent_iaccessible,
115 const ui::AXNodeData& src,
116 BrowserAccessibilityDelegate* delegate,
117 BrowserAccessibilityFactory* factory)
118 : BrowserAccessibilityManager(src, delegate, factory),
119 parent_hwnd_(parent_hwnd),
120 parent_iaccessible_(parent_iaccessible),
121 tracked_scroll_object_(NULL),
122 accessible_hwnd_(NULL) {
125 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
126 if (tracked_scroll_object_) {
127 tracked_scroll_object_->Release();
128 tracked_scroll_object_ = NULL;
130 if (accessible_hwnd_)
131 accessible_hwnd_->OnManagerDeleted();
134 // static
135 ui::AXNodeData BrowserAccessibilityManagerWin::GetEmptyDocument() {
136 ui::AXNodeData empty_document;
137 empty_document.id = 0;
138 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
139 empty_document.state =
140 (1 << blink::WebAXStateEnabled) |
141 (1 << ui::AX_STATE_READONLY) |
142 (1 << ui::AX_STATE_BUSY);
143 return empty_document;
146 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
147 LONG child_id) {
148 // Don't fire events if this view isn't hooked up to its parent.
149 if (!parent_iaccessible())
150 return;
152 #if defined(USE_AURA)
153 // If this is an Aura build on Win 7 and complete accessibility is
154 // enabled, create a fake child HWND to use as the root of the
155 // accessibility tree. See comments above AccessibleHWND for details.
156 if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser() &&
157 !accessible_hwnd_) {
158 accessible_hwnd_ = new AccessibleHWND(parent_hwnd_, this);
159 parent_hwnd_ = accessible_hwnd_->hwnd();
160 parent_iaccessible_ = accessible_hwnd_->window_accessible();
162 #endif
164 ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id);
167 void BrowserAccessibilityManagerWin::AddNodeToMap(BrowserAccessibility* node) {
168 BrowserAccessibilityManager::AddNodeToMap(node);
169 LONG unique_id_win = node->ToBrowserAccessibilityWin()->unique_id_win();
170 unique_id_to_renderer_id_map_[unique_id_win] = node->renderer_id();
173 void BrowserAccessibilityManagerWin::RemoveNode(BrowserAccessibility* node) {
174 unique_id_to_renderer_id_map_.erase(
175 node->ToBrowserAccessibilityWin()->unique_id_win());
176 BrowserAccessibilityManager::RemoveNode(node);
177 if (node == tracked_scroll_object_) {
178 tracked_scroll_object_->Release();
179 tracked_scroll_object_ = NULL;
183 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
184 ui::AXEvent event_type,
185 BrowserAccessibility* node) {
186 if (node->role() == ui::AX_ROLE_INLINE_TEXT_BOX)
187 return;
189 LONG event_id = EVENT_MIN;
190 switch (event_type) {
191 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
192 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
193 break;
194 case ui::AX_EVENT_ALERT:
195 event_id = EVENT_SYSTEM_ALERT;
196 break;
197 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
198 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
199 break;
200 case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
201 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
202 break;
203 case ui::AX_EVENT_BLUR:
204 // Equivalent to focus on the root.
205 event_id = EVENT_OBJECT_FOCUS;
206 node = GetRoot();
207 break;
208 case ui::AX_EVENT_CHECKED_STATE_CHANGED:
209 event_id = EVENT_OBJECT_STATECHANGE;
210 break;
211 case ui::AX_EVENT_CHILDREN_CHANGED:
212 event_id = EVENT_OBJECT_REORDER;
213 break;
214 case ui::AX_EVENT_FOCUS:
215 event_id = EVENT_OBJECT_FOCUS;
216 break;
217 case ui::AX_EVENT_INVALID_STATUS_CHANGED:
218 event_id = EVENT_OBJECT_STATECHANGE;
219 break;
220 case ui::AX_EVENT_LIVE_REGION_CHANGED:
221 // TODO: try not firing a native notification at all, since
222 // on Windows, each individual item in a live region that changes
223 // already gets its own notification.
224 event_id = EVENT_OBJECT_REORDER;
225 break;
226 case ui::AX_EVENT_LOAD_COMPLETE:
227 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
228 break;
229 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
230 event_id = EVENT_OBJECT_FOCUS;
231 break;
232 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
233 event_id = EVENT_OBJECT_VALUECHANGE;
234 break;
235 case ui::AX_EVENT_HIDE:
236 event_id = EVENT_OBJECT_HIDE;
237 break;
238 case ui::AX_EVENT_SHOW:
239 event_id = EVENT_OBJECT_SHOW;
240 break;
241 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
242 event_id = EVENT_SYSTEM_SCROLLINGSTART;
243 break;
244 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
245 event_id = EVENT_OBJECT_SELECTIONWITHIN;
246 break;
247 case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
248 event_id = IA2_EVENT_TEXT_CARET_MOVED;
249 break;
250 case ui::AX_EVENT_TEXT_CHANGED:
251 event_id = EVENT_OBJECT_NAMECHANGE;
252 break;
253 case ui::AX_EVENT_TEXT_INSERTED:
254 event_id = IA2_EVENT_TEXT_INSERTED;
255 break;
256 case ui::AX_EVENT_TEXT_REMOVED:
257 event_id = IA2_EVENT_TEXT_REMOVED;
258 break;
259 case ui::AX_EVENT_VALUE_CHANGED:
260 event_id = EVENT_OBJECT_VALUECHANGE;
261 break;
262 default:
263 // Not all WebKit accessibility events result in a Windows
264 // accessibility notification.
265 break;
268 if (event_id != EVENT_MIN) {
269 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
270 // the AT client will then call get_accChild on the HWND's accessibility
271 // object and pass it that same id, which we can use to retrieve the
272 // IAccessible for this node.
273 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
274 MaybeCallNotifyWinEvent(event_id, child_id);
277 // If this is a layout complete notification (sent when a container scrolls)
278 // and there is a descendant tracked object, send a notification on it.
279 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
280 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
281 tracked_scroll_object_ &&
282 tracked_scroll_object_->IsDescendantOf(node)) {
283 MaybeCallNotifyWinEvent(
284 IA2_EVENT_VISIBLE_DATA_CHANGED,
285 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
286 tracked_scroll_object_->Release();
287 tracked_scroll_object_ = NULL;
291 void BrowserAccessibilityManagerWin::TrackScrollingObject(
292 BrowserAccessibilityWin* node) {
293 if (tracked_scroll_object_)
294 tracked_scroll_object_->Release();
295 tracked_scroll_object_ = node;
296 tracked_scroll_object_->AddRef();
299 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
300 LONG unique_id_win) {
301 base::hash_map<LONG, int32>::iterator iter =
302 unique_id_to_renderer_id_map_.find(unique_id_win);
303 if (iter != unique_id_to_renderer_id_map_.end()) {
304 BrowserAccessibility* result = GetFromRendererID(iter->second);
305 if (result)
306 return result->ToBrowserAccessibilityWin();
308 return NULL;
311 void BrowserAccessibilityManagerWin::OnAccessibleHwndDeleted() {
312 // If the AccessibleHWND is deleted, |parent_hwnd_| and
313 // |parent_iaccessible_| are no longer valid either, since they were
314 // derived from AccessibleHWND. We don't have to restore them to
315 // previous values, though, because this should only happen
316 // during the destruct sequence for this window.
317 accessible_hwnd_ = NULL;
318 parent_hwnd_ = NULL;
319 parent_iaccessible_ = NULL;
322 } // namespace content