| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/accessibility/renderer_accessibility_focus_only.h" |
| |
| #include "content/renderer/render_frame_impl.h" |
| #include "content/renderer/render_view_impl.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebElement.h" |
| #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| #include "third_party/WebKit/public/web/WebNode.h" |
| #include "third_party/WebKit/public/web/WebView.h" |
| #include "ui/accessibility/ax_node_data.h" |
| |
| using blink::WebDocument; |
| using blink::WebElement; |
| using blink::WebNode; |
| using blink::WebView; |
| |
| namespace { |
| // The root node will always have id 1. Let each child node have a new |
| // id starting with 2. |
| const int kInitialId = 2; |
| } |
| |
| namespace content { |
| |
| RendererAccessibilityFocusOnly::RendererAccessibilityFocusOnly( |
| RenderFrameImpl* render_frame) |
| : RendererAccessibility(render_frame), |
| next_id_(kInitialId) { |
| } |
| |
| RendererAccessibilityFocusOnly::~RendererAccessibilityFocusOnly() { |
| } |
| |
| void RendererAccessibilityFocusOnly::HandleWebAccessibilityEvent( |
| const blink::WebAXObject& obj, blink::WebAXEvent event) { |
| // Do nothing. |
| } |
| |
| RendererAccessibilityType RendererAccessibilityFocusOnly::GetType() { |
| return RendererAccessibilityTypeFocusOnly; |
| } |
| |
| void RendererAccessibilityFocusOnly::FocusedNodeChanged(const WebNode& node) { |
| // Send the new accessible tree and post a native focus event. |
| HandleFocusedNodeChanged(node, true); |
| } |
| |
| void RendererAccessibilityFocusOnly::DidFinishLoad() { |
| // Send an accessible tree to the browser, but do not post a native |
| // focus event. This is important so that if focus is initially in an |
| // editable text field, Windows will know to pop up the keyboard if the |
| // user touches it and focus doesn't change. |
| const WebDocument& document = GetMainDocument(); |
| HandleFocusedNodeChanged(document.focusedElement(), false); |
| } |
| |
| void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged( |
| const WebNode& node, |
| bool send_focus_event) { |
| const WebDocument& document = GetMainDocument(); |
| if (document.isNull()) |
| return; |
| |
| bool node_has_focus; |
| bool node_is_editable_text; |
| // Check HasIMETextFocus first, because it will correctly handle |
| // focus in a text box inside a ppapi plug-in. Otherwise fall back on |
| // checking the focused node in Blink. |
| if (render_frame_->render_view()->HasIMETextFocus()) { |
| node_has_focus = true; |
| node_is_editable_text = true; |
| } else { |
| node_has_focus = !node.isNull(); |
| node_is_editable_text = |
| node_has_focus && render_frame_->render_view()->IsEditableNode(node); |
| } |
| |
| std::vector<AccessibilityHostMsg_EventParams> events; |
| events.push_back(AccessibilityHostMsg_EventParams()); |
| AccessibilityHostMsg_EventParams& event = events[0]; |
| |
| // If we want to update the browser's accessibility tree but not send a |
| // native focus changed event, we can send a LayoutComplete |
| // event, which doesn't post a native event on Windows. |
| event.event_type = |
| send_focus_event ? ui::AX_EVENT_FOCUS : ui::AX_EVENT_LAYOUT_COMPLETE; |
| |
| // Set the id that the event applies to: the root node if nothing |
| // has focus, otherwise the focused node. |
| event.id = node_has_focus ? next_id_ : 1; |
| |
| event.update.nodes.resize(2); |
| ui::AXNodeData& root = event.update.nodes[0]; |
| ui::AXNodeData& child = event.update.nodes[1]; |
| |
| // Always include the root of the tree, the document. It always has id 1. |
| root.id = 1; |
| root.role = ui::AX_ROLE_ROOT_WEB_AREA; |
| root.state = |
| (1 << ui::AX_STATE_READ_ONLY) | |
| (1 << ui::AX_STATE_FOCUSABLE); |
| if (!node_has_focus) |
| root.state |= (1 << ui::AX_STATE_FOCUSED); |
| root.location = gfx::Rect(render_frame_->render_view()->size()); |
| root.child_ids.push_back(next_id_); |
| |
| child.id = next_id_; |
| child.role = ui::AX_ROLE_GROUP; |
| |
| if (!node.isNull() && node.isElementNode()) { |
| child.location = gfx::Rect( |
| const_cast<WebNode&>(node).to<WebElement>().boundsInViewportSpace()); |
| } else if (render_frame_->render_view()->HasIMETextFocus()) { |
| child.location = root.location; |
| } else { |
| child.location = gfx::Rect(); |
| } |
| |
| if (node_has_focus) { |
| child.state = |
| (1 << ui::AX_STATE_FOCUSABLE) | |
| (1 << ui::AX_STATE_FOCUSED); |
| if (!node_is_editable_text) |
| child.state |= (1 << ui::AX_STATE_READ_ONLY); |
| } |
| |
| #ifndef NDEBUG |
| /** |
| if (logging_) { |
| VLOG(0) << "Accessibility update: \n" |
| << "routing id=" << routing_id() |
| << " event=" |
| << AccessibilityEventToString(event.event_type) |
| << "\n" << event.nodes[0].DebugString(true); |
| } |
| **/ |
| #endif |
| |
| Send(new AccessibilityHostMsg_Events(routing_id(), events)); |
| |
| // Increment the id, wrap back when we get past a million. |
| next_id_++; |
| if (next_id_ > 1000000) |
| next_id_ = kInitialId; |
| } |
| |
| } // namespace content |