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