Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4546)

Unified Diff: chrome/browser/extensions/api/automation_internal/automation_internal_api.cc

Issue 655273005: Implement AutomationNode.querySelector(). (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: Fix heap-use-after-free issue by not keeping a scoped_ptr to automation_api_helper in extension_hel… Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698