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

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

Issue 2525903003: Add Page.stopLoading to devtools and --timeout switch to headless_shell (Closed)
Patch Set: FixPage.stopLoading command description. Created 4 years 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
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"
11 #include "base/callback.h" 11 #include "base/callback.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/files/file_path.h" 13 #include "base/files/file_path.h"
14 #include "base/json/json_writer.h" 14 #include "base/json/json_writer.h"
15 #include "base/location.h" 15 #include "base/location.h"
16 #include "base/memory/ref_counted.h" 16 #include "base/memory/ref_counted.h"
17 #include "base/memory/weak_ptr.h"
17 #include "base/numerics/safe_conversions.h" 18 #include "base/numerics/safe_conversions.h"
18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_number_conversions.h"
19 #include "content/public/common/content_switches.h" 20 #include "content/public/common/content_switches.h"
20 #include "headless/app/headless_shell_switches.h" 21 #include "headless/app/headless_shell_switches.h"
21 #include "headless/public/devtools/domains/emulation.h" 22 #include "headless/public/devtools/domains/emulation.h"
22 #include "headless/public/devtools/domains/inspector.h" 23 #include "headless/public/devtools/domains/inspector.h"
23 #include "headless/public/devtools/domains/page.h" 24 #include "headless/public/devtools/domains/page.h"
24 #include "headless/public/devtools/domains/runtime.h" 25 #include "headless/public/devtools/domains/runtime.h"
25 #include "headless/public/headless_browser.h" 26 #include "headless/public/headless_browser.h"
26 #include "headless/public/headless_devtools_client.h" 27 #include "headless/public/headless_devtools_client.h"
(...skipping 30 matching lines...) Expand all
57 class HeadlessShell : public HeadlessWebContents::Observer, 58 class HeadlessShell : public HeadlessWebContents::Observer,
58 emulation::ExperimentalObserver, 59 emulation::ExperimentalObserver,
59 inspector::ExperimentalObserver, 60 inspector::ExperimentalObserver,
60 page::Observer { 61 page::Observer {
61 public: 62 public:
62 HeadlessShell() 63 HeadlessShell()
63 : browser_(nullptr), 64 : browser_(nullptr),
64 devtools_client_(HeadlessDevToolsClient::Create()), 65 devtools_client_(HeadlessDevToolsClient::Create()),
65 web_contents_(nullptr), 66 web_contents_(nullptr),
66 processed_page_ready_(false), 67 processed_page_ready_(false),
67 browser_context_(nullptr) {} 68 browser_context_(nullptr),
69 weak_factory_(this) {}
68 ~HeadlessShell() override {} 70 ~HeadlessShell() override {}
69 71
70 void OnStart(HeadlessBrowser* browser) { 72 void OnStart(HeadlessBrowser* browser) {
71 browser_ = browser; 73 browser_ = browser;
72 74
73 HeadlessBrowserContext::Builder context_builder = 75 HeadlessBrowserContext::Builder context_builder =
74 browser_->CreateBrowserContextBuilder(); 76 browser_->CreateBrowserContextBuilder();
75 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 77 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
76 switches::kDeterministicFetch)) { 78 switches::kDeterministicFetch)) {
77 deterministic_dispatcher_.reset( 79 deterministic_dispatcher_.reset(
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 << "Expected an integer value for --virtual-time-budget="; 158 << "Expected an integer value for --virtual-time-budget=";
157 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy( 159 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
158 emulation::SetVirtualTimePolicyParams::Builder() 160 emulation::SetVirtualTimePolicyParams::Builder()
159 .SetPolicy(emulation::VirtualTimePolicy:: 161 .SetPolicy(emulation::VirtualTimePolicy::
160 PAUSE_IF_NETWORK_FETCHES_PENDING) 162 PAUSE_IF_NETWORK_FETCHES_PENDING)
161 .SetBudget(budget_ms) 163 .SetBudget(budget_ms)
162 .Build()); 164 .Build());
163 } else { 165 } else {
164 PollReadyState(); 166 PollReadyState();
165 } 167 }
168
169 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTimeout)) {
170 std::string timeout_ms_ascii =
171 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
172 switches::kTimeout);
173 int timeout_ms;
174 CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms))
175 << "Expected an integer value for --timeout=";
176 browser_->BrowserMainThread()->PostDelayedTask(
177 FROM_HERE,
178 base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()),
179 base::TimeDelta::FromMilliseconds(timeout_ms));
180 }
181
166 // TODO(skyostil): Implement more features to demonstrate the devtools API. 182 // TODO(skyostil): Implement more features to demonstrate the devtools API.
167 } 183 }
168 184
185 void FetchTimeout() {
186 LOG(INFO) << "Timeout.";
187 devtools_client_->GetPage()->GetExperimental()->StopLoading(
188 page::StopLoadingParams::Builder().Build());
189 }
190
169 void OnTargetCrashed(const inspector::TargetCrashedParams& params) override { 191 void OnTargetCrashed(const inspector::TargetCrashedParams& params) override {
170 LOG(ERROR) << "Abnormal renderer termination."; 192 LOG(ERROR) << "Abnormal renderer termination.";
171 // NB this never gets called if remote debugging is enabled. 193 // NB this never gets called if remote debugging is enabled.
172 Shutdown(); 194 Shutdown();
173 } 195 }
174 196
175 void PollReadyState() { 197 void PollReadyState() {
176 // We need to check the current location in addition to the ready state to 198 // We need to check the current location in addition to the ready state to
177 // be sure the expected page is ready. 199 // be sure the expected page is ready.
178 devtools_client_->GetRuntime()->Evaluate( 200 devtools_client_->GetRuntime()->Evaluate(
179 "document.readyState + ' ' + document.location.href", 201 "document.readyState + ' ' + document.location.href",
180 base::Bind(&HeadlessShell::OnReadyState, base::Unretained(this))); 202 base::Bind(&HeadlessShell::OnReadyState, weak_factory_.GetWeakPtr()));
181 } 203 }
182 204
183 void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) { 205 void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) {
184 std::string ready_state_and_url; 206 std::string ready_state_and_url;
185 if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) { 207 if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) {
186 std::stringstream stream(ready_state_and_url); 208 std::stringstream stream(ready_state_and_url);
187 std::string ready_state; 209 std::string ready_state;
188 std::string url; 210 std::string url;
189 stream >> ready_state; 211 stream >> ready_state;
190 stream >> url; 212 stream >> url;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 switches::kScreenshot)) { 251 switches::kScreenshot)) {
230 CaptureScreenshot(); 252 CaptureScreenshot();
231 } else { 253 } else {
232 Shutdown(); 254 Shutdown();
233 } 255 }
234 } 256 }
235 257
236 void FetchDom() { 258 void FetchDom() {
237 devtools_client_->GetRuntime()->Evaluate( 259 devtools_client_->GetRuntime()->Evaluate(
238 "document.body.innerHTML", 260 "document.body.innerHTML",
239 base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this))); 261 base::Bind(&HeadlessShell::OnDomFetched, weak_factory_.GetWeakPtr()));
240 } 262 }
241 263
242 void OnDomFetched(std::unique_ptr<runtime::EvaluateResult> result) { 264 void OnDomFetched(std::unique_ptr<runtime::EvaluateResult> result) {
243 if (result->HasExceptionDetails()) { 265 if (result->HasExceptionDetails()) {
244 LOG(ERROR) << "Failed to evaluate document.body.innerHTML: " 266 LOG(ERROR) << "Failed to evaluate document.body.innerHTML: "
245 << result->GetExceptionDetails()->GetText(); 267 << result->GetExceptionDetails()->GetText();
246 } else { 268 } else {
247 std::string dom; 269 std::string dom;
248 if (result->GetResult()->GetValue()->GetAsString(&dom)) { 270 if (result->GetResult()->GetValue()->GetAsString(&dom)) {
249 std::cout << dom << std::endl; 271 std::cout << dom << std::endl;
250 } 272 }
251 } 273 }
252 Shutdown(); 274 Shutdown();
253 } 275 }
254 276
255 void InputExpression() { 277 void InputExpression() {
256 // Note that a real system should read user input asynchronously, because 278 // Note that a real system should read user input asynchronously, because
257 // otherwise all other browser activity is suspended (e.g., page loading). 279 // otherwise all other browser activity is suspended (e.g., page loading).
258 std::string expression; 280 std::string expression;
259 std::cout << ">>> "; 281 std::cout << ">>> ";
260 std::getline(std::cin, expression); 282 std::getline(std::cin, expression);
261 if (std::cin.bad() || std::cin.eof() || expression == "quit") { 283 if (std::cin.bad() || std::cin.eof() || expression == "quit") {
262 Shutdown(); 284 Shutdown();
263 return; 285 return;
264 } 286 }
265 devtools_client_->GetRuntime()->Evaluate( 287 devtools_client_->GetRuntime()->Evaluate(
266 expression, 288 expression, base::Bind(&HeadlessShell::OnExpressionResult,
267 base::Bind(&HeadlessShell::OnExpressionResult, base::Unretained(this))); 289 weak_factory_.GetWeakPtr()));
268 } 290 }
269 291
270 void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) { 292 void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) {
271 std::unique_ptr<base::Value> value = result->Serialize(); 293 std::unique_ptr<base::Value> value = result->Serialize();
272 std::string result_json; 294 std::string result_json;
273 base::JSONWriter::Write(*value, &result_json); 295 base::JSONWriter::Write(*value, &result_json);
274 std::cout << result_json << std::endl; 296 std::cout << result_json << std::endl;
275 InputExpression(); 297 InputExpression();
276 } 298 }
277 299
278 void CaptureScreenshot() { 300 void CaptureScreenshot() {
279 devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( 301 devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
280 page::CaptureScreenshotParams::Builder().Build(), 302 page::CaptureScreenshotParams::Builder().Build(),
281 base::Bind(&HeadlessShell::OnScreenshotCaptured, 303 base::Bind(&HeadlessShell::OnScreenshotCaptured,
282 base::Unretained(this))); 304 weak_factory_.GetWeakPtr()));
283 } 305 }
284 306
285 void OnScreenshotCaptured( 307 void OnScreenshotCaptured(
286 std::unique_ptr<page::CaptureScreenshotResult> result) { 308 std::unique_ptr<page::CaptureScreenshotResult> result) {
287 base::FilePath file_name = 309 base::FilePath file_name =
288 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( 310 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
289 switches::kScreenshot); 311 switches::kScreenshot);
290 if (file_name.empty()) { 312 if (file_name.empty()) {
291 file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName); 313 file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName);
292 } 314 }
293 315
294 screenshot_file_stream_.reset( 316 screenshot_file_stream_.reset(
295 new net::FileStream(browser_->BrowserFileThread())); 317 new net::FileStream(browser_->BrowserFileThread()));
296 const int open_result = screenshot_file_stream_->Open( 318 const int open_result = screenshot_file_stream_->Open(
297 file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | 319 file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
298 base::File::FLAG_ASYNC, 320 base::File::FLAG_ASYNC,
299 base::Bind(&HeadlessShell::OnScreenshotFileOpened, 321 base::Bind(&HeadlessShell::OnScreenshotFileOpened,
300 base::Unretained(this), base::Passed(std::move(result)), 322 weak_factory_.GetWeakPtr(), base::Passed(std::move(result)),
301 file_name)); 323 file_name));
302 if (open_result != net::ERR_IO_PENDING) { 324 if (open_result != net::ERR_IO_PENDING) {
303 // Operation could not be started. 325 // Operation could not be started.
304 OnScreenshotFileOpened(nullptr, file_name, open_result); 326 OnScreenshotFileOpened(nullptr, file_name, open_result);
305 } 327 }
306 } 328 }
307 329
308 void OnScreenshotFileOpened( 330 void OnScreenshotFileOpened(
309 std::unique_ptr<page::CaptureScreenshotResult> result, 331 std::unique_ptr<page::CaptureScreenshotResult> result,
310 const base::FilePath file_name, 332 const base::FilePath file_name,
311 const int open_result) { 333 const int open_result) {
312 if (open_result != net::OK) { 334 if (open_result != net::OK) {
313 LOG(ERROR) << "Writing screenshot to file " << file_name.value() 335 LOG(ERROR) << "Writing screenshot to file " << file_name.value()
314 << " was unsuccessful, could not open file: " 336 << " was unsuccessful, could not open file: "
315 << net::ErrorToString(open_result); 337 << net::ErrorToString(open_result);
316 return; 338 return;
317 } 339 }
318 340
319 std::string decoded_png; 341 std::string decoded_png;
320 base::Base64Decode(result->GetData(), &decoded_png); 342 base::Base64Decode(result->GetData(), &decoded_png);
321 scoped_refptr<net::IOBufferWithSize> buf = 343 scoped_refptr<net::IOBufferWithSize> buf =
322 new net::IOBufferWithSize(decoded_png.size()); 344 new net::IOBufferWithSize(decoded_png.size());
323 memcpy(buf->data(), decoded_png.data(), decoded_png.size()); 345 memcpy(buf->data(), decoded_png.data(), decoded_png.size());
324 const int write_result = screenshot_file_stream_->Write( 346 const int write_result = screenshot_file_stream_->Write(
325 buf.get(), buf->size(), 347 buf.get(), buf->size(),
326 base::Bind(&HeadlessShell::OnScreenshotFileWritten, 348 base::Bind(&HeadlessShell::OnScreenshotFileWritten,
327 base::Unretained(this), file_name, buf->size())); 349 weak_factory_.GetWeakPtr(), file_name, buf->size()));
328 if (write_result != net::ERR_IO_PENDING) { 350 if (write_result != net::ERR_IO_PENDING) {
329 // Operation may have completed successfully or failed. 351 // Operation may have completed successfully or failed.
330 OnScreenshotFileWritten(file_name, buf->size(), write_result); 352 OnScreenshotFileWritten(file_name, buf->size(), write_result);
331 } 353 }
332 } 354 }
333 355
334 void OnScreenshotFileWritten(const base::FilePath file_name, 356 void OnScreenshotFileWritten(const base::FilePath file_name,
335 const int length, 357 const int length,
336 const int write_result) { 358 const int write_result) {
337 if (write_result < length) { 359 if (write_result < length) {
338 // TODO(eseckler): Support recovering from partial writes. 360 // TODO(eseckler): Support recovering from partial writes.
339 LOG(ERROR) << "Writing screenshot to file " << file_name.value() 361 LOG(ERROR) << "Writing screenshot to file " << file_name.value()
340 << " was unsuccessful: " << net::ErrorToString(write_result); 362 << " was unsuccessful: " << net::ErrorToString(write_result);
341 } else { 363 } else {
342 std::cout << "Screenshot written to file " << file_name.value() << "." 364 std::cout << "Screenshot written to file " << file_name.value() << "."
343 << std::endl; 365 << std::endl;
344 } 366 }
345 int close_result = screenshot_file_stream_->Close(base::Bind( 367 int close_result = screenshot_file_stream_->Close(base::Bind(
346 &HeadlessShell::OnScreenshotFileClosed, base::Unretained(this))); 368 &HeadlessShell::OnScreenshotFileClosed, weak_factory_.GetWeakPtr()));
347 if (close_result != net::ERR_IO_PENDING) { 369 if (close_result != net::ERR_IO_PENDING) {
348 // Operation could not be started. 370 // Operation could not be started.
349 OnScreenshotFileClosed(close_result); 371 OnScreenshotFileClosed(close_result);
350 } 372 }
351 } 373 }
352 374
353 void OnScreenshotFileClosed(const int close_result) { Shutdown(); } 375 void OnScreenshotFileClosed(const int close_result) { Shutdown(); }
354 376
355 bool RemoteDebuggingEnabled() const { 377 bool RemoteDebuggingEnabled() const {
356 const base::CommandLine& command_line = 378 const base::CommandLine& command_line =
357 *base::CommandLine::ForCurrentProcess(); 379 *base::CommandLine::ForCurrentProcess();
358 return command_line.HasSwitch(::switches::kRemoteDebuggingPort); 380 return command_line.HasSwitch(::switches::kRemoteDebuggingPort);
359 } 381 }
360 382
361 private: 383 private:
362 GURL url_; 384 GURL url_;
363 HeadlessBrowser* browser_; // Not owned. 385 HeadlessBrowser* browser_; // Not owned.
364 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; 386 std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
365 HeadlessWebContents* web_contents_; 387 HeadlessWebContents* web_contents_;
366 bool processed_page_ready_; 388 bool processed_page_ready_;
367 std::unique_ptr<net::FileStream> screenshot_file_stream_; 389 std::unique_ptr<net::FileStream> screenshot_file_stream_;
368 HeadlessBrowserContext* browser_context_; 390 HeadlessBrowserContext* browser_context_;
369 std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_; 391 std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_;
392 base::WeakPtrFactory<HeadlessShell> weak_factory_;
370 393
371 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); 394 DISALLOW_COPY_AND_ASSIGN(HeadlessShell);
372 }; 395 };
373 396
374 int HeadlessShellMain(int argc, const char** argv) { 397 int HeadlessShellMain(int argc, const char** argv) {
375 RunChildProcessIfNeeded(argc, argv); 398 RunChildProcessIfNeeded(argc, argv);
376 HeadlessShell shell; 399 HeadlessShell shell;
377 HeadlessBrowser::Options::Builder builder(argc, argv); 400 HeadlessBrowser::Options::Builder builder(argc, argv);
378 401
379 // Enable devtools if requested. 402 // Enable devtools if requested.
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 } 465 }
443 builder.SetWindowSize(parsed_window_size); 466 builder.SetWindowSize(parsed_window_size);
444 } 467 }
445 468
446 return HeadlessBrowserMain( 469 return HeadlessBrowserMain(
447 builder.Build(), 470 builder.Build(),
448 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); 471 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell)));
449 } 472 }
450 473
451 } // namespace headless 474 } // namespace headless
OLDNEW
« no previous file with comments | « content/browser/devtools/protocol/page_handler.cc ('k') | headless/app/headless_shell_switches.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698