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 aa2aa584702966fbdbc3fd9a69f5a1c5a9fd5e98..79632d3b530bc52b21b348d31aa958027faf606c 100644 |
--- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
+++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc |
@@ -6,7 +6,9 @@ |
#include <vector> |
+#include "base/strings/string16.h" |
#include "base/strings/string_number_conversions.h" |
+#include "base/strings/utf_string_conversions.h" |
#include "chrome/browser/accessibility/ax_tree_id_registry.h" |
#include "chrome/browser/extensions/api/automation_internal/automation_action_adapter.h" |
#include "chrome/browser/extensions/api/automation_internal/automation_util.h" |
@@ -21,9 +23,11 @@ |
#include "content/public/browser/browser_accessibility_state.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) |
@@ -36,14 +40,93 @@ class AutomationWebContentsObserver; |
DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver); |
+namespace extensions { |
+ |
namespace { |
const int kDesktopTreeID = 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."; |
+const char kNoMainFrame[] = "No main frame."; |
+const char kNoDocument[] = "No document."; |
+const char kNodeDestroyed[] = |
+ "querySelector sent on node which is no longer in the tree."; |
+ |
+// 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(); |
-namespace extensions { |
+ 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 = 0; |
+ PickleIterator iter(message); |
+ if (!message.ReadInt(&iter, &message_request_id)) |
+ return false; |
+ |
+ 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, |
+ ExtensionHostMsg_AutomationQuerySelector_Error error, |
+ int result_acc_obj_id) { |
+ std::string error_string; |
+ switch (error.value) { |
+ case ExtensionHostMsg_AutomationQuerySelector_Error::kNone: |
+ error_string = ""; |
+ break; |
+ case ExtensionHostMsg_AutomationQuerySelector_Error::kNoMainFrame: |
+ error_string = kNoMainFrame; |
+ break; |
+ case ExtensionHostMsg_AutomationQuerySelector_Error::kNoDocument: |
+ error_string = kNoDocument; |
+ break; |
+ case ExtensionHostMsg_AutomationQuerySelector_Error::kNodeDestroyed: |
+ error_string = kNodeDestroyed; |
+ break; |
+ } |
+ callback_.Run(error_string, 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 +147,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); } |
+ |
+ 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); |
+}; |
+ |
+} // namespace |
+ |
// Helper class that receives accessibility data from |WebContents|. |
class AutomationWebContentsObserver |
: public content::WebContentsObserver, |
@@ -101,33 +213,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()); |
@@ -276,4 +361,52 @@ 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.tree_id == kDesktopTreeID) { |
+ return RespondNow( |
+ Error("querySelector queries may not be used on the desktop.")); |
+ } |
+ AXTreeIDRegistry::FrameID frame_id = |
+ AXTreeIDRegistry::GetInstance()->GetFrameID(params->args.tree_id); |
+ content::RenderFrameHost* rfh = |
+ content::RenderFrameHost::FromID(frame_id.first, frame_id.second); |
+ if (!rfh) |
+ return RespondNow(Error("querySelector query sent on destroyed tree.")); |
+ |
+ 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 |