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

Side by Side Diff: headless/app/headless_shell.cc

Issue 2470233003: headless: Librarify Headless Shell (Closed)
Patch Set: Created 4 years, 1 month 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 unified diff | Download patch
« no previous file with comments | « headless/BUILD.gn ('k') | headless/app/headless_shell_main.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 <iostream> 5 #include <iostream>
6 #include <memory> 6 #include <memory>
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 15 matching lines...) Expand all
26 #include "headless/public/headless_devtools_target.h" 26 #include "headless/public/headless_devtools_target.h"
27 #include "headless/public/headless_web_contents.h" 27 #include "headless/public/headless_web_contents.h"
28 #include "headless/public/util/deterministic_dispatcher.h" 28 #include "headless/public/util/deterministic_dispatcher.h"
29 #include "headless/public/util/deterministic_http_protocol_handler.h" 29 #include "headless/public/util/deterministic_http_protocol_handler.h"
30 #include "net/base/file_stream.h" 30 #include "net/base/file_stream.h"
31 #include "net/base/io_buffer.h" 31 #include "net/base/io_buffer.h"
32 #include "net/base/ip_address.h" 32 #include "net/base/ip_address.h"
33 #include "net/base/net_errors.h" 33 #include "net/base/net_errors.h"
34 #include "ui/gfx/geometry/size.h" 34 #include "ui/gfx/geometry/size.h"
35 35
36 using headless::HeadlessBrowser; 36 namespace headless {
37 using headless::HeadlessBrowserContext;
38 using headless::HeadlessDevToolsClient;
39 using headless::HeadlessWebContents;
40 namespace emulation = headless::emulation;
41 namespace page = headless::page;
42 namespace runtime = headless::runtime;
43
44 namespace { 37 namespace {
45 // Address where to listen to incoming DevTools connections. 38 // Address where to listen to incoming DevTools connections.
46 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; 39 const char kDevToolsHttpServerAddress[] = "127.0.0.1";
47 // Default file name for screenshot. Can be overriden by "--screenshot" switch. 40 // Default file name for screenshot. Can be overriden by "--screenshot" switch.
48 const char kDefaultScreenshotFileName[] = "screenshot.png"; 41 const char kDefaultScreenshotFileName[] = "screenshot.png";
49 42
50 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { 43 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) {
51 int width, height = 0; 44 int width, height = 0;
52 if (sscanf(window_size.c_str(), "%dx%d", &width, &height) >= 2 && 45 if (sscanf(window_size.c_str(), "%dx%d", &width, &height) >= 2 &&
53 width >= 0 && height >= 0) { 46 width >= 0 && height >= 0) {
54 parsed_window_size->set_width(width); 47 parsed_window_size->set_width(width);
55 parsed_window_size->set_height(height); 48 parsed_window_size->set_height(height);
56 return true; 49 return true;
57 } 50 }
58 return false; 51 return false;
59 } 52 }
60 } // namespace 53 } // namespace
61 54
62 // A sample application which demonstrates the use of the headless API. 55 // An application which implements a simple headless browser.
63 class HeadlessShell : public HeadlessWebContents::Observer, 56 class HeadlessShell : public HeadlessWebContents::Observer,
64 emulation::ExperimentalObserver, 57 emulation::ExperimentalObserver,
65 page::Observer { 58 page::Observer {
66 public: 59 public:
67 HeadlessShell() 60 HeadlessShell()
68 : browser_(nullptr), 61 : browser_(nullptr),
69 devtools_client_(HeadlessDevToolsClient::Create()), 62 devtools_client_(HeadlessDevToolsClient::Create()),
70 web_contents_(nullptr), 63 web_contents_(nullptr),
71 processed_page_ready_(false), 64 processed_page_ready_(false),
72 browser_context_(nullptr) {} 65 browser_context_(nullptr) {}
73 ~HeadlessShell() override {} 66 ~HeadlessShell() override {}
74 67
75 void OnStart(HeadlessBrowser* browser) { 68 void OnStart(HeadlessBrowser* browser) {
76 browser_ = browser; 69 browser_ = browser;
77 70
78 HeadlessBrowserContext::Builder context_builder = 71 HeadlessBrowserContext::Builder context_builder =
79 browser_->CreateBrowserContextBuilder(); 72 browser_->CreateBrowserContextBuilder();
80 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 73 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
81 headless::switches::kDeterministicFetch)) { 74 switches::kDeterministicFetch)) {
82 deterministic_dispatcher_.reset( 75 deterministic_dispatcher_.reset(
83 new headless::DeterministicDispatcher(browser_->BrowserIOThread())); 76 new DeterministicDispatcher(browser_->BrowserIOThread()));
84 77
85 headless::ProtocolHandlerMap protocol_handlers; 78 ProtocolHandlerMap protocol_handlers;
86 protocol_handlers[url::kHttpScheme] = 79 protocol_handlers[url::kHttpScheme] =
87 base::MakeUnique<headless::DeterministicHttpProtocolHandler>( 80 base::MakeUnique<DeterministicHttpProtocolHandler>(
88 deterministic_dispatcher_.get(), browser->BrowserIOThread()); 81 deterministic_dispatcher_.get(), browser->BrowserIOThread());
89 protocol_handlers[url::kHttpsScheme] = 82 protocol_handlers[url::kHttpsScheme] =
90 base::MakeUnique<headless::DeterministicHttpProtocolHandler>( 83 base::MakeUnique<DeterministicHttpProtocolHandler>(
91 deterministic_dispatcher_.get(), browser->BrowserIOThread()); 84 deterministic_dispatcher_.get(), browser->BrowserIOThread());
92 85
93 context_builder.SetProtocolHandlers(std::move(protocol_handlers)); 86 context_builder.SetProtocolHandlers(std::move(protocol_handlers));
94 } 87 }
95 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 88 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
96 headless::switches::kHideScrollbars)) { 89 switches::kHideScrollbars)) {
97 context_builder.SetOverrideWebPreferencesCallback( 90 context_builder.SetOverrideWebPreferencesCallback(
98 base::Bind([](headless::WebPreferences* preferences) { 91 base::Bind([](WebPreferences* preferences) {
99 preferences->hide_scrollbars = true; 92 preferences->hide_scrollbars = true;
100 })); 93 }));
101 } 94 }
102 browser_context_ = context_builder.Build(); 95 browser_context_ = context_builder.Build();
103 96
104 HeadlessWebContents::Builder builder( 97 HeadlessWebContents::Builder builder(
105 browser_context_->CreateWebContentsBuilder()); 98 browser_context_->CreateWebContentsBuilder());
106 base::CommandLine::StringVector args = 99 base::CommandLine::StringVector args =
107 base::CommandLine::ForCurrentProcess()->GetArgs(); 100 base::CommandLine::ForCurrentProcess()->GetArgs();
108 101
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 return; 133 return;
141 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); 134 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
142 devtools_client_->GetPage()->AddObserver(this); 135 devtools_client_->GetPage()->AddObserver(this);
143 devtools_client_->GetPage()->Enable(); 136 devtools_client_->GetPage()->Enable();
144 // Check if the document had already finished loading by the time we 137 // Check if the document had already finished loading by the time we
145 // attached. 138 // attached.
146 139
147 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); 140 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this);
148 141
149 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 142 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
150 headless::switches::kVirtualTimeBudget)) { 143 switches::kVirtualTimeBudget)) {
151 std::string budget_ms_ascii = 144 std::string budget_ms_ascii =
152 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 145 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
153 headless::switches::kVirtualTimeBudget); 146 switches::kVirtualTimeBudget);
154 int budget_ms; 147 int budget_ms;
155 CHECK(base::StringToInt(budget_ms_ascii, &budget_ms)) 148 CHECK(base::StringToInt(budget_ms_ascii, &budget_ms))
156 << "Expected an integer value for --virtual-time-budget="; 149 << "Expected an integer value for --virtual-time-budget=";
157 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy( 150 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
158 emulation::SetVirtualTimePolicyParams::Builder() 151 emulation::SetVirtualTimePolicyParams::Builder()
159 .SetPolicy(emulation::VirtualTimePolicy:: 152 .SetPolicy(emulation::VirtualTimePolicy::
160 PAUSE_IF_NETWORK_FETCHES_PENDING) 153 PAUSE_IF_NETWORK_FETCHES_PENDING)
161 .SetBudget(budget_ms) 154 .SetBudget(budget_ms)
162 .Build()); 155 .Build());
163 } else { 156 } else {
(...skipping 29 matching lines...) Expand all
193 186
194 // emulation::Observer implementation: 187 // emulation::Observer implementation:
195 void OnVirtualTimeBudgetExpired( 188 void OnVirtualTimeBudgetExpired(
196 const emulation::VirtualTimeBudgetExpiredParams& params) override { 189 const emulation::VirtualTimeBudgetExpiredParams& params) override {
197 OnPageReady(); 190 OnPageReady();
198 } 191 }
199 192
200 // page::Observer implementation: 193 // page::Observer implementation:
201 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { 194 void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
202 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 195 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
203 headless::switches::kVirtualTimeBudget)) { 196 switches::kVirtualTimeBudget)) {
204 return; 197 return;
205 } 198 }
206 OnPageReady(); 199 OnPageReady();
207 } 200 }
208 201
209 void OnPageReady() { 202 void OnPageReady() {
210 if (processed_page_ready_) 203 if (processed_page_ready_)
211 return; 204 return;
212 processed_page_ready_ = true; 205 processed_page_ready_ = true;
213 206
214 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 207 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) {
215 headless::switches::kDumpDom)) {
216 FetchDom(); 208 FetchDom();
217 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( 209 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
218 headless::switches::kRepl)) { 210 switches::kRepl)) {
219 std::cout 211 std::cout
220 << "Type a Javascript expression to evaluate or \"quit\" to exit." 212 << "Type a Javascript expression to evaluate or \"quit\" to exit."
221 << std::endl; 213 << std::endl;
222 InputExpression(); 214 InputExpression();
223 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( 215 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
224 headless::switches::kScreenshot)) { 216 switches::kScreenshot)) {
225 CaptureScreenshot(); 217 CaptureScreenshot();
226 } else { 218 } else {
227 Shutdown(); 219 Shutdown();
228 } 220 }
229 } 221 }
230 222
231 void FetchDom() { 223 void FetchDom() {
232 devtools_client_->GetRuntime()->Evaluate( 224 devtools_client_->GetRuntime()->Evaluate(
233 "document.body.innerHTML", 225 "document.body.innerHTML",
234 base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this))); 226 base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this)));
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( 266 devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
275 page::CaptureScreenshotParams::Builder().Build(), 267 page::CaptureScreenshotParams::Builder().Build(),
276 base::Bind(&HeadlessShell::OnScreenshotCaptured, 268 base::Bind(&HeadlessShell::OnScreenshotCaptured,
277 base::Unretained(this))); 269 base::Unretained(this)));
278 } 270 }
279 271
280 void OnScreenshotCaptured( 272 void OnScreenshotCaptured(
281 std::unique_ptr<page::CaptureScreenshotResult> result) { 273 std::unique_ptr<page::CaptureScreenshotResult> result) {
282 base::FilePath file_name = 274 base::FilePath file_name =
283 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( 275 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
284 headless::switches::kScreenshot); 276 switches::kScreenshot);
285 if (file_name.empty()) { 277 if (file_name.empty()) {
286 file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName); 278 file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName);
287 } 279 }
288 280
289 screenshot_file_stream_.reset( 281 screenshot_file_stream_.reset(
290 new net::FileStream(browser_->BrowserFileThread())); 282 new net::FileStream(browser_->BrowserFileThread()));
291 const int open_result = screenshot_file_stream_->Open( 283 const int open_result = screenshot_file_stream_->Open(
292 file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | 284 file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
293 base::File::FLAG_ASYNC, 285 base::File::FLAG_ASYNC,
294 base::Bind(&HeadlessShell::OnScreenshotFileOpened, 286 base::Bind(&HeadlessShell::OnScreenshotFileOpened,
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 // Operation could not be started. 335 // Operation could not be started.
344 OnScreenshotFileClosed(close_result); 336 OnScreenshotFileClosed(close_result);
345 } 337 }
346 } 338 }
347 339
348 void OnScreenshotFileClosed(const int close_result) { Shutdown(); } 340 void OnScreenshotFileClosed(const int close_result) { Shutdown(); }
349 341
350 bool RemoteDebuggingEnabled() const { 342 bool RemoteDebuggingEnabled() const {
351 const base::CommandLine& command_line = 343 const base::CommandLine& command_line =
352 *base::CommandLine::ForCurrentProcess(); 344 *base::CommandLine::ForCurrentProcess();
353 return command_line.HasSwitch(switches::kRemoteDebuggingPort); 345 return command_line.HasSwitch(::switches::kRemoteDebuggingPort);
354 } 346 }
355 347
356 private: 348 private:
357 GURL url_; 349 GURL url_;
358 HeadlessBrowser* browser_; // Not owned. 350 HeadlessBrowser* browser_; // Not owned.
359 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; 351 std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
360 HeadlessWebContents* web_contents_; 352 HeadlessWebContents* web_contents_;
361 bool processed_page_ready_; 353 bool processed_page_ready_;
362 std::unique_ptr<net::FileStream> screenshot_file_stream_; 354 std::unique_ptr<net::FileStream> screenshot_file_stream_;
363 HeadlessBrowserContext* browser_context_; 355 HeadlessBrowserContext* browser_context_;
364 std::unique_ptr<headless::DeterministicDispatcher> deterministic_dispatcher_; 356 std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_;
365 357
366 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); 358 DISALLOW_COPY_AND_ASSIGN(HeadlessShell);
367 }; 359 };
368 360
369 int main(int argc, const char** argv) { 361 int HeadlessShellMain(int argc, const char** argv) {
370 headless::RunChildProcessIfNeeded(argc, argv); 362 RunChildProcessIfNeeded(argc, argv);
371 HeadlessShell shell; 363 HeadlessShell shell;
372 HeadlessBrowser::Options::Builder builder(argc, argv); 364 HeadlessBrowser::Options::Builder builder(argc, argv);
373 365
374 // Enable devtools if requested. 366 // Enable devtools if requested.
375 base::CommandLine command_line(argc, argv); 367 base::CommandLine command_line(argc, argv);
376 if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { 368 if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) {
377 std::string address = kDevToolsHttpServerAddress; 369 std::string address = kDevToolsHttpServerAddress;
378 if (command_line.HasSwitch(headless::switches::kRemoteDebuggingAddress)) { 370 if (command_line.HasSwitch(switches::kRemoteDebuggingAddress)) {
379 address = command_line.GetSwitchValueASCII( 371 address =
380 headless::switches::kRemoteDebuggingAddress); 372 command_line.GetSwitchValueASCII(switches::kRemoteDebuggingAddress);
381 net::IPAddress parsed_address; 373 net::IPAddress parsed_address;
382 if (!net::ParseURLHostnameToAddress(address, &parsed_address)) { 374 if (!net::ParseURLHostnameToAddress(address, &parsed_address)) {
383 LOG(ERROR) << "Invalid devtools server address"; 375 LOG(ERROR) << "Invalid devtools server address";
384 return EXIT_FAILURE; 376 return EXIT_FAILURE;
385 } 377 }
386 } 378 }
387 int parsed_port; 379 int parsed_port;
388 std::string port_str = 380 std::string port_str =
389 command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); 381 command_line.GetSwitchValueASCII(::switches::kRemoteDebuggingPort);
390 if (!base::StringToInt(port_str, &parsed_port) || 382 if (!base::StringToInt(port_str, &parsed_port) ||
391 !base::IsValueInRangeForNumericType<uint16_t>(parsed_port)) { 383 !base::IsValueInRangeForNumericType<uint16_t>(parsed_port)) {
392 LOG(ERROR) << "Invalid devtools server port"; 384 LOG(ERROR) << "Invalid devtools server port";
393 return EXIT_FAILURE; 385 return EXIT_FAILURE;
394 } 386 }
395 net::IPAddress devtools_address; 387 net::IPAddress devtools_address;
396 bool result = devtools_address.AssignFromIPLiteral(address); 388 bool result = devtools_address.AssignFromIPLiteral(address);
397 DCHECK(result); 389 DCHECK(result);
398 builder.EnableDevToolsServer(net::IPEndPoint( 390 builder.EnableDevToolsServer(net::IPEndPoint(
399 devtools_address, base::checked_cast<uint16_t>(parsed_port))); 391 devtools_address, base::checked_cast<uint16_t>(parsed_port)));
400 } 392 }
401 393
402 if (command_line.HasSwitch(headless::switches::kProxyServer)) { 394 if (command_line.HasSwitch(switches::kProxyServer)) {
403 std::string proxy_server = 395 std::string proxy_server =
404 command_line.GetSwitchValueASCII(headless::switches::kProxyServer); 396 command_line.GetSwitchValueASCII(switches::kProxyServer);
405 net::HostPortPair parsed_proxy_server = 397 net::HostPortPair parsed_proxy_server =
406 net::HostPortPair::FromString(proxy_server); 398 net::HostPortPair::FromString(proxy_server);
407 if (parsed_proxy_server.host().empty() || !parsed_proxy_server.port()) { 399 if (parsed_proxy_server.host().empty() || !parsed_proxy_server.port()) {
408 LOG(ERROR) << "Malformed proxy server url"; 400 LOG(ERROR) << "Malformed proxy server url";
409 return EXIT_FAILURE; 401 return EXIT_FAILURE;
410 } 402 }
411 builder.SetProxyServer(parsed_proxy_server); 403 builder.SetProxyServer(parsed_proxy_server);
412 } 404 }
413 405
414 if (command_line.HasSwitch(switches::kHostResolverRules)) { 406 if (command_line.HasSwitch(::switches::kHostResolverRules)) {
415 builder.SetHostResolverRules( 407 builder.SetHostResolverRules(
416 command_line.GetSwitchValueASCII(switches::kHostResolverRules)); 408 command_line.GetSwitchValueASCII(::switches::kHostResolverRules));
417 } 409 }
418 410
419 if (command_line.HasSwitch(headless::switches::kUseGL)) { 411 if (command_line.HasSwitch(switches::kUseGL)) {
420 builder.SetGLImplementation( 412 builder.SetGLImplementation(
421 command_line.GetSwitchValueASCII(headless::switches::kUseGL)); 413 command_line.GetSwitchValueASCII(switches::kUseGL));
422 } 414 }
423 415
424 if (command_line.HasSwitch(headless::switches::kUserDataDir)) { 416 if (command_line.HasSwitch(switches::kUserDataDir)) {
425 builder.SetUserDataDir( 417 builder.SetUserDataDir(
426 command_line.GetSwitchValuePath(headless::switches::kUserDataDir)); 418 command_line.GetSwitchValuePath(switches::kUserDataDir));
427 builder.SetIncognitoMode(false); 419 builder.SetIncognitoMode(false);
428 } 420 }
429 421
430 if (command_line.HasSwitch(headless::switches::kWindowSize)) { 422 if (command_line.HasSwitch(switches::kWindowSize)) {
431 std::string window_size = 423 std::string window_size =
432 command_line.GetSwitchValueASCII(headless::switches::kWindowSize); 424 command_line.GetSwitchValueASCII(switches::kWindowSize);
433 gfx::Size parsed_window_size; 425 gfx::Size parsed_window_size;
434 if (!ParseWindowSize(window_size, &parsed_window_size)) { 426 if (!ParseWindowSize(window_size, &parsed_window_size)) {
435 LOG(ERROR) << "Malformed window size"; 427 LOG(ERROR) << "Malformed window size";
436 return EXIT_FAILURE; 428 return EXIT_FAILURE;
437 } 429 }
438 builder.SetWindowSize(parsed_window_size); 430 builder.SetWindowSize(parsed_window_size);
439 } 431 }
440 432
441 return HeadlessBrowserMain( 433 return HeadlessBrowserMain(
442 builder.Build(), 434 builder.Build(),
443 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); 435 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell)));
444 } 436 }
437
438 } // namespace headless
OLDNEW
« no previous file with comments | « headless/BUILD.gn ('k') | headless/app/headless_shell_main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698