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 <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/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" |
18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
19 #include "content/public/common/content_switches.h" | 19 #include "content/public/common/content_switches.h" |
20 #include "headless/app/headless_shell_switches.h" | 20 #include "headless/app/headless_shell_switches.h" |
| 21 #include "headless/public/domains/emulation.h" |
21 #include "headless/public/domains/page.h" | 22 #include "headless/public/domains/page.h" |
22 #include "headless/public/domains/runtime.h" | 23 #include "headless/public/domains/runtime.h" |
23 #include "headless/public/headless_browser.h" | 24 #include "headless/public/headless_browser.h" |
24 #include "headless/public/headless_devtools_client.h" | 25 #include "headless/public/headless_devtools_client.h" |
25 #include "headless/public/headless_devtools_target.h" | 26 #include "headless/public/headless_devtools_target.h" |
26 #include "headless/public/headless_web_contents.h" | 27 #include "headless/public/headless_web_contents.h" |
27 #include "net/base/file_stream.h" | 28 #include "net/base/file_stream.h" |
28 #include "net/base/io_buffer.h" | 29 #include "net/base/io_buffer.h" |
29 #include "net/base/ip_address.h" | 30 #include "net/base/ip_address.h" |
30 #include "net/base/net_errors.h" | 31 #include "net/base/net_errors.h" |
31 #include "ui/gfx/geometry/size.h" | 32 #include "ui/gfx/geometry/size.h" |
32 | 33 |
33 using headless::HeadlessBrowser; | 34 using headless::HeadlessBrowser; |
34 using headless::HeadlessBrowserContext; | 35 using headless::HeadlessBrowserContext; |
35 using headless::HeadlessDevToolsClient; | 36 using headless::HeadlessDevToolsClient; |
36 using headless::HeadlessWebContents; | 37 using headless::HeadlessWebContents; |
| 38 namespace emulation = headless::emulation; |
37 namespace page = headless::page; | 39 namespace page = headless::page; |
38 namespace runtime = headless::runtime; | 40 namespace runtime = headless::runtime; |
39 | 41 |
40 namespace { | 42 namespace { |
41 // Address where to listen to incoming DevTools connections. | 43 // Address where to listen to incoming DevTools connections. |
42 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; | 44 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; |
43 // Default file name for screenshot. Can be overriden by "--screenshot" switch. | 45 // Default file name for screenshot. Can be overriden by "--screenshot" switch. |
44 const char kDefaultScreenshotFileName[] = "screenshot.png"; | 46 const char kDefaultScreenshotFileName[] = "screenshot.png"; |
45 | 47 |
46 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { | 48 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { |
47 int width, height = 0; | 49 int width, height = 0; |
48 if (sscanf(window_size.c_str(), "%dx%d", &width, &height) >= 2 && | 50 if (sscanf(window_size.c_str(), "%dx%d", &width, &height) >= 2 && |
49 width >= 0 && height >= 0) { | 51 width >= 0 && height >= 0) { |
50 parsed_window_size->set_width(width); | 52 parsed_window_size->set_width(width); |
51 parsed_window_size->set_height(height); | 53 parsed_window_size->set_height(height); |
52 return true; | 54 return true; |
53 } | 55 } |
54 return false; | 56 return false; |
55 } | 57 } |
56 } // namespace | 58 } // namespace |
57 | 59 |
58 // A sample application which demonstrates the use of the headless API. | 60 // A sample application which demonstrates the use of the headless API. |
59 class HeadlessShell : public HeadlessWebContents::Observer, page::Observer { | 61 class HeadlessShell : public HeadlessWebContents::Observer, |
| 62 emulation::ExperimentalObserver, |
| 63 page::Observer { |
60 public: | 64 public: |
61 HeadlessShell() | 65 HeadlessShell() |
62 : browser_(nullptr), | 66 : browser_(nullptr), |
63 devtools_client_(HeadlessDevToolsClient::Create()), | 67 devtools_client_(HeadlessDevToolsClient::Create()), |
64 web_contents_(nullptr), | 68 web_contents_(nullptr), |
65 processed_page_ready_(false), | 69 processed_page_ready_(false), |
66 browser_context_(nullptr) {} | 70 browser_context_(nullptr) {} |
67 ~HeadlessShell() override {} | 71 ~HeadlessShell() override {} |
68 | 72 |
69 void OnStart(HeadlessBrowser* browser) { | 73 void OnStart(HeadlessBrowser* browser) { |
70 browser_ = browser; | 74 browser_ = browser; |
71 | 75 |
72 browser_context_ = browser_->CreateBrowserContextBuilder().Build(); | 76 browser_context_ = browser_->CreateBrowserContextBuilder().Build(); |
73 | 77 |
74 HeadlessWebContents::Builder builder( | 78 HeadlessWebContents::Builder builder( |
75 browser_context_->CreateWebContentsBuilder()); | 79 browser_context_->CreateWebContentsBuilder()); |
76 base::CommandLine::StringVector args = | 80 base::CommandLine::StringVector args = |
77 base::CommandLine::ForCurrentProcess()->GetArgs(); | 81 base::CommandLine::ForCurrentProcess()->GetArgs(); |
78 | 82 |
| 83 // TODO(alexclarke): Should we navigate to about:blank first if using |
| 84 // virtual time? |
79 if (!args.empty() && !args[0].empty()) | 85 if (!args.empty() && !args[0].empty()) |
80 builder.SetInitialURL(GURL(args[0])); | 86 builder.SetInitialURL(GURL(args[0])); |
81 | 87 |
82 web_contents_ = builder.Build(); | 88 web_contents_ = builder.Build(); |
83 if (!web_contents_) { | 89 if (!web_contents_) { |
84 LOG(ERROR) << "Navigation failed"; | 90 LOG(ERROR) << "Navigation failed"; |
85 browser_->Shutdown(); | 91 browser_->Shutdown(); |
86 return; | 92 return; |
87 } | 93 } |
88 web_contents_->AddObserver(this); | 94 web_contents_->AddObserver(this); |
89 } | 95 } |
90 | 96 |
91 void Shutdown() { | 97 void Shutdown() { |
92 if (!web_contents_) | 98 if (!web_contents_) |
93 return; | 99 return; |
94 if (!RemoteDebuggingEnabled()) { | 100 if (!RemoteDebuggingEnabled()) { |
| 101 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); |
95 devtools_client_->GetPage()->RemoveObserver(this); | 102 devtools_client_->GetPage()->RemoveObserver(this); |
96 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); | 103 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); |
97 } | 104 } |
98 web_contents_->RemoveObserver(this); | 105 web_contents_->RemoveObserver(this); |
99 web_contents_ = nullptr; | 106 web_contents_ = nullptr; |
100 browser_context_->Close(); | 107 browser_context_->Close(); |
101 browser_->Shutdown(); | 108 browser_->Shutdown(); |
102 } | 109 } |
103 | 110 |
104 // HeadlessWebContents::Observer implementation: | 111 // HeadlessWebContents::Observer implementation: |
105 void DevToolsTargetReady() override { | 112 void DevToolsTargetReady() override { |
106 if (RemoteDebuggingEnabled()) | 113 if (RemoteDebuggingEnabled()) |
107 return; | 114 return; |
108 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); | 115 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); |
109 devtools_client_->GetPage()->AddObserver(this); | 116 devtools_client_->GetPage()->AddObserver(this); |
110 devtools_client_->GetPage()->Enable(); | 117 devtools_client_->GetPage()->Enable(); |
111 // Check if the document had already finished loading by the time we | 118 // Check if the document had already finished loading by the time we |
112 // attached. | 119 // attached. |
113 PollReadyState(); | 120 |
| 121 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); |
| 122 |
| 123 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 124 headless::switches::kVirtualTimeBudget)) { |
| 125 std::string budget_ms_ascii = |
| 126 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 127 headless::switches::kVirtualTimeBudget); |
| 128 int budget_ms; |
| 129 CHECK(base::StringToInt(budget_ms_ascii, &budget_ms)) |
| 130 << "Expected an integer value for --virtual-time-budget="; |
| 131 devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy( |
| 132 emulation::SetVirtualTimePolicyParams::Builder() |
| 133 .SetPolicy(emulation::VirtualTimePolicy:: |
| 134 PAUSE_IF_NETWORK_FETCHES_PENDING) |
| 135 .SetBudget(budget_ms) |
| 136 .Build()); |
| 137 } else { |
| 138 PollReadyState(); |
| 139 } |
114 // TODO(skyostil): Implement more features to demonstrate the devtools API. | 140 // TODO(skyostil): Implement more features to demonstrate the devtools API. |
115 } | 141 } |
116 | 142 |
117 void PollReadyState() { | 143 void PollReadyState() { |
118 // We need to check the current location in addition to the ready state to | 144 // We need to check the current location in addition to the ready state to |
119 // be sure the expected page is ready. | 145 // be sure the expected page is ready. |
120 devtools_client_->GetRuntime()->Evaluate( | 146 devtools_client_->GetRuntime()->Evaluate( |
121 "document.readyState + ' ' + document.location.href", | 147 "document.readyState + ' ' + document.location.href", |
122 base::Bind(&HeadlessShell::OnReadyState, base::Unretained(this))); | 148 base::Bind(&HeadlessShell::OnReadyState, base::Unretained(this))); |
123 } | 149 } |
124 | 150 |
125 void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) { | 151 void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) { |
126 std::string ready_state_and_url; | 152 std::string ready_state_and_url; |
127 if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) { | 153 if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) { |
128 std::stringstream stream(ready_state_and_url); | 154 std::stringstream stream(ready_state_and_url); |
129 std::string ready_state; | 155 std::string ready_state; |
130 std::string url; | 156 std::string url; |
131 stream >> ready_state; | 157 stream >> ready_state; |
132 stream >> url; | 158 stream >> url; |
133 | 159 |
134 if (ready_state == "complete" && | 160 if (ready_state == "complete" && |
135 (url_.spec() == url || url != "about:blank")) { | 161 (url_.spec() == url || url != "about:blank")) { |
136 OnPageReady(); | 162 OnPageReady(); |
137 return; | 163 return; |
138 } | 164 } |
139 } | 165 } |
140 } | 166 } |
141 | 167 |
| 168 // emulation::Observer implementation: |
| 169 void OnVirtualTimeBudgetExpired( |
| 170 const emulation::VirtualTimeBudgetExpiredParams& params) override { |
| 171 OnPageReady(); |
| 172 } |
| 173 |
142 // page::Observer implementation: | 174 // page::Observer implementation: |
143 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { | 175 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { |
| 176 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 177 headless::switches::kVirtualTimeBudget)) { |
| 178 return; |
| 179 } |
144 OnPageReady(); | 180 OnPageReady(); |
145 } | 181 } |
146 | 182 |
147 void OnPageReady() { | 183 void OnPageReady() { |
148 if (processed_page_ready_) | 184 if (processed_page_ready_) |
149 return; | 185 return; |
150 processed_page_ready_ = true; | 186 processed_page_ready_ = true; |
151 | 187 |
152 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 188 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
153 headless::switches::kDumpDom)) { | 189 headless::switches::kDumpDom)) { |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 LOG(ERROR) << "Malformed window size"; | 407 LOG(ERROR) << "Malformed window size"; |
372 return EXIT_FAILURE; | 408 return EXIT_FAILURE; |
373 } | 409 } |
374 builder.SetWindowSize(parsed_window_size); | 410 builder.SetWindowSize(parsed_window_size); |
375 } | 411 } |
376 | 412 |
377 return HeadlessBrowserMain( | 413 return HeadlessBrowserMain( |
378 builder.Build(), | 414 builder.Build(), |
379 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); | 415 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); |
380 } | 416 } |
OLD | NEW |