Commit c54ab233 authored by kkania@chromium.org's avatar kkania@chromium.org

Allow AutomationProvider to work with other render view containers besides

tabs, particularly extension bg pages, infobars, and popups. This is
needed to enable WebDriver to work with these views.
BUG=93571
TEST=none


Review URL: http://codereview.chromium.org/8790003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113023 0039d316-1c4b-4281-b951-d872f2087c98
parent 9d996c06
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/automation/automation_util.h" #include "chrome/browser/automation/automation_util.h"
#include "chrome/common/automation_id.h"
#include "chrome/common/automation_messages.h" #include "chrome/common/automation_messages.h"
#include "content/browser/tab_contents/tab_contents.h"
namespace { namespace {
...@@ -61,15 +63,32 @@ bool GetBrowserFromJSONArgs( ...@@ -61,15 +63,32 @@ bool GetBrowserFromJSONArgs(
DictionaryValue* args, DictionaryValue* args,
Browser** browser, Browser** browser,
std::string* error) { std::string* error) {
int browser_index; if (args->HasKey("auto_id")) {
if (!args->GetInteger("windex", &browser_index)) { AutomationId id;
*error = "'windex' missing or invalid"; if (!GetAutomationIdFromJSONArgs(args, "auto_id", &id, error))
return false; return false;
} TabContents* tab;
*browser = automation_util::GetBrowserAt(browser_index); if (!automation_util::GetTabForId(id, &tab)) {
if (!*browser) { *error = "'auto_id' does not refer to an open tab";
*error = "Cannot locate browser from given index"; return false;
return false; }
Browser* container = automation_util::GetBrowserForTab(tab);
if (!container) {
*error = "tab does not belong to an open browser";
return false;
}
*browser = container;
} else {
int browser_index;
if (!args->GetInteger("windex", &browser_index)) {
*error = "'windex' missing or invalid";
return false;
}
*browser = automation_util::GetBrowserAt(browser_index);
if (!*browser) {
*error = "Cannot locate browser from given index";
return false;
}
} }
return true; return true;
} }
...@@ -78,19 +97,29 @@ bool GetTabFromJSONArgs( ...@@ -78,19 +97,29 @@ bool GetTabFromJSONArgs(
DictionaryValue* args, DictionaryValue* args,
TabContents** tab, TabContents** tab,
std::string* error) { std::string* error) {
int browser_index, tab_index; if (args->HasKey("auto_id")) {
if (!args->GetInteger("windex", &browser_index)) { AutomationId id;
*error = "'windex' missing or invalid"; if (!GetAutomationIdFromJSONArgs(args, "auto_id", &id, error))
return false; return false;
} if (!automation_util::GetTabForId(id, tab)) {
if (!args->GetInteger("tab_index", &tab_index)) { *error = "'auto_id' does not refer to an open tab";
*error = "'tab_index' missing or invalid"; return false;
return false; }
} } else {
*tab = automation_util::GetTabContentsAt(browser_index, tab_index); int browser_index, tab_index;
if (!*tab) { if (!args->GetInteger("windex", &browser_index)) {
*error = "Cannot locate tab from given indices"; *error = "'windex' missing or invalid";
return false; return false;
}
if (!args->GetInteger("tab_index", &tab_index)) {
*error = "'tab_index' missing or invalid";
return false;
}
*tab = automation_util::GetTabContentsAt(browser_index, tab_index);
if (!*tab) {
*error = "Cannot locate tab from given indices";
return false;
}
} }
return true; return true;
} }
...@@ -103,3 +132,40 @@ bool GetBrowserAndTabFromJSONArgs( ...@@ -103,3 +132,40 @@ bool GetBrowserAndTabFromJSONArgs(
return GetBrowserFromJSONArgs(args, browser, error) && return GetBrowserFromJSONArgs(args, browser, error) &&
GetTabFromJSONArgs(args, tab, error); GetTabFromJSONArgs(args, tab, error);
} }
bool GetAutomationIdFromJSONArgs(
DictionaryValue* args,
const std::string& key_name,
AutomationId* id,
std::string* error) {
Value* id_value;
if (!args->Get(key_name, &id_value)) {
*error = base::StringPrintf("Missing parameter '%s'", key_name.c_str());
return false;
}
return AutomationId::FromValue(id_value, id, error);
}
bool GetRenderViewFromJSONArgs(
DictionaryValue* args,
Profile* profile,
RenderViewHost** rvh,
std::string* error) {
Value* id_value;
if (args->Get("auto_id", &id_value)) {
AutomationId id;
if (!AutomationId::FromValue(id_value, &id, error))
return false;
if (!automation_util::GetRenderViewForId(id, profile, rvh)) {
*error = "ID does not correspond to an open view";
return false;
}
} else {
// If the render view id is not specified, check for browser/tab indices.
TabContents* tab = NULL;
if (!GetTabFromJSONArgs(args, &tab, error))
return false;
*rvh = tab->render_view_host();
}
return true;
}
// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -12,8 +12,11 @@ ...@@ -12,8 +12,11 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
class AutomationId;
class AutomationProvider; class AutomationProvider;
class Browser; class Browser;
class Profile;
class RenderViewHost;
class TabContents; class TabContents;
namespace base { namespace base {
...@@ -73,4 +76,22 @@ bool GetBrowserAndTabFromJSONArgs(base::DictionaryValue* args, ...@@ -73,4 +76,22 @@ bool GetBrowserAndTabFromJSONArgs(base::DictionaryValue* args,
TabContents** tab, TabContents** tab,
std::string* error) WARN_UNUSED_RESULT; std::string* error) WARN_UNUSED_RESULT;
// Gets an automation ID from the given value in the given dicitionary |args|.
// Returns true on success and sets |id|. Otherwise, |error| will be set.
bool GetAutomationIdFromJSONArgs(
base::DictionaryValue* args,
const std::string& key_name,
AutomationId* id,
std::string* error) WARN_UNUSED_RESULT;
// Gets the render view specified by the given dictionary |args|. |args|
// should contain a key 'view_id' which refers to an automation ID for the
// render view. Returns true on success and sets |rvh|. Otherwise, |error|
// will be set.
bool GetRenderViewFromJSONArgs(
base::DictionaryValue* args,
Profile* profile,
RenderViewHost** rvh,
std::string* error) WARN_UNUSED_RESULT;
#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_JSON_H_ #endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_JSON_H_
...@@ -281,6 +281,10 @@ NavigationNotificationObserver::NavigationNotificationObserver( ...@@ -281,6 +281,10 @@ NavigationNotificationObserver::NavigationNotificationObserver(
navigations_remaining_(number_of_navigations), navigations_remaining_(number_of_navigations),
navigation_started_(false), navigation_started_(false),
use_json_interface_(use_json_interface) { use_json_interface_(use_json_interface) {
if (number_of_navigations == 0) {
ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
return;
}
DCHECK_LT(0, navigations_remaining_); DCHECK_LT(0, navigations_remaining_);
content::Source<NavigationController> source(controller_); content::Source<NavigationController> source(controller_);
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source); registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
...@@ -652,10 +656,14 @@ void ExtensionReadyNotificationObserver::Observe( ...@@ -652,10 +656,14 @@ void ExtensionReadyNotificationObserver::Observe(
} }
if (use_json_) { if (use_json_) {
DictionaryValue dict; AutomationJSONReply reply(automation_, reply_message_.release());
dict.SetString("id", extension_->id()); if (extension_) {
AutomationJSONReply(automation_, reply_message_.release()) DictionaryValue dict;
.SendSuccess(&dict); dict.SetString("id", extension_->id());
reply.SendSuccess(&dict);
} else {
reply.SendError("Extension could not be installed");
}
} else { } else {
if (id_ == AutomationMsg_InstallExtension::ID) { if (id_ == AutomationMsg_InstallExtension::ID) {
// A handle of zero indicates an error. // A handle of zero indicates an error.
......
...@@ -8,16 +8,26 @@ ...@@ -8,16 +8,26 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/stringprintf.h"
#include "base/string_number_conversions.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/time.h" #include "base/time.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/automation/automation_provider_json.h" #include "chrome/browser/automation/automation_provider_json.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/restore_tab_helper.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/automation_id.h"
#include "chrome/common/chrome_view_type.h"
#include "chrome/common/extensions/extension.h"
#include "content/browser/renderer_host/render_view_host.h" #include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -135,6 +145,18 @@ TabContents* GetTabContentsAt(int browser_index, int tab_index) { ...@@ -135,6 +145,18 @@ TabContents* GetTabContentsAt(int browser_index, int tab_index) {
return browser->GetTabContentsAt(tab_index); return browser->GetTabContentsAt(tab_index);
} }
Browser* GetBrowserForTab(TabContents* tab) {
BrowserList::const_iterator browser_iter = BrowserList::begin();
for (; browser_iter != BrowserList::end(); ++browser_iter) {
Browser* browser = *browser_iter;
for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
if (browser->GetTabContentsAt(tab_index) == tab)
return browser;
}
}
return NULL;
}
net::URLRequestContextGetter* GetRequestContext(TabContents* contents) { net::URLRequestContextGetter* GetRequestContext(TabContents* contents) {
// Since we may be on the UI thread don't call GetURLRequestContext(). // Since we may be on the UI thread don't call GetURLRequestContext().
// Get the request context specific to the current TabContents and app. // Get the request context specific to the current TabContents and app.
...@@ -381,4 +403,155 @@ bool SendErrorIfModalDialogActive(AutomationProvider* provider, ...@@ -381,4 +403,155 @@ bool SendErrorIfModalDialogActive(AutomationProvider* provider,
return active; return active;
} }
AutomationId GetIdForTab(const TabContentsWrapper* tab) {
return AutomationId(
AutomationId::kTypeTab,
base::IntToString(tab->restore_tab_helper()->session_id().id()));
}
AutomationId GetIdForExtensionView(const ExtensionHost* ext_host) {
AutomationId::Type type;
switch (ext_host->extension_host_type()) {
case chrome::VIEW_TYPE_EXTENSION_POPUP:
type = AutomationId::kTypeExtensionPopup;
break;
case chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE:
type = AutomationId::kTypeExtensionBgPage;
break;
case chrome::VIEW_TYPE_EXTENSION_INFOBAR:
type = AutomationId::kTypeExtensionInfobar;
break;
default:
type = AutomationId::kTypeInvalid;
break;
}
// Since these extension views do not permit navigation, using the
// renderer process and view ID should suffice.
std::string id = base::StringPrintf("%d|%d",
ext_host->render_view_host()->routing_id(),
ext_host->render_process_host()->GetID());
return AutomationId(type, id);
}
AutomationId GetIdForExtension(const Extension* extension) {
return AutomationId(AutomationId::kTypeExtension, extension->id());
}
bool GetTabForId(const AutomationId& id, TabContents** tab) {
if (id.type() != AutomationId::kTypeTab)
return false;
BrowserList::const_iterator iter = BrowserList::begin();
for (; iter != BrowserList::end(); ++iter) {
Browser* browser = *iter;
for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
TabContentsWrapper* wrapper = browser->GetTabContentsWrapperAt(tab_index);
if (base::IntToString(wrapper->restore_tab_helper()->session_id().id()) ==
id.id()) {
*tab = wrapper->tab_contents();
return true;
}
}
}
return false;
}
namespace {
bool GetExtensionRenderViewForId(
const AutomationId& id,
Profile* profile,
RenderViewHost** rvh) {
content::ViewType view_type;
switch (id.type()) {
case AutomationId::kTypeExtensionPopup:
view_type = chrome::VIEW_TYPE_EXTENSION_POPUP;
break;
case AutomationId::kTypeExtensionBgPage:
view_type = chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE;
break;
case AutomationId::kTypeExtensionInfobar:
view_type = chrome::VIEW_TYPE_EXTENSION_INFOBAR;
break;
default:
return false;
}
ExtensionProcessManager* extension_mgr =
profile->GetExtensionProcessManager();
ExtensionProcessManager::const_iterator iter;
for (iter = extension_mgr->begin(); iter != extension_mgr->end();
++iter) {
ExtensionHost* host = *iter;
AutomationId this_id = GetIdForExtensionView(host);
if (id == this_id) {
*rvh = host->render_view_host();
return true;
}
}
return false;
}
} // namespace
bool GetRenderViewForId(
const AutomationId& id,
Profile* profile,
RenderViewHost** rvh) {
switch (id.type()) {
case AutomationId::kTypeTab: {
TabContents* tab;
if (!GetTabForId(id, &tab))
return false;
*rvh = tab->render_view_host();
break;
}
case AutomationId::kTypeExtensionPopup:
case AutomationId::kTypeExtensionBgPage:
case AutomationId::kTypeExtensionInfobar:
if (!GetExtensionRenderViewForId(id, profile, rvh))
return false;
break;
default:
return false;
}
return true;
}
bool GetExtensionForId(
const AutomationId& id,
Profile* profile,
const Extension** extension) {
if (id.type() != AutomationId::kTypeExtension)
return false;
ExtensionService* service = profile->GetExtensionService();
const Extension* installed_extension =
service->GetInstalledExtension(id.id());
if (installed_extension)
*extension = installed_extension;
return !!installed_extension;
}
bool DoesObjectWithIdExist(const AutomationId& id, Profile* profile) {
switch (id.type()) {
case AutomationId::kTypeTab: {
TabContents* tab;
return GetTabForId(id, &tab);
}
case AutomationId::kTypeExtensionPopup:
case AutomationId::kTypeExtensionBgPage:
case AutomationId::kTypeExtensionInfobar: {
RenderViewHost* rvh;
return GetExtensionRenderViewForId(id, profile, &rvh);
}
case AutomationId::kTypeExtension: {
const Extension* extension;
return GetExtensionForId(id, profile, &extension);
}
default:
break;
}
return false;
}
} // namespace automation_util } // namespace automation_util
...@@ -10,10 +10,16 @@ ...@@ -10,10 +10,16 @@
#include "base/basictypes.h" #include "base/basictypes.h"
class AutomationId;
class AutomationProvider; class AutomationProvider;
class Browser; class Browser;
class Extension;
class ExtensionHost;
class GURL; class GURL;
class Profile;
class RenderViewHost;
class TabContents; class TabContents;
class TabContentsWrapper;
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
...@@ -35,6 +41,9 @@ Browser* GetBrowserAt(int index); ...@@ -35,6 +41,9 @@ Browser* GetBrowserAt(int index);
// |BrowserList|. If any of these indices are invalid, NULL will be returned. // |BrowserList|. If any of these indices are invalid, NULL will be returned.
TabContents* GetTabContentsAt(int browser_index, int tab_index); TabContents* GetTabContentsAt(int browser_index, int tab_index);
// Returns the browser that contains the given tab, or NULL if none exists.
Browser* GetBrowserForTab(TabContents* tab);
// Gets the size and value of the cookie string for |url| in the given tab. // Gets the size and value of the cookie string for |url| in the given tab.
// Can be called from any thread. // Can be called from any thread.
void GetCookies(const GURL& url, void GetCookies(const GURL& url,
...@@ -77,6 +86,31 @@ void SetCookieJSON(AutomationProvider* provider, ...@@ -77,6 +86,31 @@ void SetCookieJSON(AutomationProvider* provider,
bool SendErrorIfModalDialogActive(AutomationProvider* provider, bool SendErrorIfModalDialogActive(AutomationProvider* provider,
IPC::Message* message); IPC::Message* message);
// Returns a valid automation ID for the given tab.
AutomationId GetIdForTab(const TabContentsWrapper* tab);
// Returns a valid automation ID for the extension host.
AutomationId GetIdForExtensionView(const ExtensionHost* ext_host);
// Returns a valid automation ID for the extension.
AutomationId GetIdForExtension(const Extension* extension);
// Gets the tab for the given ID. Returns true on success.
bool GetTabForId(const AutomationId& id, TabContents** tab);
// Gets the render view for the given ID. Returns true on success.
bool GetRenderViewForId(const AutomationId& id,
Profile* profile,
RenderViewHost** rvh);
// Gets the extension for the given ID. Returns true on success.
bool GetExtensionForId(const AutomationId& id,
Profile* profile,
const Extension** extension);
// Returns whether the given ID refers to an actual automation entity.
bool DoesObjectWithIdExist(const AutomationId& id, Profile* profile);
} // namespace automation_util } // namespace automation_util
#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_UTIL_H_ #endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_UTIL_H_
...@@ -61,6 +61,8 @@ ...@@ -61,6 +61,8 @@
'common/autofill_messages.h', 'common/autofill_messages.h',
'common/automation_constants.cc', 'common/automation_constants.cc',
'common/automation_constants.h', 'common/automation_constants.h',
'common/automation_id.cc',
'common/automation_id.h',
'common/automation_messages.cc', 'common/automation_messages.cc',
'common/automation_messages.h', 'common/automation_messages.h',
'common/automation_messages_internal.h', 'common/automation_messages_internal.h',
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/automation_id.h"
#include "base/stringprintf.h"
#include "base/values.h"
using base::DictionaryValue;
using base::Value;
// static
bool AutomationId::FromValue(
Value* value, AutomationId* id, std::string* error) {
DictionaryValue* dict;
if (!value->GetAsDictionary(&dict)) {
*error = "automation ID must be a dictionary";
return false;
}
int type;
if (!dict->GetInteger("type", &type)) {
*error = "automation ID 'type' missing or invalid";
return false;
}
std::string type_id;
if (!dict->GetString("id", &type_id)) {
*error = "automation ID 'type_id' missing or invalid";
return false;
}
*id = AutomationId(static_cast<Type>(type), type_id);
return true;
}
// static
bool AutomationId::FromValueInDictionary(
DictionaryValue* dict,
const std::string& key,
AutomationId* id,
std::string* error) {
Value* id_value;
if (!dict->Get(key, &id_value)) {
*error = base::StringPrintf("automation ID '%s' missing", key.c_str());
return false;
}
return FromValue(id_value, id, error);
}
AutomationId::AutomationId() : type_(kTypeInvalid) { }
AutomationId::AutomationId(Type type, const std::string& id)
: type_(type), id_(id) { }
bool AutomationId::operator==(const AutomationId& id) const {
return type_ == id.type_ && id_ == id.id_;
}
DictionaryValue* AutomationId::ToValue() const {
DictionaryValue* dict = new DictionaryValue();
dict->SetInteger("type", type_);
dict->SetString("id", id_);
return dict;
}
bool AutomationId::is_valid() const {
return type_ != kTypeInvalid;
}
AutomationId::Type AutomationId::type() const {
return type_;
}
const std::string& AutomationId::id() const {
return id_;
}
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_AUTOMATION_ID_H_
#define CHROME_COMMON_AUTOMATION_ID_H_
#pragma once
#include <string>
namespace base {
class DictionaryValue;
class Value;
}
// A unique ID that JSON automation clients can use to refer to browser
// entities. The ID has a type so that:
// 1) supplying an ID of the wrong type can be detected.
// 2) the client does not have to explicitly supply the type in case multiple
// ID types can be accepted (e.g., can use a tab ID or extension popup ID for
// executing javascript).
class AutomationId {
public:
// The value of each entry should be preserved.
enum Type {
kTypeInvalid = 0,
kTypeTab,
kTypeExtensionPopup,
kTypeExtensionBgPage,
kTypeExtensionInfobar,
kTypeExtension,
};
static bool FromValue(
base::Value* value, AutomationId* id, std::string* error);
static bool FromValueInDictionary(
base::DictionaryValue* dict, const std::string& key, AutomationId* id,
std::string* error);
// Constructs an invalid ID.
AutomationId();
// Constructs an ID from the given type and type-specific ID.
AutomationId(Type type, const std::string& id);
bool operator==(const AutomationId& id) const;
// Returns a new dictionary equivalent to this ID.
base::DictionaryValue* ToValue() const;
// Returns whether the automation ID is valid.
bool is_valid() const;
Type type() const;
const std::string& id() const;
private:
Type type_;
std::string id_;
};
#endif // CHROME_COMMON_AUTOMATION_ID_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment