Chromium Code Reviews| Index: chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
| diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
| index 40e184bbf3b64d5776b523af8abb94ca9feb063d..18bf94fb5297c3f8df3d807edddac6883fe648ae 100644 |
| --- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
| +++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
| @@ -7,6 +7,7 @@ |
| #include <vector> |
| #include "base/strings/string_number_conversions.h" |
| +#include "base/strings/utf_string_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" |
| @@ -19,9 +20,11 @@ |
| #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/extension_messages.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #if defined(OS_CHROMEOS) |
| @@ -34,6 +37,8 @@ class AutomationWebContentsObserver; |
| DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver); |
| +namespace extensions { |
| + |
| namespace { |
| const int kDesktopProcessID = 0; |
| const int kDesktopRoutingID = 0; |
| @@ -41,9 +46,65 @@ const int kDesktopRoutingID = 0; |
| const char kCannotRequestAutomationOnPage[] = |
| "Cannot request automation tree on url \"*\". " |
| "Extension manifest must request permission to access this host."; |
| -} // namespace |
| +const char kRendererDestroyed[] = "The tab was closed."; |
| -namespace extensions { |
| +// Handles sending and receiving IPCs for a single querySelector request. On |
| +// creation, sends the request IPC, and is destroyed either when the response is |
| +// received or the renderer is destroyed. |
| +class QuerySelectorHandler : public content::WebContentsObserver { |
| + public: |
| + QuerySelectorHandler( |
| + content::WebContents* web_contents, |
| + int request_id, |
| + int acc_obj_id, |
| + const base::string16& query, |
| + const extensions::AutomationInternalQuerySelectorFunction::Callback& |
| + callback) |
| + : content::WebContentsObserver(web_contents), |
| + request_id_(request_id), |
| + callback_(callback) { |
| + content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); |
| + rvh->Send(new ExtensionMsg_AutomationQuerySelector( |
| + rvh->GetRoutingID(), request_id, acc_obj_id, query)); |
| + } |
| + |
| + ~QuerySelectorHandler() override {} |
| + |
| + bool OnMessageReceived(const IPC::Message& message) override { |
| + if (message.type() != ExtensionHostMsg_AutomationQuerySelector_Result::ID) |
| + return false; |
| + |
| + // There may be several requests in flight; check this response matches. |
| + int message_request_id; |
|
Devlin
2014/10/30 23:26:53
nit: initialize to 0.
aboxhall
2014/10/31 20:32:22
Done, although note that this is not the norm: htt
|
| + PickleIterator iter(message); |
| + CHECK(message.ReadInt(&iter, &message_request_id)); |
| + |
| + if (message_request_id != request_id_) |
| + return false; |
| + |
| + IPC_BEGIN_MESSAGE_MAP(QuerySelectorHandler, message) |
| + IPC_MESSAGE_HANDLER(ExtensionHostMsg_AutomationQuerySelector_Result, |
| + OnQueryResponse) |
| + IPC_END_MESSAGE_MAP() |
| + return true; |
| + } |
| + |
| + void WebContentsDestroyed() override { |
| + callback_.Run(kRendererDestroyed, 0); |
| + delete this; |
| + } |
| + |
| + private: |
| + void OnQueryResponse(int request_id, |
| + const std::string& error, |
| + int result_acc_obj_id) { |
| + callback_.Run(error, result_acc_obj_id); |
| + delete this; |
| + } |
| + |
| + int request_id_; |
| + const extensions::AutomationInternalQuerySelectorFunction::Callback callback_; |
| +}; |
| bool CanRequestAutomation(const Extension* extension, |
| const AutomationInfo* automation_info, |
| @@ -64,6 +125,35 @@ bool CanRequestAutomation(const Extension* extension, |
| extension, url, url, tab_id, process_id, &unused_error); |
| } |
| +// 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. |
| + void DoDefault(int32 id) override { rfh_->AccessibilityDoDefaultAction(id); } |
|
Devlin
2014/10/30 23:26:53
use int32_t and include stdint.h
aboxhall
2014/10/31 20:32:21
This isn't new code.
|
| + |
| + void Focus(int32 id) override { rfh_->AccessibilitySetFocus(id); } |
| + |
| + void MakeVisible(int32 id) override { |
| + rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect()); |
| + } |
| + |
| + void SetSelection(int32 id, int32 start, int32 end) override { |
| + rfh_->AccessibilitySetTextSelection(id, start, end); |
| + } |
| + |
| + private: |
| + content::RenderFrameHost* rfh_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter); |
| +}; |
|
Devlin
2014/10/30 23:26:53
nit: newline
aboxhall
2014/10/31 20:32:22
Done.
|
| +} // anonymous namespace |
|
Devlin
2014/10/30 23:26:53
nit: typically just "namespace" is enough.
aboxhall
2014/10/31 20:32:22
Done.
|
| + |
| + |
|
Devlin
2014/10/30 23:26:53
nit: just one newline. :)
aboxhall
2014/10/31 20:32:22
Done.
|
| // Helper class that receives accessibility data from |WebContents|. |
| class AutomationWebContentsObserver |
| : public content::WebContentsObserver, |
| @@ -101,33 +191,6 @@ class AutomationWebContentsObserver |
| DISALLOW_COPY_AND_ASSIGN(AutomationWebContentsObserver); |
| }; |
| -// 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. |
| - void DoDefault(int32 id) override { rfh_->AccessibilityDoDefaultAction(id); } |
| - |
| - void Focus(int32 id) override { rfh_->AccessibilitySetFocus(id); } |
| - |
| - void MakeVisible(int32 id) override { |
| - rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect()); |
| - } |
| - |
| - void SetSelection(int32 id, int32 start, int32 end) override { |
| - rfh_->AccessibilitySetTextSelection(id, start, end); |
| - } |
| - |
| - private: |
| - content::RenderFrameHost* rfh_; |
| - |
| - DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter); |
| -}; |
| - |
| ExtensionFunction::ResponseAction |
| AutomationInternalEnableTabFunction::Run() { |
| const AutomationInfo* automation_info = AutomationInfo::Get(extension()); |
| @@ -167,7 +230,7 @@ AutomationInternalEnableTabFunction::Run() { |
| return RespondNow( |
| ArgumentList(api::automation_internal::EnableTab::Results::Create( |
| rfh->GetProcess()->GetID(), rfh->GetRoutingID()))); |
| - } |
| +} |
| ExtensionFunction::ResponseAction |
| AutomationInternalPerformActionFunction::Run() { |
| @@ -251,4 +314,51 @@ AutomationInternalEnableDesktopFunction::Run() { |
| #endif // defined(OS_CHROMEOS) |
| } |
| +// static |
| +int AutomationInternalQuerySelectorFunction::query_request_id_counter_ = 0; |
| + |
| +ExtensionFunction::ResponseAction |
| +AutomationInternalQuerySelectorFunction::Run() { |
| + const AutomationInfo* automation_info = AutomationInfo::Get(extension()); |
| + EXTENSION_FUNCTION_VALIDATE(automation_info); |
| + |
| + using api::automation_internal::QuerySelector::Params; |
| + scoped_ptr<Params> params(Params::Create(*args_)); |
| + EXTENSION_FUNCTION_VALIDATE(params.get()); |
| + |
| + if (params->args.process_id == kDesktopProcessID && |
| + params->args.routing_id == kDesktopRoutingID) { |
| + return RespondNow( |
| + Error("querySelector queries may not be used on the desktop.")); |
| + } |
| + content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( |
| + params->args.process_id, params->args.routing_id); |
| + if (!rfh) |
| + return RespondNow(Error("querySelector query sent on destroyed node")); |
|
dmazzoni
2014/10/30 23:32:42
nit: this is really "destroyed tree" or "destroyed
aboxhall
2014/10/31 20:32:22
Good point, fixed.
|
| + |
| + content::WebContents* contents = |
| + content::WebContents::FromRenderFrameHost(rfh); |
| + |
| + int request_id = query_request_id_counter_++; |
| + base::string16 selector = base::UTF8ToUTF16(params->args.selector); |
| + |
| + // QuerySelectorHandler handles IPCs and deletes itself on completion. |
| + new QuerySelectorHandler( |
| + contents, request_id, params->args.automation_node_id, selector, |
| + base::Bind(&AutomationInternalQuerySelectorFunction::OnResponse, this)); |
| + |
| + return RespondLater(); |
| +} |
| + |
| +void AutomationInternalQuerySelectorFunction::OnResponse( |
| + const std::string& error, |
| + int result_acc_obj_id) { |
| + if (!error.empty()) { |
| + Respond(Error(error)); |
| + return; |
| + } |
| + |
| + Respond(OneArgument(new base::FundamentalValue(result_acc_obj_id))); |
| +} |
| + |
| } // namespace extensions |