#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 {
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,
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:
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