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

Unified Diff: chrome/test/chromedriver/chrome_impl.cc

Issue 12321057: [chromedriver] Implement reconnection to DevTools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix some nits and add one unit test. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/test/chromedriver/chrome_impl.cc
diff --git a/chrome/test/chromedriver/chrome_impl.cc b/chrome/test/chromedriver/chrome_impl.cc
index 78038c3112222a2cdf82545123845a681cd769d6..8b665cccd840a70d138c51dc6643b2e6c70c0311 100644
--- a/chrome/test/chromedriver/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome_impl.cc
@@ -23,21 +23,30 @@
namespace {
+typedef base::Callback<Status(base::ListValue*)> ParserFunc;
Status FetchPagesInfo(URLRequestContextGetter* context_getter,
int port,
- std::list<std::string>* page_ids) {
+ const ParserFunc& parser) {
kkania 2013/02/22 01:35:24 i think this might be simpler to understand if ins
chrisgao (Use stgao instead) 2013/02/27 19:29:44 Done.
std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port);
std::string data;
if (!FetchUrl(GURL(url), context_getter, &data))
return Status(kChromeNotReachable);
- return internal::ParsePagesInfo(data, page_ids);
+ scoped_ptr<base::Value> value(base::JSONReader::Read(data));
+ if (!value.get())
+ return Status(kUnknownError, "DevTools returned invalid JSON");
+ base::ListValue* list;
+ if (!value->GetAsList(&list))
+ return Status(kUnknownError, "DevTools did not return list");
+
+ return parser.Run(list);
}
Status CloseWebView(URLRequestContextGetter* context_getter,
int port,
const std::string& web_view_id) {
std::list<std::string> ids;
- Status status = FetchPagesInfo(context_getter, port, &ids);
+ Status status = FetchPagesInfo(
+ context_getter, port, base::Bind(internal::ParsePagesInfo, &ids));
if (status.IsError())
return status;
if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end())
@@ -57,7 +66,8 @@ Status CloseWebView(URLRequestContextGetter* context_getter,
base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
while (base::Time::Now() < deadline) {
ids.clear();
- status = FetchPagesInfo(context_getter, port, &ids);
+ status = FetchPagesInfo(
+ context_getter, port, base::Bind(internal::ParsePagesInfo, &ids));
if (is_last_web_view && status.code() == kChromeNotReachable)
return Status(kOk); // Closing the last web view leads chrome to quit.
if (status.IsError())
@@ -70,6 +80,83 @@ Status CloseWebView(URLRequestContextGetter* context_getter,
return Status(kUnknownError, "failed to close window in 20 seconds");
}
+Status FakeCloseWebView() {
+ return Status(kUnknownError,
+ "built-in DevTools frontend should be closed by Javascript");
+}
+
+Status FakeCloseDevToolsFrontend() {
+ return Status(kUnknownError,
+ "built-in DevTools frontend should not try to close others");
+}
+
+Status CloseDevToolsFrontend(ChromeImpl* chrome,
+ const SyncWebSocketFactory& socket_factory,
+ URLRequestContextGetter* context_getter,
+ int port,
+ const std::string& page_id) {
+ std::list<std::string> page_ids; // DevTools opened in a separate page.
+ std::list<std::string> frontend_ids; // In-page DevTools frontend.
kkania 2013/02/22 01:35:24 the naming here is a bit confusing. how about call
chrisgao (Use stgao instead) 2013/02/27 19:29:44 Good suggestion.
+
+ Status status = FetchPagesInfo(
+ context_getter, port,
+ base::Bind(internal::ParseDevToolsInfo, &page_ids, &frontend_ids));
+ if (status.IsError())
+ return status;
+
+ if (!page_ids.empty()) {
+ // Close DevTools page in the same way as a normal web view by json/close.
+ std::list<WebView*> web_views;
+ status = chrome->GetWebViews(&web_views);
+ if (status.IsError())
+ return status;
+ for (std::list<WebView*>::const_iterator it = web_views.begin();
+ it != web_views.end(); ++it) {
+ if (std::find(page_ids.begin(), page_ids.end(), (*it)->GetId())
+ != page_ids.end()) {
+ status = (*it)->Close();
+ if (status.IsError())
+ return status;
+ }
+ }
+ }
+
+ // Close in-page DevTools frontend by Javascript.
+ for (std::list<std::string>::const_iterator it = frontend_ids.begin();
+ it != frontend_ids.end(); ++it) {
+ std::string ws_url = base::StringPrintf(
+ "ws://127.0.0.1:%d/devtools/page/%s", port, it->c_str());
+ scoped_ptr<WebViewImpl> web_view(new WebViewImpl(
+ *it,
+ new DevToolsClientImpl(socket_factory, ws_url,
+ base::Bind(&FakeCloseDevToolsFrontend)),
+ chrome, base::Bind(&FakeCloseWebView)));
+
+ scoped_ptr<base::Value> result;
+ status = web_view->EvaluateScript(
+ "", "document.getElementById('close-button-right').click();", &result);
+ // Ignore Disconnected error, because the DevTools frontend is closed.
kkania 2013/02/22 01:35:24 disconnected, D->d
chrisgao (Use stgao instead) 2013/02/27 19:29:44 Done.
+ if (status.IsError() && status.code() != kDisconnected)
+ return status;
+ }
+
+ // Wait until DevTools UI disconnects from the given web view.
+ base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
+ bool has_debugger_url = false;
+ base::Callback<Status(base::ListValue*)> checker =
+ base::Bind(&internal::CheckPageDebuggerUrl, page_id, &has_debugger_url);
+ while (base::Time::Now() < deadline) {
+ status = FetchPagesInfo(context_getter, port, checker);
+ if (status.IsError())
+ return status;
+ if (has_debugger_url)
+ return Status(kOk);
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
+ }
+
+ return Status(kUnknownError, "failed to close DevTools frontend");
+}
+
} // namespace
ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter,
@@ -85,7 +172,8 @@ ChromeImpl::~ChromeImpl() {
Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
std::list<std::string> ids;
- Status status = FetchPagesInfo(context_getter_, port_, &ids);
+ Status status = FetchPagesInfo(
+ context_getter_, port_, base::Bind(internal::ParsePagesInfo, &ids));
if (status.IsError())
return status;
@@ -100,9 +188,14 @@ Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
std::string ws_url = base::StringPrintf(
"ws://127.0.0.1:%d/devtools/page/%s", port_, it->c_str());
+ DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind(
+ &CloseDevToolsFrontend, this, socket_factory_,
+ context_getter_, port_, *it);
web_view_map_[*it] = make_linked_ptr(new WebViewImpl(
- *it, new DevToolsClientImpl(socket_factory_, ws_url),
- this, base::Bind(&CloseWebView, context_getter_, port_, *it)));
+ *it,
+ new DevToolsClientImpl(socket_factory_, ws_url, frontend_closer_func),
+ this,
+ base::Bind(&CloseWebView, context_getter_, port_, *it)));
internal_web_views.push_back(web_view_map_[*it].get());
}
@@ -118,7 +211,8 @@ Status ChromeImpl::Init() {
base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
std::list<std::string> page_ids;
while (base::Time::Now() < deadline) {
- FetchPagesInfo(context_getter_, port_, &page_ids);
+ FetchPagesInfo(context_getter_, port_,
+ base::Bind(internal::ParsePagesInfo, &page_ids));
if (page_ids.empty())
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
else
@@ -136,19 +230,12 @@ int ChromeImpl::GetPort() const {
namespace internal {
-Status ParsePagesInfo(const std::string& data,
- std::list<std::string>* page_ids) {
- scoped_ptr<base::Value> value(base::JSONReader::Read(data));
- if (!value.get())
- return Status(kUnknownError, "DevTools returned invalid JSON");
- base::ListValue* list;
- if (!value->GetAsList(&list))
- return Status(kUnknownError, "DevTools did not return list");
-
+Status ParsePagesInfo(std::list<std::string>* page_ids,
+ base::ListValue* page_info_list) {
std::list<std::string> ids;
- for (size_t i = 0; i < list->GetSize(); ++i) {
+ for (size_t i = 0; i < page_info_list->GetSize(); ++i) {
base::DictionaryValue* info;
- if (!list->GetDictionary(i, &info))
+ if (!page_info_list->GetDictionary(i, &info))
return Status(kUnknownError, "DevTools contains non-dictionary item");
std::string type;
if (!info->GetString("type", &type))
@@ -164,4 +251,58 @@ Status ParsePagesInfo(const std::string& data,
return Status(kOk);
}
+Status ParseDevToolsInfo(std::list<std::string>* page_devtools_ids,
+ std::list<std::string>* frontend_devtools_ids,
+ base::ListValue* page_info_list) {
+ std::list<std::string> page_devtools_ids_tmp;
+ std::list<std::string> frontend_devtools_ids_tmp;
+ for (size_t i = 0; i < page_info_list->GetSize(); ++i) {
+ base::DictionaryValue* info;
+ if (!page_info_list->GetDictionary(i, &info))
+ return Status(kUnknownError, "DevTools contains non-dictionary item");
+ std::string url;
+ if (!info->GetString("url", &url))
+ return Status(kUnknownError, "DevTools did not include url");
+ if (url.find("chrome-devtools://") != 0u)
+ continue;
+ std::string id;
+ if (!info->GetString("id", &id))
+ return Status(kUnknownError, "DevTools did not include id");
+ std::string type;
+ if (!info->GetString("type", &type))
+ return Status(kUnknownError, "DevTools did not include type");
+ if (type == "page")
+ page_devtools_ids_tmp.push_back(id);
+ else if (type == "other")
+ frontend_devtools_ids_tmp.push_back(id);
+ else
+ return Status(kUnknownError, "unknown type of devtools:" + type);
+ }
+
+ page_devtools_ids->swap(page_devtools_ids_tmp);
+ frontend_devtools_ids->swap(frontend_devtools_ids_tmp);
+ return Status(kOk);
+}
+
+Status CheckPageDebuggerUrl(const std::string& page_id,
+ bool* has_debugger_url,
+ base::ListValue* page_info_list) {
+ std::list<std::string> ids;
+ for (size_t i = 0; i < page_info_list->GetSize(); ++i) {
+ base::DictionaryValue* info;
+ if (!page_info_list->GetDictionary(i, &info))
+ return Status(kUnknownError, "DevTools contains non-dictionary item");
+ std::string id;
+ if (!info->GetString("id", &id))
+ return Status(kUnknownError, "DevTools did not include id");
+ if (id == page_id) {
+ std::string debugger_url;
+ *has_debugger_url = info->GetString("webSocketDebuggerUrl", &debugger_url)
+ && !debugger_url.empty();
+ return Status(kOk);
+ }
+ }
+ return Status(kUnknownError, "page closed while closing devtools frontend");
+}
+
} // namespace internal

Powered by Google App Engine
This is Rietveld 408576698