Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 <memory> | 5 #include <memory> |
| 6 #include <sstream> | 6 #include <sstream> |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 #include "headless/public/devtools/domains/emulation.h" | 22 #include "headless/public/devtools/domains/emulation.h" |
| 23 #include "headless/public/devtools/domains/inspector.h" | 23 #include "headless/public/devtools/domains/inspector.h" |
| 24 #include "headless/public/devtools/domains/page.h" | 24 #include "headless/public/devtools/domains/page.h" |
| 25 #include "headless/public/devtools/domains/runtime.h" | 25 #include "headless/public/devtools/domains/runtime.h" |
| 26 #include "headless/public/headless_browser.h" | 26 #include "headless/public/headless_browser.h" |
| 27 #include "headless/public/headless_devtools_client.h" | 27 #include "headless/public/headless_devtools_client.h" |
| 28 #include "headless/public/headless_devtools_target.h" | 28 #include "headless/public/headless_devtools_target.h" |
| 29 #include "headless/public/headless_web_contents.h" | 29 #include "headless/public/headless_web_contents.h" |
| 30 #include "headless/public/util/deterministic_dispatcher.h" | 30 #include "headless/public/util/deterministic_dispatcher.h" |
| 31 #include "headless/public/util/deterministic_http_protocol_handler.h" | 31 #include "headless/public/util/deterministic_http_protocol_handler.h" |
| 32 #include "headless/public/util/navigation_request.h" | |
| 32 #include "net/base/file_stream.h" | 33 #include "net/base/file_stream.h" |
| 33 #include "net/base/io_buffer.h" | 34 #include "net/base/io_buffer.h" |
| 34 #include "net/base/ip_address.h" | 35 #include "net/base/ip_address.h" |
| 35 #include "net/base/net_errors.h" | 36 #include "net/base/net_errors.h" |
| 36 #include "ui/gfx/geometry/size.h" | 37 #include "ui/gfx/geometry/size.h" |
| 37 | 38 |
| 38 namespace headless { | 39 namespace headless { |
| 40 class HeadlessShell; | |
| 41 | |
| 39 namespace { | 42 namespace { |
| 40 // Address where to listen to incoming DevTools connections. | 43 // Address where to listen to incoming DevTools connections. |
| 41 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; | 44 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; |
| 42 // Default file name for screenshot. Can be overriden by "--screenshot" switch. | 45 // Default file name for screenshot. Can be overriden by "--screenshot" switch. |
| 43 const char kDefaultScreenshotFileName[] = "screenshot.png"; | 46 const char kDefaultScreenshotFileName[] = "screenshot.png"; |
| 44 | 47 |
| 45 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { | 48 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { |
| 46 int width, height = 0; | 49 int width, height = 0; |
| 47 if (sscanf(window_size.c_str(), "%d%*[x,]%d", &width, &height) >= 2 && | 50 if (sscanf(window_size.c_str(), "%d%*[x,]%d", &width, &height) >= 2 && |
| 48 width >= 0 && height >= 0) { | 51 width >= 0 && height >= 0) { |
| 49 parsed_window_size->set_width(width); | 52 parsed_window_size->set_width(width); |
| 50 parsed_window_size->set_height(height); | 53 parsed_window_size->set_height(height); |
| 51 return true; | 54 return true; |
| 52 } | 55 } |
| 53 return false; | 56 return false; |
| 54 } | 57 } |
| 55 } // namespace | 58 } // namespace |
| 56 | 59 |
| 60 // Used in deterministic mode to make sure navigations and resource requests | |
| 61 // complete in the order requested. | |
| 62 class SimpleNavigationRequest : public NavigationRequest { | |
|
Sami
2017/02/09 14:05:11
s/Simple/Shell/? Not sure what a complex navigatio
alex clarke (OOO till 29th)
2017/02/09 15:08:32
Done.
| |
| 63 public: | |
| 64 SimpleNavigationRequest(base::WeakPtr<HeadlessShell> headless_shell, | |
| 65 const page::NavigationRequestedParams& params) | |
| 66 : headless_shell_(headless_shell), | |
| 67 navigation_id_(params.GetNavigationId()) {} | |
| 68 | |
| 69 ~SimpleNavigationRequest() override {} | |
| 70 | |
| 71 void StartProcessing(base::Closure done_callback) override; | |
| 72 | |
| 73 // Note the navigation likely isn't done when this is called, however we | |
| 74 // expect it will have been committed and the initial resource load requested. | |
| 75 static void ProcessNavigationResult( | |
| 76 base::Closure done_callback, | |
| 77 std::unique_ptr<page::ProcessNavigationResult>) { | |
| 78 done_callback.Run(); | |
| 79 } | |
| 80 | |
| 81 private: | |
| 82 base::WeakPtr<HeadlessShell> headless_shell_; | |
| 83 int navigation_id_; | |
| 84 }; | |
| 85 | |
| 57 // An application which implements a simple headless browser. | 86 // An application which implements a simple headless browser. |
| 58 class HeadlessShell : public HeadlessWebContents::Observer, | 87 class HeadlessShell : public HeadlessWebContents::Observer, |
| 59 emulation::ExperimentalObserver, | 88 emulation::ExperimentalObserver, |
| 60 inspector::ExperimentalObserver, | 89 inspector::ExperimentalObserver, |
| 61 page::Observer { | 90 page::ExperimentalObserver { |
| 62 public: | 91 public: |
| 63 HeadlessShell() | 92 HeadlessShell() |
| 64 : browser_(nullptr), | 93 : browser_(nullptr), |
| 65 devtools_client_(HeadlessDevToolsClient::Create()), | 94 devtools_client_(HeadlessDevToolsClient::Create()), |
| 66 web_contents_(nullptr), | 95 web_contents_(nullptr), |
| 67 processed_page_ready_(false), | 96 processed_page_ready_(false), |
| 68 browser_context_(nullptr), | 97 browser_context_(nullptr), |
| 69 weak_factory_(this) {} | 98 weak_factory_(this) {} |
| 70 ~HeadlessShell() override {} | 99 ~HeadlessShell() override {} |
| 71 | 100 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 119 } | 148 } |
| 120 } | 149 } |
| 121 } | 150 } |
| 122 | 151 |
| 123 void Shutdown() { | 152 void Shutdown() { |
| 124 if (!web_contents_) | 153 if (!web_contents_) |
| 125 return; | 154 return; |
| 126 if (!RemoteDebuggingEnabled()) { | 155 if (!RemoteDebuggingEnabled()) { |
| 127 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); | 156 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); |
| 128 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); | 157 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); |
| 129 devtools_client_->GetPage()->RemoveObserver(this); | 158 devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); |
| 130 if (web_contents_->GetDevToolsTarget()) { | 159 if (web_contents_->GetDevToolsTarget()) { |
| 131 web_contents_->GetDevToolsTarget()->DetachClient( | 160 web_contents_->GetDevToolsTarget()->DetachClient( |
| 132 devtools_client_.get()); | 161 devtools_client_.get()); |
| 133 } | 162 } |
| 134 } | 163 } |
| 135 web_contents_->RemoveObserver(this); | 164 web_contents_->RemoveObserver(this); |
| 136 web_contents_ = nullptr; | 165 web_contents_ = nullptr; |
| 137 browser_context_->Close(); | 166 browser_context_->Close(); |
| 138 browser_->Shutdown(); | 167 browser_->Shutdown(); |
| 139 } | 168 } |
| 140 | 169 |
| 141 // HeadlessWebContents::Observer implementation: | 170 // HeadlessWebContents::Observer implementation: |
| 142 void DevToolsTargetReady() override { | 171 void DevToolsTargetReady() override { |
| 143 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); | 172 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); |
| 144 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); | 173 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); |
| 145 devtools_client_->GetPage()->AddObserver(this); | 174 devtools_client_->GetPage()->GetExperimental()->AddObserver(this); |
| 146 devtools_client_->GetPage()->Enable(); | 175 devtools_client_->GetPage()->Enable(); |
| 147 // Check if the document had already finished loading by the time we | 176 // Check if the document had already finished loading by the time we |
| 148 // attached. | 177 // attached. |
| 149 | 178 |
| 150 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); | 179 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); |
| 151 | 180 |
| 152 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 181 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 182 switches::kDeterministicFetch)) { | |
| 183 devtools_client_->GetPage()->GetExperimental()->SetControlNavigations( | |
| 184 headless::page::SetControlNavigationsParams::Builder() | |
| 185 .SetEnabled(true) | |
| 186 .Build()); | |
| 187 } | |
| 188 | |
| 189 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 153 switches::kVirtualTimeBudget)) { | 190 switches::kVirtualTimeBudget)) { |
| 154 std::string budget_ms_ascii = | 191 std::string budget_ms_ascii = |
| 155 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 192 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 156 switches::kVirtualTimeBudget); | 193 switches::kVirtualTimeBudget); |
| 157 int budget_ms; | 194 int budget_ms; |
| 158 CHECK(base::StringToInt(budget_ms_ascii, &budget_ms)) | 195 CHECK(base::StringToInt(budget_ms_ascii, &budget_ms)) |
| 159 << "Expected an integer value for --virtual-time-budget="; | 196 << "Expected an integer value for --virtual-time-budget="; |
| 160 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy( | 197 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy( |
| 161 emulation::SetVirtualTimePolicyParams::Builder() | 198 emulation::SetVirtualTimePolicyParams::Builder() |
| 162 .SetPolicy(emulation::VirtualTimePolicy:: | 199 .SetPolicy(emulation::VirtualTimePolicy:: |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 228 | 265 |
| 229 // page::Observer implementation: | 266 // page::Observer implementation: |
| 230 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { | 267 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { |
| 231 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 268 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 232 switches::kVirtualTimeBudget)) { | 269 switches::kVirtualTimeBudget)) { |
| 233 return; | 270 return; |
| 234 } | 271 } |
| 235 OnPageReady(); | 272 OnPageReady(); |
| 236 } | 273 } |
| 237 | 274 |
| 275 void OnNavigationRequested( | |
| 276 const headless::page::NavigationRequestedParams& params) override { | |
| 277 deterministic_dispatcher_->NavigationRequested( | |
| 278 base::MakeUnique<SimpleNavigationRequest>(weak_factory_.GetWeakPtr(), | |
| 279 params)); | |
| 280 } | |
| 281 | |
| 238 void OnPageReady() { | 282 void OnPageReady() { |
| 239 if (processed_page_ready_) | 283 if (processed_page_ready_) |
| 240 return; | 284 return; |
| 241 processed_page_ready_ = true; | 285 processed_page_ready_ = true; |
| 242 | 286 |
| 243 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) { | 287 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) { |
| 244 FetchDom(); | 288 FetchDom(); |
| 245 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 289 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 246 switches::kRepl)) { | 290 switches::kRepl)) { |
| 247 LOG(INFO) | 291 LOG(INFO) |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 379 } | 423 } |
| 380 | 424 |
| 381 void OnScreenshotFileClosed(const int close_result) { Shutdown(); } | 425 void OnScreenshotFileClosed(const int close_result) { Shutdown(); } |
| 382 | 426 |
| 383 bool RemoteDebuggingEnabled() const { | 427 bool RemoteDebuggingEnabled() const { |
| 384 const base::CommandLine& command_line = | 428 const base::CommandLine& command_line = |
| 385 *base::CommandLine::ForCurrentProcess(); | 429 *base::CommandLine::ForCurrentProcess(); |
| 386 return command_line.HasSwitch(switches::kRemoteDebuggingPort); | 430 return command_line.HasSwitch(switches::kRemoteDebuggingPort); |
| 387 } | 431 } |
| 388 | 432 |
| 433 HeadlessDevToolsClient* devtools_client() const { | |
| 434 return devtools_client_.get(); | |
| 435 } | |
| 436 | |
| 389 private: | 437 private: |
| 390 GURL url_; | 438 GURL url_; |
| 391 HeadlessBrowser* browser_; // Not owned. | 439 HeadlessBrowser* browser_; // Not owned. |
| 392 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; | 440 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; |
| 393 HeadlessWebContents* web_contents_; | 441 HeadlessWebContents* web_contents_; |
| 394 bool processed_page_ready_; | 442 bool processed_page_ready_; |
| 395 std::unique_ptr<net::FileStream> screenshot_file_stream_; | 443 std::unique_ptr<net::FileStream> screenshot_file_stream_; |
| 396 HeadlessBrowserContext* browser_context_; | 444 HeadlessBrowserContext* browser_context_; |
| 397 std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_; | 445 std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_; |
| 398 base::WeakPtrFactory<HeadlessShell> weak_factory_; | 446 base::WeakPtrFactory<HeadlessShell> weak_factory_; |
| 399 | 447 |
| 400 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); | 448 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); |
| 401 }; | 449 }; |
| 402 | 450 |
| 451 void SimpleNavigationRequest::StartProcessing(base::Closure done_callback) { | |
| 452 if (!headless_shell_) | |
| 453 return; | |
| 454 | |
| 455 // Allow the navigation to proceed. | |
| 456 headless_shell_->devtools_client() | |
| 457 ->GetPage() | |
| 458 ->GetExperimental() | |
| 459 ->ProcessNavigation( | |
| 460 headless::page::ProcessNavigationParams::Builder() | |
| 461 .SetNavigationId(navigation_id_) | |
| 462 .SetResponse(headless::page::NavigationResponse::PROCEED) | |
| 463 .Build(), | |
| 464 base::Bind(&SimpleNavigationRequest::ProcessNavigationResult, | |
| 465 done_callback)); | |
| 466 } | |
| 467 | |
| 403 bool ValidateCommandLine(const base::CommandLine& command_line) { | 468 bool ValidateCommandLine(const base::CommandLine& command_line) { |
| 404 if (!command_line.HasSwitch(switches::kRemoteDebuggingPort)) { | 469 if (!command_line.HasSwitch(switches::kRemoteDebuggingPort)) { |
| 405 if (command_line.GetArgs().size() <= 1) | 470 if (command_line.GetArgs().size() <= 1) |
| 406 return true; | 471 return true; |
| 407 LOG(ERROR) << "Open multiple tabs is only supported when the " | 472 LOG(ERROR) << "Open multiple tabs is only supported when the " |
| 408 << "remote debug port is set."; | 473 << "remote debug port is set."; |
| 409 return false; | 474 return false; |
| 410 } | 475 } |
| 411 if (command_line.HasSwitch(switches::kDumpDom)) { | 476 if (command_line.HasSwitch(switches::kDumpDom)) { |
| 412 LOG(ERROR) << "Dump DOM is disabled when remote debugging is enabled."; | 477 LOG(ERROR) << "Dump DOM is disabled when remote debugging is enabled."; |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 516 builder.SetOverrideWebPreferencesCallback(base::Bind([]( | 581 builder.SetOverrideWebPreferencesCallback(base::Bind([]( |
| 517 WebPreferences* preferences) { preferences->hide_scrollbars = true; })); | 582 WebPreferences* preferences) { preferences->hide_scrollbars = true; })); |
| 518 } | 583 } |
| 519 | 584 |
| 520 return HeadlessBrowserMain( | 585 return HeadlessBrowserMain( |
| 521 builder.Build(), | 586 builder.Build(), |
| 522 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); | 587 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); |
| 523 } | 588 } |
| 524 | 589 |
| 525 } // namespace headless | 590 } // namespace headless |
| OLD | NEW |