1 of 41

Headless

Chrome

skyostil@ / alexclarke@

BlinkOn 6

2 of 41

Headless mode

  • Browser without a UI
  • Able to run in a server environment
  • Intended for testing and automation

Out of scope:

  • Not an embeddable browser widget

3 of 41

Motivation

  • Existing solutions
    • Regular browser using virtual framebuffer (xvfb)
    • PhantomJS, NW.js (node-webkit)
    • Frameworks such as Chromium Embedded Framework, QtWebEngine, etc.
  • Clients
    • Selenium & WebDriver
    • Karma test runner
    • Chromium Telemetry, layout test runner

4 of 41

Motivation

  • Problems
    • High resource usage
    • Painful to set up
    • No support for modern web features (e.g., ES6, Service Workers, WebGL, ...)
    • No auto-updates
    • Non-deterministic behavior
    • May have less security isolation

5 of 41

6 of 41

7 of 41

Headless Chrome

8 of 41

9 of 41

Headless Chrome

Headless API

Headless browser

10 of 41

Headless Chrome

Headless API

Headless browser

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

11 of 41

Headless Chrome

Headless API

Embedding app

Headless browser

Embedding app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

12 of 41

Headless Chrome

Headless API

Embedding app

Headless browser

Embedding app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

Headless API

Embedding app

Headless library

Embedding C++ app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

13 of 41

Headless Chrome

Headless API

Embedding app

Headless browser

Embedding app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

Headless API

Embedding app

Headless library

Embedding C++ app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

DevTools over websocket

DevTools C++ API

14 of 41

Headless Chrome

Headless API

Embedding app

Headless browser

Embedding app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

Headless API

Embedding app

Headless library

Embedding C++ app

Headless API

Renderer

Tab

Tab

Headless API

Renderer

Tab

Custom networking

Mojo modules

DevTools C++ API

DevTools over websocket

15 of 41

Headless Chrome

  • Differences from regular Chrome
    • No Chrome-level features (e.g., profiles, sync, safe browsing, ...)
    • No UI -- offscreen software rendering
    • No extensions (for now)
    • No audio

Content API

Ozone

Scheduler

Net

Content

base/

net/

Ozone

Content API

Blink

Content API

Components

Content API

Chrome

Content API

Chrome

16 of 41

Headless Chrome

  • Differences from regular Chrome
    • No Chrome-level features (e.g., profiles, sync, safe browsing, ...)
    • No UI -- offscreen software rendering
    • No extensions (for now)
    • No audio
  • What you do get:
    • Everything below the content layer
    • Blink with the latest web platform awesomeness
    • Profiling and introspection tools (DevTools UI, tracing, etc.)

Content API

Ozone

Scheduler

Net

Content

base/

net/

Ozone

Content API

Blink

Content API

Components

Content API

Chrome

17 of 41

Headless additions

  • More DevTools API capabilities for everyone
    • Web data extraction
    • DevTools forwarding
    • Opening and closing tabs
  • Determinism
    • Session isolation
    • Virtual time
    • Deterministic time, date, random numbers, etc.
  • DevTools bindings for C++ and Javascript
  • Network customization (C++)
  • Mojo modules (C++)

18 of 41

Web data extraction

  • More flexible screenshots
    • Layout & visual viewport controls
  • DOM dumping
    • Atomic snapshotting of the DOM for the entire page
  • Chrome traces
  • Resource fetches
  • More data about the layout tree

19 of 41

Determinism: Session isolation

  • Save resources by running parallel sessions in a single browser
  • Each session gets unique
    • Cache
    • Cookies
    • Local storage
    • ...
  • Session serialization
    • Avoids need to restart browser�or mess with file system to get a clean state

Headless browser

Browser

context #1

Browser process

Renderer #1

Browser

context #2

Cookies, local storage, cache, ...

Cookies, local storage, cache, ...

Renderer processes

Renderer #1

WebContents

WebContents

Renderer #1

Renderer #2

WebContents

20 of 41

Determinism: Virtual time

  • Timers can use virtual time instead of real time
  • Examples
    • Fast-forward time to speed up page loading
    • Stop time while network requests are pending
    • Suspend and resume renderer

21 of 41

Determinism: Virtual time

Timer t=1

Timer t=3

Timer t=1

Timer t=3

Timer t=1

Timer t=3

Timer t=1

1 2 3 4 real time

Timer t=3

Timer t=1

1 2 3 4 real time

Timer t=3

Timer t=1

1 2 3 4 real time

Timer t=3

No virtual time

Fast forwarding

Suspension

22 of 41

Controlling the browser: DevTools protocol

  • Magic behind Chrome Developer Tools
  • Allows controlling, inspecting and debugging pages
  • Commands, events

23 of 41

Controlling the browser: DevTools protocol

Accessibility

Console

Device orientation

Emulation

Animation

CSS

DOM

Heap profiler

Application cache

Database

DOM debugger

IndexedDB

Cache storage

Debugger

DOM storage

Input

Inspector

IO

Layer tree

Memory

Network

Page

Profiler

Rendering

Runtime

Security

Service worker

Tracing

Worker

24 of 41

DevTools wire protocol

Headless API

Embedding app

DevTools target

DevTools client

25 of 41

