Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / automation_internal / automation_internal_api.cc
index 8d2769c..0f56660 100644 (file)
@@ -6,21 +6,26 @@
 
 #include <vector>
 
-#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/extensions/api/automation_internal/automation_action_adapter.h"
 #include "chrome/browser/extensions/api/automation_internal/automation_util.h"
+#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.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 "chrome/common/extensions/manifest_handlers/automation.h"
 #include "content/public/browser/ax_event_notification_details.h"
+#include "content/public/browser/render_frame_host.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_view.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/common/permissions/permissions_data.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/ash/accessibility/automation_manager_views.h"
+#include "chrome/browser/ui/ash/accessibility/automation_manager_ash.h"
 #endif
 
 namespace extensions {
@@ -29,8 +34,36 @@ class AutomationWebContentsObserver;
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver);
 
+namespace {
+const int kDesktopProcessID = 0;
+const int kDesktopRoutingID = 0;
+
+const char kCannotRequestAutomationOnPage[] =
+    "Cannot request automation tree on url \"*\". "
+    "Extension manifest must request permission to access this host.";
+}  // namespace
+
 namespace extensions {
 
+bool CanRequestAutomation(const Extension* extension,
+                          const AutomationInfo* automation_info,
+                          const content::WebContents* contents) {
+  if (automation_info->desktop)
+    return true;
+
+  const GURL& url = contents->GetURL();
+  // TODO(aboxhall): check for webstore URL
+  if (automation_info->matches.MatchesURL(url))
+    return true;
+
+  int tab_id = ExtensionTabUtil::GetTabId(contents);
+  content::RenderProcessHost* process = contents->GetRenderProcessHost();
+  int process_id = process ? process->GetID() : -1;
+  std::string unused_error;
+  return extension->permissions_data()->CanAccessPage(
+      extension, url, url, tab_id, process_id, &unused_error);
+}
+
 // Helper class that receives accessibility data from |WebContents|.
 class AutomationWebContentsObserver
     : public content::WebContentsObserver,
@@ -43,7 +76,16 @@ class AutomationWebContentsObserver
       const std::vector<content::AXEventNotificationDetails>& details)
       OVERRIDE {
     automation_util::DispatchAccessibilityEventsToAutomation(
-        details, browser_context_);
+        details, browser_context_,
+        web_contents()->GetContainerBounds().OffsetFromOrigin());
+  }
+
+  virtual void RenderFrameDeleted(
+      content::RenderFrameHost* render_frame_host) OVERRIDE {
+    automation_util::DispatchTreeDestroyedEventToAutomation(
+        render_frame_host->GetProcess()->GetID(),
+        render_frame_host->GetRoutingID(),
+        browser_context_);
   }
 
  private:
@@ -59,88 +101,158 @@ class AutomationWebContentsObserver
   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::RunAsync() {
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableAutomationAPI)) {
-    return false;
+// Helper class that implements an action adapter for a |RenderFrameHost|.
+class RenderFrameHostActionAdapter : public AutomationActionAdapter {
+ public:
+  explicit RenderFrameHostActionAdapter(content::RenderFrameHost* rfh)
+      : rfh_(rfh) {}
+
+  virtual ~RenderFrameHostActionAdapter() {}
+
+  // AutomationActionAdapter implementation.
+  virtual void DoDefault(int32 id) OVERRIDE {
+    rfh_->AccessibilityDoDefaultAction(id);
   }
 
-  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;
+  virtual void Focus(int32 id) OVERRIDE {
+    rfh_->AccessibilitySetFocus(id);
+  }
 
-  results_ = api::automation_internal::EnableCurrentTab::Results::Create(
-      rwh->GetProcess()->GetID(), rwh->GetRoutingID());
+  virtual void MakeVisible(int32 id) OVERRIDE {
+    rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect());
+  }
 
-  SendResponse(true);
+  virtual void SetSelection(int32 id, int32 start, int32 end) OVERRIDE {
+    rfh_->AccessibilitySetTextSelection(id, start, end);
+  }
 
-  AutomationWebContentsObserver::CreateForWebContents(contents);
+ private:
+  content::RenderFrameHost* rfh_;
 
-  rwh->EnableTreeOnlyAccessibilityMode();
+  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter);
+};
 
