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