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 | 8 |
| 9 #include "base/base64.h" |
8 #include "base/bind.h" | 10 #include "base/bind.h" |
9 #include "base/callback.h" | 11 #include "base/callback.h" |
10 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/files/file_path.h" |
11 #include "base/json/json_writer.h" | 14 #include "base/json/json_writer.h" |
12 #include "base/location.h" | 15 #include "base/location.h" |
13 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
14 #include "base/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" |
15 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
16 #include "content/public/common/content_switches.h" | 19 #include "content/public/common/content_switches.h" |
17 #include "headless/app/headless_shell_switches.h" | 20 #include "headless/app/headless_shell_switches.h" |
18 #include "headless/public/domains/page.h" | 21 #include "headless/public/domains/page.h" |
19 #include "headless/public/domains/runtime.h" | 22 #include "headless/public/domains/runtime.h" |
20 #include "headless/public/headless_browser.h" | 23 #include "headless/public/headless_browser.h" |
21 #include "headless/public/headless_devtools_client.h" | 24 #include "headless/public/headless_devtools_client.h" |
22 #include "headless/public/headless_devtools_target.h" | 25 #include "headless/public/headless_devtools_target.h" |
23 #include "headless/public/headless_web_contents.h" | 26 #include "headless/public/headless_web_contents.h" |
| 27 #include "net/base/file_stream.h" |
| 28 #include "net/base/io_buffer.h" |
24 #include "net/base/ip_address.h" | 29 #include "net/base/ip_address.h" |
| 30 #include "net/base/net_errors.h" |
25 #include "ui/gfx/geometry/size.h" | 31 #include "ui/gfx/geometry/size.h" |
26 | 32 |
27 using headless::HeadlessBrowser; | 33 using headless::HeadlessBrowser; |
28 using headless::HeadlessDevToolsClient; | 34 using headless::HeadlessDevToolsClient; |
29 using headless::HeadlessWebContents; | 35 using headless::HeadlessWebContents; |
30 namespace page = headless::page; | 36 namespace page = headless::page; |
31 namespace runtime = headless::runtime; | 37 namespace runtime = headless::runtime; |
32 | 38 |
33 namespace { | 39 namespace { |
34 // Address where to listen to incoming DevTools connections. | 40 // Address where to listen to incoming DevTools connections. |
35 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; | 41 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; |
| 42 // Default file name for screenshot. Can be overriden by "--screenshot" switch. |
| 43 const char kDefaultScreenshotFileName[] = "screenshot.png"; |
36 } | 44 } |
37 | 45 |
38 // A sample application which demonstrates the use of the headless API. | 46 // A sample application which demonstrates the use of the headless API. |
39 class HeadlessShell : public HeadlessWebContents::Observer, page::Observer { | 47 class HeadlessShell : public HeadlessWebContents::Observer, page::Observer { |
40 public: | 48 public: |
41 HeadlessShell() | 49 HeadlessShell() |
42 : browser_(nullptr), | 50 : browser_(nullptr), |
43 devtools_client_(HeadlessDevToolsClient::Create()), | 51 devtools_client_(HeadlessDevToolsClient::Create()), |
| 52 web_contents_(nullptr), |
44 processed_page_ready_(false) {} | 53 processed_page_ready_(false) {} |
45 ~HeadlessShell() override {} | 54 ~HeadlessShell() override {} |
46 | 55 |
47 void OnStart(HeadlessBrowser* browser) { | 56 void OnStart(HeadlessBrowser* browser) { |
48 browser_ = browser; | 57 browser_ = browser; |
49 | 58 |
50 base::CommandLine::StringVector args = | 59 base::CommandLine::StringVector args = |
51 base::CommandLine::ForCurrentProcess()->GetArgs(); | 60 base::CommandLine::ForCurrentProcess()->GetArgs(); |
52 | 61 |
53 const char kDefaultUrl[] = "about:blank"; | 62 const char kDefaultUrl[] = "about:blank"; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 | 137 |
129 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 138 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
130 headless::switches::kDumpDom)) { | 139 headless::switches::kDumpDom)) { |
131 FetchDom(); | 140 FetchDom(); |
132 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 141 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
133 headless::switches::kRepl)) { | 142 headless::switches::kRepl)) { |
134 std::cout | 143 std::cout |
135 << "Type a Javascript expression to evaluate or \"quit\" to exit." | 144 << "Type a Javascript expression to evaluate or \"quit\" to exit." |
136 << std::endl; | 145 << std::endl; |
137 InputExpression(); | 146 InputExpression(); |
| 147 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 148 headless::switches::kScreenshot)) { |
| 149 CaptureScreenshot(); |
138 } else { | 150 } else { |
139 Shutdown(); | 151 Shutdown(); |
140 } | 152 } |
141 } | 153 } |
142 | 154 |
143 void FetchDom() { | 155 void FetchDom() { |
144 devtools_client_->GetRuntime()->Evaluate( | 156 devtools_client_->GetRuntime()->Evaluate( |
145 "document.body.innerHTML", | 157 "document.body.innerHTML", |
146 base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this))); | 158 base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this))); |
147 } | 159 } |
(...skipping 26 matching lines...) Expand all Loading... |
174 } | 186 } |
175 | 187 |
176 void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) { | 188 void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) { |
177 std::unique_ptr<base::Value> value = result->Serialize(); | 189 std::unique_ptr<base::Value> value = result->Serialize(); |
178 std::string result_json; | 190 std::string result_json; |
179 base::JSONWriter::Write(*value, &result_json); | 191 base::JSONWriter::Write(*value, &result_json); |
180 std::cout << result_json << std::endl; | 192 std::cout << result_json << std::endl; |
181 InputExpression(); | 193 InputExpression(); |
182 } | 194 } |
183 | 195 |
| 196 void CaptureScreenshot() { |
| 197 devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( |
| 198 page::CaptureScreenshotParams::Builder().Build(), |
| 199 base::Bind(&HeadlessShell::OnScreenshotCaptured, |
| 200 base::Unretained(this))); |
| 201 } |
| 202 |
| 203 void OnScreenshotCaptured( |
| 204 std::unique_ptr<page::CaptureScreenshotResult> result) { |
| 205 base::FilePath file_name = |
| 206 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( |
| 207 headless::switches::kScreenshot); |
| 208 if (file_name.empty()) { |
| 209 file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName); |
| 210 } |
| 211 |
| 212 screenshot_file_stream_.reset( |
| 213 new net::FileStream(browser_->BrowserFileThread())); |
| 214 const int open_result = screenshot_file_stream_->Open( |
| 215 file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | |
| 216 base::File::FLAG_ASYNC, |
| 217 base::Bind(&HeadlessShell::OnScreenshotFileOpened, |
| 218 base::Unretained(this), base::Passed(std::move(result)), |
| 219 file_name)); |
| 220 if (open_result != net::ERR_IO_PENDING) { |
| 221 // Operation could not be started. |
| 222 OnScreenshotFileOpened(nullptr, file_name, open_result); |
| 223 } |
| 224 } |
| 225 |
| 226 void OnScreenshotFileOpened( |
| 227 std::unique_ptr<page::CaptureScreenshotResult> result, |
| 228 const base::FilePath file_name, |
| 229 const int open_result) { |
| 230 if (open_result != net::OK) { |
| 231 LOG(ERROR) << "Writing screenshot to file " << file_name.value() |
| 232 << " was unsuccessful, could not open file: " |
| 233 << net::ErrorToString(open_result); |
| 234 return; |
| 235 } |
| 236 |
| 237 std::string decoded_png; |
| 238 base::Base64Decode(result->GetData(), &decoded_png); |
| 239 scoped_refptr<net::IOBufferWithSize> buf = |
| 240 new net::IOBufferWithSize(decoded_png.size()); |
| 241 memcpy(buf->data(), decoded_png.data(), decoded_png.size()); |
| 242 const int write_result = screenshot_file_stream_->Write( |
| 243 buf.get(), buf->size(), |
| 244 base::Bind(&HeadlessShell::OnScreenshotFileWritten, |
| 245 base::Unretained(this), file_name, buf->size())); |
| 246 if (write_result != net::ERR_IO_PENDING) { |
| 247 // Operation may have completed successfully or failed. |
| 248 OnScreenshotFileWritten(file_name, buf->size(), write_result); |
| 249 } |
| 250 } |
| 251 |
| 252 void OnScreenshotFileWritten(const base::FilePath file_name, |
| 253 const int length, |
| 254 const int write_result) { |
| 255 if (write_result < length) { |
| 256 // TODO(eseckler): Support recovering from partial writes. |
| 257 LOG(ERROR) << "Writing screenshot to file " << file_name.value() |
| 258 << " was unsuccessful: " << net::ErrorToString(write_result); |
| 259 } else { |
| 260 std::cout << "Screenshot written to file " << file_name.value() << "." |
| 261 << std::endl; |
| 262 } |
| 263 int close_result = screenshot_file_stream_->Close(base::Bind( |
| 264 &HeadlessShell::OnScreenshotFileClosed, base::Unretained(this))); |
| 265 if (close_result != net::ERR_IO_PENDING) { |
| 266 // Operation could not be started. |
| 267 OnScreenshotFileClosed(close_result); |
| 268 } |
| 269 } |
| 270 |
| 271 void OnScreenshotFileClosed(const int close_result) { Shutdown(); } |
| 272 |
184 bool RemoteDebuggingEnabled() const { | 273 bool RemoteDebuggingEnabled() const { |
185 const base::CommandLine& command_line = | 274 const base::CommandLine& command_line = |
186 *base::CommandLine::ForCurrentProcess(); | 275 *base::CommandLine::ForCurrentProcess(); |
187 return command_line.HasSwitch(switches::kRemoteDebuggingPort); | 276 return command_line.HasSwitch(switches::kRemoteDebuggingPort); |
188 } | 277 } |
189 | 278 |
190 private: | 279 private: |
191 GURL url_; | 280 GURL url_; |
192 HeadlessBrowser* browser_; // Not owned. | 281 HeadlessBrowser* browser_; // Not owned. |
193 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; | 282 std::unique_ptr<HeadlessDevToolsClient> devtools_client_; |
194 HeadlessWebContents* web_contents_; | 283 HeadlessWebContents* web_contents_; |
195 bool processed_page_ready_; | 284 bool processed_page_ready_; |
| 285 std::unique_ptr<net::FileStream> screenshot_file_stream_; |
196 | 286 |
197 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); | 287 DISALLOW_COPY_AND_ASSIGN(HeadlessShell); |
198 }; | 288 }; |
199 | 289 |
200 int main(int argc, const char** argv) { | 290 int main(int argc, const char** argv) { |
201 HeadlessShell shell; | 291 HeadlessShell shell; |
202 HeadlessBrowser::Options::Builder builder(argc, argv); | 292 HeadlessBrowser::Options::Builder builder(argc, argv); |
203 | 293 |
204 // Enable devtools if requested. | 294 // Enable devtools if requested. |
205 base::CommandLine command_line(argc, argv); | 295 base::CommandLine command_line(argc, argv); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 | 333 |
244 if (command_line.HasSwitch(switches::kHostResolverRules)) { | 334 if (command_line.HasSwitch(switches::kHostResolverRules)) { |
245 builder.SetHostResolverRules( | 335 builder.SetHostResolverRules( |
246 command_line.GetSwitchValueASCII(switches::kHostResolverRules)); | 336 command_line.GetSwitchValueASCII(switches::kHostResolverRules)); |
247 } | 337 } |
248 | 338 |
249 return HeadlessBrowserMain( | 339 return HeadlessBrowserMain( |
250 builder.Build(), | 340 builder.Build(), |
251 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); | 341 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); |
252 } | 342 } |
OLD | NEW |