-  return true;
-}
+ExtensionFunction::ResponseAction
+AutomationInternalEnableTabFunction::Run() {
+  const AutomationInfo* automation_info = AutomationInfo::Get(extension());
+  EXTENSION_FUNCTION_VALIDATE(automation_info);
+
+  using api::automation_internal::EnableTab::Params;
+  scoped_ptr<Params> params(Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+  content::WebContents* contents = NULL;
+  if (params->tab_id.get()) {
+    int tab_id = *params->tab_id;
+    if (!ExtensionTabUtil::GetTabById(tab_id,
+                                      GetProfile(),
+                                      include_incognito(),
+                                      NULL, /* browser out param*/
+                                      NULL, /* tab_strip out param */
+                                      &contents,
+                                      NULL /* tab_index out param */)) {
+      return RespondNow(
+          Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
+    }
+  } else {
+    contents = GetCurrentBrowser()->tab_strip_model()->GetActiveWebContents();
+    if (!contents)
+      return RespondNow(Error("No active tab"));
+  }
+  content::RenderFrameHost* rfh = contents->GetMainFrame();
+  if (!rfh)
+    return RespondNow(Error("Could not enable accessibility for active tab"));
+
+  if (!CanRequestAutomation(extension(), automation_info, contents)) {
+    return RespondNow(
+        Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
+  }
+  AutomationWebContentsObserver::CreateForWebContents(contents);
+  contents->EnableTreeOnlyAccessibilityMode();
+  return RespondNow(
+      ArgumentList(api::automation_internal::EnableTab::Results::Create(
+          rfh->GetProcess()->GetID(), rfh->GetRoutingID())));
+  }
+
+ExtensionFunction::ResponseAction
+AutomationInternalPerformActionFunction::Run() {
+  const AutomationInfo* automation_info = AutomationInfo::Get(extension());
+  EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact);
 
-bool AutomationInternalPerformActionFunction::RunAsync() {
   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);
+  if (params->args.process_id == kDesktopProcessID &&
+      params->args.routing_id == kDesktopRoutingID) {
+#if defined(OS_CHROMEOS)
+    return RouteActionToAdapter(
+        params.get(), AutomationManagerAsh::GetInstance());
+#else
+    NOTREACHED();
+    return RespondNow(Error("Unexpected action on desktop automation tree;"
+                            " platform does not support desktop automation"));
+#endif  // defined(OS_CHROMEOS)
+  }
+  content::RenderFrameHost* rfh =
+      content::RenderFrameHost::FromID(params->args.process_id,
+                                       params->args.routing_id);
+  if (!rfh)
+    return RespondNow(Error("Ignoring action on destroyed node"));
+
+  const content::WebContents* contents =
+      content::WebContents::FromRenderFrameHost(rfh);
+  if (!CanRequestAutomation(extension(), automation_info, contents)) {
+    return RespondNow(
+        Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
+  }
+
+  RenderFrameHostActionAdapter adapter(rfh);
+  return RouteActionToAdapter(params.get(), &adapter);
+}
 
+ExtensionFunction::ResponseAction
+AutomationInternalPerformActionFunction::RouteActionToAdapter(
+    api::automation_internal::PerformAction::Params* params,
+    AutomationActionAdapter* adapter) {
+  int32 automation_id = params->args.automation_node_id;
   switch (params->args.action_type) {
-    case api::automation_internal::ACTION_TYPE_DO_DEFAULT:
-      rwh->AccessibilityDoDefaultAction(params->args.automation_node_id);
+    case api::automation_internal::ACTION_TYPE_DODEFAULT:
+      adapter->DoDefault(automation_id);
       break;
     case api::automation_internal::ACTION_TYPE_FOCUS:
-      rwh->AccessibilitySetFocus(params->args.automation_node_id);
+      adapter->Focus(automation_id);
       break;
-    case api::automation_internal::ACTION_TYPE_MAKE_VISIBLE:
-      rwh->AccessibilityScrollToMakeVisible(params->args.automation_node_id,
-                                            gfx::Rect());
+    case api::automation_internal::ACTION_TYPE_MAKEVISIBLE:
+      adapter->MakeVisible(automation_id);
       break;
-    case api::automation_internal::ACTION_TYPE_SET_SELECTION: {
-      extensions::api::automation_internal::SetSelectionParams selection_params;
+    case api::automation_internal::ACTION_TYPE_SETSELECTION: {
+      api::automation_internal::SetSelectionParams selection_params;
       EXTENSION_FUNCTION_VALIDATE(
-          extensions::api::automation_internal::SetSelectionParams::Populate(
+          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);
+      adapter->SetSelection(automation_id,
+                           selection_params.start_index,
+                           selection_params.end_index);
       break;
     }
     default:
       NOTREACHED();
   }
-  return true;
+  return RespondNow(NoArguments());
 }
 
-bool AutomationInternalEnableDesktopFunction::RunAsync() {
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableAutomationAPI)) {
-    return false;
-  }
-
+ExtensionFunction::ResponseAction
+AutomationInternalEnableDesktopFunction::Run() {
 #if defined(OS_CHROMEOS)
-  AutomationManagerViews::GetInstance()->Enable(browser_context());
-#else
-  error_ = "getDesktop is unsupported by this platform";
-#endif
+  const AutomationInfo* automation_info = AutomationInfo::Get(extension());
+  if (!automation_info || !automation_info->desktop)
+    return RespondNow(Error("desktop permission must be requested"));
 
-  return true;
+  AutomationManagerAsh::GetInstance()->Enable(browser_context());
+  return RespondNow(NoArguments());
+#else
+  return RespondNow(Error("getDesktop is unsupported by this platform"));
+#endif  // defined(OS_CHROMEOS)
 }
 
 }  // namespace extensions