OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "chrome/browser/accessibility/browser_accessibility_manager_win.h" | 5 #include "chrome/browser/accessibility/browser_accessibility_manager_win.h" |
6 | 6 |
7 #include "chrome/browser/accessibility/browser_accessibility_win.h" | 7 #include "chrome/browser/accessibility/browser_accessibility_win.h" |
| 8 #include "chrome/browser/renderer_host/render_process_host.h" |
| 9 #include "chrome/browser/renderer_host/render_view_host.h" |
| 10 #include "chrome/common/render_messages.h" |
| 11 #include "chrome/common/render_messages_params.h" |
8 | 12 |
9 using webkit_glue::WebAccessibility; | 13 using webkit_glue::WebAccessibility; |
10 | 14 |
11 // static | 15 // static |
12 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( | 16 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( |
13 gfx::NativeView parent_view, | 17 gfx::NativeWindow parent_window, |
14 const WebAccessibility& src, | 18 const webkit_glue::WebAccessibility& src, |
| 19 BrowserAccessibilityDelegate* delegate) { |
| 20 return new BrowserAccessibilityManagerWin( |
| 21 parent_window, src, delegate, new BrowserAccessibilityWinFactory()); |
| 22 } |
| 23 |
| 24 // Factory method to create an instance of BrowserAccessibility |
| 25 BrowserAccessibilityWin* BrowserAccessibilityWinFactory::Create() { |
| 26 CComObject<BrowserAccessibilityWin>* instance; |
| 27 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance); |
| 28 DCHECK(SUCCEEDED(hr)); |
| 29 return instance->NewReference(); |
| 30 } |
| 31 |
| 32 // static |
| 33 // Start child IDs at -1 and decrement each time, because clients use |
| 34 // child IDs of 1, 2, 3, ... to access the children of an object by |
| 35 // index, so we use negative IDs to clearly distinguish between indices |
| 36 // and unique IDs. |
| 37 LONG BrowserAccessibilityManagerWin::next_child_id_ = -1; |
| 38 |
| 39 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin( |
| 40 HWND parent_window, |
| 41 const webkit_glue::WebAccessibility& src, |
15 BrowserAccessibilityDelegate* delegate, | 42 BrowserAccessibilityDelegate* delegate, |
16 BrowserAccessibilityFactory* factory) { | 43 BrowserAccessibilityWinFactory* factory) |
17 return new BrowserAccessibilityManagerWin( | 44 : BrowserAccessibilityManager(parent_window), |
18 parent_view, | 45 delegate_(delegate), |
19 src, | 46 factory_(factory), |
20 delegate, | 47 focus_(NULL) { |
21 factory); | |
22 } | |
23 | |
24 BrowserAccessibilityManagerWin* | |
25 BrowserAccessibilityManager::toBrowserAccessibilityManagerWin() { | |
26 return static_cast<BrowserAccessibilityManagerWin*>(this); | |
27 } | |
28 | |
29 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin( | |
30 HWND parent_view, | |
31 const WebAccessibility& src, | |
32 BrowserAccessibilityDelegate* delegate, | |
33 BrowserAccessibilityFactory* factory) | |
34 : BrowserAccessibilityManager(parent_view, src, delegate, factory) { | |
35 HRESULT hr = ::CreateStdAccessibleObject( | 48 HRESULT hr = ::CreateStdAccessibleObject( |
36 parent_view, OBJID_WINDOW, IID_IAccessible, | 49 parent_window, OBJID_WINDOW, IID_IAccessible, |
37 reinterpret_cast<void **>(&window_iaccessible_)); | 50 reinterpret_cast<void **>(&window_iaccessible_)); |
38 DCHECK(SUCCEEDED(hr)); | 51 DCHECK(SUCCEEDED(hr)); |
| 52 root_ = CreateAccessibilityTree(NULL, GetNextChildID(), src, 0); |
| 53 if (!focus_) |
| 54 focus_ = root_; |
39 } | 55 } |
40 | 56 |
41 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { | 57 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { |
| 58 // Clients could still hold references to some nodes of the tree, so |
| 59 // calling Inactivate will make sure that as many nodes as possible are |
| 60 // released now, and remaining nodes are marked as inactive so that |
| 61 // calls to any methods on them will return E_FAIL; |
| 62 root_->InactivateTree(); |
| 63 root_->Release(); |
| 64 } |
| 65 |
| 66 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetRoot() { |
| 67 return root_; |
| 68 } |
| 69 |
| 70 void BrowserAccessibilityManagerWin::Remove(LONG child_id) { |
| 71 child_id_map_.erase(child_id); |
| 72 } |
| 73 |
| 74 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromChildID( |
| 75 LONG child_id) { |
| 76 base::hash_map<LONG, BrowserAccessibilityWin*>::iterator iter = |
| 77 child_id_map_.find(child_id); |
| 78 if (iter != child_id_map_.end()) { |
| 79 return iter->second; |
| 80 } else { |
| 81 return NULL; |
| 82 } |
42 } | 83 } |
43 | 84 |
44 IAccessible* BrowserAccessibilityManagerWin::GetParentWindowIAccessible() { | 85 IAccessible* BrowserAccessibilityManagerWin::GetParentWindowIAccessible() { |
45 return window_iaccessible_; | 86 return window_iaccessible_; |
46 } | 87 } |
47 | 88 |
48 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( | 89 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFocus( |
49 ViewHostMsg_AccessibilityNotification_Params::NotificationType n, | 90 BrowserAccessibilityWin* root) { |
50 BrowserAccessibility* node) { | 91 if (focus_ && (!root || focus_->IsDescendantOf(root))) |
51 LONG event_id; | 92 return focus_; |
52 switch (n) { | 93 |
53 case ViewHostMsg_AccessibilityNotification_Params:: | 94 return NULL; |
54 NOTIFICATION_TYPE_CHECK_STATE_CHANGED: | 95 } |
55 event_id = EVENT_OBJECT_STATECHANGE; | 96 |
56 break; | 97 void BrowserAccessibilityManagerWin::SetFocus( |
57 case ViewHostMsg_AccessibilityNotification_Params:: | 98 const BrowserAccessibilityWin& node) { |
58 NOTIFICATION_TYPE_CHILDREN_CHANGED: | 99 if (delegate_) |
59 event_id = EVENT_OBJECT_REORDER; | 100 delegate_->SetAccessibilityFocus(node.renderer_id()); |
60 break; | 101 } |
61 case ViewHostMsg_AccessibilityNotification_Params:: | 102 |
62 NOTIFICATION_TYPE_FOCUS_CHANGED: | 103 void BrowserAccessibilityManagerWin::DoDefaultAction( |
63 event_id = EVENT_OBJECT_FOCUS; | 104 const BrowserAccessibilityWin& node) { |
64 break; | 105 if (delegate_) |
65 case ViewHostMsg_AccessibilityNotification_Params:: | 106 delegate_->AccessibilityDoDefaultAction(node.renderer_id()); |
66 NOTIFICATION_TYPE_LOAD_COMPLETE: | 107 } |
67 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; | 108 |
68 break; | 109 bool BrowserAccessibilityManagerWin::CanModifyTreeInPlace( |
69 case ViewHostMsg_AccessibilityNotification_Params:: | 110 BrowserAccessibilityWin* current_root, |
70 NOTIFICATION_TYPE_VALUE_CHANGED: | 111 const webkit_glue::WebAccessibility& new_root) { |
71 event_id = EVENT_OBJECT_VALUECHANGE; | 112 if (current_root->renderer_id() != new_root.id) |
72 break; | 113 return false; |
73 case ViewHostMsg_AccessibilityNotification_Params:: | 114 if (current_root->GetChildCount() != new_root.children.size()) |
74 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: | 115 return false; |
75 event_id = IA2_EVENT_TEXT_CARET_MOVED; | 116 for (unsigned int i = 0; i < current_root->GetChildCount(); i++) { |
76 } | 117 if (!CanModifyTreeInPlace(current_root->GetChild(i), |
77 | 118 new_root.children[i])) { |
78 NotifyWinEvent(event_id, GetParentView(), OBJID_CLIENT, node->child_id()); | 119 return false; |
79 } | 120 } |
| 121 } |
| 122 return true; |
| 123 } |
| 124 |
| 125 void BrowserAccessibilityManagerWin::ModifyTreeInPlace( |
| 126 BrowserAccessibilityWin* current_root, |
| 127 const webkit_glue::WebAccessibility& new_root) { |
| 128 DCHECK_EQ(current_root->renderer_id(), new_root.id); |
| 129 DCHECK_EQ(current_root->GetChildCount(), new_root.children.size()); |
| 130 for (unsigned int i = 0; i < current_root->GetChildCount(); i++) |
| 131 ModifyTreeInPlace(current_root->GetChild(i), new_root.children[i]); |
| 132 current_root->Initialize( |
| 133 this, |
| 134 current_root->GetParent(), |
| 135 current_root->child_id(), |
| 136 current_root->index_in_parent(), |
| 137 new_root); |
| 138 } |
| 139 |
| 140 |
| 141 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::UpdateTree( |
| 142 const webkit_glue::WebAccessibility& acc_obj) { |
| 143 base::hash_map<int, LONG>::iterator iter = |
| 144 renderer_id_to_child_id_map_.find(acc_obj.id); |
| 145 if (iter == renderer_id_to_child_id_map_.end()) |
| 146 return NULL; |
| 147 |
| 148 LONG child_id = iter->second; |
| 149 BrowserAccessibilityWin* old_browser_acc = GetFromChildID(child_id); |
| 150 if (!old_browser_acc) |
| 151 return NULL; |
| 152 |
| 153 if (CanModifyTreeInPlace(old_browser_acc, acc_obj)) { |
| 154 ModifyTreeInPlace(old_browser_acc, acc_obj); |
| 155 return old_browser_acc; |
| 156 } |
| 157 |
| 158 BrowserAccessibilityWin* new_browser_acc = CreateAccessibilityTree( |
| 159 old_browser_acc->GetParent(), |
| 160 child_id, |
| 161 acc_obj, |
| 162 old_browser_acc->index_in_parent()); |
| 163 |
| 164 if (old_browser_acc->GetParent()) { |
| 165 old_browser_acc->GetParent()->ReplaceChild( |
| 166 old_browser_acc, |
| 167 new_browser_acc); |
| 168 } else { |
| 169 DCHECK_EQ(old_browser_acc, root_); |
| 170 root_ = new_browser_acc; |
| 171 } |
| 172 old_browser_acc->InactivateTree(); |
| 173 old_browser_acc->Release(); |
| 174 child_id_map_[child_id] = new_browser_acc; |
| 175 |
| 176 return new_browser_acc; |
| 177 } |
| 178 |
| 179 void BrowserAccessibilityManagerWin::GotFocus() { |
| 180 // TODO(ctguil): Remove when tree update logic handles focus changes. |
| 181 if (!focus_) |
| 182 return; |
| 183 |
| 184 NotifyWinEvent( |
| 185 EVENT_OBJECT_FOCUS, |
| 186 GetParentWindow(), |
| 187 OBJID_CLIENT, |
| 188 focus_->child_id()); |
| 189 } |
| 190 |
| 191 IAccessible* BrowserAccessibilityManagerWin::GetRootAccessible() { |
| 192 return root_; |
| 193 } |
| 194 |
| 195 void BrowserAccessibilityManagerWin::OnAccessibilityObjectStateChange( |
| 196 const webkit_glue::WebAccessibility& acc_obj) { |
| 197 BrowserAccessibilityWin* new_browser_acc = UpdateTree(acc_obj); |
| 198 if (!new_browser_acc) |
| 199 return; |
| 200 |
| 201 LONG child_id = new_browser_acc->child_id(); |
| 202 NotifyWinEvent( |
| 203 EVENT_OBJECT_STATECHANGE, GetParentWindow(), OBJID_CLIENT, child_id); |
| 204 } |
| 205 |
| 206 void BrowserAccessibilityManagerWin::OnAccessibilityObjectChildrenChange( |
| 207 const webkit_glue::WebAccessibility& acc_obj) { |
| 208 BrowserAccessibilityWin* new_browser_acc = UpdateTree(acc_obj); |
| 209 if (!new_browser_acc) |
| 210 return; |
| 211 |
| 212 LONG child_id; |
| 213 if (root_ != new_browser_acc) { |
| 214 child_id = new_browser_acc->GetParent()->child_id(); |
| 215 } else { |
| 216 child_id = CHILDID_SELF; |
| 217 } |
| 218 |
| 219 NotifyWinEvent( |
| 220 EVENT_OBJECT_REORDER, GetParentWindow(), OBJID_CLIENT, child_id); |
| 221 } |
| 222 |
| 223 void BrowserAccessibilityManagerWin::OnAccessibilityObjectFocusChange( |
| 224 const webkit_glue::WebAccessibility& acc_obj) { |
| 225 BrowserAccessibilityWin* new_browser_acc = UpdateTree(acc_obj); |
| 226 if (!new_browser_acc) |
| 227 return; |
| 228 |
| 229 focus_ = new_browser_acc; |
| 230 LONG child_id = new_browser_acc->child_id(); |
| 231 if (delegate_ && delegate_->HasFocus()) |
| 232 GotFocus(); |
| 233 } |
| 234 |
| 235 void BrowserAccessibilityManagerWin::OnAccessibilityObjectLoadComplete( |
| 236 const webkit_glue::WebAccessibility& acc_obj) { |
| 237 root_->InactivateTree(); |
| 238 root_->Release(); |
| 239 focus_ = NULL; |
| 240 |
| 241 root_ = CreateAccessibilityTree(NULL, GetNextChildID(), acc_obj, 0); |
| 242 if (!focus_) |
| 243 focus_ = root_; |
| 244 |
| 245 NotifyWinEvent( |
| 246 IA2_EVENT_DOCUMENT_LOAD_COMPLETE, |
| 247 GetParentWindow(), |
| 248 OBJID_CLIENT, |
| 249 root_->child_id()); |
| 250 if (delegate_ && delegate_->HasFocus()) |
| 251 GotFocus(); |
| 252 } |
| 253 |
| 254 void BrowserAccessibilityManagerWin::OnAccessibilityObjectValueChange( |
| 255 const webkit_glue::WebAccessibility& acc_obj) { |
| 256 BrowserAccessibilityWin* new_browser_acc = UpdateTree(acc_obj); |
| 257 if (!new_browser_acc) |
| 258 return; |
| 259 |
| 260 LONG child_id = new_browser_acc->child_id(); |
| 261 NotifyWinEvent( |
| 262 EVENT_OBJECT_VALUECHANGE, GetParentWindow(), OBJID_CLIENT, child_id); |
| 263 } |
| 264 |
| 265 void BrowserAccessibilityManagerWin::OnAccessibilityObjectTextChange( |
| 266 const webkit_glue::WebAccessibility& acc_obj) { |
| 267 BrowserAccessibilityWin* new_browser_acc = UpdateTree(acc_obj); |
| 268 if (!new_browser_acc) |
| 269 return; |
| 270 |
| 271 LONG child_id = new_browser_acc->child_id(); |
| 272 NotifyWinEvent( |
| 273 IA2_EVENT_TEXT_CARET_MOVED, GetParentWindow(), OBJID_CLIENT, child_id); |
| 274 } |
| 275 |
| 276 LONG BrowserAccessibilityManagerWin::GetNextChildID() { |
| 277 // Get the next child ID, and wrap around when we get near the end |
| 278 // of a 32-bit integer range. It's okay to wrap around; we just want |
| 279 // to avoid it as long as possible because clients may cache the ID of |
| 280 // an object for a while to determine if they've seen it before. |
| 281 next_child_id_--; |
| 282 if (next_child_id_ == -2000000000) |
| 283 next_child_id_ = -1; |
| 284 |
| 285 return next_child_id_; |
| 286 } |
| 287 |
| 288 BrowserAccessibilityWin* |
| 289 BrowserAccessibilityManagerWin::CreateAccessibilityTree( |
| 290 BrowserAccessibilityWin* parent, |
| 291 int child_id, |
| 292 const webkit_glue::WebAccessibility& src, |
| 293 int index_in_parent) { |
| 294 BrowserAccessibilityWin* instance = factory_->Create(); |
| 295 |
| 296 instance->Initialize(this, parent, child_id, index_in_parent, src); |
| 297 child_id_map_[child_id] = instance; |
| 298 renderer_id_to_child_id_map_[src.id] = child_id; |
| 299 if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1) |
| 300 focus_ = instance; |
| 301 for (int i = 0; i < static_cast<int>(src.children.size()); ++i) { |
| 302 BrowserAccessibilityWin* child = CreateAccessibilityTree( |
| 303 instance, GetNextChildID(), src.children[i], i); |
| 304 instance->AddChild(child); |
| 305 } |
| 306 |
| 307 return instance; |
| 308 } |
OLD | NEW |