root/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. DispatchEvent
  2. AccessibilityEventReceived
  3. AddNodeData
  4. RunImpl
  5. RunImpl

// Copyright 2014 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 "chrome/browser/extensions/api/automation_internal/automation_internal_api.h"

#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/automation_internal.h"
#include "content/public/browser/ax_event_notification_details.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_system.h"
#include "ui/accessibility/ax_enums.h"
#include "ui/accessibility/ax_node_data.h"

namespace extensions {
class AutomationWebContentsObserver;
} // namespace extensions

DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver);

namespace extensions {

using ui::AXNodeData;

namespace {

// Dispatches events to the extension message service.
void DispatchEvent(content::BrowserContext* context,
                   const std::string& event_name,
                   scoped_ptr<base::ListValue> args) {
  if (context && extensions::ExtensionSystem::Get(context)->event_router()) {
    scoped_ptr<Event> event(new Event(event_name, args.Pass()));
    event->restrict_to_browser_context = context;
    ExtensionSystem::Get(context)->event_router()->BroadcastEvent(event.Pass());
  }
}

}  // namespace

// Helper class that receives accessibility data from |WebContents|.
class AutomationWebContentsObserver
    : public content::WebContentsObserver,
      public content::WebContentsUserData<AutomationWebContentsObserver> {
 public:
  virtual ~AutomationWebContentsObserver() {}

  // content::WebContentsObserver overrides.
  virtual void AccessibilityEventReceived(
      const std::vector<content::AXEventNotificationDetails>& details)
      OVERRIDE {
    if (!CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableAutomationAPI)) {
      return;
    }

    std::vector<content::AXEventNotificationDetails>::const_iterator iter =
        details.begin();
    for (; iter != details.end(); iter++) {
      scoped_ptr<base::ListValue> args(new base::ListValue());
      const content::AXEventNotificationDetails& event = *iter;
      int process_id = event.process_id;
      int routing_id = event.routing_id;

      base::DictionaryValue* axTreeUpdate = new base::DictionaryValue();
      // TODO(dtseng): These strings should be auto-generated by the IDL
      // compiler.
      axTreeUpdate->Set("processID",
                        base::Value::CreateIntegerValue(process_id));
      axTreeUpdate->Set("routingID",
                        base::Value::CreateIntegerValue(routing_id));
      axTreeUpdate->SetString("eventType", ToString(iter->event_type));
      base::ListValue* nodes = new base::ListValue();
      axTreeUpdate->Set("nodes", nodes);
      for (size_t i = 0; i < event.nodes.size(); i++) {
        const ui::AXNodeData& node = event.nodes[i];
        AddNodeData(node, nodes);
      }
      args->Append(axTreeUpdate);
      DispatchEvent(browser_context_,
                    api::automation_internal::OnAccessibilityEvent::kEventName,
                    args.Pass());
    }
    return;
  }

 private:
  friend class content::WebContentsUserData<AutomationWebContentsObserver>;

  AutomationWebContentsObserver(
      content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents),
        browser_context_(web_contents->GetBrowserContext()) {}

  void AddNodeData(const ui::AXNodeData& node,
                   base::ListValue* nodes) {
    base::DictionaryValue* axNodeData = new base::DictionaryValue();
    axNodeData->SetInteger("id", node.id);
    axNodeData->SetString("role", ToString(node.role));

    base::DictionaryValue* state_dict = new base::DictionaryValue();
    uint32 state_pos = 0, state_shifter = node.state;
    while (state_shifter) {
      if (state_shifter & 1)
        state_dict->SetBoolean(ToString(static_cast<ui::AXState>(state_pos)),
                               true);
      state_shifter = state_shifter >> 1;
      state_pos++;
    }
    axNodeData->Set("state", state_dict);

    if (!node.bool_attributes.empty()) {
      base::DictionaryValue* bool_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.bool_attributes.size(); ++i) {
        std::pair<ui::AXBoolAttribute, bool> attr = node.bool_attributes[i];
        bool_attributes->SetBoolean(ToString(attr.first), attr.second);
      }
      axNodeData->Set("boolAttributes", bool_attributes);
    }

