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/renderer/accessibility/renderer_accessibility_focus_only.h"
6
7#include "content/renderer/render_frame_impl.h"
8#include "content/renderer/render_view_impl.h"
9#include "third_party/WebKit/public/web/WebDocument.h"
10#include "third_party/WebKit/public/web/WebElement.h"
11#include "third_party/WebKit/public/web/WebLocalFrame.h"
12#include "third_party/WebKit/public/web/WebNode.h"
13#include "third_party/WebKit/public/web/WebView.h"
14#include "ui/accessibility/ax_node_data.h"
15
16using blink::WebDocument;
17using blink::WebElement;
18using blink::WebNode;
19using blink::WebView;
20
21namespace {
22// The root node will always have id 1. Let each child node have a new
23// id starting with 2.
24const int kInitialId = 2;
25}
26
27namespace content {
28
29RendererAccessibilityFocusOnly::RendererAccessibilityFocusOnly(
30    RenderFrameImpl* render_frame)
31    : RendererAccessibility(render_frame),
32      next_id_(kInitialId) {
33}
34
35RendererAccessibilityFocusOnly::~RendererAccessibilityFocusOnly() {
36}
37
38void RendererAccessibilityFocusOnly::HandleWebAccessibilityEvent(
39    const blink::WebAXObject& obj, blink::WebAXEvent event) {
40  // Do nothing.
41}
42
43RendererAccessibilityType RendererAccessibilityFocusOnly::GetType() {
44  return RendererAccessibilityTypeFocusOnly;
45}
46
47void RendererAccessibilityFocusOnly::FocusedNodeChanged(const WebNode& node) {
48  // Send the new accessible tree and post a native focus event.
49  HandleFocusedNodeChanged(node, true);
50}
51
52void RendererAccessibilityFocusOnly::DidFinishLoad() {
53  // Send an accessible tree to the browser, but do not post a native
54  // focus event. This is important so that if focus is initially in an
55  // editable text field, Windows will know to pop up the keyboard if the
56  // user touches it and focus doesn't change.
57  const WebDocument& document = GetMainDocument();
58  HandleFocusedNodeChanged(document.focusedElement(), false);
59}
60
61void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
62    const WebNode& node,
63    bool send_focus_event) {
64  const WebDocument& document = GetMainDocument();
65  if (document.isNull())
66    return;
67
68  bool node_has_focus;
69  bool node_is_editable_text;
70  // Check HasIMETextFocus first, because it will correctly handle
71  // focus in a text box inside a ppapi plug-in. Otherwise fall back on
72  // checking the focused node in Blink.
73  if (render_frame_->render_view()->HasIMETextFocus()) {
74    node_has_focus = true;
75    node_is_editable_text = true;
76  } else {
77    node_has_focus = !node.isNull();
78    node_is_editable_text =
79        node_has_focus && render_frame_->render_view()->IsEditableNode(node);
80  }
81
82  std::vector<AccessibilityHostMsg_EventParams> events;
83  events.push_back(AccessibilityHostMsg_EventParams());
84  AccessibilityHostMsg_EventParams& event = events[0];
85
86  // If we want to update the browser's accessibility tree but not send a
87  // native focus changed event, we can send a LayoutComplete
88  // event, which doesn't post a native event on Windows.
89  event.event_type =
90      send_focus_event ? ui::AX_EVENT_FOCUS : ui::AX_EVENT_LAYOUT_COMPLETE;
91
92  // Set the id that the event applies to: the root node if nothing
93  // has focus, otherwise the focused node.
94  event.id = node_has_focus ? next_id_ : 1;
95
96  event.update.nodes.resize(2);
97  ui::AXNodeData& root = event.update.nodes[0];
98  ui::AXNodeData& child = event.update.nodes[1];
99
100  // Always include the root of the tree, the document. It always has id 1.
101  root.id = 1;
102  root.role = ui::AX_ROLE_ROOT_WEB_AREA;
103  root.state =
104      (1 << ui::AX_STATE_READ_ONLY) |
105      (1 << ui::AX_STATE_FOCUSABLE);
106  if (!node_has_focus)
107    root.state |= (1 << ui::AX_STATE_FOCUSED);
108  root.location = gfx::Rect(render_frame_->render_view()->size());
109  root.child_ids.push_back(next_id_);
110
111  child.id = next_id_;
112  child.role = ui::AX_ROLE_GROUP;
113
114  if (!node.isNull() && node.isElementNode()) {
115    child.location = gfx::Rect(
116        const_cast<WebNode&>(node).to<WebElement>().boundsInViewportSpace());
117  } else if (render_frame_->render_view()->HasIMETextFocus()) {
118    child.location = root.location;
119  } else {
120    child.location = gfx::Rect();
121  }
122
123  if (node_has_focus) {
124    child.state =
125        (1 << ui::AX_STATE_FOCUSABLE) |
126        (1 << ui::AX_STATE_FOCUSED);
127    if (!node_is_editable_text)
128      child.state |= (1 << ui::AX_STATE_READ_ONLY);
129  }
130
131#ifndef NDEBUG
132  /**
133  if (logging_) {
134    VLOG(0) << "Accessibility update: \n"
135        << "routing id=" << routing_id()
136        << " event="
137        << AccessibilityEventToString(event.event_type)
138        << "\n" << event.nodes[0].DebugString(true);
139  }
140  **/
141#endif
142
143  Send(new AccessibilityHostMsg_Events(routing_id(), events, false));
144
145  // Increment the id, wrap back when we get past a million.
146  next_id_++;
147  if (next_id_ > 1000000)
148    next_id_ = kInitialId;
149}
150
151}  // namespace content
152