Chromium Code Reviews| 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, | |
|
alex clarke (OOO till 29th)
2017/02/10 14:46:47
This should probably extend inspector::Experimenta
Sami
2017/02/10 16:25:26
Hmm, this is supposed to be a really simple exampl
| |
| 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 void OnDomFetched(std::unique_ptr<headless::runtime::EvaluateResult> result); | |
| 40 | |
| 41 private: | |
| 42 // The headless browser instance. See main(). | |
| 43 headless::HeadlessBrowser* browser_; | |
|
altimin
2017/02/10 14:39:43
UNOWNED?
Sami
2017/02/10 16:25:26
I'm thinking in the age of unique_ptr and friends,
| |
| 44 // Our tab. | |
| 45 headless::HeadlessWebContents* web_contents_; | |
|
altimin
2017/02/10 14:39:43
UNOWNED?
Sami
2017/02/10 16:25:26
Done.
| |
| 46 // The DevTools client used to control the tab. | |
| 47 std::unique_ptr<headless::HeadlessDevToolsClient> devtools_client_; | |
| 48 // A helper for creating weak pointers to this class. | |
| 49 base::WeakPtrFactory<HeadlessExample> weak_factory_; | |
| 50 }; | |
| 51 | |
| 52 namespace { | |
| 53 std::unique_ptr<HeadlessExample> g_example; | |
|
altimin
2017/02/10 14:39:43
I'm slightly terrified about the idea that Headles
Sami
2017/02/10 16:25:26
Good point, let's make this a raw ptr.
| |
| 54 } | |
| 55 | |
| 56 HeadlessExample::HeadlessExample(headless::HeadlessBrowser* browser, | |
| 57 headless::HeadlessWebContents* web_contents) | |
| 58 : browser_(browser), | |
| 59 web_contents_(web_contents), | |
| 60 devtools_client_(headless::HeadlessDevToolsClient::Create()), | |
| 61 weak_factory_(this) { | |
| 62 web_contents_->AddObserver(this); | |
| 63 } | |
| 64 | |
| 65 HeadlessExample::~HeadlessExample() { | |
| 66 // Note that we shut down the browser last, because it owns objects such as | |
| 67 // the web contents which can no longer be accessed after the browser is gone. | |
| 68 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); | |
|
altimin
2017/02/10 14:39:43
We should also add removing observer from Page dom
Sami
2017/02/10 16:25:26
Oops, done.
| |
| 69 web_contents_->RemoveObserver(this); | |
| 70 browser_->Shutdown(); | |
| 71 } | |
| 72 | |
| 73 // This method is called when the tab is ready for DevTools inspection. | |
| 74 void HeadlessExample::DevToolsTargetReady() { | |
| 75 // Attach our DevTools client to the tab so that we can send commands to it | |
| 76 // and observe events. | |
| 77 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); | |
| 78 | |
| 79 // Start observing events from DevTools's page domain. This lets us get | |
| 80 // notified when the page has finished loading. Note that it is possible | |
| 81 // the page has already finished loading by now. See | |
| 82 // HeadlessShell::DevToolTargetReady for how to handle that case correctly. | |
| 83 devtools_client_->GetPage()->AddObserver(this); | |
| 84 devtools_client_->GetPage()->Enable(); | |
| 85 } | |
| 86 | |
| 87 void HeadlessExample::OnLoadEventFired( | |
| 88 const headless::page::LoadEventFiredParams& params) { | |
| 89 // The page has now finished loading. Let's grab a snapshot of the DOM by | |
| 90 // evaluating the outerHTML property on the body element. | |
| 91 devtools_client_->GetRuntime()->Evaluate( | |
| 92 "document.body.outerHTML", | |
| 93 base::Bind(&HeadlessExample::OnDomFetched, weak_factory_.GetWeakPtr())); | |
| 94 } | |
| 95 | |
| 96 void HeadlessExample::OnDomFetched( | |
| 97 std::unique_ptr<headless::runtime::EvaluateResult> result) { | |
| 98 std::string dom; | |
|
altimin
2017/02/10 14:39:43
Check that we got a valid result here?
Sami
2017/02/10 16:25:26
Done.
| |
| 99 if (result->GetResult()->GetValue()->GetAsString(&dom)) | |
| 100 printf("%s\n", dom.c_str()); | |
| 101 | |
| 102 // Shut down the browser (see ~HeadlessExample). | |
| 103 g_example.reset(); | |
| 104 } | |
| 105 | |
| 106 // This function is called by the headless library after the browser has been | |
| 107 // initialized. It runs on the UI thread. | |
| 108 void OnHeadlessBrowserStarted(headless::HeadlessBrowser* browser) { | |
| 109 // In order to open tabs, we first need a browser context. It corresponds to a | |
| 110 // user profile and contains things like the user's cookies, local storage, | |
| 111 // cache, etc. | |
| 112 headless::HeadlessBrowserContext::Builder context_builder = | |
| 113 browser->CreateBrowserContextBuilder(); | |
| 114 | |
| 115 // Here we can set options for the browser context. As an example we enable | |
| 116 // incognito mode, which makes sure profile data is not written to disk. | |
| 117 context_builder.SetIncognitoMode(true); | |
| 118 | |
| 119 // Construct the context and set it as the default. The default browser | |
| 120 // context is used by the Target.createTarget() DevTools command when no other | |
| 121 // context is given. | |
| 122 headless::HeadlessBrowserContext* browser_context = context_builder.Build(); | |
| 123 browser->SetDefaultBrowserContext(browser_context); | |
| 124 | |
| 125 // Get the URL from the command line. | |
| 126 base::CommandLine::StringVector args = | |
| 127 base::CommandLine::ForCurrentProcess()->GetArgs(); | |
| 128 if (args.empty()) { | |
| 129 LOG(ERROR) << "No URL to load"; | |
| 130 browser->Shutdown(); | |
| 131 return; | |
| 132 } | |
| 133 GURL url(args[0]); | |
| 134 | |
| 135 // Open a tab (i.e., HeadlessWebContents) in the newly created browser | |
| 136 // context. | |
| 137 headless::HeadlessWebContents::Builder tab_builder( | |
| 138 browser_context->CreateWebContentsBuilder()); | |
| 139 | |
| 140 // We can set options for the opened tab here. In this example we are just | |
| 141 // setting the initial URL to navigate to. | |
| 142 tab_builder.SetInitialURL(url); | |
| 143 | |
| 144 // Create an instance of the example app, which will wait for the page to load | |
| 145 // and print its DOM. | |
| 146 headless::HeadlessWebContents* web_contents = tab_builder.Build(); | |
| 147 g_example.reset(new HeadlessExample(browser, web_contents)); | |
| 148 } | |
| 149 | |
| 150 int main(int argc, const char** argv) { | |
| 151 // This function must be the first thing we call to make sure child processes | |
| 152 // such as the renderer are started properly. The headless library starts | |
| 153 // child processes by forking and exec'ing the main application. | |
| 154 headless::RunChildProcessIfNeeded(argc, argv); | |
| 155 | |
| 156 // Create a headless browser instance. There can be one of these per process | |
| 157 // and it can only be initialized once. | |
| 158 headless::HeadlessBrowser::Options::Builder builder(argc, argv); | |
| 159 | |
| 160 // Here you can customize browser options. As an example we set the window | |
| 161 // size. | |
| 162 builder.SetWindowSize(gfx::Size(800, 600)); | |
| 163 | |
| 164 // Pass control to the headless library. It will bring up the browser and | |
| 165 // invoke the given callback on the browser UI thread. Note: if you need to | |
| 166 // pass more parameters to the callback, you can add them to the Bind() call | |
| 167 // below. | |
| 168 return headless::HeadlessBrowserMain(builder.Build(), | |
| 169 base::Bind(&OnHeadlessBrowserStarted)); | |
| 170 } | |
| OLD | NEW |