    if (!node.float_attributes.empty()) {
      base::DictionaryValue* float_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.float_attributes.size(); ++i) {
        std::pair<ui::AXFloatAttribute, float> attr = node.float_attributes[i];
        float_attributes->SetDouble(ToString(attr.first), attr.second);
      }
      axNodeData->Set("floatAttributes", float_attributes);
    }

    if (!node.html_attributes.empty()) {
      base::DictionaryValue* html_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.html_attributes.size(); ++i) {
        std::pair<std::string, std::string> attr = node.html_attributes[i];
        html_attributes->SetString(attr.first, attr.second);
      }
      axNodeData->Set("htmlAttributes", html_attributes);
    }

    if (!node.int_attributes.empty()) {
      base::DictionaryValue* int_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.int_attributes.size(); ++i) {
        std::pair<ui::AXIntAttribute, int> attr = node.int_attributes[i];
        int_attributes->SetInteger(ToString(attr.first), attr.second);
      }
      axNodeData->Set("intAttributes", int_attributes);
    }

    if (!node.intlist_attributes.empty()) {
      base::DictionaryValue* intlist_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.intlist_attributes.size(); ++i) {
        std::pair<ui::AXIntListAttribute, std::vector<int32> > attr =
            node.intlist_attributes[i];
        base::ListValue* intlist = new base::ListValue();
        for (size_t j = 0; j < attr.second.size(); j++)
          intlist->AppendInteger(attr.second[j]);
        intlist_attributes->Set(ToString(attr.first), intlist);
      }
      axNodeData->Set("intlistAttributes", intlist_attributes);
    }

    if (!node.string_attributes.empty()) {
      base::DictionaryValue* string_attributes = new base::DictionaryValue();
      for (size_t i = 0; i < node.string_attributes.size(); ++i) {
        std::pair<ui::AXStringAttribute, std::string> attr =
            node.string_attributes[i];
        string_attributes->SetString(ToString(attr.first), attr.second);
      }
      axNodeData->Set("stringAttributes", string_attributes);
    }

    base::ListValue* child_ids = new base::ListValue();
    for (size_t i = 0; i < node.child_ids.size(); ++i) {
      child_ids->AppendInteger(node.child_ids[i]);
    }
    axNodeData->Set("childIDs", child_ids);

    nodes->Append(axNodeData);
  }

  content::BrowserContext* browser_context_;

  DISALLOW_COPY_AND_ASSIGN(AutomationWebContentsObserver);
};

// TODO(aboxhall/dtseng): ensure that the initial data is sent down for the tab
// if this doesn't turn accessibility on for the first time (e.g. if a
// RendererAccessibility object existed already because a screenreader has been
// run at some point).
bool AutomationInternalEnableCurrentTabFunction::RunImpl() {
  if (!CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableAutomationAPI)) {
    return false;
  }

  Browser* current_browser = GetCurrentBrowser();
  TabStripModel* tab_strip = current_browser->tab_strip_model();
  content::WebContents* contents =
      tab_strip->GetWebContentsAt(tab_strip->active_index());
  if (!contents)
    return false;
  content::RenderWidgetHost* rwh =
      contents->GetRenderWidgetHostView()->GetRenderWidgetHost();
  if (!rwh)
    return false;

  AutomationWebContentsObserver::CreateForWebContents(contents);

  rwh->EnableTreeOnlyAccessibilityMode();

  results_ = api::automation_internal::EnableCurrentTab::Results::Create(
      rwh->GetProcess()->GetID(), rwh->GetRoutingID());

  SendResponse(true);
  return true;
}

bool AutomationInternalPerformActionFunction::RunImpl() {
  using api::automation_internal::PerformAction::Params;
  scoped_ptr<Params> params(Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  content::RenderWidgetHost* rwh =
      content::RenderWidgetHost::FromID(params->args.process_id,
                                        params->args.routing_id);

  switch (params->args.action_type) {
    case api::automation_internal::ACTION_TYPE_DO_DEFAULT:
      rwh->AccessibilityDoDefaultAction(params->args.automation_node_id);
      break;
    case api::automation_internal::ACTION_TYPE_FOCUS:
      rwh->AccessibilitySetFocus(params->args.automation_node_id);
      break;
    case api::automation_internal::ACTION_TYPE_MAKE_VISIBLE:
      rwh->AccessibilityScrollToMakeVisible(params->args.automation_node_id,
                                            gfx::Rect());
      break;
    case api::automation_internal::ACTION_TYPE_SET_SELECTION: {
      extensions::api::automation_internal::SetSelectionParams selection_params;
      EXTENSION_FUNCTION_VALIDATE(
          extensions::api::automation_internal::SetSelectionParams::Populate(
              params->opt_args.additional_properties, &selection_params));
      rwh->AccessibilitySetTextSelection(params->args.automation_node_id,
                                         selection_params.start_index,
                                         selection_params.end_index);
      break;
    }
    default:
      NOTREACHED();
  }
  return true;
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */