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.
5 #include "content/renderer/renderer_accessibility_focus_only.h"
7 #include "content/common/accessibility_node_data.h"
8 #include "content/renderer/render_view_impl.h"
9 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
15 using WebKit::WebDocument
;
16 using WebKit::WebElement
;
17 using WebKit::WebFrame
;
18 using WebKit::WebNode
;
19 using WebKit::WebView
;
22 // The root node will always have id 1. Let each child node have a new
23 // id starting with 2.
24 const int kInitialId
= 2;
29 RendererAccessibilityFocusOnly::RendererAccessibilityFocusOnly(
30 RenderViewImpl
* render_view
)
31 : RendererAccessibility(render_view
),
32 next_id_(kInitialId
) {
35 RendererAccessibilityFocusOnly::~RendererAccessibilityFocusOnly() {
38 void RendererAccessibilityFocusOnly::HandleWebAccessibilityNotification(
39 const WebKit::WebAccessibilityObject
& obj
,
40 WebKit::WebAccessibilityNotification notification
) {
44 void RendererAccessibilityFocusOnly::FocusedNodeChanged(const WebNode
& node
) {
45 // Send the new accessible tree and post a native focus event.
46 HandleFocusedNodeChanged(node
, true);
49 void RendererAccessibilityFocusOnly::DidFinishLoad(WebKit::WebFrame
* frame
) {
50 WebView
* view
= render_view()->GetWebView();
51 if (view
->focusedFrame() != frame
)
54 WebDocument document
= frame
->document();
55 // Send an accessible tree to the browser, but do not post a native
56 // focus event. This is important so that if focus is initially in an
57 // editable text field, Windows will know to pop up the keyboard if the
58 // user touches it and focus doesn't change.
59 HandleFocusedNodeChanged(document
.focusedNode(), false);
62 void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
64 bool send_focus_event
) {
65 const WebDocument
& document
= GetMainDocument();
66 if (document
.isNull())
70 bool node_is_editable_text
;
71 // Check HasIMETextFocus first, because it will correctly handle
72 // focus in a text box inside a ppapi plug-in. Otherwise fall back on
73 // checking the focused node in WebKit.
74 if (render_view_
->HasIMETextFocus()) {
75 node_has_focus
= true;
76 node_is_editable_text
= true;
78 node_has_focus
= !node
.isNull();
79 node_is_editable_text
=
80 node_has_focus
&& render_view_
->IsEditableNode(node
);
83 std::vector
<AccessibilityHostMsg_NotificationParams
> notifications
;
84 notifications
.push_back(AccessibilityHostMsg_NotificationParams());
85 AccessibilityHostMsg_NotificationParams
& notification
= notifications
[0];
87 // If we want to update the browser's accessibility tree but not send a
88 // native focus changed notification, we can send a LayoutComplete
89 // notification, which doesn't post a native event on Windows.
90 notification
.notification_type
=
92 AccessibilityNotificationFocusChanged
:
93 AccessibilityNotificationLayoutComplete
;
95 // This means that the new tree we send supercedes any previous tree,
96 // not just a previous node.
97 notification
.includes_children
= true;
99 // Set the id that the notification applies to: the root node if nothing
100 // has focus, otherwise the focused node.
101 notification
.id
= node_has_focus
? next_id_
: 1;
103 // Always include the root of the tree, the document. It always has id 1.
104 notification
.acc_tree
.id
= 1;
105 notification
.acc_tree
.role
= AccessibilityNodeData::ROLE_ROOT_WEB_AREA
;
106 notification
.acc_tree
.state
=
107 (1 << AccessibilityNodeData::STATE_READONLY
) |
108 (1 << AccessibilityNodeData::STATE_FOCUSABLE
);
110 notification
.acc_tree
.state
|= (1 << AccessibilityNodeData::STATE_FOCUSED
);
111 notification
.acc_tree
.location
= gfx::Rect(render_view_
->size());
113 notification
.acc_tree
.children
.push_back(AccessibilityNodeData());
114 AccessibilityNodeData
& child
= notification
.acc_tree
.children
[0];
116 child
.role
= AccessibilityNodeData::ROLE_GROUP
;
118 if (!node
.isNull() && node
.isElementNode()) {
119 child
.location
= gfx::Rect(
120 const_cast<WebNode
&>(node
).to
<WebElement
>().boundsInViewportSpace());
122 child
.location
= gfx::Rect();
125 if (node_has_focus
) {
127 (1 << AccessibilityNodeData::STATE_FOCUSABLE
) |
128 (1 << AccessibilityNodeData::STATE_FOCUSED
);
129 if (!node_is_editable_text
)
130 child
.state
|= (1 << AccessibilityNodeData::STATE_READONLY
);
135 LOG(INFO
) << "Accessibility update: \n"
136 << "routing id=" << routing_id()
138 << AccessibilityNotificationToString(notification
.notification_type
)
139 << "\n" << notification
.acc_tree
.DebugString(true);
143 Send(new AccessibilityHostMsg_Notifications(routing_id(), notifications
));
145 // Increment the id, wrap back when we get past a million.
147 if (next_id_
> 1000000)
148 next_id_
= kInitialId
;
151 } // namespace content