DevTools wire protocol

Headless API

Embedding app

DevTools target

DevTools client

{id: 1, method: "Runtime.evaluate", params: {

expression: "1 + 1"

}}

26 of 41

DevTools wire protocol

Headless API

Embedding app

DevTools target

DevTools client

{id:1, result: {

result: {

type: "number", value: 2, description: "2"

},

wasThrown: false

}}

{id: 1, method: "Runtime.evaluate", params: {

expression: "1 + 1"

}}

27 of 41

DevTools wire protocol

Headless API

Embedding app

DevTools target

DevTools client

{method: "Page.loadEventFired", params: {

timestamp: 376371.511946

}}

28 of 41

DevTools bindings for Headless

  • Two flavors
    • C++
    • Javascript
  • Type-safe
  • Generated for a specific protocol version
    • Backwards compatible
  • Stable and experimental APIs

29 of 41

DevTools bindings for Headless

client->GetRuntime()->Evaluate("1 + 1", base::Bind(&OnResult));

void OnResult(std::unique_ptr<headless::runtime::EvaluateResult> result) {

std::string value;

result->GetResult()->GetValue()->GetAsString(&value);

std::cout << value << std::endl;

}

C++

30 of 41

DevTools bindings for Headless

client->GetRuntime()->Evaluate("1 + 1", base::Bind(&OnResult));

void OnResult(std::unique_ptr<headless::runtime::EvaluateResult> result) {

std::string value;

result->GetResult()->GetValue()->GetAsString(&value);

std::cout << value << std::endl;

}

client.runtime.evaluate("1 + 1").then(response => {

console.log(response.result.value);

});

C++

JS

31 of 41

DevTools bindings for Headless

client->GetRuntime()->Evaluate("1 + 1", base::Bind(&OnResult));

void OnResult(std::unique_ptr<headless::runtime::EvaluateResult> result) {

std::string value;

result->GetResult()->GetValue()->GetAsString(&value);

std::cout << value << std::endl;

}

let response = await client.runtime.evaluate("1 + 1");

console.log(response.result.value);

C++

ES6

32 of 41

DevTools bindings for Headless

client->GetPage()->Enable();

client->GetPage()->AddObserver(this);

void MyObserver::OnLoadEventFired(

std::unique_ptr<page::LoadEventFiredParams> params) override {

std::cout << params->GetTimestamp() << std::endl;

}

client.page.enable();

client.page.addLoadEventFiredListener(params => {

console.log(params.timestamp);

});

C++

JS

33 of 41

Network customization (C++)

  • Implement custom handlers for http://, https://, file://, ...
  • Examples
    • Fetch pages from disk instead of the internet
    • Replace specific resources
    • Runtime generated response (e.g., for a test harness)

Headless browser

content/

content/

Network backend

Request

MyProtocolHandler

MyURLRequestJob

Response

net/

net/

MyProtocolHandler

MyURLRequestJob

net::URLRequest�JobFactoryImpl

http

Creates

34 of 41

Mojo services

  • Provide JS bindings to C++ objects
  • Examples
    • Test runner objects (e.g., testRunner.dumpAsText())
    • Host OS interfaces (e.g., enabling airplane mode)

Headless API

Embedding app

Headless browser

Embedding C++ app

Mojo service

35 of 41

Mojo services

Mojo service

Headless API

Renderer

36 of 41

Mojo services

Mojo service

Headless API

Renderer

module example;

MyService {

SendTestResults(string result);

};

.mojom

37 of 41

Mojo services

Mojo service

Headless API

Renderer

Mojo.example.MyService.then(service => {

service.sendTestResults("all passed");

});

module example;

MyService {

SendTestResults(string result);

};

JS

.mojom

38 of 41

Mojo services

Mojo service

Headless API

Renderer

Mojo.example.MyService.then(service => {

service.sendTestResults("all passed");

});

module example;

MyService {

SendTestResults(string result);

};

class MyServiceImpl : public example::MyService {

void SendTestResult(const mojo::String& result) override {

EXPECT_EQ("all passed", result.get());

}

};

C++

JS

.mojom

39 of 41

Distribution

  • Chrome in headless mode

$ chrome --headless --remote-debugging-port=9222

    • Doesn’t quite exist yet
    • Experimental version: Headless Shell

  • C++ library
    • Lives in headless/ in the Chromium repository
    • Distributed as source code (no stable ABI)
    • See headless/README.md (bit.ly/23LR2pW)

40 of 41

Future work

  • Finish all the things we promised in these slides :)
  • Test harness integration (WebDriver, Selenium)
  • Windows & OSX macOS support
  • Node.js package
  • Reduce memory footprint
  • WebGL (software rendering and/or offscreen GPU)
  • Chrome extensions (maybe)
  • Trace analysis for guiding performance optimization

41 of 41

Give it a try

$ mkdir -p out/Debug

$ echo 'import("//build/args/headless.gn")' > out/Debug/args.gn

$ gn gen out/Debug

$ ninja -C out/Debug headless_shell

$ out/Debug/headless_shell https://www.chromium.org

Mailing list: headless-dev@chromium.org (bit.ly/1ZBcyLx)

Documentation: bit.ly/23LR2pW Bug label: Proj-Headless