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

Unified Diff: headless/app/headless_shell.cc

Issue 1430673002: Headless demo (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Better javascript Created 5 years, 1 month 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « headless/DEPS ('k') | headless/lib/browser/headless_browser_context.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: headless/app/headless_shell.cc
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
new file mode 100644
index 0000000000000000000000000000000000000000..76566b78f8aef39053422ee5e9919f9fa6c83ee8
--- /dev/null
+++ b/headless/app/headless_shell.cc
@@ -0,0 +1,267 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/trace_config.h"
+#include "base/values.h"
+#include "cc/output/copy_output_request.h"
+#include "cc/output/copy_output_result.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "headless/public/headless_browser.h"
+#include "headless/public/web_contents.h"
+#include "headless/public/web_document.h"
+#include "headless/public/web_element.h"
+#include "headless/public/web_frame.h"
+#include "headless/public/web_node.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+
+using headless::HeadlessBrowser;
+using headless::WebContents;
+using headless::WebElement;
+using headless::WebNode;
+
+void DoNothing() {}
+
+void DumpElementNode(const WebElement& element,
+ base::DictionaryValue* output) {
+ output->SetString("tag", element.TagName());
+
+ if (!element.Attributes().empty()) {
+ base::DictionaryValue* attributes = new base::DictionaryValue();
+ output->Set("attributes", attributes);
+
+ for (const auto& attribute : element.Attributes()) {
+ attributes->SetString(attribute.first, attribute.second);
+ }
+ }
+}
+
+scoped_ptr<base::DictionaryValue> DumpNode(const WebNode& node) {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ result->SetString("value", node.Value());
+
+ if (node.IsElement()) {
+ DumpElementNode(node.AsElement(), result.get());
+ }
+
+ if (node.HasChildNodes()) {
+ result->Set("children", new base::ListValue());
+
+ base::ListValue* children;
+ result->GetList("children", &children);
+
+ for (WebNode child = node.FirstChild();;
+ child = child.NextSibling()) {
+ children->Append(DumpNode(child));
+
+ if (child == node.LastChild()) {
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+class Observer : public WebContents::Observer {
+ public:
+ Observer(HeadlessBrowser* browser,
+ WebContents* web_contents)
+ : WebContents::Observer(web_contents)
+ , browser_(browser)
+ , web_contents_(web_contents)
+ , screenshot_captured_(false, false)
+ , javascript_executed_(false, false)
+ , dom_dumped_(false, false)
+ , trace_recorded_(false, false) {}
+
+ ~Observer() override {}
+
+
+ void DumpDOMTree() {
+ scoped_ptr<headless::WebFrame> main_frame = web_contents_->main_frame();
+
+ scoped_ptr<base::DictionaryValue> dumped_tree = DumpNode(main_frame->document());
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&Observer::WriteDOMToFile,
+ base::Unretained(this),
+ std::string("dom.json"),
+ base::Owned(dumped_tree.release())));
+ }
+
+ std::string JSONToString(base::Value* value) {
+ std::string string;
+ base::JSONWriter::WriteWithOptions(
+ *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &string);
+ return string;
+ }
+
+ void WriteDOMToFile(
+ const std::string& path,
+ base::Value* dumped_tree) {
+ std::string dumped_string = JSONToString(dumped_tree);
+
+ base::WriteFile(base::FilePath(path),
+ dumped_string.c_str(),
+ dumped_string.size());
+
+ dom_dumped_.Signal();
+ }
+
+ void GetScreenshot(scoped_ptr<SkBitmap> bitmap) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&Observer::WriteScreenshot,
+ base::Unretained(this),
+ base::Owned(bitmap.release())));
+ }
+
+ void WriteScreenshot(SkBitmap* bitmap) {
+ std::vector<unsigned char> png_data;
+ gfx::PNGCodec::FastEncodeBGRASkBitmap(*bitmap, true, &png_data);
+ base::WriteFile(base::FilePath("screenshot.png"),
+ reinterpret_cast<const char*>(vector_as_array(&png_data)),
+ png_data.size());
+ screenshot_captured_.Signal();
+ }
+
+ void ProcessJavaScriptOutput(const std::vector<scoped_ptr<base::Value>>& values) {
+ std::cerr << "Got " << values.size() << " values from javascript" << std::endl;
+ for (auto& value : values) {
+ std::cerr << "JavaScript value: "
+ << (value ? JSONToString(value.get()) : "empty value") << std::endl;
+ }
+ javascript_executed_.Signal();
+ }
+
+ void ExecuteJavaScript() {
+ web_contents_->main_frame()->ExecuteScript("console.log('Written from JavaScript')");
+ web_contents_->main_frame()->ExecuteScriptAndReturnValue(
+ "[1, 2, {'a': 3, 'b': 'c'}]",
+ base::Bind(&Observer::ProcessJavaScriptOutput, base::Unretained(this)));
+ }
+
+ void MaybeShutdown() {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (!command_line.HasSwitch(switches::kRemoteDebuggingPort))
+ browser_->Stop();
+ }
+
+ void StopShutdownThread() {
+ stop_browser_thread_->Stop();
+ }
+
+ void DidFinishTracing() {
+ trace_recorded_.Signal();
+ }
+
+ void WaitForTasksToComplete() {
+ screenshot_captured_.Wait();
+ javascript_executed_.Wait();
+ dom_dumped_.Wait();
+ trace_recorded_.Wait();
+
+ browser_->browser_main_thread()->PostTask(
+ FROM_HERE,
+ base::Bind(&Observer::MaybeShutdown, base::Unretained(this)));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::PROCESS_LAUNCHER,
+ FROM_HERE,
+ base::Bind(&Observer::StopShutdownThread, base::Unretained(this)));
+ }
+
+
+ void DocumentOnLoadCompletedInMainFrame() override {
+ fprintf(stderr, "Loaded\n");
+ web_contents_->GetScreenshot(
+ base::Bind(&Observer::GetScreenshot, base::Unretained(this)));
+
+ browser_->renderer_main_thread()->PostTask(
+ FROM_HERE,
+ base::Bind(&Observer::ExecuteJavaScript, base::Unretained(this)));
+
+ browser_->renderer_main_thread()->PostTask(
+ FROM_HERE,
+ base::Bind(&Observer::DumpDOMTree, base::Unretained(this)));
+
+ browser_->StopTracing("chrometrace.log",
+ base::Bind(&Observer::DidFinishTracing,
+ base::Unretained(this)));
+
+ stop_browser_thread_.reset(new base::Thread("WaitForTasksAndStopBrowserThread"));
+ stop_browser_thread_->Start();
+
+ stop_browser_thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&Observer::WaitForTasksToComplete, base::Unretained(this)));
+ }
+
+ private:
+ HeadlessBrowser* browser_;
+ WebContents* web_contents_;
+
+ scoped_ptr<base::Thread> stop_browser_thread_;
+
+ base::WaitableEvent screenshot_captured_;
+ base::WaitableEvent javascript_executed_;
+ base::WaitableEvent dom_dumped_;
+ base::WaitableEvent trace_recorded_;
+};
+
+void Start() {
+ HeadlessBrowser* browser = HeadlessBrowser::Get();
+
+ browser->StartTracing(
+ base::trace_event::TraceConfig("", ""),
+ base::Bind(&DoNothing));
+
+ static scoped_ptr<WebContents> web_contents = browser->CreateWebContents({800, 600});
+ static scoped_ptr<Observer> observer(
+ new Observer(browser, web_contents.get()));
+
+ base::CommandLine::StringVector args =
+ base::CommandLine::ForCurrentProcess()->GetArgs();
+
+ const std::string DEFAULT_URL = "https://google.com";
+
+ std::string url;
+ if (args.empty() || args[0].empty()) {
+ LOG(ERROR) << "No url provided, opening default one";
+ url = DEFAULT_URL;
+ } else {
+ url = args[0];
+ }
+ web_contents->OpenURL(GURL(url));
+}
+
+int main(int argc, const char** argv) {
+ HeadlessBrowser* browser = HeadlessBrowser::Get();
+
+ return browser->Run(
+ HeadlessBrowser::Options::Builder(argc, argv).Build(),
+ base::Bind(Start));
+}
« no previous file with comments | « headless/DEPS ('k') | headless/lib/browser/headless_browser_context.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698