summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsrawlins@google.com <srawlins@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-21 20:04:49 +0000
committersrawlins@google.com <srawlins@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-21 20:04:49 +0000
commitd682978b9c077121cb8f9d7c65604ea9199438e2 (patch)
tree90bc4375cdff462291674de6d8881a62929032b0
parent612f95c234dc405a9194b42b74f870f75a89d99b (diff)
downloadchromium_src-d682978b9c077121cb8f9d7c65604ea9199438e2.zip
chromium_src-d682978b9c077121cb8f9d7c65604ea9199438e2.tar.gz
chromium_src-d682978b9c077121cb8f9d7c65604ea9199438e2.tar.bz2
[Chromedriver] Add Device Metrics override support to ChromeDriver via Capabilities
BUG=chromedriver:399 All of the code reviews for this patch were written against https://codereview.chromium.org/251933005/ . I used the wrong email address to create said patch, so I have created this one with the correct email address. Review URL: https://codereview.chromium.org/288193004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271947 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_tests.gypi28
-rw-r--r--chrome/test/chromedriver/capabilities.cc66
-rw-r--r--chrome/test/chromedriver/capabilities.h4
-rw-r--r--chrome/test/chromedriver/capabilities_unittest.cc95
-rw-r--r--chrome/test/chromedriver/chrome/chrome.h3
-rw-r--r--chrome/test/chromedriver/chrome/chrome_desktop_impl.cc7
-rw-r--r--chrome/test/chromedriver/chrome/chrome_desktop_impl.h1
-rw-r--r--chrome/test/chromedriver/chrome/chrome_impl.cc9
-rw-r--r--chrome/test/chromedriver/chrome/chrome_impl.h1
-rw-r--r--chrome/test/chromedriver/chrome/device_metrics.cc16
-rw-r--r--chrome/test/chromedriver/chrome/device_metrics.h21
-rw-r--r--chrome/test/chromedriver/chrome/devtools_http_client.cc13
-rw-r--r--chrome/test/chromedriver/chrome/devtools_http_client.h6
-rw-r--r--chrome/test/chromedriver/chrome/mobile_device.cc87
-rw-r--r--chrome/test/chromedriver/chrome/mobile_device.h24
-rw-r--r--chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc55
-rw-r--r--chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h46
-rw-r--r--chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc117
-rw-r--r--chrome/test/chromedriver/chrome/stub_chrome.cc4
-rw-r--r--chrome/test/chromedriver/chrome/stub_chrome.h1
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.cc6
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.h7
-rw-r--r--chrome/test/chromedriver/chrome_launcher.cc13
-rw-r--r--chrome/test/chromedriver/client/chromedriver.py6
-rw-r--r--chrome/test/chromedriver/client/webelement.py3
-rwxr-xr-xchrome/test/chromedriver/embed_mobile_devices_in_cpp.py48
-rw-r--r--chrome/test/chromedriver/session.h2
-rw-r--r--chrome/test/chromedriver/session_commands.cc3
-rwxr-xr-xchrome/test/chromedriver/test/run_py_tests.py56
-rw-r--r--chrome/test/chromedriver/test/webserver.py32
30 files changed, 762 insertions, 18 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 94ea1e0..6dcc50b 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -402,6 +402,8 @@
'<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/user_data_dir.h',
'<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/embedded_automation_extension.cc',
'<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/embedded_automation_extension.h',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/mobile_device_list.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/mobile_device_list.h',
'test/chromedriver/chrome/adb.h',
'test/chromedriver/chrome/adb_impl.cc',
'test/chromedriver/chrome/adb_impl.h',
@@ -425,6 +427,8 @@
'test/chromedriver/chrome/debugger_tracker.h',
'test/chromedriver/chrome/device_manager.cc',
'test/chromedriver/chrome/device_manager.h',
+ 'test/chromedriver/chrome/device_metrics.cc',
+ 'test/chromedriver/chrome/device_metrics.h',
'test/chromedriver/chrome/devtools_client.h',
'test/chromedriver/chrome/devtools_client_impl.cc',
'test/chromedriver/chrome/devtools_client_impl.h',
@@ -445,6 +449,10 @@
'test/chromedriver/chrome/javascript_dialog_manager.h',
'test/chromedriver/chrome/log.h',
'test/chromedriver/chrome/log.cc',
+ 'test/chromedriver/chrome/mobile_device.cc',
+ 'test/chromedriver/chrome/mobile_device.h',
+ 'test/chromedriver/chrome/mobile_emulation_override_manager.cc',
+ 'test/chromedriver/chrome/mobile_emulation_override_manager.h',
'test/chromedriver/chrome/navigation_tracker.cc',
'test/chromedriver/chrome/navigation_tracker.h',
'test/chromedriver/chrome/performance_logger.h',
@@ -550,6 +558,25 @@
],
'message': 'Generating sources for embedding automation extension',
},
+ {
+ 'action_name': 'embed_mobile_devices_in_cpp',
+ 'inputs': [
+ 'test/chromedriver/cpp_source.py',
+ 'test/chromedriver/embed_mobile_devices_in_cpp.py',
+ '../third_party/WebKit/Source/devtools/front_end/elements/OverridesView.js',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/mobile_device_list.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome/mobile_device_list.h',
+ ],
+ 'action': [ 'python',
+ 'test/chromedriver/embed_mobile_devices_in_cpp.py',
+ '--directory',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/test/chromedriver/chrome',
+ '../third_party/WebKit/Source/devtools/front_end/elements/OverridesView.js',
+ ],
+ 'message': 'Generating sources for embedding mobile devices in chromedriver',
+ },
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [ 4267, ],
@@ -699,6 +726,7 @@
'test/chromedriver/chrome/geolocation_override_manager_unittest.cc',
'test/chromedriver/chrome/heap_snapshot_taker_unittest.cc',
'test/chromedriver/chrome/javascript_dialog_manager_unittest.cc',
+ 'test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc',
'test/chromedriver/chrome/navigation_tracker_unittest.cc',
'test/chromedriver/chrome/performance_logger_unittest.cc',
'test/chromedriver/chrome/status_unittest.cc',
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 518af52..b6fb0a5 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -17,6 +17,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/mobile_device.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/logging.h"
#include "net/base/net_util.h"
@@ -84,6 +85,70 @@ Status ParseLogPath(const base::Value& option, Capabilities* capabilities) {
return Status(kOk);
}
+Status ParseDeviceName(std::string device_name, Capabilities* capabilities) {
+ scoped_ptr<MobileDevice> device;
+ Status status = FindMobileDevice(device_name, &device);
+
+ if (status.IsError()) {
+ return Status(kUnknownError,
+ "'" + device_name + "' must be a valid device",
+ status);
+ }
+
+ capabilities->device_metrics.reset(device->device_metrics.release());
+ capabilities->switches.SetSwitch("user-agent", device->user_agent);
+
+ return Status(kOk);
+}
+
+Status ParseMobileEmulation(const base::Value& option,
+ Capabilities* capabilities) {
+ const base::DictionaryValue* mobile_emulation;
+ if (!option.GetAsDictionary(&mobile_emulation))
+ return Status(kUnknownError, "'mobileEmulation' must be a dictionary");
+
+ if (mobile_emulation->HasKey("deviceName")) {
+ // Cannot use any other options with deviceName.
+ if (mobile_emulation->size() > 1)
+ return Status(kUnknownError, "'deviceName' must be used alone");
+
+ std::string device_name;
+ if (!mobile_emulation->GetString("deviceName", &device_name))
+ return Status(kUnknownError, "'deviceName' must be a string");
+
+ return ParseDeviceName(device_name, capabilities);
+ }
+
+ if (mobile_emulation->HasKey("deviceMetrics")) {
+ const base::DictionaryValue* metrics;
+ if (!mobile_emulation->GetDictionary("deviceMetrics", &metrics))
+ return Status(kUnknownError, "'deviceMetrics' must be a dictionary");
+
+ int width;
+ int height;
+ double device_scale_factor;
+ if (!metrics->GetInteger("width", &width) ||
+ !metrics->GetInteger("height", &height) ||
+ !metrics->GetDouble("pixelRatio", &device_scale_factor))
+ return Status(kUnknownError, "invalid 'deviceMetrics'");
+
+ DeviceMetrics* device_metrics =
+ new DeviceMetrics(width, height, device_scale_factor);
+ capabilities->device_metrics =
+ scoped_ptr<DeviceMetrics>(device_metrics);
+ }
+
+ if (mobile_emulation->HasKey("userAgent")) {
+ std::string user_agent;
+ if (!mobile_emulation->GetString("userAgent", &user_agent))
+ return Status(kUnknownError, "'userAgent' must be a string");
+
+ capabilities->switches.SetSwitch("user-agent", user_agent);
+ }
+
+ return Status(kOk);
+}
+
Status ParseSwitches(const base::Value& option,
Capabilities* capabilities) {
const base::ListValue* switches_list = NULL;
@@ -272,6 +337,7 @@ Status ParseChromeOptions(
parser_map["args"] = base::Bind(&ParseSwitches);
parser_map["binary"] = base::Bind(&ParseFilePath, &capabilities->binary);
parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
+ parser_map["mobileEmulation"] = base::Bind(&ParseMobileEmulation);
parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
parser_map["extensions"] = base::Bind(&ParseExtensions);
parser_map["forceDevToolsScreenshot"] = base::Bind(
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index 998c56a..c21c045 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -14,6 +14,7 @@
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/net/net_util.h"
@@ -91,6 +92,9 @@ struct Capabilities {
// ChromeDriver dies.
bool detach;
+ // Device metrics for use in Device Emulation.
+ scoped_ptr<DeviceMetrics> device_metrics;
+
// Set of switches which should be removed from default list when launching
// Chrome.
std::set<std::string> exclude_switches;
diff --git a/chrome/test/chromedriver/capabilities_unittest.cc b/chrome/test/chromedriver/capabilities_unittest.cc
index f4b313e..ea1e87c 100644
--- a/chrome/test/chromedriver/capabilities_unittest.cc
+++ b/chrome/test/chromedriver/capabilities_unittest.cc
@@ -361,3 +361,98 @@ TEST(ParseCapabilities, UseExistingBrowser) {
ASSERT_EQ("abc", capabilities.debugger_address.host());
ASSERT_EQ(123, capabilities.debugger_address.port());
}
+
+TEST(ParseCapabilities, MobileEmulationUserAgent) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetString("userAgent", "Agent Smith");
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_TRUE(status.IsOk());
+
+ ASSERT_EQ(1u, capabilities.switches.GetSize());
+ ASSERT_TRUE(capabilities.switches.HasSwitch("user-agent"));
+ ASSERT_EQ("Agent Smith", capabilities.switches.GetSwitchValue("user-agent"));
+}
+
+TEST(ParseCapabilities, MobileEmulationDeviceMetrics) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetInteger("deviceMetrics.width", 360);
+ mobile_emulation.SetInteger("deviceMetrics.height", 640);
+ mobile_emulation.SetDouble("deviceMetrics.pixelRatio", 3.0);
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_TRUE(status.IsOk());
+
+ ASSERT_EQ(360, capabilities.device_metrics->width);
+ ASSERT_EQ(640, capabilities.device_metrics->height);
+ ASSERT_EQ(3.0, capabilities.device_metrics->device_scale_factor);
+}
+
+TEST(ParseCapabilities, MobileEmulationDeviceName) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetString("deviceName", "Google Nexus 5");
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_TRUE(status.IsOk());
+
+ ASSERT_EQ(1u, capabilities.switches.GetSize());
+ ASSERT_TRUE(capabilities.switches.HasSwitch("user-agent"));
+ ASSERT_EQ(
+ "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) "
+ "AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 "
+ "Mobile Safari/535.19",
+ capabilities.switches.GetSwitchValue("user-agent"));
+
+ ASSERT_EQ(360, capabilities.device_metrics->width);
+ ASSERT_EQ(640, capabilities.device_metrics->height);
+ ASSERT_EQ(3.0, capabilities.device_metrics->device_scale_factor);
+}
+
+TEST(ParseCapabilities, MobileEmulationNotDict) {
+ Capabilities capabilities;
+ base::DictionaryValue caps;
+ caps.SetString("chromeOptions.mobileEmulation", "Google Nexus 5");
+ Status status = capabilities.Parse(caps);
+ ASSERT_FALSE(status.IsOk());
+}
+
+TEST(ParseCapabilities, MobileEmulationDeviceMetricsNotDict) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetInteger("deviceMetrics", 360);
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_FALSE(status.IsOk());
+}
+
+TEST(ParseCapabilities, MobileEmulationDeviceMetricsNotNumbers) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetString("deviceMetrics.width", "360");
+ mobile_emulation.SetString("deviceMetrics.height", "640");
+ mobile_emulation.SetString("deviceMetrics.pixelRatio", "3.0");
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_FALSE(status.IsOk());
+}
+
+TEST(ParseCapabilities, MobileEmulationBadDict) {
+ Capabilities capabilities;
+ base::DictionaryValue mobile_emulation;
+ mobile_emulation.SetString("deviceName", "Google Nexus 5");
+ mobile_emulation.SetInteger("deviceMetrics.width", 360);
+ mobile_emulation.SetInteger("deviceMetrics.height", 640);
+ mobile_emulation.SetDouble("deviceMetrics.pixelRatio", 3.0);
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.mobileEmulation", mobile_emulation.DeepCopy());
+ Status status = capabilities.Parse(caps);
+ ASSERT_FALSE(status.IsOk());
+}
diff --git a/chrome/test/chromedriver/chrome/chrome.h b/chrome/test/chromedriver/chrome/chrome.h
index 56885bc..277c58d 100644
--- a/chrome/test/chromedriver/chrome/chrome.h
+++ b/chrome/test/chromedriver/chrome/chrome.h
@@ -40,6 +40,9 @@ class Chrome {
// Get the operation system where Chrome is running.
virtual std::string GetOperatingSystemName() = 0;
+ // Return whether the mobileEmulation capability has been enabled.
+ virtual bool IsMobileEmulationEnabled() const = 0;
+
// Quits Chrome.
virtual Status Quit() = 0;
};
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index 8cdd130..d737249 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -117,7 +117,8 @@ Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url,
scoped_ptr<WebView> web_view_tmp(
new WebViewImpl(id,
devtools_http_client_->browser_info(),
- devtools_http_client_->CreateClient(id)));
+ devtools_http_client_->CreateClient(id),
+ devtools_http_client_->device_metrics()));
Status status = web_view_tmp->ConnectIfNecessary();
if (status.IsError())
return status;
@@ -155,6 +156,10 @@ std::string ChromeDesktopImpl::GetOperatingSystemName() {
return base::SysInfo::OperatingSystemName();
}
+bool ChromeDesktopImpl::IsMobileEmulationEnabled() const {
+ return devtools_http_client_->device_metrics() != NULL;
+}
+
Status ChromeDesktopImpl::QuitImpl() {
if (!KillProcess(process_))
return Status(kUnknownError, "cannot kill Chrome");
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
index c8e86a7..d448419 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
@@ -49,6 +49,7 @@ class ChromeDesktopImpl : public ChromeImpl {
virtual std::string GetOperatingSystemName() OVERRIDE;
// Overridden from ChromeImpl:
+ virtual bool IsMobileEmulationEnabled() const OVERRIDE;
virtual Status QuitImpl() OVERRIDE;
const base::CommandLine& command() const;
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index f0b87d6..7f796d2 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -83,7 +83,10 @@ Status ChromeImpl::GetWebViewIds(std::list<std::string>* web_view_ids) {
// OnConnected will fire when DevToolsClient connects later.
}
web_views_.push_back(make_linked_ptr(new WebViewImpl(
- view.id, devtools_http_client_->browser_info(), client.Pass())));
+ view.id,
+ devtools_http_client_->browser_info(),
+ client.Pass(),
+ devtools_http_client_->device_metrics())));
}
}
@@ -125,6 +128,10 @@ Status ChromeImpl::ActivateWebView(const std::string& id) {
return devtools_http_client_->ActivateWebView(id);
}
+bool ChromeImpl::IsMobileEmulationEnabled() const {
+ return false;
+}
+
Status ChromeImpl::Quit() {
Status status = QuitImpl();
if (status.IsOk())
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.h b/chrome/test/chromedriver/chrome/chrome_impl.h
index 01ad596..d852351 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_impl.h
@@ -37,6 +37,7 @@ class ChromeImpl : public Chrome {
WebView** web_view) OVERRIDE;
virtual Status CloseWebView(const std::string& id) OVERRIDE;
virtual Status ActivateWebView(const std::string& id) OVERRIDE;
+ virtual bool IsMobileEmulationEnabled() const OVERRIDE;
virtual Status Quit() OVERRIDE;
protected:
diff --git a/chrome/test/chromedriver/chrome/device_metrics.cc b/chrome/test/chromedriver/chrome/device_metrics.cc
new file mode 100644
index 0000000..059e55d
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/device_metrics.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 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/test/chromedriver/chrome/device_metrics.h"
+
+DeviceMetrics::DeviceMetrics(int width, int height, double device_scale_factor)
+ : width(width),
+ height(height),
+ device_scale_factor(device_scale_factor),
+ emulate_viewport(false),
+ fit_window(true),
+ text_autosizing(true),
+ font_scale_factor(1) {}
+
+DeviceMetrics::~DeviceMetrics() {}
diff --git a/chrome/test/chromedriver/chrome/device_metrics.h b/chrome/test/chromedriver/chrome/device_metrics.h
new file mode 100644
index 0000000..d40159b
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/device_metrics.h
@@ -0,0 +1,21 @@
+// Copyright 2014 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_TEST_CHROMEDRIVER_CHROME_DEVICE_METRICS_H_
+#define CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_METRICS_H_
+
+struct DeviceMetrics {
+ DeviceMetrics(int width, int height, double device_scale_factor);
+ ~DeviceMetrics();
+
+ int width;
+ int height;
+ double device_scale_factor;
+ bool emulate_viewport;
+ bool fit_window;
+ bool text_autosizing;
+ double font_scale_factor;
+};
+
+#endif // CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_METRICS_H_
diff --git a/chrome/test/chromedriver/chrome/devtools_http_client.cc b/chrome/test/chromedriver/chrome/devtools_http_client.cc
index 2d380a8..99ff945 100644
--- a/chrome/test/chromedriver/chrome/devtools_http_client.cc
+++ b/chrome/test/chromedriver/chrome/devtools_http_client.cc
@@ -13,6 +13,7 @@
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
#include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/status.h"
@@ -67,12 +68,14 @@ const WebViewInfo* WebViewsInfo::GetForId(const std::string& id) const {
DevToolsHttpClient::DevToolsHttpClient(
const NetAddress& address,
scoped_refptr<URLRequestContextGetter> context_getter,
- const SyncWebSocketFactory& socket_factory)
+ const SyncWebSocketFactory& socket_factory,
+ scoped_ptr<DeviceMetrics> device_metrics)
: context_getter_(context_getter),
socket_factory_(socket_factory),
server_url_("http://" + address.ToString()),
web_socket_url_prefix_(base::StringPrintf(
- "ws://%s/devtools/page/", address.ToString().c_str())) {}
+ "ws://%s/devtools/page/", address.ToString().c_str())),
+ device_metrics_(device_metrics.Pass()) {}
DevToolsHttpClient::~DevToolsHttpClient() {}
@@ -196,6 +199,10 @@ const BrowserInfo* DevToolsHttpClient::browser_info() {
return &browser_info_;
}
+const DeviceMetrics* DevToolsHttpClient::device_metrics() {
+ return device_metrics_.get();
+}
+
Status DevToolsHttpClient::GetVersion(std::string* browser_version,
std::string* blink_version) {
std::string data;
@@ -248,7 +255,7 @@ Status DevToolsHttpClient::CloseFrontends(const std::string& for_client_id) {
*it,
base::Bind(&FakeCloseFrontends)));
scoped_ptr<WebViewImpl> web_view(
- new WebViewImpl(*it, &browser_info_, client.Pass()));
+ new WebViewImpl(*it, &browser_info_, client.Pass(), NULL));
status = web_view->ConnectIfNecessary();
// Ignore disconnected error, because the debugger might have closed when
diff --git a/chrome/test/chromedriver/chrome/devtools_http_client.h b/chrome/test/chromedriver/chrome/devtools_http_client.h
index 693f0c0..1e1c5eb 100644
--- a/chrome/test/chromedriver/chrome/devtools_http_client.h
+++ b/chrome/test/chromedriver/chrome/devtools_http_client.h
@@ -17,6 +17,7 @@ namespace base {
class TimeDelta;
}
+struct DeviceMetrics;
class DevToolsClient;
class NetAddress;
class Status;
@@ -64,7 +65,8 @@ class DevToolsHttpClient {
DevToolsHttpClient(
const NetAddress& address,
scoped_refptr<URLRequestContextGetter> context_getter,
- const SyncWebSocketFactory& socket_factory);
+ const SyncWebSocketFactory& socket_factory,
+ scoped_ptr<DeviceMetrics> device_metrics);
~DevToolsHttpClient();
Status Init(const base::TimeDelta& timeout);
@@ -78,6 +80,7 @@ class DevToolsHttpClient {
Status ActivateWebView(const std::string& id);
const BrowserInfo* browser_info();
+ const DeviceMetrics* device_metrics();
private:
Status GetVersion(std::string* browser_version, std::string* blink_version);
@@ -91,6 +94,7 @@ class DevToolsHttpClient {
std::string server_url_;
std::string web_socket_url_prefix_;
BrowserInfo browser_info_;
+ scoped_ptr<DeviceMetrics> device_metrics_;
DISALLOW_COPY_AND_ASSIGN(DevToolsHttpClient);
};
diff --git a/chrome/test/chromedriver/chrome/mobile_device.cc b/chrome/test/chromedriver/chrome/mobile_device.cc
new file mode 100644
index 0000000..fe6b4d1
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/mobile_device.cc
@@ -0,0 +1,87 @@
+// Copyright 2014 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 "base/json/json_reader.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/mobile_device.h"
+#include "chrome/test/chromedriver/chrome/mobile_device_list.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+
+MobileDevice::MobileDevice() {}
+MobileDevice::~MobileDevice() {}
+
+Status FindMobileDevice(std::string device_name,
+ scoped_ptr<MobileDevice>* mobile_device) {
+ base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS);
+ scoped_ptr<base::Value> devices_value;
+ devices_value.reset(json_reader.ReadToValue(kMobileDevices));
+ if (!devices_value.get())
+ return Status(kUnknownError,
+ "could not parse mobile device list because " +
+ json_reader.GetErrorMessage());
+
+ base::ListValue* mobile_devices;
+ if (!devices_value->GetAsList(&mobile_devices))
+ return Status(kUnknownError, "malformed device metrics list");
+
+ for (base::ListValue::iterator it = mobile_devices->begin();
+ it != mobile_devices->end();
+ ++it) {
+ base::ListValue* device = NULL;
+ if (!(*it)->GetAsList(&device)) {
+ return Status(kUnknownError,
+ "malformed device in list: should be an array");
+ }
+
+ if (device != NULL) {
+ std::string name;
+ if (!device->GetString(0, &name)) {
+ return Status(kUnknownError,
+ "malformed device name: should be a string");
+ }
+ if (name != device_name)
+ continue;
+
+ scoped_ptr<MobileDevice> tmp_mobile_device(new MobileDevice());
+ std::string device_metrics_string;
+ if (!device->GetString(1, &tmp_mobile_device->user_agent)) {
+ return Status(kUnknownError,
+ "malformed device user agent: should be a string");
+ }
+ if (!device->GetString(2, &device_metrics_string)) {
+ return Status(kUnknownError,
+ "malformed device metrics: should be a string");
+ }
+ std::vector<std::string> metrics_vector;
+ base::SplitString(device_metrics_string, 'x', &metrics_vector);
+ if (metrics_vector.size() < 3)
+ return Status(kUnknownError, "malformed device metrics string");
+
+ int width = 0;
+ int height = 0;
+ double device_scale_factor = 0.0;
+ if (!base::StringToInt(metrics_vector[0], &width)) {
+ return Status(kUnknownError,
+ "malformed device width: should be an integer");
+ }
+ if (!base::StringToInt(metrics_vector[1], &height)) {
+ return Status(kUnknownError,
+ "malformed device height: should be an integer");
+ }
+ if (!base::StringToDouble(metrics_vector[2], &device_scale_factor)) {
+ return Status(kUnknownError,
+ "malformed device scale factor: should be a double");
+ }
+ tmp_mobile_device->device_metrics.reset(
+ new DeviceMetrics(width, height, device_scale_factor));
+
+ *mobile_device = tmp_mobile_device.Pass();
+ return Status(kOk);
+ }
+ }
+
+ return Status(kUnknownError, "must be a valid device");
+}
diff --git a/chrome/test/chromedriver/chrome/mobile_device.h b/chrome/test/chromedriver/chrome/mobile_device.h
new file mode 100644
index 0000000..fbb91eb
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/mobile_device.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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_TEST_CHROMEDRIVER_CHROME_MOBILE_DEVICE_H_
+#define CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_DEVICE_H_
+
+#include <string>
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
+
+class Status;
+
+struct MobileDevice {
+ MobileDevice();
+ ~MobileDevice();
+ scoped_ptr<DeviceMetrics> device_metrics;
+ std::string user_agent;
+};
+
+Status FindMobileDevice(std::string device_name,
+ scoped_ptr<MobileDevice>* mobile_device);
+
+#endif // CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_DEVICE_H_
diff --git a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc
new file mode 100644
index 0000000..4a69c66
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 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 "base/values.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
+#include "chrome/test/chromedriver/chrome/devtools_client.h"
+#include "chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+
+MobileEmulationOverrideManager::MobileEmulationOverrideManager(
+ DevToolsClient* client,
+ const DeviceMetrics* device_metrics)
+ : client_(client), overridden_device_metrics_(device_metrics) {
+ if (overridden_device_metrics_)
+ client_->AddListener(this);
+}
+
+MobileEmulationOverrideManager::~MobileEmulationOverrideManager() {
+}
+
+Status MobileEmulationOverrideManager::OnConnected(DevToolsClient* client) {
+ return ApplyOverrideIfNeeded();
+}
+
+Status MobileEmulationOverrideManager::OnEvent(
+ DevToolsClient* client,
+ const std::string& method,
+ const base::DictionaryValue& params) {
+ if (method == "Page.frameNavigated") {
+ const base::Value* unused_value;
+ if (!params.Get("frame.parentId", &unused_value))
+ return ApplyOverrideIfNeeded();
+ }
+ return Status(kOk);
+}
+
+Status MobileEmulationOverrideManager::ApplyOverrideIfNeeded() {
+ if (overridden_device_metrics_ == NULL)
+ return Status(kOk);
+
+ base::DictionaryValue params;
+ params.SetInteger("width", overridden_device_metrics_->width);
+ params.SetInteger("height", overridden_device_metrics_->height);
+ params.SetDouble("deviceScaleFactor",
+ overridden_device_metrics_->device_scale_factor);
+ params.SetBoolean("emulateViewport",
+ overridden_device_metrics_->emulate_viewport);
+ params.SetBoolean("fitWindow", overridden_device_metrics_->fit_window);
+ params.SetBoolean("textAutosizing",
+ overridden_device_metrics_->text_autosizing);
+ params.SetDouble("fontScaleFactor",
+ overridden_device_metrics_->font_scale_factor);
+ return client_->SendCommand("Page.setDeviceMetricsOverride", params);
+}
diff --git a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h
new file mode 100644
index 0000000..4c08f44
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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_TEST_CHROMEDRIVER_CHROME_MOBILE_EMULATION_OVERRIDE_MANAGER_H_
+#define CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_EMULATION_OVERRIDE_MANAGER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+class DevToolsClient;
+struct DeviceMetrics;
+class Status;
+
+// Overrides the device metrics, if requested, for the duration of the
+// given |DevToolsClient|'s lifetime.
+class MobileEmulationOverrideManager : public DevToolsEventListener {
+ public:
+ MobileEmulationOverrideManager(DevToolsClient* client,
+ const DeviceMetrics* device_metrics);
+ virtual ~MobileEmulationOverrideManager();
+
+ // Overridden from DevToolsEventListener:
+ virtual Status OnConnected(DevToolsClient* client) OVERRIDE;
+ virtual Status OnEvent(DevToolsClient* client,
+ const std::string& method,
+ const base::DictionaryValue& params) OVERRIDE;
+
+ private:
+ Status ApplyOverrideIfNeeded();
+
+ DevToolsClient* client_;
+ const DeviceMetrics* overridden_device_metrics_;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileEmulationOverrideManager);
+};
+
+#endif // CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_EMULATION_OVERRIDE_MANAGER_H_
diff --git a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc
new file mode 100644
index 0000000..a318783
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 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 <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
+#include "chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+#include "chrome/test/chromedriver/chrome/stub_devtools_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+struct Command {
+ Command() {}
+ Command(const std::string& method, const base::DictionaryValue& params)
+ : method(method) {
+ this->params.MergeDictionary(&params);
+ }
+ Command(const Command& command) {
+ *this = command;
+ }
+ Command& operator=(const Command& command) {
+ method = command.method;
+ params.Clear();
+ params.MergeDictionary(&command.params);
+ return *this;
+ }
+ ~Command() {}
+
+ std::string method;
+ base::DictionaryValue params;
+};
+
+class RecorderDevToolsClient : public StubDevToolsClient {
+ public:
+ RecorderDevToolsClient() {}
+ virtual ~RecorderDevToolsClient() {}
+
+ // Overridden from StubDevToolsClient:
+ virtual Status SendCommandAndGetResult(
+ const std::string& method,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::DictionaryValue>* result) OVERRIDE {
+ commands_.push_back(Command(method, params));
+ return Status(kOk);
+ }
+
+ std::vector<Command> commands_;
+};
+
+void AssertDeviceMetricsCommand(const Command& command,
+ const DeviceMetrics& device_metrics) {
+ ASSERT_EQ("Page.setDeviceMetricsOverride", command.method);
+ int width, height;
+ double device_scale_factor, font_scale_factor;
+ bool emulate_viewport, fit_window, text_autosizing;
+ ASSERT_TRUE(command.params.GetInteger("width", &width));
+ ASSERT_TRUE(command.params.GetInteger("height", &height));
+ ASSERT_TRUE(command.params.GetDouble("deviceScaleFactor",
+ &device_scale_factor));
+ ASSERT_TRUE(command.params.GetBoolean("emulateViewport", &emulate_viewport));
+ ASSERT_TRUE(command.params.GetBoolean("fitWindow", &fit_window));
+ ASSERT_TRUE(command.params.GetBoolean("textAutosizing", &text_autosizing));
+ ASSERT_TRUE(command.params.GetDouble("fontScaleFactor", &font_scale_factor));
+ ASSERT_EQ(device_metrics.width, width);
+ ASSERT_EQ(device_metrics.height, height);
+ ASSERT_EQ(device_metrics.device_scale_factor, device_scale_factor);
+ ASSERT_EQ(device_metrics.emulate_viewport, emulate_viewport);
+ ASSERT_EQ(device_metrics.fit_window, fit_window);
+ ASSERT_EQ(device_metrics.text_autosizing, text_autosizing);
+ ASSERT_EQ(device_metrics.font_scale_factor, font_scale_factor);
+}
+
+} // namespace
+
+TEST(MobileEmulationOverrideManager, SendsCommandOnConnect) {
+ RecorderDevToolsClient client;
+ DeviceMetrics device_metrics(1, 2, 3.0);
+ MobileEmulationOverrideManager manager(&client, &device_metrics);
+ ASSERT_EQ(0u, client.commands_.size());
+ ASSERT_EQ(kOk, manager.OnConnected(&client).code());
+
+ ASSERT_EQ(1u, client.commands_.size());
+ ASSERT_EQ(kOk, manager.OnConnected(&client).code());
+ ASSERT_EQ(2u, client.commands_.size());
+ ASSERT_NO_FATAL_FAILURE(
+ AssertDeviceMetricsCommand(client.commands_[1], device_metrics));
+}
+
+TEST(MobileEmulationOverrideManager, SendsCommandOnNavigation) {
+ RecorderDevToolsClient client;
+ DeviceMetrics device_metrics(1, 2, 3.0);
+ MobileEmulationOverrideManager manager(&client, &device_metrics);
+ base::DictionaryValue main_frame_params;
+ ASSERT_EQ(kOk,
+ manager.OnEvent(&client, "Page.frameNavigated", main_frame_params)
+ .code());
+ ASSERT_EQ(1u, client.commands_.size());
+ ASSERT_EQ(kOk,
+ manager.OnEvent(&client, "Page.frameNavigated", main_frame_params)
+ .code());
+ ASSERT_EQ(2u, client.commands_.size());
+ ASSERT_NO_FATAL_FAILURE(
+ AssertDeviceMetricsCommand(client.commands_[1], device_metrics));
+
+ base::DictionaryValue sub_frame_params;
+ sub_frame_params.SetString("frame.parentId", "id");
+ ASSERT_EQ(
+ kOk,
+ manager.OnEvent(&client, "Page.frameNavigated", sub_frame_params).code());
+ ASSERT_EQ(2u, client.commands_.size());
+}
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.cc b/chrome/test/chromedriver/chrome/stub_chrome.cc
index 5e1cbe1..394a183 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.cc
+++ b/chrome/test/chromedriver/chrome/stub_chrome.cc
@@ -43,6 +43,10 @@ std::string StubChrome::GetOperatingSystemName() {
return std::string();
}
+bool StubChrome::IsMobileEmulationEnabled() const {
+ return false;
+}
+
Status StubChrome::Quit() {
return Status(kOk);
}
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.h b/chrome/test/chromedriver/chrome/stub_chrome.h
index 1e07aa0..ba09328 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.h
+++ b/chrome/test/chromedriver/chrome/stub_chrome.h
@@ -30,6 +30,7 @@ class StubChrome : public Chrome {
virtual Status CloseWebView(const std::string& id) OVERRIDE;
virtual Status ActivateWebView(const std::string& id) OVERRIDE;
virtual std::string GetOperatingSystemName() OVERRIDE;
+ virtual bool IsMobileEmulationEnabled() const OVERRIDE;
virtual Status Quit() OVERRIDE;
private:
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 4403e89..f49485d 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -21,6 +21,7 @@
#include "chrome/test/chromedriver/chrome/heap_snapshot_taker.h"
#include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h"
#include "chrome/test/chromedriver/chrome/js.h"
+#include "chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h"
#include "chrome/test/chromedriver/chrome/navigation_tracker.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/ui_events.h"
@@ -114,13 +115,16 @@ const char* GetAsString(KeyEventType type) {
WebViewImpl::WebViewImpl(const std::string& id,
const BrowserInfo* browser_info,
- scoped_ptr<DevToolsClient> client)
+ scoped_ptr<DevToolsClient> client,
+ const DeviceMetrics* device_metrics)
: id_(id),
browser_info_(browser_info),
dom_tracker_(new DomTracker(client.get())),
frame_tracker_(new FrameTracker(client.get())),
navigation_tracker_(new NavigationTracker(client.get(), browser_info)),
dialog_manager_(new JavaScriptDialogManager(client.get())),
+ mobile_emulation_override_manager_(
+ new MobileEmulationOverrideManager(client.get(), device_metrics)),
geolocation_override_manager_(
new GeolocationOverrideManager(client.get())),
heap_snapshot_taker_(new HeapSnapshotTaker(client.get())),
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index c1424c8..06718b7 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -21,10 +21,12 @@ class Value;
struct BrowserInfo;
class DebuggerTracker;
+struct DeviceMetrics;
class DevToolsClient;
class DomTracker;
class FrameTracker;
class GeolocationOverrideManager;
+class MobileEmulationOverrideManager;
class HeapSnapshotTaker;
struct KeyEvent;
struct MouseEvent;
@@ -36,6 +38,10 @@ class WebViewImpl : public WebView {
WebViewImpl(const std::string& id,
const BrowserInfo* browser_info,
scoped_ptr<DevToolsClient> client);
+ WebViewImpl(const std::string& id,
+ const BrowserInfo* browser_info,
+ scoped_ptr<DevToolsClient> client,
+ const DeviceMetrics* device_metrics);
virtual ~WebViewImpl();
// Overridden from WebView:
@@ -111,6 +117,7 @@ class WebViewImpl : public WebView {
scoped_ptr<FrameTracker> frame_tracker_;
scoped_ptr<NavigationTracker> navigation_tracker_;
scoped_ptr<JavaScriptDialogManager> dialog_manager_;
+ scoped_ptr<MobileEmulationOverrideManager> mobile_emulation_override_manager_;
scoped_ptr<GeolocationOverrideManager> geolocation_override_manager_;
scoped_ptr<HeapSnapshotTaker> heap_snapshot_taker_;
scoped_ptr<DebuggerTracker> debugger_;
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index 24037f5..49d5f05 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -157,9 +157,14 @@ Status WaitForDevToolsAndCheckVersion(
const NetAddress& address,
URLRequestContextGetter* context_getter,
const SyncWebSocketFactory& socket_factory,
+ const Capabilities* capabilities,
scoped_ptr<DevToolsHttpClient>* user_client) {
+ scoped_ptr<DeviceMetrics> device_metrics;
+ if (capabilities && capabilities->device_metrics)
+ device_metrics.reset(new DeviceMetrics(*capabilities->device_metrics));
+
scoped_ptr<DevToolsHttpClient> client(new DevToolsHttpClient(
- address, context_getter, socket_factory));
+ address, context_getter, socket_factory, device_metrics.Pass()));
base::TimeTicks deadline =
base::TimeTicks::Now() + base::TimeDelta::FromSeconds(60);
Status status = client->Init(deadline - base::TimeTicks::Now());
@@ -194,7 +199,7 @@ Status LaunchExistingChromeSession(
scoped_ptr<DevToolsHttpClient> devtools_client;
status = WaitForDevToolsAndCheckVersion(
capabilities.debugger_address, context_getter, socket_factory,
- &devtools_client);
+ NULL, &devtools_client);
if (status.IsError()) {
return Status(kUnknownError, "cannot connect to chrome at " +
capabilities.debugger_address.ToString(),
@@ -278,7 +283,8 @@ Status LaunchDesktopChrome(
scoped_ptr<DevToolsHttpClient> devtools_client;
status = WaitForDevToolsAndCheckVersion(
- NetAddress(port), context_getter, socket_factory, &devtools_client);
+ NetAddress(port), context_getter, socket_factory, &capabilities,
+ &devtools_client);
if (status.IsError()) {
int exit_code;
@@ -378,6 +384,7 @@ Status LaunchAndroidChrome(
status = WaitForDevToolsAndCheckVersion(NetAddress(port),
context_getter,
socket_factory,
+ &capabilities,
&devtools_client);
if (status.IsError()) {
device->TearDown();
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index 946f85c..063de99 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -65,7 +65,7 @@ class ChromeDriver(object):
android_use_running_app=None, chrome_switches=None,
chrome_extensions=None, chrome_log_path=None,
debugger_address=None, browser_log_level=None,
- experimental_options=None):
+ mobile_emulation=None, experimental_options=None):
self._executor = command_executor.CommandExecutor(server_url)
options = {}
@@ -89,6 +89,10 @@ class ChromeDriver(object):
assert type(chrome_switches) is list
options['args'] = chrome_switches
+ if mobile_emulation:
+ assert type(mobile_emulation) is dict
+ options['mobileEmulation'] = mobile_emulation
+
if chrome_extensions:
assert type(chrome_extensions) is list
options['extensions'] = chrome_extensions
diff --git a/chrome/test/chromedriver/client/webelement.py b/chrome/test/chromedriver/client/webelement.py
index b3e6a64..5d7dda6 100644
--- a/chrome/test/chromedriver/client/webelement.py
+++ b/chrome/test/chromedriver/client/webelement.py
@@ -25,6 +25,9 @@ class WebElement(object):
return self._Execute(
Command.FIND_CHILD_ELEMENTS, {'using': strategy, 'value': target})
+ def GetText(self):
+ return self._Execute(Command.GET_ELEMENT_TEXT)
+
def HoverOver(self):
self._Execute(Command.HOVER_OVER_ELEMENT)
diff --git a/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
new file mode 100755
index 0000000..766edce
--- /dev/null
+++ b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""Embeds standalone JavaScript snippets in C++ code.
+
+The script requires the OverridesView file from WebKit that lists the known
+mobile devices to be passed in as the only argument. The list of known devices
+will be written to a C-style string to be parsed with JSONReader.
+"""
+
+import optparse
+import os
+import sys
+
+import cpp_source
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '', '--directory', type='string', default='.',
+ help='Path to directory where the cc/h files should be created')
+ options, args = parser.parse_args()
+
+ devices = '['
+ file_name = args[0]
+ inside_list = False
+ with open(file_name, 'r') as f:
+ for line in f:
+ if not inside_list:
+ if 'DeviceTab._phones = [' in line or 'DeviceTab._tablets = [' in line:
+ inside_list = True
+ else:
+ if line.strip() == '];':
+ inside_list = False
+ continue
+ devices += line.strip()
+
+ devices += ']'
+ cpp_source.WriteSource('mobile_device_list',
+ 'chrome/test/chromedriver/chrome',
+ options.directory, {'kMobileDevices': devices})
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index 38c59e2..fe837fb 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -14,6 +14,7 @@
#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
#include "chrome/test/chromedriver/basic_types.h"
+#include "chrome/test/chromedriver/chrome/device_metrics.h"
#include "chrome/test/chromedriver/chrome/geoposition.h"
namespace base {
@@ -69,6 +70,7 @@ struct Session {
base::TimeDelta script_timeout;
scoped_ptr<std::string> prompt_text;
scoped_ptr<Geoposition> overridden_geoposition;
+ scoped_ptr<DeviceMetrics> overridden_device_metrics;
// Logs that populate from DevTools events.
ScopedVector<WebDriverLog> devtools_logs;
scoped_ptr<WebDriverLog> driver_log;
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index b0ad052..864ffd7 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -81,6 +81,8 @@ scoped_ptr<base::DictionaryValue> CreateCapabilities(Chrome* chrome) {
caps->SetBoolean("handlesAlerts", true);
caps->SetBoolean("databaseEnabled", false);
caps->SetBoolean("locationContextEnabled", true);
+ caps->SetBoolean("mobileEmulationEnabled",
+ chrome->IsMobileEmulationEnabled());
caps->SetBoolean("applicationCacheEnabled", false);
caps->SetBoolean("browserConnectionEnabled", false);
caps->SetBoolean("cssSelectorsEnabled", true);
@@ -99,7 +101,6 @@ scoped_ptr<base::DictionaryValue> CreateCapabilities(Chrome* chrome) {
return caps.Pass();
}
-
Status InitSessionHelper(
const InitSessionParams& bound_params,
Session* session,
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index bce431b..1a83bb6 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -100,6 +100,7 @@ _ANDROID_NEGATIVE_FILTER['chrome'] = (
# Android doesn't support switches and extensions.
'ChromeSwitchesCapabilityTest.*',
'ChromeExtensionsCapabilityTest.*',
+ 'MobileEmulationCapabilityTest.*',
# https://crbug.com/274650
'ChromeDriverTest.testCloseWindow',
# https://code.google.com/p/chromedriver/issues/detail?id=270
@@ -708,6 +709,9 @@ class ChromeDriverTest(ChromeDriverBaseTest):
def testDoesntHangOnDebugger(self):
self._driver.ExecuteScript('debugger;')
+ def testMobileEmulationDisabledByDefault(self):
+ self.assertFalse(self._driver.capabilities['mobileEmulationEnabled'])
+
class ChromeDriverAndroidTest(ChromeDriverBaseTest):
"""End to end tests for Android-specific tests."""
@@ -814,6 +818,56 @@ class ChromeLogPathCapabilityTest(ChromeDriverBaseTest):
self.assertTrue(self.LOG_MESSAGE in open(tmp_log_path.name).read())
+class MobileEmulationCapabilityTest(ChromeDriverBaseTest):
+ """Tests that ChromeDriver processes chromeOptions.mobileEmulation.
+
+ Makes sure the device metrics are overridden in DevTools and user agent is
+ overridden in Chrome.
+ """
+
+ @staticmethod
+ def GlobalSetUp():
+ def respondWithUserAgentString(request):
+ return request.GetHeader('User-Agent')
+
+ MobileEmulationCapabilityTest._http_server = webserver.WebServer(
+ chrome_paths.GetTestData())
+ MobileEmulationCapabilityTest._http_server.SetCallbackForPath(
+ '/userAgent', respondWithUserAgentString)
+
+ @staticmethod
+ def GlobalTearDown():
+ MobileEmulationCapabilityTest._http_server.Shutdown()
+
+ def testDeviceMetrics(self):
+ driver = self.CreateDriver(
+ mobile_emulation = {
+ 'deviceMetrics': {'width': 360, 'height': 640, 'pixelRatio': 3}})
+ self.assertTrue(driver.capabilities['mobileEmulationEnabled'])
+ self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
+ self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
+
+ def testUserAgent(self):
+ driver = self.CreateDriver(
+ mobile_emulation = {'userAgent': 'Agent Smith'})
+ driver.Load(self._http_server.GetUrl() + '/userAgent')
+ body_tag = driver.FindElement('tag name', 'body')
+ self.assertEqual("Agent Smith", body_tag.GetText())
+
+ def testDeviceName(self):
+ driver = self.CreateDriver(
+ mobile_emulation = {'deviceName': 'Google Nexus 5'})
+ driver.Load(self._http_server.GetUrl() + '/userAgent')
+ self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
+ self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
+ body_tag = driver.FindElement('tag name', 'body')
+ self.assertEqual(
+ 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleW'
+ 'ebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/53'
+ '5.19',
+ body_tag.GetText())
+
+
class ChromeDriverLogTest(unittest.TestCase):
"""Tests that chromedriver produces the expected log file."""
@@ -1007,6 +1061,8 @@ if __name__ == '__main__':
sys.modules[__name__])
tests = unittest_util.FilterTestSuite(all_tests_suite, options.filter)
ChromeDriverTest.GlobalSetUp()
+ MobileEmulationCapabilityTest.GlobalSetUp()
result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(tests)
ChromeDriverTest.GlobalTearDown()
+ MobileEmulationCapabilityTest.GlobalTearDown()
sys.exit(len(result.failures) + len(result.errors))
diff --git a/chrome/test/chromedriver/test/webserver.py b/chrome/test/chromedriver/test/webserver.py
index 716e0f4..9de0339 100644
--- a/chrome/test/chromedriver/test/webserver.py
+++ b/chrome/test/chromedriver/test/webserver.py
@@ -48,6 +48,9 @@ class Request(object):
def GetPath(self):
return self._handler.path
+ def GetHeader(self, name):
+ return self._handler.headers.getheader(name)
+
class _BaseServer(BaseHTTPServer.HTTPServer):
"""Internal server that throws if timed out waiting for a request."""
@@ -120,19 +123,28 @@ class WebServer(object):
self._thread.daemon = True
self._thread.start()
self._path_data_map = {}
- self._path_data_lock = threading.Lock()
+ self._path_callback_map = {}
+ self._path_maps_lock = threading.Lock()
def _OnRequest(self, request, responder):
path = request.GetPath().split('?')[0]
- # Serve from path -> data map.
- self._path_data_lock.acquire()
+ # Serve from path -> callback and data maps.
+ self._path_maps_lock.acquire()
try:
+ if path in self._path_callback_map:
+ body = self._path_callback_map[path](request)
+ if body:
+ responder.SendResponse(body)
+ else:
+ responder.SendError(503)
+ return
+
if path in self._path_data_map:
responder.SendResponse(self._path_data_map[path])
return
finally:
- self._path_data_lock.release()
+ self._path_maps_lock.release()
# Serve from file.
path = os.path.normpath(
@@ -146,11 +158,19 @@ class WebServer(object):
responder.SendResponseFromFile(path)
def SetDataForPath(self, path, data):
- self._path_data_lock.acquire()
+ self._path_maps_lock.acquire()
try:
self._path_data_map[path] = data
finally:
- self._path_data_lock.release()
+ self._path_maps_lock.release()
+
+ def SetCallbackForPath(self, path, func):
+ self._path_maps_lock.acquire()
+ try:
+ self._path_callback_map[path] = func
+ finally:
+ self._path_maps_lock.release()
+
def GetUrl(self):
"""Returns the base URL of the server."""