• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "chrome/browser/accessibility/browser_accessibility_manager.h"
6 
7 #include "base/logging.h"
8 #include "chrome/browser/accessibility/browser_accessibility.h"
9 #include "content/common/view_messages.h"
10 
11 using webkit_glue::WebAccessibility;
12 
Create()13 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
14   return BrowserAccessibility::Create();
15 }
16 
17 // Start child IDs at -1 and decrement each time, because clients use
18 // child IDs of 1, 2, 3, ... to access the children of an object by
19 // index, so we use negative IDs to clearly distinguish between indices
20 // and unique IDs.
21 // static
22 int32 BrowserAccessibilityManager::next_child_id_ = -1;
23 
24 #if defined(OS_LINUX)
25 // There's no OS-specific implementation of BrowserAccessibilityManager
26 // on Linux, so just instantiate the base class.
27 // static
Create(gfx::NativeView parent_view,const WebAccessibility & src,BrowserAccessibilityDelegate * delegate,BrowserAccessibilityFactory * factory)28 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
29     gfx::NativeView parent_view,
30     const WebAccessibility& src,
31     BrowserAccessibilityDelegate* delegate,
32     BrowserAccessibilityFactory* factory) {
33   return new BrowserAccessibilityManager(
34       parent_view, src, delegate, factory);
35 }
36 #endif
37 
BrowserAccessibilityManager(gfx::NativeView parent_view,const WebAccessibility & src,BrowserAccessibilityDelegate * delegate,BrowserAccessibilityFactory * factory)38 BrowserAccessibilityManager::BrowserAccessibilityManager(
39     gfx::NativeView parent_view,
40     const WebAccessibility& src,
41     BrowserAccessibilityDelegate* delegate,
42     BrowserAccessibilityFactory* factory)
43     : parent_view_(parent_view),
44       delegate_(delegate),
45       factory_(factory),
46       focus_(NULL) {
47   root_ = CreateAccessibilityTree(NULL, src, 0);
48   if (!focus_)
49     SetFocus(root_, false);
50 }
51 
52 // static
GetNextChildID()53 int32 BrowserAccessibilityManager::GetNextChildID() {
54   // Get the next child ID, and wrap around when we get near the end
55   // of a 32-bit integer range. It's okay to wrap around; we just want
56   // to avoid it as long as possible because clients may cache the ID of
57   // an object for a while to determine if they've seen it before.
58   next_child_id_--;
59   if (next_child_id_ == -2000000000)
60     next_child_id_ = -1;
61 
62   return next_child_id_;
63 }
64 
~BrowserAccessibilityManager()65 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
66   // Clients could still hold references to some nodes of the tree, so
67   // calling InternalReleaseReference will make sure that as many nodes
68   // as possible are released now, and remaining nodes are marked as
69   // inactive so that calls to any methods on them will fail gracefully.
70   focus_->InternalReleaseReference(false);
71   root_->InternalReleaseReference(true);
72 }
73 
GetRoot()74 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
75   return root_;
76 }
77 
GetFromChildID(int32 child_id)78 BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID(
79     int32 child_id) {
80   base::hash_map<int32, BrowserAccessibility*>::iterator iter =
81       child_id_map_.find(child_id);
82   if (iter != child_id_map_.end()) {
83     return iter->second;
84   } else {
85     return NULL;
86   }
87 }
88 
Remove(int32 child_id,int32 renderer_id)89 void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) {
90   child_id_map_.erase(child_id);
91   renderer_id_to_child_id_map_.erase(renderer_id);
92 }
93 
OnAccessibilityNotifications(const std::vector<ViewHostMsg_AccessibilityNotification_Params> & params)94 void BrowserAccessibilityManager::OnAccessibilityNotifications(
95     const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
96   for (uint32 index = 0; index < params.size(); index++) {
97     const ViewHostMsg_AccessibilityNotification_Params& param = params[index];
98 
99     switch (param.notification_type) {
100       case ViewHostMsg_AccessibilityNotification_Type::
101           NOTIFICATION_TYPE_CHECK_STATE_CHANGED:
102         OnAccessibilityObjectStateChange(param.acc_obj);
103         break;
104       case ViewHostMsg_AccessibilityNotification_Type::
105           NOTIFICATION_TYPE_CHILDREN_CHANGED:
106         OnAccessibilityObjectChildrenChange(param.acc_obj);
107         break;
108       case ViewHostMsg_AccessibilityNotification_Type::
109           NOTIFICATION_TYPE_FOCUS_CHANGED:
110         OnAccessibilityObjectFocusChange(param.acc_obj);
111         break;
112       case ViewHostMsg_AccessibilityNotification_Type::
113           NOTIFICATION_TYPE_LOAD_COMPLETE:
114         OnAccessibilityObjectLoadComplete(param.acc_obj);
115         break;
116       case ViewHostMsg_AccessibilityNotification_Type::
117           NOTIFICATION_TYPE_VALUE_CHANGED:
118         OnAccessibilityObjectValueChange(param.acc_obj);
119         break;
120       case ViewHostMsg_AccessibilityNotification_Type::
121           NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED:
122         OnAccessibilityObjectTextChange(param.acc_obj);
123         break;
124       default:
125         DCHECK(0);
126         break;
127     }
128   }
129 }
130 
OnAccessibilityObjectStateChange(const WebAccessibility & acc_obj)131 void BrowserAccessibilityManager::OnAccessibilityObjectStateChange(
132     const WebAccessibility& acc_obj) {
133   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
134   if (!new_browser_acc)
135     return;
136 
137   NotifyAccessibilityEvent(
138       ViewHostMsg_AccessibilityNotification_Type::
139           NOTIFICATION_TYPE_CHECK_STATE_CHANGED,
140       new_browser_acc);
141 }
142 
OnAccessibilityObjectChildrenChange(const WebAccessibility & acc_obj)143 void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange(
144     const WebAccessibility& acc_obj) {
145   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true);
146   if (!new_browser_acc)
147     return;
148 
149   NotifyAccessibilityEvent(
150       ViewHostMsg_AccessibilityNotification_Type::
151           NOTIFICATION_TYPE_CHILDREN_CHANGED,
152       new_browser_acc);
153 }
154 
OnAccessibilityObjectFocusChange(const WebAccessibility & acc_obj)155 void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange(
156   const WebAccessibility& acc_obj) {
157   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
158   if (!new_browser_acc)
159     return;
160 
161   SetFocus(new_browser_acc, false);
162   if (delegate_ && delegate_->HasFocus()) {
163     GotFocus();
164   } else if (!delegate_) {
165     // Mac currently does not have a BrowserAccessibilityDelegate.
166     NotifyAccessibilityEvent(
167         ViewHostMsg_AccessibilityNotification_Type::
168         NOTIFICATION_TYPE_FOCUS_CHANGED,
169         focus_);
170   }
171 }
172 
OnAccessibilityObjectLoadComplete(const WebAccessibility & acc_obj)173 void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete(
174   const WebAccessibility& acc_obj) {
175   SetFocus(NULL, false);
176   root_->InternalReleaseReference(true);
177 
178   root_ = CreateAccessibilityTree(NULL, acc_obj, 0);
179   if (!focus_)
180     SetFocus(root_, false);
181 
182   NotifyAccessibilityEvent(
183       ViewHostMsg_AccessibilityNotification_Type::
184           NOTIFICATION_TYPE_LOAD_COMPLETE,
185       root_);
186   if (delegate_ && delegate_->HasFocus())
187     GotFocus();
188 }
189 
OnAccessibilityObjectValueChange(const WebAccessibility & acc_obj)190 void BrowserAccessibilityManager::OnAccessibilityObjectValueChange(
191     const WebAccessibility& acc_obj) {
192   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
193   if (!new_browser_acc)
194     return;
195 
196   NotifyAccessibilityEvent(
197       ViewHostMsg_AccessibilityNotification_Type::
198           NOTIFICATION_TYPE_VALUE_CHANGED,
199       new_browser_acc);
200 }
201 
OnAccessibilityObjectTextChange(const WebAccessibility & acc_obj)202 void BrowserAccessibilityManager::OnAccessibilityObjectTextChange(
203     const WebAccessibility& acc_obj) {
204   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
205   if (!new_browser_acc)
206     return;
207 
208   NotifyAccessibilityEvent(
209       ViewHostMsg_AccessibilityNotification_Type::
210           NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED,
211       new_browser_acc);
212 }
213 
GotFocus()214 void BrowserAccessibilityManager::GotFocus() {
215   // TODO(ctguil): Remove when tree update logic handles focus changes.
216   if (!focus_)
217     return;
218 
219   NotifyAccessibilityEvent(
220       ViewHostMsg_AccessibilityNotification_Type::
221           NOTIFICATION_TYPE_FOCUS_CHANGED,
222       focus_);
223 }
224 
GetParentView()225 gfx::NativeView BrowserAccessibilityManager::GetParentView() {
226   return parent_view_;
227 }
228 
GetFocus(BrowserAccessibility * root)229 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
230     BrowserAccessibility* root) {
231   if (focus_ && (!root || focus_->IsDescendantOf(root)))
232     return focus_;
233 
234   return NULL;
235 }
236 
SetFocus(BrowserAccessibility * node,bool notify)237 void BrowserAccessibilityManager::SetFocus(
238     BrowserAccessibility* node, bool notify) {
239   if (focus_)
240     focus_->InternalReleaseReference(false);
241   focus_ = node;
242   if (focus_)
243     focus_->InternalAddReference();
244 
245   if (notify && node && delegate_)
246     delegate_->SetAccessibilityFocus(node->renderer_id());
247 }
248 
DoDefaultAction(const BrowserAccessibility & node)249 void BrowserAccessibilityManager::DoDefaultAction(
250     const BrowserAccessibility& node) {
251   if (delegate_)
252     delegate_->AccessibilityDoDefaultAction(node.renderer_id());
253 }
254 
GetViewBounds()255 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
256   if (delegate_)
257     return delegate_->GetViewBounds();
258   return gfx::Rect();
259 }
260 
UpdateNode(const WebAccessibility & src,bool include_children)261 BrowserAccessibility* BrowserAccessibilityManager::UpdateNode(
262     const WebAccessibility& src,
263     bool include_children) {
264   base::hash_map<int32, int32>::iterator iter =
265       renderer_id_to_child_id_map_.find(src.id);
266   if (iter == renderer_id_to_child_id_map_.end())
267     return NULL;
268 
269   int32 child_id = iter->second;
270   BrowserAccessibility* current = GetFromChildID(child_id);
271   if (!current)
272     return NULL;
273 
274   if (!include_children) {
275     DCHECK_EQ(0U, src.children.size());
276     current->Initialize(
277         this,
278         current->parent(),
279         current->child_id(),
280         current->index_in_parent(),
281         src);
282     return current;
283   }
284 
285   // Detach all of the nodes in the old tree and get a single flat vector
286   // of all node pointers.
287   std::vector<BrowserAccessibility*> old_tree_nodes;
288   current->DetachTree(&old_tree_nodes);
289 
290   // Build a new tree, reusing old nodes if possible. Each node that's
291   // reused will have its reference count incremented by one.
292   current = CreateAccessibilityTree(NULL, src, -1);
293 
294   // Decrement the reference count of all nodes in the old tree, which will
295   // delete any nodes no longer needed.
296   for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++)
297     old_tree_nodes[i]->InternalReleaseReference(false);
298 
299   DCHECK(focus_);
300   if (!focus_->instance_active())
301     SetFocus(root_, false);
302 
303   return current;
304 }
305 
CreateAccessibilityTree(BrowserAccessibility * parent,const WebAccessibility & src,int index_in_parent)306 BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
307     BrowserAccessibility* parent,
308     const WebAccessibility& src,
309     int index_in_parent) {
310   BrowserAccessibility* instance = NULL;
311   int32 child_id = 0;
312   base::hash_map<int32, int32>::iterator iter =
313       renderer_id_to_child_id_map_.find(src.id);
314 
315   // If a BrowserAccessibility instance for this ID already exists, add a
316   // new reference to it and retrieve its children vector.
317   if (iter != renderer_id_to_child_id_map_.end()) {
318     child_id = iter->second;
319     instance = GetFromChildID(child_id);
320   }
321 
322   // If the node has changed roles, don't reuse a BrowserAccessibility
323   // object, that could confuse a screen reader.
324   if (instance && instance->role() != src.role)
325     instance = NULL;
326 
327   if (instance) {
328     // If we're reusing a node, it should already be detached from a parent
329     // and any children. If not, that means we have a serious bug somewhere,
330     // like the same child is reachable from two places in the same tree.
331     DCHECK_EQ(static_cast<BrowserAccessibility*>(NULL), instance->parent());
332     DCHECK_EQ(0U, instance->child_count());
333 
334     // If we're reusing a node, update its parent and increment its
335     // reference count.
336     instance->UpdateParent(parent, index_in_parent);
337     instance->InternalAddReference();
338   } else {
339     // Otherwise, create a new instance.
340     instance = factory_->Create();
341     child_id = GetNextChildID();
342   }
343 
344   instance->Initialize(this, parent, child_id, index_in_parent, src);
345   child_id_map_[child_id] = instance;
346   renderer_id_to_child_id_map_[src.id] = child_id;
347   if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1)
348     SetFocus(instance, false);
349   for (int i = 0; i < static_cast<int>(src.children.size()); ++i) {
350     BrowserAccessibility* child = CreateAccessibilityTree(
351         instance, src.children[i], i);
352     instance->AddChild(child);
353   }
354 
355   return instance;
356 }
357