OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 // A small example application showing the use of the C++ Headless Chrome |
| 6 // library. It navigates to a web site given on the command line, waits for it |
| 7 // to load and prints out the DOM. |
| 8 // |
| 9 // Tip: start reading from the main() function below. |
| 10 |
| 11 #include "base/bind.h" |
| 12 #include "base/command_line.h" |
| 13 #include "base/memory/weak_ptr.h" |
| 14 #include "headless/public/devtools/domains/page.h" |
| 15 #include "headless/public/devtools/domains/runtime.h" |
| 16 #include "headless/public/headless_browser.h" |
| 17 #include "headless/public/headless_devtools_client.h" |
| 18 #include "headless/public/headless_devtools_target.h" |
| 19 #include "headless/public/headless_web_contents.h" |
| 20 #include "ui/gfx/geometry/size.h" |
| 21 |
| 22 // This class contains the main application logic, i.e., waiting for a page to |
| 23 // load and printing its DOM. Note that browser initialization happens outside |
| 24 // this class. |
| 25 class HeadlessExample : public headless::HeadlessWebContents::Observer, |
| 26 public headless::page::Observer { |
| 27 public: |
| 28 HeadlessExample(headless::HeadlessBrowser* browser, |
| 29 headless::HeadlessWebContents* web_contents); |
| 30 ~HeadlessExample() override; |
| 31 |
| 32 // headless::HeadlessWebContents::Observer implementation: |
| 33 void DevToolsTargetReady() override; |
| 34 |
| 35 // headless::page::Observer implementation: |
| 36 void OnLoadEventFired( |
| 37 const headless::page::LoadEventFiredParams& params) override; |
| 38 |
| 39 // Tip: Observe headless::inspector::ExperimentalObserver::OnTargetCrashed to |
| 40 // be notified of renderer crashes. |
| 41 |
| 42 void OnDomFetched(std::unique_ptr<headless::runtime::EvaluateResult> result); |
| 43 |
| 44 private: |
| 45 // The headless browser instance. Owned by the headless library. See main(). |
| 46 headless::HeadlessBrowser* browser_; |
| 47 // Our tab. Owned by |browser_|. |
| 48 headless::HeadlessWebContents* web_contents_; |
| 49 // The DevTools client used to control the tab. |
| 50 std::unique_ptr<headless::HeadlessDevToolsClient> devtools_client_; |
| 51 // A helper for creating weak pointers to this class. |
| 52 base::WeakPtrFactory<HeadlessExample> weak_factory_; |
| 53 }; |
| 54 |
| 55 namespace { |
| 56 HeadlessExample* g_example; |
| 57 } |
| 58 |
| 59 HeadlessExample::HeadlessExample(headless::HeadlessBrowser* browser, |
| 60 headless::HeadlessWebContents* web_contents) |
| 61 : browser_(browser), |
| 62 web_contents_(web_contents), |
| 63 devtools_client_(headless::HeadlessDevToolsClient::Create()), |
| 64 weak_factory_(this) { |
| 65 web_contents_->AddObserver(this); |
| 66 } |
| 67 |
| 68 HeadlessExample::~HeadlessExample() { |
| 69 // Note that we shut down the browser last, because it owns objects such as |
| 70 // the web contents which can no longer be accessed after the browser is gone. |
| 71 devtools_client_->GetPage()->RemoveObserver(this); |
| 72 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); |
| 73 web_contents_->RemoveObserver(this); |
| 74 browser_->Shutdown(); |
| 75 } |
| 76 |
| 77 // This method is called when the tab is ready for DevTools inspection. |
| 78 void HeadlessExample::DevToolsTargetReady() { |
| 79 // Attach our DevTools client to the tab so that we can send commands to it |
| 80 // and observe events. |
| 81 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); |
| 82 |
| 83 // Start observing events from DevTools's page domain. This lets us get |
| 84 // notified when the page has finished loading. Note that it is possible |
| 85 // the page has already finished loading by now. See |
| 86 // HeadlessShell::DevToolTargetReady for how to handle that case correctly. |
| 87 devtools_client_->GetPage()->AddObserver(this); |
| 88 devtools_client_->GetPage()->Enable(); |
| 89 } |
| 90 |
| 91 void HeadlessExample::OnLoadEventFired( |
| 92 const headless::page::LoadEventFiredParams& params) { |
| 93 // The page has now finished loading. Let's grab a snapshot of the DOM by |
| 94 // evaluating the outerHTML property on the body element. |
| 95 devtools_client_->GetRuntime()->Evaluate( |
| 96 "document.body.outerHTML", |
| 97 base::Bind(&HeadlessExample::OnDomFetched, weak_factory_.GetWeakPtr())); |
| 98 } |
| 99 |
| 100 void HeadlessExample::OnDomFetched( |
| 101 std::unique_ptr<headless::runtime::EvaluateResult> result) { |
| 102 std::string dom; |
| 103 // Make sure the evaluation succeeded before reading the result. |
| 104 if (result->HasExceptionDetails()) { |
| 105 LOG(ERROR) << "Failed to evaluate document.body.outerHTML: " |
| 106 << result->GetExceptionDetails()->GetText(); |
| 107 } else if (result->GetResult()->GetValue()->GetAsString(&dom)) { |
| 108 printf("%s\n", dom.c_str()); |
| 109 } |
| 110 |
| 111 // Shut down the browser (see ~HeadlessExample). |
| 112 delete g_example; |
| 113 g_example = nullptr; |
| 114 } |
| 115 |
| 116 // This function is called by the headless library after the browser has been |
| 117 // initialized. It runs on the UI thread. |
| 118 void OnHeadlessBrowserStarted(headless::HeadlessBrowser* browser) { |
| 119 // In order to open tabs, we first need a browser context. It corresponds to a |
| 120 // user profile and contains things like the user's cookies, local storage, |
| 121 // cache, etc. |
| 122 headless::HeadlessBrowserContext::Builder context_builder = |
| 123 browser->CreateBrowserContextBuilder(); |
| 124 |
| 125 // Here we can set options for the browser context. As an example we enable |
| 126 // incognito mode, which makes sure profile data is not written to disk. |
| 127 context_builder.SetIncognitoMode(true); |
| 128 |
| 129 // Construct the context and set it as the default. The default browser |
| 130 // context is used by the Target.createTarget() DevTools command when no other |
| 131 // context is given. |
| 132 headless::HeadlessBrowserContext* browser_context = context_builder.Build(); |
| 133 browser->SetDefaultBrowserContext(browser_context); |
| 134 |
| 135 // Get the URL from the command line. |
| 136 base::CommandLine::StringVector args = |
| 137 base::CommandLine::ForCurrentProcess()->GetArgs(); |
| 138 if (args.empty()) { |
| 139 LOG(ERROR) << "No URL to load"; |
| 140 browser->Shutdown(); |
| 141 return; |
| 142 } |
| 143 GURL url(args[0]); |
| 144 |
| 145 // Open a tab (i.e., HeadlessWebContents) in the newly created browser |
| 146 // context. |
| 147 headless::HeadlessWebContents::Builder tab_builder( |
| 148 browser_context->CreateWebContentsBuilder()); |
| 149 |
| 150 // We can set options for the opened tab here. In this example we are just |
| 151 // setting the initial URL to navigate to. |
| 152 tab_builder.SetInitialURL(url); |
| 153 |
| 154 // Create an instance of the example app, which will wait for the page to load |
| 155 // and print its DOM. |
| 156 headless::HeadlessWebContents* web_contents = tab_builder.Build(); |
| 157 g_example = new HeadlessExample(browser, web_contents); |
| 158 } |
| 159 |
| 160 int main(int argc, const char** argv) { |
| 161 // This function must be the first thing we call to make sure child processes |
| 162 // such as the renderer are started properly. The headless library starts |
| 163 // child processes by forking and exec'ing the main application. |
| 164 headless::RunChildProcessIfNeeded(argc, argv); |
| 165 |
| 166 // Create a headless browser instance. There can be one of these per process |
| 167 // and it can only be initialized once. |
| 168 headless::HeadlessBrowser::Options::Builder builder(argc, argv); |
| 169 |
| 170 // Here you can customize browser options. As an example we set the window |
| 171 // size. |
| 172 builder.SetWindowSize(gfx::Size(800, 600)); |
| 173 |
| 174 // Pass control to the headless library. It will bring up the browser and |
| 175 // invoke the given callback on the browser UI thread. Note: if you need to |
| 176 // pass more parameters to the callback, you can add them to the Bind() call |
| 177 // below. |
| 178 return headless::HeadlessBrowserMain(builder.Build(), |
| 179 base::Bind(&OnHeadlessBrowserStarted)); |
| 180 } |
OLD | NEW |