• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.h"
6 
7 #include "base/logging.h"
8 #include "content/browser/accessibility/browser_accessibility.h"
9 #include "content/common/accessibility_messages.h"
10 
11 namespace content {
12 
Create()13 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
14   return BrowserAccessibility::Create();
15 }
16 
17 #if !defined(OS_MACOSX) && \
18     !defined(OS_WIN) && \
19     !defined(TOOLKIT_GTK) && \
20     !defined(OS_ANDROID) \
21 // We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK,
22 // and Win. For any other platform, instantiate the base class.
23 // static
Create(const AccessibilityNodeData & src,BrowserAccessibilityDelegate * delegate,BrowserAccessibilityFactory * factory)24 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
25     const AccessibilityNodeData& src,
26     BrowserAccessibilityDelegate* delegate,
27     BrowserAccessibilityFactory* factory) {
28   return new BrowserAccessibilityManager(src, delegate, factory);
29 }
30 #endif
31 
BrowserAccessibilityManager(BrowserAccessibilityDelegate * delegate,BrowserAccessibilityFactory * factory)32 BrowserAccessibilityManager::BrowserAccessibilityManager(
33     BrowserAccessibilityDelegate* delegate,
34     BrowserAccessibilityFactory* factory)
35     : delegate_(delegate),
36       factory_(factory),
37       root_(NULL),
38       focus_(NULL),
39       osk_state_(OSK_ALLOWED) {
40 }
41 
BrowserAccessibilityManager(const AccessibilityNodeData & src,BrowserAccessibilityDelegate * delegate,BrowserAccessibilityFactory * factory)42 BrowserAccessibilityManager::BrowserAccessibilityManager(
43     const AccessibilityNodeData& src,
44     BrowserAccessibilityDelegate* delegate,
45     BrowserAccessibilityFactory* factory)
46     : delegate_(delegate),
47       factory_(factory),
48       root_(NULL),
49       focus_(NULL),
50       osk_state_(OSK_ALLOWED) {
51   Initialize(src);
52 }
53 
~BrowserAccessibilityManager()54 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
55   if (root_)
56     root_->Destroy();
57 }
58 
Initialize(const AccessibilityNodeData src)59 void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) {
60   std::vector<AccessibilityNodeData> nodes;
61   nodes.push_back(src);
62   if (!UpdateNodes(nodes))
63     return;
64   if (!focus_)
65     SetFocus(root_, false);
66 }
67 
68 // static
GetEmptyDocument()69 AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() {
70   AccessibilityNodeData empty_document;
71   empty_document.id = 0;
72   empty_document.role = blink::WebAXRoleRootWebArea;
73   return empty_document;
74 }
75 
GetRoot()76 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
77   return root_;
78 }
79 
GetFromRendererID(int32 renderer_id)80 BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
81     int32 renderer_id) {
82   base::hash_map<int32, BrowserAccessibility*>::iterator iter =
83       renderer_id_map_.find(renderer_id);
84   if (iter != renderer_id_map_.end())
85     return iter->second;
86   return NULL;
87 }
88 
GotFocus(bool touch_event_context)89 void BrowserAccessibilityManager::GotFocus(bool touch_event_context) {
90   if (!touch_event_context)
91     osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED;
92 
93   if (!focus_)
94     return;
95 
96   NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
97 }
98 
WasHidden()99 void BrowserAccessibilityManager::WasHidden() {
100   osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN;
101 }
102 
GotMouseDown()103 void BrowserAccessibilityManager::GotMouseDown() {
104   osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
105   NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
106 }
107 
IsOSKAllowed(const gfx::Rect & bounds)108 bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
109   if (!delegate_ || !delegate_->HasFocus())
110     return false;
111 
112   gfx::Point touch_point = delegate_->GetLastTouchEventLocation();
113   return bounds.Contains(touch_point);
114 }
115 
UseRootScrollOffsetsWhenComputingBounds()116 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
117   return true;
118 }
119 
RemoveNode(BrowserAccessibility * node)120 void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) {
121   if (node == focus_)
122     SetFocus(root_, false);
123   int renderer_id = node->renderer_id();
124   renderer_id_map_.erase(renderer_id);
125 }
126 
OnAccessibilityEvents(const std::vector<AccessibilityHostMsg_EventParams> & params)127 void BrowserAccessibilityManager::OnAccessibilityEvents(
128     const std::vector<AccessibilityHostMsg_EventParams>& params) {
129   bool should_send_initial_focus = false;
130 
131   // Process all changes to the accessibility tree first.
132   for (uint32 index = 0; index < params.size(); index++) {
133     const AccessibilityHostMsg_EventParams& param = params[index];
134     if (!UpdateNodes(param.nodes))
135       return;
136 
137     // Set initial focus when a page is loaded.
138     blink::WebAXEvent event_type = param.event_type;
139     if (event_type == blink::WebAXEventLoadComplete) {
140       if (!focus_) {
141         SetFocus(root_, false);
142         should_send_initial_focus = true;
143       }
144     }
145   }
146 
147   if (should_send_initial_focus &&
148       (!delegate_ || delegate_->HasFocus())) {
149     NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
150   }
151 
152   // Now iterate over the events again and fire the events.
153   for (uint32 index = 0; index < params.size(); index++) {
154     const AccessibilityHostMsg_EventParams& param = params[index];
155 
156     // Find the node corresponding to the id that's the target of the
157     // event (which may not be the root of the update tree).
158     BrowserAccessibility* node = GetFromRendererID(param.id);
159     if (!node)
160       continue;
161 
162     blink::WebAXEvent event_type = param.event_type;
163     if (event_type == blink::WebAXEventFocus ||
164         event_type == blink::WebAXEventBlur) {
165       SetFocus(node, false);
166 
167       if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
168           osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
169         osk_state_ = OSK_ALLOWED;
170 
171       // Don't send a native focus event if the window itself doesn't
172       // have focus.
173       if (delegate_ && !delegate_->HasFocus())
174         continue;
175     }
176 
177     // Send the event event to the operating system.
178     NotifyAccessibilityEvent(event_type, node);
179   }
180 }
181 
GetFocus(BrowserAccessibility * root)182 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
183     BrowserAccessibility* root) {
184   if (focus_ && (!root || focus_->IsDescendantOf(root)))
185     return focus_;
186 
187   return NULL;
188 }
189 
SetFocus(BrowserAccessibility * node,bool notify)190 void BrowserAccessibilityManager::SetFocus(
191     BrowserAccessibility* node, bool notify) {
192   if (focus_ != node)
193     focus_ = node;
194 
195   if (notify && node && delegate_)
196     delegate_->SetAccessibilityFocus(node->renderer_id());
197 }
198 
SetRoot(BrowserAccessibility * node)199 void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
200   root_ = node;
201   NotifyRootChanged();
202 }
203 
DoDefaultAction(const BrowserAccessibility & node)204 void BrowserAccessibilityManager::DoDefaultAction(
205     const BrowserAccessibility& node) {
206   if (delegate_)
207     delegate_->AccessibilityDoDefaultAction(node.renderer_id());
208 }
209 
ScrollToMakeVisible(const BrowserAccessibility & node,gfx::Rect subfocus)210 void BrowserAccessibilityManager::ScrollToMakeVisible(
211     const BrowserAccessibility& node, gfx::Rect subfocus) {
212   if (delegate_) {
213     delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
214   }
215 }
216 
ScrollToPoint(const BrowserAccessibility & node,gfx::Point point)217 void BrowserAccessibilityManager::ScrollToPoint(
218     const BrowserAccessibility& node, gfx::Point point) {
219   if (delegate_) {
220     delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
221   }
222 }
223 
SetTextSelection(const BrowserAccessibility & node,int start_offset,int end_offset)224 void BrowserAccessibilityManager::SetTextSelection(
225     const BrowserAccessibility& node, int start_offset, int end_offset) {
226   if (delegate_) {
227     delegate_->AccessibilitySetTextSelection(
228         node.renderer_id(), start_offset, end_offset);
229   }
230 }
231 
GetViewBounds()232 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
233   if (delegate_)
234     return delegate_->GetViewBounds();
235   return gfx::Rect();
236 }
237 
UpdateNodesForTesting(const AccessibilityNodeData & node1,const AccessibilityNodeData & node2,const AccessibilityNodeData & node3,const AccessibilityNodeData & node4,const AccessibilityNodeData & node5,const AccessibilityNodeData & node6,const AccessibilityNodeData & node7)238 void BrowserAccessibilityManager::UpdateNodesForTesting(
239     const AccessibilityNodeData& node1,
240     const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
241     const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
242     const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
243     const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
244     const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
245     const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
246   std::vector<AccessibilityNodeData> nodes;
247   nodes.push_back(node1);
248   if (node2.id != AccessibilityNodeData().id)
249     nodes.push_back(node2);
250   if (node3.id != AccessibilityNodeData().id)
251     nodes.push_back(node3);
252   if (node4.id != AccessibilityNodeData().id)
253     nodes.push_back(node4);
254   if (node5.id != AccessibilityNodeData().id)
255     nodes.push_back(node5);
256   if (node6.id != AccessibilityNodeData().id)
257     nodes.push_back(node6);
258   if (node7.id != AccessibilityNodeData().id)
259     nodes.push_back(node7);
260   UpdateNodes(nodes);
261 }
262 
UpdateNodes(const std::vector<AccessibilityNodeData> & nodes)263 bool BrowserAccessibilityManager::UpdateNodes(
264     const std::vector<AccessibilityNodeData>& nodes) {
265   bool success = true;
266 
267   // First, update all of the nodes in the tree.
268   for (size_t i = 0; i < nodes.size() && success; i++) {
269     if (!UpdateNode(nodes[i]))
270       success = false;
271   }
272 
273   // In a second pass, call PostInitialize on each one - this must
274   // be called after all of each node's children are initialized too.
275   for (size_t i = 0; i < nodes.size() && success; i++) {
276     // Note: it's not a bug for nodes[i].id to not be found in the tree.
277     // Consider this example:
278     // Before:
279     // A
280     //   B
281     //     C
282     //   D
283     //     E
284     //       F
285     // After:
286     // A
287     //   B
288     //     C
289     //       F
290     //   D
291     // In this example, F is being reparented. The renderer scans the tree
292     // in order. If can't update "C" to add "F" as a child, when "F" is still
293     // a child of "E". So it first updates "E", to remove "F" as a child.
294     // Later, it ends up deleting "E". So when we get here, "E" was updated as
295     // part of this sequence but it no longer exists in the final tree, so
296     // there's nothing to postinitialize.
297     BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
298     if (instance)
299       instance->PostInitialize();
300   }
301 
302   if (!success) {
303     // A bad accessibility tree could lead to memory corruption.
304     // Ask the delegate to crash the renderer, or if not available,
305     // crash the browser.
306     if (delegate_)
307       delegate_->FatalAccessibilityTreeError();
308     else
309       CHECK(false);
310   }
311 
312   return success;
313 }
314 
CreateNode(BrowserAccessibility * parent,int32 renderer_id,int32 index_in_parent)315 BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
316     BrowserAccessibility* parent,
317     int32 renderer_id,
318     int32 index_in_parent) {
319   BrowserAccessibility* node = factory_->Create();
320   node->InitializeTreeStructure(
321       this, parent, renderer_id, index_in_parent);
322   AddNodeToMap(node);
323   return node;
324 }
325 
AddNodeToMap(BrowserAccessibility * node)326 void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
327   renderer_id_map_[node->renderer_id()] = node;
328 }
329 
UpdateNode(const AccessibilityNodeData & src)330 bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
331   // This method updates one node in the tree based on serialized data
332   // received from the renderer.
333 
334   // Create a set of child ids in |src| for fast lookup. If a duplicate id is
335   // found, exit now with a fatal error before changing anything else.
336   std::set<int32> new_child_ids;
337   for (size_t i = 0; i < src.child_ids.size(); ++i) {
338     if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
339       return false;
340     new_child_ids.insert(src.child_ids[i]);
341   }
342 
343   // Look up the node by id. If it's not found, then either the root
344   // of the tree is being swapped, or we're out of sync with the renderer
345   // and this is a serious error.
346   BrowserAccessibility* instance = GetFromRendererID(src.id);
347   if (!instance) {
348     if (src.role != blink::WebAXRoleRootWebArea)
349       return false;
350     instance = CreateNode(NULL, src.id, 0);
351   }
352 
353   // TODO(dmazzoni): avoid a linear scan here.
354   for (size_t i = 0; i < src.bool_attributes.size(); i++) {
355     if (src.bool_attributes[i].first ==
356         AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) {
357       instance->SetLocation(src.location);
358       return true;
359     }
360   }
361 
362   // Update all of the node-specific data, like its role, state, name, etc.
363   instance->InitializeData(src);
364 
365   //
366   // Update the children in three steps:
367   //
368   // 1. Iterate over the old children and delete nodes that are no longer
369   //    in the tree.
370   // 2. Build up a vector of new children, reusing children that haven't
371   //    changed (but may have been reordered) and adding new empty
372   //    objects for new children.
373   // 3. Swap in the new children vector for the old one.
374 
375   // Delete any previous children of this instance that are no longer
376   // children first. We make a deletion-only pass first to prevent a
377   // node that's being reparented from being the child of both its old
378   // parent and new parent, which could lead to a double-free.
379   // If a node is reparented, the renderer will always send us a fresh
380   // copy of the node.
381   const std::vector<BrowserAccessibility*>& old_children = instance->children();
382   for (size_t i = 0; i < old_children.size(); ++i) {
383     int old_id = old_children[i]->renderer_id();
384     if (new_child_ids.find(old_id) == new_child_ids.end())
385       old_children[i]->Destroy();
386   }
387 
388   // Now build a vector of new children, reusing objects that were already
389   // children of this node before.
390   std::vector<BrowserAccessibility*> new_children;
391   bool success = true;
392   for (size_t i = 0; i < src.child_ids.size(); i++) {
393     int32 child_renderer_id = src.child_ids[i];
394     int32 index_in_parent = static_cast<int32>(i);
395     BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
396     if (child) {
397       if (child->parent() != instance) {
398         // This is a serious error - nodes should never be reparented.
399         // If this case occurs, continue so this node isn't left in an
400         // inconsistent state, but return failure at the end.
401         success = false;
402         continue;
403       }
404       child->UpdateParent(instance, index_in_parent);
405     } else {
406       child = CreateNode(instance, child_renderer_id, index_in_parent);
407     }
408     new_children.push_back(child);
409   }
410 
411   // Finally, swap in the new children vector for the old.
412   instance->SwapChildren(new_children);
413 
414   // Handle the case where this node is the new root of the tree.
415   if (src.role == blink::WebAXRoleRootWebArea &&
416       (!root_ || root_->renderer_id() != src.id)) {
417     if (root_)
418       root_->Destroy();
419     if (focus_ == root_)
420       SetFocus(instance, false);
421     SetRoot(instance);
422   }
423 
424   // Keep track of what node is focused.
425   if (src.role != blink::WebAXRoleRootWebArea &&
426       src.role != blink::WebAXRoleWebArea &&
427       (src.state >> blink::WebAXStateFocused & 1)) {
428     SetFocus(instance, false);
429   }
430   return success;
431 }
432 
433 }  // namespace content
434