Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/test/chromedriver/chrome_impl.h" | 5 #include "chrome/test/chromedriver/chrome_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <list> | 8 #include <list> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
| 12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
| 14 #include "base/time.h" | 14 #include "base/time.h" |
| 15 #include "base/values.h" | 15 #include "base/values.h" |
| 16 #include "chrome/test/chromedriver/devtools_client_impl.h" | 16 #include "chrome/test/chromedriver/devtools_client_impl.h" |
| 17 #include "chrome/test/chromedriver/javascript_dialog_manager.h" | 17 #include "chrome/test/chromedriver/javascript_dialog_manager.h" |
| 18 #include "chrome/test/chromedriver/net/net_util.h" | 18 #include "chrome/test/chromedriver/net/net_util.h" |
| 19 #include "chrome/test/chromedriver/net/sync_websocket_impl.h" | 19 #include "chrome/test/chromedriver/net/sync_websocket_impl.h" |
| 20 #include "chrome/test/chromedriver/net/url_request_context_getter.h" | 20 #include "chrome/test/chromedriver/net/url_request_context_getter.h" |
| 21 #include "chrome/test/chromedriver/status.h" | 21 #include "chrome/test/chromedriver/status.h" |
| 22 #include "chrome/test/chromedriver/web_view_impl.h" | 22 #include "chrome/test/chromedriver/web_view_impl.h" |
| 23 #include "googleurl/src/gurl.h" | 23 #include "googleurl/src/gurl.h" |
| 24 | 24 |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 typedef std::list<internal::WebViewInfo> WebViewInfoList; | |
| 28 | |
| 27 Status FetchPagesInfo(URLRequestContextGetter* context_getter, | 29 Status FetchPagesInfo(URLRequestContextGetter* context_getter, |
|
kkania
2013/03/04 23:33:57
this naming is no longer consistent since we've ch
chrisgao (Use stgao instead)
2013/03/05 06:58:17
Done. Changed name of function.
| |
| 28 int port, | 30 int port, |
| 29 std::list<std::string>* page_ids) { | 31 WebViewInfoList* web_view_info_list) { |
| 30 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port); | 32 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port); |
| 31 std::string data; | 33 std::string data; |
| 32 if (!FetchUrl(GURL(url), context_getter, &data)) | 34 if (!FetchUrl(GURL(url), context_getter, &data)) |
| 33 return Status(kChromeNotReachable); | 35 return Status(kChromeNotReachable); |
| 34 return internal::ParsePagesInfo(data, page_ids); | 36 |
| 37 return ParsePagesInfo(data, web_view_info_list); | |
| 38 } | |
| 39 | |
| 40 bool WebViewExist(const std::string& web_view_id, | |
|
kkania
2013/03/04 23:33:57
convention is to name this as DoesWebViewExist
chrisgao (Use stgao instead)
2013/03/05 06:58:17
Done.
| |
| 41 const WebViewInfoList& info_list) { | |
| 42 for (WebViewInfoList::const_iterator it = info_list.begin(); | |
| 43 it != info_list.end(); ++it) { | |
| 44 if (it->id == web_view_id) | |
| 45 return true; | |
| 46 } | |
| 47 return false; | |
| 35 } | 48 } |
| 36 | 49 |
| 37 Status CloseWebView(URLRequestContextGetter* context_getter, | 50 Status CloseWebView(URLRequestContextGetter* context_getter, |
| 38 int port, | 51 int port, |
| 39 const std::string& web_view_id) { | 52 const std::string& web_view_id) { |
| 40 std::list<std::string> ids; | 53 WebViewInfoList web_view_info_list; |
| 41 Status status = FetchPagesInfo(context_getter, port, &ids); | 54 Status status = FetchPagesInfo(context_getter, port, &web_view_info_list); |
| 42 if (status.IsError()) | 55 if (status.IsError()) |
| 43 return status; | 56 return status; |
| 44 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) | 57 if (!WebViewExist(web_view_id, web_view_info_list)) |
| 45 return Status(kOk); | 58 return Status(kOk); |
| 46 | 59 |
| 47 bool is_last_web_view = ids.size() == 1; | 60 bool is_last_web_view = web_view_info_list.size() == 1u; |
| 48 | 61 |
| 49 std::string url = base::StringPrintf( | 62 std::string url = base::StringPrintf( |
| 50 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str()); | 63 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str()); |
| 51 std::string data; | 64 std::string data; |
| 52 if (!FetchUrl(GURL(url), context_getter, &data)) | 65 if (!FetchUrl(GURL(url), context_getter, &data)) |
| 53 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable); | 66 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable); |
| 54 if (data != "Target is closing") | 67 if (data != "Target is closing") |
| 55 return Status(kOk); | 68 return Status(kOk); |
| 56 | 69 |
| 57 // Wait for the target window to be completely closed. | 70 // Wait for the target window to be completely closed. |
| 58 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); | 71 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); |
| 59 while (base::Time::Now() < deadline) { | 72 while (base::Time::Now() < deadline) { |
| 60 ids.clear(); | 73 web_view_info_list.clear(); |
| 61 status = FetchPagesInfo(context_getter, port, &ids); | 74 status = FetchPagesInfo(context_getter, port, &web_view_info_list); |
| 62 if (is_last_web_view && status.code() == kChromeNotReachable) | 75 if (is_last_web_view && status.code() == kChromeNotReachable) |
| 63 return Status(kOk); // Closing the last web view leads chrome to quit. | 76 return Status(kOk); // Closing the last web view leads chrome to quit. |
| 64 if (status.IsError()) | 77 if (status.IsError()) |
| 65 return status; | 78 return status; |
| 66 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) | 79 if (!WebViewExist(web_view_id, web_view_info_list)) |
| 67 return Status(kOk); | 80 return Status(kOk); |
| 68 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); | 81 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); |
| 69 } | 82 } |
| 70 | 83 |
| 71 return Status(kUnknownError, "failed to close window in 20 seconds"); | 84 return Status(kUnknownError, "failed to close window in 20 seconds"); |
| 72 } | 85 } |
| 73 | 86 |
| 87 Status FakeCloseWebView() { | |
| 88 // This is for the docked DevTools frontend only. | |
| 89 return Status(kUnknownError, | |
| 90 "docked DevTools frontend should be closed by Javascript"); | |
| 91 } | |
| 92 | |
| 93 Status FakeCloseDevToolsFrontend() { | |
| 94 // This is for the docked DevTools frontend only. | |
| 95 return Status(kOk); | |
| 96 } | |
| 97 | |
| 98 Status CloseDevToolsFrontend(ChromeImpl* chrome, | |
| 99 const SyncWebSocketFactory& socket_factory, | |
| 100 URLRequestContextGetter* context_getter, | |
| 101 int port, | |
| 102 const std::string& web_view_id) { | |
| 103 WebViewInfoList web_view_info_list; | |
| 104 Status status = FetchPagesInfo(context_getter, port, &web_view_info_list); | |
| 105 if (status.IsError()) | |
| 106 return status; | |
| 107 | |
| 108 std::list<std::string> tab_frontend_ids; | |
| 109 std::list<std::string> docked_frontend_ids; | |
| 110 // Filter out DevTools frontend. | |
| 111 for (WebViewInfoList::const_iterator it = web_view_info_list.begin(); | |
| 112 it != web_view_info_list.end(); ++it) { | |
| 113 if (it->url.find("chrome-devtools://") == 0u) { | |
| 114 if (it->type == internal::WebViewInfo::kPage) | |
| 115 tab_frontend_ids.push_back(it->id); | |
| 116 else if (it->type == internal::WebViewInfo::kOther) | |
| 117 docked_frontend_ids.push_back(it->id); | |
| 118 else | |
| 119 return Status(kUnknownError, "unknown type of DevTools frontend"); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 // Close tab DevTools frontend as if closing a normal web view. | |
| 124 for (std::list<std::string>::const_iterator it = tab_frontend_ids.begin(); | |
| 125 it != tab_frontend_ids.end(); ++it) { | |
| 126 status = CloseWebView(context_getter, port, *it); | |
| 127 if (status.IsError()) | |
| 128 return status; | |
| 129 } | |
| 130 | |
| 131 // Close docked DevTools frontend by Javascript. | |
| 132 for (std::list<std::string>::const_iterator it = docked_frontend_ids.begin(); | |
| 133 it != docked_frontend_ids.end(); ++it) { | |
| 134 std::string ws_url = base::StringPrintf( | |
| 135 "ws://127.0.0.1:%d/devtools/page/%s", port, it->c_str()); | |
| 136 scoped_ptr<WebViewImpl> web_view(new WebViewImpl( | |
| 137 *it, | |
| 138 new DevToolsClientImpl(socket_factory, ws_url, | |
| 139 base::Bind(&FakeCloseDevToolsFrontend)), | |
| 140 chrome, base::Bind(&FakeCloseWebView))); | |
| 141 | |
| 142 status = web_view->EnsureAlive(); | |
| 143 if (status.IsError()) | |
| 144 return status; | |
| 145 | |
| 146 scoped_ptr<base::Value> result; | |
| 147 status = web_view->EvaluateScript( | |
| 148 "", "document.getElementById('close-button-right').click();", &result); | |
| 149 // Ignore disconnected error, because the DevTools frontend is closed. | |
| 150 if (status.IsError() && status.code() != kDisconnected) | |
| 151 return status; | |
| 152 } | |
| 153 | |
| 154 // Wait until DevTools UI disconnects from the given web view. | |
| 155 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); | |
| 156 bool web_view_still_open = false; | |
| 157 while (base::Time::Now() < deadline) { | |
| 158 web_view_info_list.clear(); | |
| 159 status = FetchPagesInfo(context_getter, port, &web_view_info_list); | |
| 160 if (status.IsError()) | |
| 161 return status; | |
| 162 | |
| 163 web_view_still_open = false; | |
| 164 for (WebViewInfoList::const_iterator it = web_view_info_list.begin(); | |
| 165 it != web_view_info_list.end(); ++it) { | |
| 166 if (it->id == web_view_id) { | |
| 167 if (!it->debugger_url.empty()) | |
| 168 return Status(kOk); | |
| 169 web_view_still_open = true; | |
| 170 break; | |
| 171 } | |
| 172 } | |
| 173 if (!web_view_still_open) | |
| 174 return Status(kUnknownError, "window closed while closing devtools"); | |
| 175 | |
| 176 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); | |
| 177 } | |
| 178 | |
| 179 return Status(kUnknownError, "failed to close DevTools frontend"); | |
| 180 } | |
| 181 | |
| 74 } // namespace | 182 } // namespace |
| 75 | 183 |
| 76 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter, | 184 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter, |
| 77 int port, | 185 int port, |
| 78 const SyncWebSocketFactory& socket_factory) | 186 const SyncWebSocketFactory& socket_factory) |
| 79 : context_getter_(context_getter), | 187 : context_getter_(context_getter), |
| 80 port_(port), | 188 port_(port), |
| 81 socket_factory_(socket_factory) {} | 189 socket_factory_(socket_factory) {} |
| 82 | 190 |
| 83 ChromeImpl::~ChromeImpl() { | 191 ChromeImpl::~ChromeImpl() { |
| 84 web_view_map_.clear(); | 192 web_view_map_.clear(); |
| 85 } | 193 } |
| 86 | 194 |
| 87 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) { | 195 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) { |
| 88 std::list<std::string> ids; | 196 WebViewInfoList web_view_info_list; |
| 89 Status status = FetchPagesInfo(context_getter_, port_, &ids); | 197 Status status = FetchPagesInfo(context_getter_, port_, &web_view_info_list); |
| 90 if (status.IsError()) | 198 if (status.IsError()) |
| 91 return status; | 199 return status; |
| 92 | 200 |
| 93 std::list<WebView*> internal_web_views; | 201 std::list<WebView*> internal_web_views; |
| 94 for (std::list<std::string>::const_iterator it = ids.begin(); | 202 for (WebViewInfoList::const_iterator it = web_view_info_list.begin(); |
| 95 it != ids.end(); ++it) { | 203 it != web_view_info_list.end(); ++it) { |
| 96 WebViewMap::const_iterator found = web_view_map_.find(*it); | 204 WebViewMap::const_iterator found = web_view_map_.find(it->id); |
| 97 if (found != web_view_map_.end()) { | 205 if (found != web_view_map_.end()) { |
| 98 internal_web_views.push_back(found->second.get()); | 206 internal_web_views.push_back(found->second.get()); |
| 99 continue; | 207 continue; |
| 100 } | 208 } |
| 101 | 209 |
| 102 std::string ws_url = base::StringPrintf( | 210 std::string ws_url = base::StringPrintf( |
| 103 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->c_str()); | 211 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->id.c_str()); |
| 104 web_view_map_[*it] = make_linked_ptr(new WebViewImpl( | 212 DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind( |
| 105 *it, new DevToolsClientImpl(socket_factory_, ws_url), | 213 &CloseDevToolsFrontend, this, socket_factory_, |
| 106 this, base::Bind(&CloseWebView, context_getter_, port_, *it))); | 214 context_getter_, port_, it->id); |
| 107 internal_web_views.push_back(web_view_map_[*it].get()); | 215 web_view_map_[it->id] = make_linked_ptr(new WebViewImpl( |
| 216 it->id, | |
| 217 new DevToolsClientImpl(socket_factory_, ws_url, frontend_closer_func), | |
| 218 this, | |
| 219 base::Bind(&CloseWebView, context_getter_, port_, it->id))); | |
| 220 internal_web_views.push_back(web_view_map_[it->id].get()); | |
| 108 } | 221 } |
| 109 | 222 |
| 110 web_views->swap(internal_web_views); | 223 web_views->swap(internal_web_views); |
| 111 return Status(kOk); | 224 return Status(kOk); |
| 112 } | 225 } |
| 113 | 226 |
| 114 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) { | 227 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) { |
| 115 JavaScriptDialogManager* manager; | 228 JavaScriptDialogManager* manager; |
| 116 Status status = GetDialogManagerForOpenDialog(&manager); | 229 Status status = GetDialogManagerForOpenDialog(&manager); |
| 117 if (status.IsError()) | 230 if (status.IsError()) |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 142 | 255 |
| 143 return manager->HandleDialog(accept, prompt_text); | 256 return manager->HandleDialog(accept, prompt_text); |
| 144 } | 257 } |
| 145 | 258 |
| 146 void ChromeImpl::OnWebViewClose(WebView* web_view) { | 259 void ChromeImpl::OnWebViewClose(WebView* web_view) { |
| 147 web_view_map_.erase(web_view->GetId()); | 260 web_view_map_.erase(web_view->GetId()); |
| 148 } | 261 } |
| 149 | 262 |
| 150 Status ChromeImpl::Init() { | 263 Status ChromeImpl::Init() { |
| 151 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); | 264 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); |
| 152 std::list<std::string> page_ids; | 265 WebViewInfoList web_view_info_list; |
| 153 while (base::Time::Now() < deadline) { | 266 while (base::Time::Now() < deadline) { |
| 154 FetchPagesInfo(context_getter_, port_, &page_ids); | 267 FetchPagesInfo(context_getter_, port_, &web_view_info_list); |
| 155 if (page_ids.empty()) | 268 if (web_view_info_list.empty()) |
| 156 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); | 269 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); |
| 157 else | 270 else |
| 158 break; | 271 return Status(kOk); |
| 159 } | 272 } |
| 160 if (page_ids.empty()) | 273 return Status(kUnknownError, "unable to discover open pages"); |
| 161 return Status(kUnknownError, "unable to discover open pages"); | |
| 162 | |
| 163 return Status(kOk); | |
| 164 } | 274 } |
| 165 | 275 |
| 166 int ChromeImpl::GetPort() const { | 276 int ChromeImpl::GetPort() const { |
| 167 return port_; | 277 return port_; |
| 168 } | 278 } |
| 169 | 279 |
| 170 Status ChromeImpl::GetDialogManagerForOpenDialog( | 280 Status ChromeImpl::GetDialogManagerForOpenDialog( |
| 171 JavaScriptDialogManager** manager) { | 281 JavaScriptDialogManager** manager) { |
| 172 std::list<WebView*> web_views; | 282 std::list<WebView*> web_views; |
| 173 Status status = GetWebViews(&web_views); | 283 Status status = GetWebViews(&web_views); |
| 174 if (status.IsError()) | 284 if (status.IsError()) |
| 175 return status; | 285 return status; |
| 176 | 286 |
| 177 for (std::list<WebView*>::const_iterator it = web_views.begin(); | 287 for (std::list<WebView*>::const_iterator it = web_views.begin(); |
| 178 it != web_views.end(); ++it) { | 288 it != web_views.end(); ++it) { |
| 179 if ((*it)->GetJavaScriptDialogManager()->IsDialogOpen()) { | 289 if ((*it)->GetJavaScriptDialogManager()->IsDialogOpen()) { |
| 180 *manager = (*it)->GetJavaScriptDialogManager(); | 290 *manager = (*it)->GetJavaScriptDialogManager(); |
| 181 return Status(kOk); | 291 return Status(kOk); |
| 182 } | 292 } |
| 183 } | 293 } |
| 184 *manager = NULL; | 294 *manager = NULL; |
| 185 return Status(kOk); | 295 return Status(kOk); |
| 186 } | 296 } |
| 187 | 297 |
| 188 namespace internal { | 298 namespace internal { |
| 189 | 299 |
| 300 WebViewInfo::WebViewInfo(const std::string& id, | |
| 301 const std::string& debugger_url, | |
| 302 const std::string& url, | |
| 303 Type type) | |
| 304 : id(id), debugger_url(debugger_url), url(url), type(type) {} | |
| 305 | |
| 190 Status ParsePagesInfo(const std::string& data, | 306 Status ParsePagesInfo(const std::string& data, |
| 191 std::list<std::string>* page_ids) { | 307 std::list<WebViewInfo>* web_view_info_list) { |
| 192 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); | 308 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); |
| 193 if (!value.get()) | 309 if (!value.get()) |
| 194 return Status(kUnknownError, "DevTools returned invalid JSON"); | 310 return Status(kUnknownError, "DevTools returned invalid JSON"); |
| 195 base::ListValue* list; | 311 base::ListValue* list; |
| 196 if (!value->GetAsList(&list)) | 312 if (!value->GetAsList(&list)) |
| 197 return Status(kUnknownError, "DevTools did not return list"); | 313 return Status(kUnknownError, "DevTools did not return list"); |
| 198 | 314 |
| 199 std::list<std::string> ids; | 315 std::list<WebViewInfo> web_view_info_list_tmp; |
| 200 for (size_t i = 0; i < list->GetSize(); ++i) { | 316 for (size_t i = 0; i < list->GetSize(); ++i) { |
| 201 base::DictionaryValue* info; | 317 base::DictionaryValue* info; |
| 202 if (!list->GetDictionary(i, &info)) | 318 if (!list->GetDictionary(i, &info)) |
| 203 return Status(kUnknownError, "DevTools contains non-dictionary item"); | 319 return Status(kUnknownError, "DevTools contains non-dictionary item"); |
| 320 std::string id; | |
| 321 if (!info->GetString("id", &id)) | |
| 322 return Status(kUnknownError, "DevTools did not include id"); | |
| 204 std::string type; | 323 std::string type; |
| 205 if (!info->GetString("type", &type)) | 324 if (!info->GetString("type", &type)) |
| 206 return Status(kUnknownError, "DevTools did not include type"); | 325 return Status(kUnknownError, "DevTools did not include type"); |
| 207 if (type != "page") | 326 std::string url; |
| 208 continue; | 327 if (!info->GetString("url", &url)) |
| 209 std::string id; | 328 return Status(kUnknownError, "DevTools did not include url"); |
| 210 if (!info->GetString("id", &id)) | 329 std::string debugger_url; |
| 211 return Status(kUnknownError, "DevTools did not include id"); | 330 info->GetString("webSocketDebuggerUrl", &debugger_url); |
| 212 ids.push_back(id); | 331 if (type == "page") |
| 332 web_view_info_list_tmp.push_back( | |
| 333 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kPage)); | |
| 334 else if (type == "other") | |
| 335 web_view_info_list_tmp.push_back( | |
| 336 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kOther)); | |
| 337 else | |
| 338 return Status(kUnknownError, "DevTools returned unknown type:" + type); | |
| 213 } | 339 } |
| 214 page_ids->swap(ids); | 340 web_view_info_list->swap(web_view_info_list_tmp); |
| 215 return Status(kOk); | 341 return Status(kOk); |
| 216 } | 342 } |
| 217 | 343 |
| 218 } // namespace internal | 344 } // namespace internal |
| OLD | NEW |