summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-27 16:21:21 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-27 16:21:21 +0000
commit9e0e15862416722ed91ae3c9094adcc503285233 (patch)
treee0c60d8c1dddb977ad7c8b16d3445a9434fd4fd7
parentd0cd54b2249a54ac0818c123ca11bdc7abfda90d (diff)
downloadchromium_src-9e0e15862416722ed91ae3c9094adcc503285233.zip
chromium_src-9e0e15862416722ed91ae3c9094adcc503285233.tar.gz
chromium_src-9e0e15862416722ed91ae3c9094adcc503285233.tar.bz2
[chromedriver] Improve testing experience when using extensions.
-Ensure we use the ID from the CRX, in case the user depends on the ID in some way -Wait for extension background pages to load BUG=none R=chrisgao@chromium.org Review URL: https://codereview.chromium.org/23054005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219801 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/chromedriver/alert_commands.cc4
-rw-r--r--chrome/test/chromedriver/chrome/chrome_desktop_impl.cc76
-rw-r--r--chrome/test/chromedriver/chrome/chrome_desktop_impl.h12
-rw-r--r--chrome/test/chromedriver/chrome/stub_web_view.cc3
-rw-r--r--chrome/test/chromedriver/chrome/stub_web_view.h3
-rw-r--r--chrome/test/chromedriver/chrome/web_view.h8
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.cc10
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.h3
-rw-r--r--chrome/test/chromedriver/chrome_launcher.cc205
-rw-r--r--chrome/test/chromedriver/chrome_launcher.h3
-rw-r--r--chrome/test/chromedriver/chrome_launcher_unittest.cc71
-rw-r--r--chrome/test/chromedriver/client/chromedriver.py10
-rw-r--r--chrome/test/chromedriver/commands_unittest.cc10
-rw-r--r--chrome/test/chromedriver/element_commands.cc3
-rw-r--r--chrome/test/chromedriver/element_util.cc3
-rw-r--r--chrome/test/chromedriver/session.cc14
-rw-r--r--chrome/test/chromedriver/session.h7
-rw-r--r--chrome/test/chromedriver/session_commands.cc26
-rwxr-xr-xchrome/test/chromedriver/test/run_py_tests.py27
-rw-r--r--chrome/test/chromedriver/test/webserver.py1
-rw-r--r--chrome/test/chromedriver/window_commands.cc12
22 files changed, 353 insertions, 159 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 88abe82..2cc4cc1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -950,6 +950,7 @@
'chrome_devtools_lib',
'../base/base.gyp:base',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../crypto/crypto.gyp:crypto',
'../net/net.gyp:net',
'../ui/ui.gyp:ui',
],
diff --git a/chrome/test/chromedriver/alert_commands.cc b/chrome/test/chromedriver/alert_commands.cc
index 85836b9..de7118d 100644
--- a/chrome/test/chromedriver/alert_commands.cc
+++ b/chrome/test/chromedriver/alert_commands.cc
@@ -31,8 +31,8 @@ Status ExecuteAlertCommand(
if (status.IsError())
return status;
- status = web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
- session->page_load_timeout);
+ status = web_view->WaitForPendingNavigations(
+ session->GetCurrentFrameId(), session->page_load_timeout, true);
if (status.IsError() && status.code() != kUnexpectedAlertOpen)
return status;
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index c454917..799abbe 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -87,42 +87,54 @@ ChromeDesktopImpl::~ChromeDesktopImpl() {
base::CloseProcessHandle(process_);
}
-Status ChromeDesktopImpl::GetAutomationExtension(
- AutomationExtension** extension) {
- if (!automation_extension_) {
- base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(10);
- std::string id;
- while (base::Time::Now() < deadline) {
- WebViewsInfo views_info;
- Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
- if (status.IsError())
- return status;
-
- for (size_t i = 0; i < views_info.GetSize(); ++i) {
- if (views_info.Get(i).url.find(
- "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb") == 0) {
- id = views_info.Get(i).id;
- break;
- }
- }
- if (!id.empty())
- break;
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
- }
- if (id.empty())
- return Status(kUnknownError, "automation extension cannot be found");
-
- scoped_ptr<WebView> web_view(new WebViewImpl(
- id, GetBuildNo(), devtools_http_client_->CreateClient(id), log_));
- Status status = web_view->ConnectIfNecessary();
+Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url,
+ const base::TimeDelta& timeout,
+ scoped_ptr<WebView>* web_view) {
+ base::Time deadline = base::Time::Now() + timeout;
+ std::string id;
+ while (base::Time::Now() < deadline) {
+ WebViewsInfo views_info;
+ Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
if (status.IsError())
return status;
- // Wait for the extension background page to load.
- status = web_view->WaitForPendingNavigations(
- std::string(), 5 * 60 * 1000);
+ for (size_t i = 0; i < views_info.GetSize(); ++i) {
+ if (views_info.Get(i).url.find(url) == 0) {
+ id = views_info.Get(i).id;
+ break;
+ }
+ }
+ if (!id.empty())
+ break;
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ }
+ if (id.empty())
+ return Status(kUnknownError, "page could not be found: " + url);
+
+ scoped_ptr<WebView> web_view_tmp(new WebViewImpl(
+ id, GetBuildNo(), devtools_http_client_->CreateClient(id), log_));
+ Status status = web_view_tmp->ConnectIfNecessary();
+ if (status.IsError())
+ return status;
+
+ status = web_view_tmp->WaitForPendingNavigations(
+ std::string(), deadline - base::Time::Now(), false);
+ if (status.IsOk())
+ *web_view = web_view_tmp.Pass();
+ return status;
+}
+
+Status ChromeDesktopImpl::GetAutomationExtension(
+ AutomationExtension** extension) {
+ if (!automation_extension_) {
+ scoped_ptr<WebView> web_view;
+ Status status = WaitForPageToLoad(
+ "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/"
+ "_generated_background_page.html",
+ base::TimeDelta::FromSeconds(10),
+ &web_view);
if (status.IsError())
- return status;
+ return Status(kUnknownError, "cannot get automation extension", status);
automation_extension_.reset(new AutomationExtension(web_view.Pass()));
}
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
index bc17620..d033e23 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
@@ -9,12 +9,18 @@
#include "base/compiler_specific.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
#include "base/process/process.h"
#include "chrome/test/chromedriver/chrome/chrome_impl.h"
+namespace base {
+class TimeDelta;
+}
+
class AutomationExtension;
class DevToolsHttpClient;
class Status;
+class WebView;
class ChromeDesktopImpl : public ChromeImpl {
public:
@@ -27,6 +33,12 @@ class ChromeDesktopImpl : public ChromeImpl {
base::ScopedTempDir* extension_dir);
virtual ~ChromeDesktopImpl();
+ // Waits for a page with the given URL to appear and finish loading.
+ // Returns an error if the timeout is exceeded.
+ Status WaitForPageToLoad(const std::string& url,
+ const base::TimeDelta& timeout,
+ scoped_ptr<WebView>* web_view);
+
// Overridden from Chrome:
virtual Status GetAutomationExtension(
AutomationExtension** extension) OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.cc b/chrome/test/chromedriver/chrome/stub_web_view.cc
index a40fb60..9d90fd7 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.cc
+++ b/chrome/test/chromedriver/chrome/stub_web_view.cc
@@ -91,7 +91,8 @@ Status StubWebView::DeleteCookie(const std::string& name,
}
Status StubWebView::WaitForPendingNavigations(const std::string& frame_id,
- int timeout) {
+ const base::TimeDelta& timeout,
+ bool stop_load_on_timeout) {
return Status(kOk);
}
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.h b/chrome/test/chromedriver/chrome/stub_web_view.h
index 952b1b5..b19baf3 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.h
+++ b/chrome/test/chromedriver/chrome/stub_web_view.h
@@ -54,7 +54,8 @@ class StubWebView : public WebView {
virtual Status DeleteCookie(const std::string& name,
const std::string& url) OVERRIDE;
virtual Status WaitForPendingNavigations(const std::string& frame_id,
- int timeout) OVERRIDE;
+ const base::TimeDelta& timeout,
+ bool stop_load_on_timeout) OVERRIDE;
virtual Status IsPendingNavigation(
const std::string& frame_id, bool* is_pending) OVERRIDE;
virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome/web_view.h b/chrome/test/chromedriver/chrome/web_view.h
index 5e11d59..e3cf35a 100644
--- a/chrome/test/chromedriver/chrome/web_view.h
+++ b/chrome/test/chromedriver/chrome/web_view.h
@@ -112,10 +112,12 @@ class WebView {
// If |frame_id| is "", waits for navigations on the main frame.
// If a modal dialog appears while waiting, kUnexpectedAlertOpen will be
// returned.
- // If there are still pending navigations after |timeout|ms,
- // page load is stopped, and kTimeout status is returned.
+ // If timeout is exceeded, will return a timeout status.
+ // If |stop_load_on_timeout| is true, will attempt to stop the page load on
+ // timeout before returning the timeout status.
virtual Status WaitForPendingNavigations(const std::string& frame_id,
- int timeout) = 0;
+ const base::TimeDelta& timeout,
+ bool stop_load_on_timeout) = 0;
// Returns whether the frame is pending navigation.
virtual Status IsPendingNavigation(
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index b463b0e..dd5d8ac 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -320,13 +320,15 @@ Status WebViewImpl::DeleteCookie(const std::string& name,
}
Status WebViewImpl::WaitForPendingNavigations(const std::string& frame_id,
- int timeout) {
+ const base::TimeDelta& timeout,
+ bool stop_load_on_timeout) {
log_->AddEntry(Log::kLog, "waiting for pending navigations...");
Status status = client_->HandleEventsUntil(
- base::Bind(&WebViewImpl::IsNotPendingNavigation, base::Unretained(this),
+ base::Bind(&WebViewImpl::IsNotPendingNavigation,
+ base::Unretained(this),
frame_id),
- base::TimeDelta::FromMilliseconds(timeout));
- if (status.code() == kTimeout) {
+ timeout);
+ if (status.code() == kTimeout && stop_load_on_timeout) {
log_->AddEntry(Log::kLog, "timed out. stopping navigations...");
scoped_ptr<base::Value> unused_value;
EvaluateScript(std::string(), "window.stop();", &unused_value);
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index 239d7b4..1067ad0 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -74,7 +74,8 @@ class WebViewImpl : public WebView {
virtual Status DeleteCookie(const std::string& name,
const std::string& url) OVERRIDE;
virtual Status WaitForPendingNavigations(const std::string& frame_id,
- int timeout) OVERRIDE;
+ const base::TimeDelta& timeout,
+ bool stop_load_on_timeout) OVERRIDE;
virtual Status IsPendingNavigation(
const std::string& frame_id, bool* is_pending) OVERRIDE;
virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index 369b24e..36e2a1c 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -36,8 +36,10 @@
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/user_data_dir.h"
#include "chrome/test/chromedriver/chrome/version.h"
+#include "chrome/test/chromedriver/chrome/web_view.h"
#include "chrome/test/chromedriver/chrome/zip.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
+#include "crypto/sha2.h"
namespace {
@@ -79,7 +81,8 @@ Status PrepareCommandLine(int port,
const Capabilities& capabilities,
CommandLine* prepared_command,
base::ScopedTempDir* user_data_dir,
- base::ScopedTempDir* extension_dir) {
+ base::ScopedTempDir* extension_dir,
+ std::vector<std::string>* extension_bg_pages) {
CommandLine command = capabilities.command;
base::FilePath program = command.GetProgram();
if (program.empty()) {
@@ -135,8 +138,11 @@ Status PrepareCommandLine(int port,
return Status(kUnknownError,
"cannot create temp dir for unpacking extensions");
}
- Status status = internal::ProcessExtensions(
- capabilities.extensions, extension_dir->path(), true, &command);
+ Status status = internal::ProcessExtensions(capabilities.extensions,
+ extension_dir->path(),
+ true,
+ &command,
+ extension_bg_pages);
if (status.IsError())
return status;
@@ -205,8 +211,13 @@ Status LaunchDesktopChrome(
CommandLine command(CommandLine::NO_PROGRAM);
base::ScopedTempDir user_data_dir;
base::ScopedTempDir extension_dir;
- Status status = PrepareCommandLine(port, capabilities,
- &command, &user_data_dir, &extension_dir);
+ std::vector<std::string> extension_bg_pages;
+ Status status = PrepareCommandLine(port,
+ capabilities,
+ &command,
+ &user_data_dir,
+ &extension_dir,
+ &extension_bg_pages);
if (status.IsError())
return status;
@@ -272,12 +283,25 @@ Status LaunchDesktopChrome(
}
return status;
}
- chrome->reset(new ChromeDesktopImpl(devtools_client.Pass(),
- devtools_event_listeners,
- log,
- process,
- &user_data_dir,
- &extension_dir));
+ scoped_ptr<ChromeDesktopImpl> chrome_desktop(
+ new ChromeDesktopImpl(devtools_client.Pass(),
+ devtools_event_listeners,
+ log,
+ process,
+ &user_data_dir,
+ &extension_dir));
+ for (size_t i = 0; i < extension_bg_pages.size(); ++i) {
+ scoped_ptr<WebView> web_view;
+ Status status = chrome_desktop->WaitForPageToLoad(
+ extension_bg_pages[i], base::TimeDelta::FromSeconds(10), &web_view);
+ if (status.IsError()) {
+ return Status(kUnknownError,
+ "failed to wait for extension background page to load: " +
+ extension_bg_pages[i],
+ status);
+ }
+ }
+ *chrome = chrome_desktop.Pass();
return Status(kOk);
}
@@ -358,43 +382,137 @@ Status LaunchChrome(
namespace internal {
+void ConvertHexadecimalToIDAlphabet(std::string* id) {
+ for (size_t i = 0; i < id->size(); ++i) {
+ int val;
+ if (base::HexStringToInt(base::StringPiece(id->begin() + i,
+ id->begin() + i + 1),
+ &val)) {
+ (*id)[i] = val + 'a';
+ } else {
+ (*id)[i] = 'a';
+ }
+ }
+}
+
+std::string GenerateExtensionId(const std::string& input) {
+ uint8 hash[16];
+ crypto::SHA256HashString(input, hash, sizeof(hash));
+ std::string output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
+ ConvertHexadecimalToIDAlphabet(&output);
+ return output;
+}
+
+Status GetExtensionBackgroundPage(const base::DictionaryValue* manifest,
+ const std::string& id,
+ std::string* bg_page) {
+ std::string bg_page_name;
+ bool persistent = true;
+ manifest->GetBoolean("background.persistent", &persistent);
+ const base::Value* unused_value;
+ if (manifest->Get("background.scripts", &unused_value))
+ bg_page_name = "_generated_background_page.html";
+ manifest->GetString("background.page", &bg_page_name);
+ manifest->GetString("background_page", &bg_page_name);
+ if (bg_page_name.empty() || !persistent)
+ return Status(kOk);
+ *bg_page = "chrome-extension://" + id + "/" + bg_page_name;
+ return Status(kOk);
+}
+
+Status ProcessExtension(const std::string& extension,
+ const base::FilePath& temp_dir,
+ base::FilePath* path,
+ std::string* bg_page) {
+ // Decodes extension string.
+ // Some WebDriver client base64 encoders follow RFC 1521, which require that
+ // 'encoded lines be no more than 76 characters long'. Just remove any
+ // newlines.
+ std::string extension_base64;
+ RemoveChars(extension, "\n", &extension_base64);
+ std::string decoded_extension;
+ if (!base::Base64Decode(extension_base64, &decoded_extension))
+ return Status(kUnknownError, "cannot base64 decode");
+
+ // Get extension's ID from public key in crx file.
+ // Assumes crx v2. See http://developer.chrome.com/extensions/crx.html.
+ std::string key_len_str = decoded_extension.substr(8, 4);
+ if (key_len_str.size() != 4)
+ return Status(kUnknownError, "cannot extract public key length");
+ uint32 key_len = *reinterpret_cast<const uint32*>(key_len_str.c_str());
+ std::string public_key = decoded_extension.substr(16, key_len);
+ if (key_len != public_key.size())
+ return Status(kUnknownError, "invalid public key length");
+ std::string public_key_base64;
+ if (!base::Base64Encode(public_key, &public_key_base64))
+ return Status(kUnknownError, "cannot base64 encode public key");
+ std::string id = GenerateExtensionId(public_key);
+
+ // Unzip the crx file.
+ base::ScopedTempDir temp_crx_dir;
+ if (!temp_crx_dir.CreateUniqueTempDir())
+ return Status(kUnknownError, "cannot create temp dir");
+ base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
+ int size = static_cast<int>(decoded_extension.length());
+ if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size) !=
+ size) {
+ return Status(kUnknownError, "cannot write file");
+ }
+ base::FilePath extension_dir = temp_dir.AppendASCII("extension_" + id);
+ if (!zip::Unzip(extension_crx, extension_dir))
+ return Status(kUnknownError, "cannot unzip");
+
+ // Parse the manifest and set the 'key' if not already present.
+ base::FilePath manifest_path(extension_dir.AppendASCII("manifest.json"));
+ std::string manifest_data;
+ if (!file_util::ReadFileToString(manifest_path, &manifest_data))
+ return Status(kUnknownError, "cannot read manifest");
+ scoped_ptr<base::Value> manifest_value(base::JSONReader::Read(manifest_data));
+ base::DictionaryValue* manifest;
+ if (!manifest_value || !manifest_value->GetAsDictionary(&manifest))
+ return Status(kUnknownError, "invalid manifest");
+ if (!manifest->HasKey("key")) {
+ manifest->SetString("key", public_key_base64);
+ base::JSONWriter::Write(manifest, &manifest_data);
+ if (file_util::WriteFile(
+ manifest_path, manifest_data.c_str(), manifest_data.size()) !=
+ static_cast<int>(manifest_data.size())) {
+ return Status(kUnknownError, "cannot add 'key' to manifest");
+ }
+ }
+
+ // Get extension's background page URL, if there is one.
+ std::string bg_page_tmp;
+ Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp);
+ if (status.IsError())
+ return status;
+
+ *path = extension_dir;
+ if (bg_page_tmp.size())
+ *bg_page = bg_page_tmp;
+ return Status(kOk);
+}
+
Status ProcessExtensions(const std::vector<std::string>& extensions,
const base::FilePath& temp_dir,
bool include_automation_extension,
- CommandLine* command) {
+ CommandLine* command,
+ std::vector<std::string>* bg_pages) {
+ std::vector<std::string> bg_pages_tmp;
std::vector<base::FilePath::StringType> extension_paths;
- size_t count = 0;
- for (std::vector<std::string>::const_iterator it = extensions.begin();
- it != extensions.end(); ++it) {
- std::string extension_base64;
- // Decodes extension string.
- // Some WebDriver client base64 encoders follow RFC 1521, which require that
- // 'encoded lines be no more than 76 characters long'. Just remove any
- // newlines.
- RemoveChars(*it, "\n", &extension_base64);
- std::string decoded_extension;
- if (!base::Base64Decode(extension_base64, &decoded_extension))
- return Status(kUnknownError, "failed to base64 decode extension");
-
- // Writes decoded extension into a temporary .crx file.
- base::ScopedTempDir temp_crx_dir;
- if (!temp_crx_dir.CreateUniqueTempDir())
- return Status(kUnknownError,
- "cannot create temp dir for writing extension CRX file");
- base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
- int size = static_cast<int>(decoded_extension.length());
- if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size)
- != size) {
- return Status(kUnknownError, "failed to write extension file");
+ for (size_t i = 0; i < extensions.size(); ++i) {
+ base::FilePath path;
+ std::string bg_page;
+ Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page);
+ if (status.IsError()) {
+ return Status(
+ kUnknownError,
+ base::StringPrintf("cannot process extension #%" PRIuS, i + 1),
+ status);
}
-
- // Unzips the temporary .crx file.
- count++;
- base::FilePath extension_dir = temp_dir.AppendASCII(
- base::StringPrintf("extension%" PRIuS, count));
- if (!zip::Unzip(extension_crx, extension_dir))
- return Status(kUnknownError, "failed to unzip the extension CRX file");
- extension_paths.push_back(extension_dir.value());
+ extension_paths.push_back(path.value());
+ if (bg_page.length())
+ bg_pages_tmp.push_back(bg_page);
}
if (include_automation_extension) {
@@ -415,6 +533,7 @@ Status ProcessExtensions(const std::vector<std::string>& extensions,
extension_paths, FILE_PATH_LITERAL(','));
command->AppendSwitchNative("load-extension", extension_paths_value);
}
+ bg_pages->swap(bg_pages_tmp);
return Status(kOk);
}
diff --git a/chrome/test/chromedriver/chrome_launcher.h b/chrome/test/chromedriver/chrome_launcher.h
index 405c275..d671b56 100644
--- a/chrome/test/chromedriver/chrome_launcher.h
+++ b/chrome/test/chromedriver/chrome_launcher.h
@@ -42,7 +42,8 @@ namespace internal {
Status ProcessExtensions(const std::vector<std::string>& extensions,
const base::FilePath& temp_dir,
bool include_automation_extension,
- CommandLine* command);
+ CommandLine* command,
+ std::vector<std::string>* bg_pages);
Status PrepareUserDataDir(
const base::FilePath& user_data_dir,
const base::DictionaryValue* custom_prefs,
diff --git a/chrome/test/chromedriver/chrome_launcher_unittest.cc b/chrome/test/chromedriver/chrome_launcher_unittest.cc
index 369fb9c..c613460 100644
--- a/chrome/test/chromedriver/chrome_launcher_unittest.cc
+++ b/chrome/test/chromedriver/chrome_launcher_unittest.cc
@@ -21,62 +21,80 @@ TEST(ProcessExtensions, NoExtension) {
CommandLine command(CommandLine::NO_PROGRAM);
std::vector<std::string> extensions;
base::FilePath extension_dir;
+ std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir,
- false, &command);
+ false, &command, &bg_pages);
ASSERT_TRUE(status.IsOk());
ASSERT_FALSE(command.HasSwitch("load-extension"));
+ ASSERT_EQ(0u, bg_pages.size());
}
-TEST(ProcessExtensions, SingleExtension) {
+bool AddExtensionForInstall(const std::string& relative_path,
+ std::vector<std::string>* extensions) {
base::FilePath source_root;
PathService::Get(base::DIR_SOURCE_ROOT, &source_root);
base::FilePath crx_file_path = source_root.AppendASCII(
- "chrome/test/data/chromedriver/ext_test_1.crx");
+ "chrome/test/data/chromedriver/" + relative_path);
std::string crx_contents;
- ASSERT_TRUE(file_util::ReadFileToString(crx_file_path, &crx_contents));
+ if (!file_util::ReadFileToString(crx_file_path, &crx_contents))
+ return false;
- std::vector<std::string> extensions;
std::string crx_encoded;
- ASSERT_TRUE(base::Base64Encode(crx_contents, &crx_encoded));
- extensions.push_back(crx_encoded);
+ if (!base::Base64Encode(crx_contents, &crx_encoded))
+ return false;
+ extensions->push_back(crx_encoded);
+ return true;
+}
+
+TEST(ProcessExtensions, SingleExtensionWithBgPage) {
+ std::vector<std::string> extensions;
+ ASSERT_TRUE(AddExtensionForInstall("ext_slow_loader.crx", &extensions));
base::ScopedTempDir extension_dir;
ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
CommandLine command(CommandLine::NO_PROGRAM);
+ std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
- false, &command);
+ false, &command, &bg_pages);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(command.HasSwitch("load-extension"));
base::FilePath temp_ext_path = command.GetSwitchValuePath("load-extension");
ASSERT_TRUE(base::PathExists(temp_ext_path));
+ std::string manifest_txt;
+ ASSERT_TRUE(file_util::ReadFileToString(
+ temp_ext_path.AppendASCII("manifest.json"), &manifest_txt));
+ scoped_ptr<base::Value> manifest(base::JSONReader::Read(manifest_txt));
+ ASSERT_TRUE(manifest);
+ base::DictionaryValue* manifest_dict = NULL;
+ ASSERT_TRUE(manifest->GetAsDictionary(&manifest_dict));
+ std::string key;
+ ASSERT_TRUE(manifest_dict->GetString("key", &key));
+ ASSERT_EQ(
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8qhZthEHjTIA3IYMzi79s2KFepVziY0du"
+ "JzHcqRUB/YHSGseIUqcYXGazJhDz/"
+ "4FbRg8ef9fQazL1UbMMGBIf4za1kJ2os2MsRrNXzHslkbtcLVj2VfofhuHJmu+"
+ "CnKJ77UWamJiNAaQSiclu4duwnEWrkx+g/8ChQfhZzC4jvQIDAQAB",
+ key);
+ ASSERT_EQ(1u, bg_pages.size());
+ ASSERT_EQ(
+ "chrome-extension://jijhlkpcmmeckhlgdipjhnchhoabdjae/"
+ "_generated_background_page.html",
+ bg_pages[0]);
}
-TEST(ProcessExtensions, MultipleExtensions) {
- base::FilePath source_root;
- PathService::Get(base::DIR_SOURCE_ROOT, &source_root);
- base::FilePath test_ext_path = source_root.AppendASCII(
- "chrome/test/data/chromedriver");
- base::FilePath test_crx_1 = test_ext_path.AppendASCII("ext_test_1.crx");
- base::FilePath test_crx_2 = test_ext_path.AppendASCII("ext_test_2.crx");
-
- std::string crx_1_contents, crx_2_contents;
- ASSERT_TRUE(file_util::ReadFileToString(test_crx_1, &crx_1_contents));
- ASSERT_TRUE(file_util::ReadFileToString(test_crx_2, &crx_2_contents));
-
+TEST(ProcessExtensions, MultipleExtensionsNoBgPages) {
std::vector<std::string> extensions;
- std::string crx_1_encoded, crx_2_encoded;
- ASSERT_TRUE(base::Base64Encode(crx_1_contents, &crx_1_encoded));
- ASSERT_TRUE(base::Base64Encode(crx_2_contents, &crx_2_encoded));
- extensions.push_back(crx_1_encoded);
- extensions.push_back(crx_2_encoded);
+ ASSERT_TRUE(AddExtensionForInstall("ext_test_1.crx", &extensions));
+ ASSERT_TRUE(AddExtensionForInstall("ext_test_2.crx", &extensions));
base::ScopedTempDir extension_dir;
ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
CommandLine command(CommandLine::NO_PROGRAM);
+ std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
- false, &command);
+ false, &command, &bg_pages);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(command.HasSwitch("load-extension"));
CommandLine::StringType ext_paths = command.GetSwitchValueNative(
@@ -86,6 +104,7 @@ TEST(ProcessExtensions, MultipleExtensions) {
ASSERT_EQ(2u, ext_path_list.size());
ASSERT_TRUE(base::PathExists(base::FilePath(ext_path_list[0])));
ASSERT_TRUE(base::PathExists(base::FilePath(ext_path_list[1])));
+ ASSERT_EQ(0u, bg_pages.size());
}
namespace {
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index db71e2b..ae2ecd8 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -93,7 +93,7 @@ class ChromeDriver(object):
}
}
- self._session_id = self._executor.Execute(
+ self._session_id = self._ExecuteCommand(
Command.NEW_SESSION, params)['sessionId']
def _WrapValue(self, value):
@@ -126,12 +126,16 @@ class ChromeDriver(object):
else:
return value
- def ExecuteCommand(self, command, params={}):
- params['sessionId'] = self._session_id
+ def _ExecuteCommand(self, command, params={}):
params = self._WrapValue(params)
response = self._executor.Execute(command, params)
if response['status'] != 0:
raise _ExceptionForResponse(response)
+ return response
+
+ def ExecuteCommand(self, command, params={}):
+ params['sessionId'] = self._session_id
+ response = self._ExecuteCommand(command, params)
return self._UnwrapValue(response['value'])
def GetWindowHandles(self):
diff --git a/chrome/test/chromedriver/commands_unittest.cc b/chrome/test/chromedriver/commands_unittest.cc
index 064fb95..e377d29 100644
--- a/chrome/test/chromedriver/commands_unittest.cc
+++ b/chrome/test/chromedriver/commands_unittest.cc
@@ -338,7 +338,7 @@ class FindElementWebView : public StubWebView {
TEST(CommandsTest, SuccessfulFindElement) {
FindElementWebView web_view(true, kElementExistsQueryTwice);
Session session("id");
- session.implicit_wait = 1000;
+ session.implicit_wait = base::TimeDelta::FromSeconds(1);
session.SwitchToSubFrame("frame_id1", std::string());
base::DictionaryValue params;
params.SetString("using", "id");
@@ -367,7 +367,7 @@ TEST(CommandsTest, FailedFindElement) {
TEST(CommandsTest, SuccessfulFindElements) {
FindElementWebView web_view(false, kElementExistsQueryTwice);
Session session("id");
- session.implicit_wait = 1000;
+ session.implicit_wait = base::TimeDelta::FromSeconds(1);
session.SwitchToSubFrame("frame_id2", std::string());
base::DictionaryValue params;
params.SetString("using", "name");
@@ -401,7 +401,7 @@ TEST(CommandsTest, FailedFindElements) {
TEST(CommandsTest, SuccessfulFindChildElement) {
FindElementWebView web_view(true, kElementExistsQueryTwice);
Session session("id");
- session.implicit_wait = 1000;
+ session.implicit_wait = base::TimeDelta::FromSeconds(1);
session.SwitchToSubFrame("frame_id3", std::string());
base::DictionaryValue params;
params.SetString("using", "tag name");
@@ -439,7 +439,7 @@ TEST(CommandsTest, FailedFindChildElement) {
TEST(CommandsTest, SuccessfulFindChildElements) {
FindElementWebView web_view(false, kElementExistsQueryTwice);
Session session("id");
- session.implicit_wait = 1000;
+ session.implicit_wait = base::TimeDelta::FromSeconds(1);
session.SwitchToSubFrame("frame_id4", std::string());
base::DictionaryValue params;
params.SetString("using", "class name");
@@ -480,7 +480,7 @@ TEST(CommandsTest, FailedFindChildElements) {
TEST(CommandsTest, TimeoutInFindElement) {
Session session("id");
FindElementWebView web_view(true, kElementExistsTimeout);
- session.implicit_wait = 2;
+ session.implicit_wait = base::TimeDelta::FromMilliseconds(2);
base::DictionaryValue params;
params.SetString("using", "id");
params.SetString("value", "a");
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index 81e83a3..989ed72 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -47,8 +47,7 @@ Status SendKeysToElement(
return status;
if (is_focused)
break;
- if ((base::Time::Now() - start_time).InMilliseconds() >=
- session->implicit_wait) {
+ if (base::Time::Now() - start_time >= session->implicit_wait) {
return Status(kElementNotVisible);
}
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 6c4bca0..0ba4246 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -271,8 +271,7 @@ Status FindElement(
}
}
- if ((base::Time::Now() - start_time).InMilliseconds() >=
- session->implicit_wait) {
+ if (base::Time::Now() - start_time >= session->implicit_wait) {
if (only_one) {
return Status(kNoSuchElement);
} else {
diff --git a/chrome/test/chromedriver/session.cc b/chrome/test/chromedriver/session.cc
index ea6da2c..5a18729 100644
--- a/chrome/test/chromedriver/session.cc
+++ b/chrome/test/chromedriver/session.cc
@@ -28,10 +28,8 @@ Session::Session(const std::string& id)
detach(false),
sticky_modifiers(0),
mouse_position(0, 0),
- implicit_wait(0),
- page_load_timeout(kDefaultPageLoadTimeoutMs),
- script_timeout(0) {
-}
+ page_load_timeout(
+ base::TimeDelta::FromMilliseconds(kDefaultPageLoadTimeoutMs)) {}
Session::Session(const std::string& id, scoped_ptr<Chrome> chrome)
: id(id),
@@ -40,11 +38,9 @@ Session::Session(const std::string& id, scoped_ptr<Chrome> chrome)
chrome(chrome.Pass()),
sticky_modifiers(0),
mouse_position(0, 0),
- implicit_wait(0),
- page_load_timeout(kDefaultPageLoadTimeoutMs),
- script_timeout(0),
- capabilities(CreateCapabilities()) {
-}
+ page_load_timeout(
+ base::TimeDelta::FromMilliseconds(kDefaultPageLoadTimeoutMs)),
+ capabilities(CreateCapabilities()) {}
Session::~Session() {}
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index 40ec9e9..fef177a 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -12,6 +12,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/time/time.h"
#include "chrome/test/chromedriver/basic_types.h"
#include "chrome/test/chromedriver/chrome/geoposition.h"
@@ -59,9 +60,9 @@ struct Session {
// this list will be empty.
std::list<FrameInfo> frames;
WebPoint mouse_position;
- int implicit_wait;
- int page_load_timeout;
- int script_timeout;
+ base::TimeDelta implicit_wait;
+ base::TimeDelta page_load_timeout;
+ base::TimeDelta script_timeout;
scoped_ptr<std::string> prompt_text;
scoped_ptr<Geoposition> overridden_geoposition;
// Logs that populate from DevTools events.
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 23f362b..53b3933 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -212,18 +212,22 @@ Status ExecuteSetTimeout(
if (!params.GetString("type", &type))
return Status(kUnknownError, "'type' must be a string");
- int ms = static_cast<int>(ms_double);
+ base::TimeDelta timeout =
+ base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
// TODO(frankf): implicit and script timeout should be cleared
// if negative timeout is specified.
- if (type == "implicit")
- session->implicit_wait = ms;
- else if (type == "script")
- session->script_timeout = ms;
- else if (type == "page load")
+ if (type == "implicit") {
+ session->implicit_wait = timeout;
+ } else if (type == "script") {
+ session->script_timeout = timeout;
+ } else if (type == "page load") {
session->page_load_timeout =
- ((ms < 0) ? Session::kDefaultPageLoadTimeoutMs : ms);
- else
+ ((timeout < base::TimeDelta()) ? base::TimeDelta::FromMilliseconds(
+ Session::kDefaultPageLoadTimeoutMs)
+ : timeout);
+ } else {
return Status(kUnknownError, "unknown type of timeout:" + type);
+ }
return Status(kOk);
}
@@ -234,7 +238,8 @@ Status ExecuteSetScriptTimeout(
double ms;
if (!params.GetDouble("ms", &ms) || ms < 0)
return Status(kUnknownError, "'ms' must be a non-negative number");
- session->script_timeout = static_cast<int>(ms);
+ session->script_timeout =
+ base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
return Status(kOk);
}
@@ -245,7 +250,8 @@ Status ExecuteImplicitlyWait(
double ms;
if (!params.GetDouble("ms", &ms) || ms < 0)
return Status(kUnknownError, "'ms' must be a non-negative number");
- session->implicit_wait = static_cast<int>(ms);
+ session->implicit_wait =
+ base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
return Status(kOk);
}
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 0b9099d..2b01b5a 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -12,6 +12,7 @@ import os
import sys
import socket
import tempfile
+import threading
import time
import unittest
@@ -638,14 +639,32 @@ class ChromeSwitchesCapabilityTest(ChromeDriverBaseTest):
class ChromeExtensionsCapabilityTest(ChromeDriverBaseTest):
"""Tests that chromedriver properly processes chromeOptions.extensions."""
+ def _PackExtension(self, ext_path):
+ return base64.b64encode(open(ext_path, 'rb').read())
+
def testExtensionsInstall(self):
"""Checks that chromedriver can take the extensions."""
crx_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.crx')
crx_2 = os.path.join(_TEST_DATA_DIR, 'ext_test_2.crx')
- crx_1_encoded = base64.b64encode(open(crx_1, 'rb').read())
- crx_2_encoded = base64.b64encode(open(crx_2, 'rb').read())
- extensions = [crx_1_encoded, crx_2_encoded]
- self.CreateDriver(chrome_extensions=extensions)
+ self.CreateDriver(chrome_extensions=[self._PackExtension(crx_1),
+ self._PackExtension(crx_2)])
+
+ def testWaitsForExtensionToLoad(self):
+ did_load_event = threading.Event()
+ server = webserver.SyncWebServer()
+ def RunServer():
+ time.sleep(5)
+ server.RespondWithContent('<html>iframe</html>')
+ did_load_event.set()
+
+ thread = threading.Thread(target=RunServer)
+ thread.daemon = True
+ thread.start()
+ crx = os.path.join(_TEST_DATA_DIR, 'ext_slow_loader.crx')
+ driver = self.CreateDriver(
+ chrome_switches=['user-agent=' + server.GetUrl()],
+ chrome_extensions=[self._PackExtension(crx)])
+ self.assertTrue(did_load_event.is_set())
class ChromeLogPathCapabilityTest(ChromeDriverBaseTest):
diff --git a/chrome/test/chromedriver/test/webserver.py b/chrome/test/chromedriver/test/webserver.py
index fb48cc5..716e0f4 100644
--- a/chrome/test/chromedriver/test/webserver.py
+++ b/chrome/test/chromedriver/test/webserver.py
@@ -117,6 +117,7 @@ class WebServer(object):
self._root_dir = os.path.abspath(root_dir)
self._server = _BaseServer(self._OnRequest, server_cert_and_key_path)
self._thread = threading.Thread(target=self._server.serve_forever)
+ self._thread.daemon = True
self._thread.start()
self._path_data_map = {}
self._path_data_lock = threading.Lock()
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index ad62a9e..b3b9f8a 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -215,18 +215,16 @@ Status ExecuteWindowCommand(
else
break;
}
- nav_status =
- web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
- session->page_load_timeout);
+ nav_status = web_view->WaitForPendingNavigations(
+ session->GetCurrentFrameId(), session->page_load_timeout, true);
if (nav_status.IsError())
return nav_status;
status = command.Run(session, web_view, params, value);
}
- nav_status =
- web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
- session->page_load_timeout);
+ nav_status = web_view->WaitForPendingNavigations(
+ session->GetCurrentFrameId(), session->page_load_timeout, true);
if (status.IsOk() && nav_status.IsError() &&
nav_status.code() != kDisconnected &&
@@ -278,7 +276,7 @@ Status ExecuteExecuteAsyncScript(
return web_view->CallUserAsyncFunction(
session->GetCurrentFrameId(), "function(){" + script + "}", *args,
- base::TimeDelta::FromMilliseconds(session->script_timeout), value);
+ session->script_timeout, value);
}
Status ExecuteSwitchToFrame(