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 |