Make Mozart view manager use the new compositor.

This patch updates the view manager to use the new compositor.

It also implements a few new features that were required to port all
of the UI examples to Mozart:

- Defined the ViewAssociate mechanism for offering services to views
  and view trees such as input dispatch.
- Added a skeleton implementation of an input manager service.  This
  initial implementation simply passes events dispatched through the
  view tree to the first view which registers to listen to them.
  Later on, we'll add hit testing and give input events a complete
  overhaul.
- Changed how scenes are associated with views (previously surfaces)
  to make the interface easier to use.  Clients only need to connect
  to the ViewManager to register their views; they don't need to
  connect to the Compositor since they can simply create the Scene
  through the ViewHost.  This approach is also much friendlier for
  multi-threaded clients.

The clients of the view manager are updated in following patches.

Some things we'll do later: refactor the view registry, design a
new input event dispatch protocol, invert how layout occurs to take
advantage of Scene versioning capabilities to avoid stalls, make
the View interface work more like Scene.

BUG=
R=abarth@google.com

Review URL: https://codereview.chromium.org/1552043002 .
diff --git a/mojo/dart/packages/mojo_services/BUILD.gn b/mojo/dart/packages/mojo_services/BUILD.gn
index cf558aa..61a6541 100644
--- a/mojo/dart/packages/mojo_services/BUILD.gn
+++ b/mojo/dart/packages/mojo_services/BUILD.gn
@@ -70,7 +70,10 @@
   "lib/mojo/terminal/terminal_client.mojom.dart",
   "lib/mojo/terminal/terminal.mojom.dart",
   "lib/mojo/udp_socket.mojom.dart",
+  "lib/mojo/ui/input_connection.mojom.dart",
+  "lib/mojo/ui/input_dispatcher.mojom.dart",
   "lib/mojo/ui/layouts.mojom.dart",
+  "lib/mojo/ui/view_associates.mojom.dart",
   "lib/mojo/ui/view_manager.mojom.dart",
   "lib/mojo/ui/view_provider.mojom.dart",
   "lib/mojo/ui/views.mojom.dart",
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/input_connection.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/input_connection.mojom.dart
new file mode 100644
index 0000000..6e42a45
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/input_connection.mojom.dart
@@ -0,0 +1,593 @@
+// Copyright 2014 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.
+
+library input_connection_mojom;
+
+import 'dart:async';
+
+import 'package:mojo/bindings.dart' as bindings;
+import 'package:mojo/core.dart' as core;
+import 'package:mojo_services/mojo/input_events.mojom.dart' as input_events_mojom;
+
+
+
+class _InputConnectionSetListenerParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Object listener = null;
+
+  _InputConnectionSetListenerParams() : super(kVersions.last.size);
+
+  static _InputConnectionSetListenerParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _InputConnectionSetListenerParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _InputConnectionSetListenerParams result = new _InputConnectionSetListenerParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.listener = decoder0.decodeServiceInterface(8, true, InputListenerProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInterface(listener, 8, true);
+  }
+
+  String toString() {
+    return "_InputConnectionSetListenerParams("
+           "listener: $listener" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _InputListenerOnEventParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  input_events_mojom.Event event = null;
+
+  _InputListenerOnEventParams() : super(kVersions.last.size);
+
+  static _InputListenerOnEventParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _InputListenerOnEventParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _InputListenerOnEventParams result = new _InputListenerOnEventParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.event = input_events_mojom.Event.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(event, 8, false);
+  }
+
+  String toString() {
+    return "_InputListenerOnEventParams("
+           "event: $event" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["event"] = event;
+    return map;
+  }
+}
+
+
+class InputListenerOnEventResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool consumed = false;
+
+  InputListenerOnEventResponseParams() : super(kVersions.last.size);
+
+  static InputListenerOnEventResponseParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static InputListenerOnEventResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    InputListenerOnEventResponseParams result = new InputListenerOnEventResponseParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.consumed = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(consumed, 8, 0);
+  }
+
+  String toString() {
+    return "InputListenerOnEventResponseParams("
+           "consumed: $consumed" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["consumed"] = consumed;
+    return map;
+  }
+}
+
+const int _InputConnection_setListenerName = 0;
+
+abstract class InputConnection {
+  static const String serviceName = "mojo::ui::InputConnection";
+  void setListener(Object listener);
+}
+
+
+class _InputConnectionProxyImpl extends bindings.Proxy {
+  _InputConnectionProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _InputConnectionProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _InputConnectionProxyImpl.unbound() : super.unbound();
+
+  static _InputConnectionProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _InputConnectionProxyImpl"));
+    return new _InputConnectionProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      default:
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_InputConnectionProxyImpl($superString)";
+  }
+}
+
+
+class _InputConnectionProxyCalls implements InputConnection {
+  _InputConnectionProxyImpl _proxyImpl;
+
+  _InputConnectionProxyCalls(this._proxyImpl);
+    void setListener(Object listener) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _InputConnectionSetListenerParams();
+      params.listener = listener;
+      _proxyImpl.sendMessage(params, _InputConnection_setListenerName);
+    }
+}
+
+
+class InputConnectionProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  InputConnection ptr;
+
+  InputConnectionProxy(_InputConnectionProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _InputConnectionProxyCalls(proxyImpl);
+
+  InputConnectionProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _InputConnectionProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _InputConnectionProxyCalls(impl);
+  }
+
+  InputConnectionProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _InputConnectionProxyImpl.fromHandle(handle) {
+    ptr = new _InputConnectionProxyCalls(impl);
+  }
+
+  InputConnectionProxy.unbound() :
+      impl = new _InputConnectionProxyImpl.unbound() {
+    ptr = new _InputConnectionProxyCalls(impl);
+  }
+
+  factory InputConnectionProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    InputConnectionProxy p = new InputConnectionProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static InputConnectionProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputConnectionProxy"));
+    return new InputConnectionProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => InputConnection.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "InputConnectionProxy($impl)";
+  }
+}
+
+
+class InputConnectionStub extends bindings.Stub {
+  InputConnection _impl = null;
+
+  InputConnectionStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  InputConnectionStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  InputConnectionStub.unbound() : super.unbound();
+
+  static InputConnectionStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputConnectionStub"));
+    return new InputConnectionStub.fromEndpoint(endpoint);
+  }
+
+
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case _InputConnection_setListenerName:
+        var params = _InputConnectionSetListenerParams.deserialize(
+            message.payload);
+        _impl.setListener(params.listener);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  InputConnection get impl => _impl;
+  set impl(InputConnection d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "InputConnectionStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int _InputListener_onEventName = 0;
+
+abstract class InputListener {
+  static const String serviceName = null;
+  dynamic onEvent(input_events_mojom.Event event,[Function responseFactory = null]);
+}
+
+
+class _InputListenerProxyImpl extends bindings.Proxy {
+  _InputListenerProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _InputListenerProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _InputListenerProxyImpl.unbound() : super.unbound();
+
+  static _InputListenerProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _InputListenerProxyImpl"));
+    return new _InputListenerProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _InputListener_onEventName:
+        var r = InputListenerOnEventResponseParams.deserialize(
+            message.payload);
+        if (!message.header.hasRequestId) {
+          proxyError("Expected a message with a valid request Id.");
+          return;
+        }
+        Completer c = completerMap[message.header.requestId];
+        if (c == null) {
+          proxyError(
+              "Message had unknown request Id: ${message.header.requestId}");
+          return;
+        }
+        completerMap.remove(message.header.requestId);
+        if (c.isCompleted) {
+          proxyError("Response completer already completed");
+          return;
+        }
+        c.complete(r);
+        break;
+      default:
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_InputListenerProxyImpl($superString)";
+  }
+}
+
+
+class _InputListenerProxyCalls implements InputListener {
+  _InputListenerProxyImpl _proxyImpl;
+
+  _InputListenerProxyCalls(this._proxyImpl);
+    dynamic onEvent(input_events_mojom.Event event,[Function responseFactory = null]) {
+      var params = new _InputListenerOnEventParams();
+      params.event = event;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _InputListener_onEventName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+}
+
+
+class InputListenerProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  InputListener ptr;
+
+  InputListenerProxy(_InputListenerProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _InputListenerProxyCalls(proxyImpl);
+
+  InputListenerProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _InputListenerProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _InputListenerProxyCalls(impl);
+  }
+
+  InputListenerProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _InputListenerProxyImpl.fromHandle(handle) {
+    ptr = new _InputListenerProxyCalls(impl);
+  }
+
+  InputListenerProxy.unbound() :
+      impl = new _InputListenerProxyImpl.unbound() {
+    ptr = new _InputListenerProxyCalls(impl);
+  }
+
+  factory InputListenerProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    InputListenerProxy p = new InputListenerProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static InputListenerProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputListenerProxy"));
+    return new InputListenerProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => InputListener.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "InputListenerProxy($impl)";
+  }
+}
+
+
+class InputListenerStub extends bindings.Stub {
+  InputListener _impl = null;
+
+  InputListenerStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  InputListenerStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  InputListenerStub.unbound() : super.unbound();
+
+  static InputListenerStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputListenerStub"));
+    return new InputListenerStub.fromEndpoint(endpoint);
+  }
+
+
+  InputListenerOnEventResponseParams _InputListenerOnEventResponseParamsFactory(bool consumed) {
+    var mojo_factory_result = new InputListenerOnEventResponseParams();
+    mojo_factory_result.consumed = consumed;
+    return mojo_factory_result;
+  }
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case _InputListener_onEventName:
+        var params = _InputListenerOnEventParams.deserialize(
+            message.payload);
+        var response = _impl.onEvent(params.event,_InputListenerOnEventResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _InputListener_onEventName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _InputListener_onEventName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  InputListener get impl => _impl;
+  set impl(InputListener d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "InputListenerStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/input_dispatcher.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/input_dispatcher.mojom.dart
new file mode 100644
index 0000000..14e4556
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/input_dispatcher.mojom.dart
@@ -0,0 +1,250 @@
+// Copyright 2014 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.
+
+library input_dispatcher_mojom;
+
+import 'dart:async';
+
+import 'package:mojo/bindings.dart' as bindings;
+import 'package:mojo/core.dart' as core;
+import 'package:mojo_services/mojo/input_events.mojom.dart' as input_events_mojom;
+
+
+
+class _InputDispatcherDispatchEventParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  input_events_mojom.Event event = null;
+
+  _InputDispatcherDispatchEventParams() : super(kVersions.last.size);
+
+  static _InputDispatcherDispatchEventParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _InputDispatcherDispatchEventParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _InputDispatcherDispatchEventParams result = new _InputDispatcherDispatchEventParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.event = input_events_mojom.Event.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(event, 8, false);
+  }
+
+  String toString() {
+    return "_InputDispatcherDispatchEventParams("
+           "event: $event" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["event"] = event;
+    return map;
+  }
+}
+
+const int _InputDispatcher_dispatchEventName = 0;
+
+abstract class InputDispatcher {
+  static const String serviceName = "mojo::ui::InputDispatcher";
+  void dispatchEvent(input_events_mojom.Event event);
+}
+
+
+class _InputDispatcherProxyImpl extends bindings.Proxy {
+  _InputDispatcherProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _InputDispatcherProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _InputDispatcherProxyImpl.unbound() : super.unbound();
+
+  static _InputDispatcherProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _InputDispatcherProxyImpl"));
+    return new _InputDispatcherProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      default:
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_InputDispatcherProxyImpl($superString)";
+  }
+}
+
+
+class _InputDispatcherProxyCalls implements InputDispatcher {
+  _InputDispatcherProxyImpl _proxyImpl;
+
+  _InputDispatcherProxyCalls(this._proxyImpl);
+    void dispatchEvent(input_events_mojom.Event event) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _InputDispatcherDispatchEventParams();
+      params.event = event;
+      _proxyImpl.sendMessage(params, _InputDispatcher_dispatchEventName);
+    }
+}
+
+
+class InputDispatcherProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  InputDispatcher ptr;
+
+  InputDispatcherProxy(_InputDispatcherProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _InputDispatcherProxyCalls(proxyImpl);
+
+  InputDispatcherProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _InputDispatcherProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _InputDispatcherProxyCalls(impl);
+  }
+
+  InputDispatcherProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _InputDispatcherProxyImpl.fromHandle(handle) {
+    ptr = new _InputDispatcherProxyCalls(impl);
+  }
+
+  InputDispatcherProxy.unbound() :
+      impl = new _InputDispatcherProxyImpl.unbound() {
+    ptr = new _InputDispatcherProxyCalls(impl);
+  }
+
+  factory InputDispatcherProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    InputDispatcherProxy p = new InputDispatcherProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static InputDispatcherProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputDispatcherProxy"));
+    return new InputDispatcherProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => InputDispatcher.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "InputDispatcherProxy($impl)";
+  }
+}
+
+
+class InputDispatcherStub extends bindings.Stub {
+  InputDispatcher _impl = null;
+
+  InputDispatcherStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  InputDispatcherStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  InputDispatcherStub.unbound() : super.unbound();
+
+  static InputDispatcherStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For InputDispatcherStub"));
+    return new InputDispatcherStub.fromEndpoint(endpoint);
+  }
+
+
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case _InputDispatcher_dispatchEventName:
+        var params = _InputDispatcherDispatchEventParams.deserialize(
+            message.payload);
+        _impl.dispatchEvent(params.event);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  InputDispatcher get impl => _impl;
+  set impl(InputDispatcher d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "InputDispatcherStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/layouts.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/layouts.mojom.dart
index bc16ff7..8e07861 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/ui/layouts.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/layouts.mojom.dart
@@ -9,7 +9,7 @@
 import 'package:mojo/bindings.dart' as bindings;
 import 'package:mojo/core.dart' as core;
 import 'package:mojo_services/mojo/geometry.mojom.dart' as geometry_mojom;
-import 'package:mojo_services/mojo/surface_id.mojom.dart' as surface_id_mojom;
+import 'package:mojo_services/mojo/gfx/composition/scene_token.mojom.dart' as scene_token_mojom;
 
 
 
@@ -188,7 +188,7 @@
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(24, 0)
   ];
-  surface_id_mojom.SurfaceId surfaceId = null;
+  scene_token_mojom.SceneToken sceneToken = null;
   geometry_mojom.Size size = null;
 
   ViewLayoutInfo() : super(kVersions.last.size);
@@ -229,7 +229,7 @@
     if (mainDataHeader.version >= 0) {
       
       var decoder1 = decoder0.decodePointer(8, false);
-      result.surfaceId = surface_id_mojom.SurfaceId.decode(decoder1);
+      result.sceneToken = scene_token_mojom.SceneToken.decode(decoder1);
     }
     if (mainDataHeader.version >= 0) {
       
@@ -242,20 +242,88 @@
   void encode(bindings.Encoder encoder) {
     var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
     
-    encoder0.encodeStruct(surfaceId, 8, false);
+    encoder0.encodeStruct(sceneToken, 8, false);
     
     encoder0.encodeStruct(size, 16, false);
   }
 
   String toString() {
     return "ViewLayoutInfo("
-           "surfaceId: $surfaceId" ", "
+           "sceneToken: $sceneToken" ", "
            "size: $size" ")";
   }
 
   Map toJson() {
     Map map = new Map();
-    map["surfaceId"] = surfaceId;
+    map["sceneToken"] = sceneToken;
+    map["size"] = size;
+    return map;
+  }
+}
+
+
+class ViewLayoutResult extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  geometry_mojom.Size size = null;
+
+  ViewLayoutResult() : super(kVersions.last.size);
+
+  static ViewLayoutResult deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static ViewLayoutResult decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewLayoutResult result = new ViewLayoutResult();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.size = geometry_mojom.Size.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(size, 8, false);
+  }
+
+  String toString() {
+    return "ViewLayoutResult("
+           "size: $size" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
     map["size"] = size;
     return map;
   }
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart
new file mode 100644
index 0000000..4f23b7e
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart
@@ -0,0 +1,826 @@
+// Copyright 2014 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.
+
+library view_associates_mojom;
+
+import 'dart:async';
+
+import 'package:mojo/bindings.dart' as bindings;
+import 'package:mojo/core.dart' as core;
+import 'package:mojo/mojo/service_provider.mojom.dart' as service_provider_mojom;
+import 'package:mojo_services/mojo/ui/views.mojom.dart' as views_mojom;
+import 'package:mojo_services/mojo/ui/view_trees.mojom.dart' as view_trees_mojom;
+
+
+
+class ViewAssociateInfo extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  List<String> viewServiceNames = null;
+  List<String> viewTreeServiceNames = null;
+
+  ViewAssociateInfo() : super(kVersions.last.size);
+
+  static ViewAssociateInfo deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static ViewAssociateInfo decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewAssociateInfo result = new ViewAssociateInfo();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, true);
+      if (decoder1 == null) {
+        result.viewServiceNames = null;
+      } else {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.viewServiceNames = new List<String>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          result.viewServiceNames[i1] = decoder1.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, true);
+      if (decoder1 == null) {
+        result.viewTreeServiceNames = null;
+      } else {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.viewTreeServiceNames = new List<String>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          result.viewTreeServiceNames[i1] = decoder1.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    if (viewServiceNames == null) {
+      encoder0.encodeNullPointer(8, true);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(viewServiceNames.length, 8, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < viewServiceNames.length; ++i0) {
+        
+        encoder1.encodeString(viewServiceNames[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+    
+    if (viewTreeServiceNames == null) {
+      encoder0.encodeNullPointer(16, true);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(viewTreeServiceNames.length, 16, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < viewTreeServiceNames.length; ++i0) {
+        
+        encoder1.encodeString(viewTreeServiceNames[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "ViewAssociateInfo("
+           "viewServiceNames: $viewServiceNames" ", "
+           "viewTreeServiceNames: $viewTreeServiceNames" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewServiceNames"] = viewServiceNames;
+    map["viewTreeServiceNames"] = viewTreeServiceNames;
+    return map;
+  }
+}
+
+
+class _ViewAssociateConnectParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Object inspector = null;
+
+  _ViewAssociateConnectParams() : super(kVersions.last.size);
+
+  static _ViewAssociateConnectParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _ViewAssociateConnectParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewAssociateConnectParams result = new _ViewAssociateConnectParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.inspector = decoder0.decodeServiceInterface(8, false, ViewInspectorProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInterface(inspector, 8, false);
+  }
+
+  String toString() {
+    return "_ViewAssociateConnectParams("
+           "inspector: $inspector" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class ViewAssociateConnectResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  ViewAssociateInfo info = null;
+
+  ViewAssociateConnectResponseParams() : super(kVersions.last.size);
+
+  static ViewAssociateConnectResponseParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static ViewAssociateConnectResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewAssociateConnectResponseParams result = new ViewAssociateConnectResponseParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.info = ViewAssociateInfo.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(info, 8, false);
+  }
+
+  String toString() {
+    return "ViewAssociateConnectResponseParams("
+           "info: $info" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["info"] = info;
+    return map;
+  }
+}
+
+
+class _ViewAssociateConnectToViewServiceParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  views_mojom.ViewToken viewToken = null;
+  String serviceName_ = null;
+  core.MojoMessagePipeEndpoint pipe = null;
+
+  _ViewAssociateConnectToViewServiceParams() : super(kVersions.last.size);
+
+  static _ViewAssociateConnectToViewServiceParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _ViewAssociateConnectToViewServiceParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewAssociateConnectToViewServiceParams result = new _ViewAssociateConnectToViewServiceParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.viewToken = views_mojom.ViewToken.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.serviceName_ = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.pipe = decoder0.decodeMessagePipeHandle(24, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(viewToken, 8, false);
+    
+    encoder0.encodeString(serviceName_, 16, false);
+    
+    encoder0.encodeMessagePipeHandle(pipe, 24, false);
+  }
+
+  String toString() {
+    return "_ViewAssociateConnectToViewServiceParams("
+           "viewToken: $viewToken" ", "
+           "serviceName_: $serviceName_" ", "
+           "pipe: $pipe" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _ViewAssociateConnectToViewTreeServiceParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  view_trees_mojom.ViewTreeToken viewTreeToken = null;
+  String serviceName_ = null;
+  core.MojoMessagePipeEndpoint pipe = null;
+
+  _ViewAssociateConnectToViewTreeServiceParams() : super(kVersions.last.size);
+
+  static _ViewAssociateConnectToViewTreeServiceParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _ViewAssociateConnectToViewTreeServiceParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewAssociateConnectToViewTreeServiceParams result = new _ViewAssociateConnectToViewTreeServiceParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.viewTreeToken = view_trees_mojom.ViewTreeToken.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.serviceName_ = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.pipe = decoder0.decodeMessagePipeHandle(24, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(viewTreeToken, 8, false);
+    
+    encoder0.encodeString(serviceName_, 16, false);
+    
+    encoder0.encodeMessagePipeHandle(pipe, 24, false);
+  }
+
+  String toString() {
+    return "_ViewAssociateConnectToViewTreeServiceParams("
+           "viewTreeToken: $viewTreeToken" ", "
+           "serviceName_: $serviceName_" ", "
+           "pipe: $pipe" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+const int _ViewAssociate_connectName = 0;
+const int _ViewAssociate_connectToViewServiceName = 1;
+const int _ViewAssociate_connectToViewTreeServiceName = 2;
+
+abstract class ViewAssociate {
+  static const String serviceName = "mojo::ui::ViewAssociate";
+  dynamic connect(Object inspector,[Function responseFactory = null]);
+  void connectToViewService(views_mojom.ViewToken viewToken, String serviceName_, core.MojoMessagePipeEndpoint pipe);
+  void connectToViewTreeService(view_trees_mojom.ViewTreeToken viewTreeToken, String serviceName_, core.MojoMessagePipeEndpoint pipe);
+}
+
+
+class _ViewAssociateProxyImpl extends bindings.Proxy {
+  _ViewAssociateProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _ViewAssociateProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _ViewAssociateProxyImpl.unbound() : super.unbound();
+
+  static _ViewAssociateProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _ViewAssociateProxyImpl"));
+    return new _ViewAssociateProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _ViewAssociate_connectName:
+        var r = ViewAssociateConnectResponseParams.deserialize(
+            message.payload);
+        if (!message.header.hasRequestId) {
+          proxyError("Expected a message with a valid request Id.");
+          return;
+        }
+        Completer c = completerMap[message.header.requestId];
+        if (c == null) {
+          proxyError(
+              "Message had unknown request Id: ${message.header.requestId}");
+          return;
+        }
+        completerMap.remove(message.header.requestId);
+        if (c.isCompleted) {
+          proxyError("Response completer already completed");
+          return;
+        }
+        c.complete(r);
+        break;
+      default:
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_ViewAssociateProxyImpl($superString)";
+  }
+}
+
+
+class _ViewAssociateProxyCalls implements ViewAssociate {
+  _ViewAssociateProxyImpl _proxyImpl;
+
+  _ViewAssociateProxyCalls(this._proxyImpl);
+    dynamic connect(Object inspector,[Function responseFactory = null]) {
+      var params = new _ViewAssociateConnectParams();
+      params.inspector = inspector;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewAssociate_connectName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    void connectToViewService(views_mojom.ViewToken viewToken, String serviceName_, core.MojoMessagePipeEndpoint pipe) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewAssociateConnectToViewServiceParams();
+      params.viewToken = viewToken;
+      params.serviceName_ = serviceName_;
+      params.pipe = pipe;
+      _proxyImpl.sendMessage(params, _ViewAssociate_connectToViewServiceName);
+    }
+    void connectToViewTreeService(view_trees_mojom.ViewTreeToken viewTreeToken, String serviceName_, core.MojoMessagePipeEndpoint pipe) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewAssociateConnectToViewTreeServiceParams();
+      params.viewTreeToken = viewTreeToken;
+      params.serviceName_ = serviceName_;
+      params.pipe = pipe;
+      _proxyImpl.sendMessage(params, _ViewAssociate_connectToViewTreeServiceName);
+    }
+}
+
+
+class ViewAssociateProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  ViewAssociate ptr;
+
+  ViewAssociateProxy(_ViewAssociateProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ViewAssociateProxyCalls(proxyImpl);
+
+  ViewAssociateProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _ViewAssociateProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ViewAssociateProxyCalls(impl);
+  }
+
+  ViewAssociateProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _ViewAssociateProxyImpl.fromHandle(handle) {
+    ptr = new _ViewAssociateProxyCalls(impl);
+  }
+
+  ViewAssociateProxy.unbound() :
+      impl = new _ViewAssociateProxyImpl.unbound() {
+    ptr = new _ViewAssociateProxyCalls(impl);
+  }
+
+  factory ViewAssociateProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    ViewAssociateProxy p = new ViewAssociateProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static ViewAssociateProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewAssociateProxy"));
+    return new ViewAssociateProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => ViewAssociate.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "ViewAssociateProxy($impl)";
+  }
+}
+
+
+class ViewAssociateStub extends bindings.Stub {
+  ViewAssociate _impl = null;
+
+  ViewAssociateStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ViewAssociateStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ViewAssociateStub.unbound() : super.unbound();
+
+  static ViewAssociateStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewAssociateStub"));
+    return new ViewAssociateStub.fromEndpoint(endpoint);
+  }
+
+
+  ViewAssociateConnectResponseParams _ViewAssociateConnectResponseParamsFactory(ViewAssociateInfo info) {
+    var mojo_factory_result = new ViewAssociateConnectResponseParams();
+    mojo_factory_result.info = info;
+    return mojo_factory_result;
+  }
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case _ViewAssociate_connectName:
+        var params = _ViewAssociateConnectParams.deserialize(
+            message.payload);
+        var response = _impl.connect(params.inspector,_ViewAssociateConnectResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewAssociate_connectName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewAssociate_connectName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewAssociate_connectToViewServiceName:
+        var params = _ViewAssociateConnectToViewServiceParams.deserialize(
+            message.payload);
+        _impl.connectToViewService(params.viewToken, params.serviceName_, params.pipe);
+        break;
+      case _ViewAssociate_connectToViewTreeServiceName:
+        var params = _ViewAssociateConnectToViewTreeServiceParams.deserialize(
+            message.payload);
+        _impl.connectToViewTreeService(params.viewTreeToken, params.serviceName_, params.pipe);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  ViewAssociate get impl => _impl;
+  set impl(ViewAssociate d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ViewAssociateStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
+abstract class ViewInspector {
+  static const String serviceName = null;
+}
+
+
+class _ViewInspectorProxyImpl extends bindings.Proxy {
+  _ViewInspectorProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _ViewInspectorProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _ViewInspectorProxyImpl.unbound() : super.unbound();
+
+  static _ViewInspectorProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _ViewInspectorProxyImpl"));
+    return new _ViewInspectorProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      default:
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_ViewInspectorProxyImpl($superString)";
+  }
+}
+
+
+class _ViewInspectorProxyCalls implements ViewInspector {
+  _ViewInspectorProxyImpl _proxyImpl;
+
+  _ViewInspectorProxyCalls(this._proxyImpl);
+}
+
+
+class ViewInspectorProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  ViewInspector ptr;
+
+  ViewInspectorProxy(_ViewInspectorProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ViewInspectorProxyCalls(proxyImpl);
+
+  ViewInspectorProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _ViewInspectorProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ViewInspectorProxyCalls(impl);
+  }
+
+  ViewInspectorProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _ViewInspectorProxyImpl.fromHandle(handle) {
+    ptr = new _ViewInspectorProxyCalls(impl);
+  }
+
+  ViewInspectorProxy.unbound() :
+      impl = new _ViewInspectorProxyImpl.unbound() {
+    ptr = new _ViewInspectorProxyCalls(impl);
+  }
+
+  factory ViewInspectorProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    ViewInspectorProxy p = new ViewInspectorProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static ViewInspectorProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewInspectorProxy"));
+    return new ViewInspectorProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => ViewInspector.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "ViewInspectorProxy($impl)";
+  }
+}
+
+
+class ViewInspectorStub extends bindings.Stub {
+  ViewInspector _impl = null;
+
+  ViewInspectorStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ViewInspectorStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ViewInspectorStub.unbound() : super.unbound();
+
+  static ViewInspectorStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewInspectorStub"));
+    return new ViewInspectorStub.fromEndpoint(endpoint);
+  }
+
+
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  ViewInspector get impl => _impl;
+  set impl(ViewInspector d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ViewInspectorStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart
index a5d5f56..4c90616 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart
@@ -9,16 +9,19 @@
 import 'package:mojo/bindings.dart' as bindings;
 import 'package:mojo/core.dart' as core;
 import 'package:mojo_services/mojo/ui/views.mojom.dart' as views_mojom;
+import 'package:mojo_services/mojo/ui/view_associates.mojom.dart' as view_associates_mojom;
 import 'package:mojo_services/mojo/ui/view_trees.mojom.dart' as view_trees_mojom;
+const int kLabelMaxLength = 32;
 
 
 
 class _ViewManagerRegisterViewParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
-    const bindings.StructDataHeader(24, 0)
+    const bindings.StructDataHeader(32, 0)
   ];
   Object view = null;
   Object viewHost = null;
+  String label = null;
 
   _ViewManagerRegisterViewParams() : super(kVersions.last.size);
 
@@ -63,6 +66,10 @@
       
       result.viewHost = decoder0.decodeInterfaceRequest(16, false, views_mojom.ViewHostStub.newFromEndpoint);
     }
+    if (mainDataHeader.version >= 0) {
+      
+      result.label = decoder0.decodeString(24, true);
+    }
     return result;
   }
 
@@ -72,12 +79,15 @@
     encoder0.encodeInterface(view, 8, false);
     
     encoder0.encodeInterfaceRequest(viewHost, 16, false);
+    
+    encoder0.encodeString(label, 24, true);
   }
 
   String toString() {
     return "_ViewManagerRegisterViewParams("
            "view: $view" ", "
-           "viewHost: $viewHost" ")";
+           "viewHost: $viewHost" ", "
+           "label: $label" ")";
   }
 
   Map toJson() {
@@ -157,10 +167,11 @@
 
 class _ViewManagerRegisterViewTreeParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
-    const bindings.StructDataHeader(24, 0)
+    const bindings.StructDataHeader(32, 0)
   ];
   Object viewTree = null;
   Object viewTreeHost = null;
+  String label = null;
 
   _ViewManagerRegisterViewTreeParams() : super(kVersions.last.size);
 
@@ -205,6 +216,10 @@
       
       result.viewTreeHost = decoder0.decodeInterfaceRequest(16, false, view_trees_mojom.ViewTreeHostStub.newFromEndpoint);
     }
+    if (mainDataHeader.version >= 0) {
+      
+      result.label = decoder0.decodeString(24, true);
+    }
     return result;
   }
 
@@ -214,12 +229,15 @@
     encoder0.encodeInterface(viewTree, 8, false);
     
     encoder0.encodeInterfaceRequest(viewTreeHost, 16, false);
+    
+    encoder0.encodeString(label, 24, true);
   }
 
   String toString() {
     return "_ViewManagerRegisterViewTreeParams("
            "viewTree: $viewTree" ", "
-           "viewTreeHost: $viewTreeHost" ")";
+           "viewTreeHost: $viewTreeHost" ", "
+           "label: $label" ")";
   }
 
   Map toJson() {
@@ -231,8 +249,9 @@
 
 class ViewManagerRegisterViewTreeResponseParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
-    const bindings.StructDataHeader(8, 0)
+    const bindings.StructDataHeader(16, 0)
   ];
+  view_trees_mojom.ViewTreeToken viewTreeToken = null;
 
   ViewManagerRegisterViewTreeResponseParams() : super(kVersions.last.size);
 
@@ -269,19 +288,28 @@
         'Message newer than the last known version cannot be shorter than '
         'required by the last known version.');
     }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.viewTreeToken = view_trees_mojom.ViewTreeToken.decode(decoder1);
+    }
     return result;
   }
 
   void encode(bindings.Encoder encoder) {
-    encoder.getStructEncoderAtOffset(kVersions.last);
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(viewTreeToken, 8, false);
   }
 
   String toString() {
-    return "ViewManagerRegisterViewTreeResponseParams("")";
+    return "ViewManagerRegisterViewTreeResponseParams("
+           "viewTreeToken: $viewTreeToken" ")";
   }
 
   Map toJson() {
     Map map = new Map();
+    map["viewTreeToken"] = viewTreeToken;
     return map;
   }
 }
@@ -291,8 +319,8 @@
 
 abstract class ViewManager {
   static const String serviceName = "mojo::ui::ViewManager";
-  dynamic registerView(Object view,Object viewHost,[Function responseFactory = null]);
-  dynamic registerViewTree(Object viewTree,Object viewTreeHost,[Function responseFactory = null]);
+  dynamic registerView(Object view,Object viewHost,String label,[Function responseFactory = null]);
+  dynamic registerViewTree(Object viewTree,Object viewTreeHost,String label,[Function responseFactory = null]);
 }
 
 
@@ -371,20 +399,22 @@
   _ViewManagerProxyImpl _proxyImpl;
 
   _ViewManagerProxyCalls(this._proxyImpl);
-    dynamic registerView(Object view,Object viewHost,[Function responseFactory = null]) {
+    dynamic registerView(Object view,Object viewHost,String label,[Function responseFactory = null]) {
       var params = new _ViewManagerRegisterViewParams();
       params.view = view;
       params.viewHost = viewHost;
+      params.label = label;
       return _proxyImpl.sendMessageWithRequestId(
           params,
           _ViewManager_registerViewName,
           -1,
           bindings.MessageHeader.kMessageExpectsResponse);
     }
-    dynamic registerViewTree(Object viewTree,Object viewTreeHost,[Function responseFactory = null]) {
+    dynamic registerViewTree(Object viewTree,Object viewTreeHost,String label,[Function responseFactory = null]) {
       var params = new _ViewManagerRegisterViewTreeParams();
       params.viewTree = viewTree;
       params.viewTreeHost = viewTreeHost;
+      params.label = label;
       return _proxyImpl.sendMessageWithRequestId(
           params,
           _ViewManager_registerViewTreeName,
@@ -477,8 +507,9 @@
     mojo_factory_result.viewToken = viewToken;
     return mojo_factory_result;
   }
-  ViewManagerRegisterViewTreeResponseParams _ViewManagerRegisterViewTreeResponseParamsFactory() {
+  ViewManagerRegisterViewTreeResponseParams _ViewManagerRegisterViewTreeResponseParamsFactory(view_trees_mojom.ViewTreeToken viewTreeToken) {
     var mojo_factory_result = new ViewManagerRegisterViewTreeResponseParams();
+    mojo_factory_result.viewTreeToken = viewTreeToken;
     return mojo_factory_result;
   }
 
@@ -493,7 +524,7 @@
       case _ViewManager_registerViewName:
         var params = _ViewManagerRegisterViewParams.deserialize(
             message.payload);
-        var response = _impl.registerView(params.view,params.viewHost,_ViewManagerRegisterViewResponseParamsFactory);
+        var response = _impl.registerView(params.view,params.viewHost,params.label,_ViewManagerRegisterViewResponseParamsFactory);
         if (response is Future) {
           return response.then((response) {
             if (response != null) {
@@ -515,7 +546,7 @@
       case _ViewManager_registerViewTreeName:
         var params = _ViewManagerRegisterViewTreeParams.deserialize(
             message.payload);
-        var response = _impl.registerViewTree(params.viewTree,params.viewTreeHost,_ViewManagerRegisterViewTreeResponseParamsFactory);
+        var response = _impl.registerViewTree(params.viewTree,params.viewTreeHost,params.label,_ViewManagerRegisterViewTreeResponseParamsFactory);
         if (response is Future) {
           return response.then((response) {
             if (response != null) {
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_trees.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_trees.mojom.dart
index 37bea0b..7e4a094 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_trees.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_trees.mojom.dart
@@ -8,11 +8,79 @@
 
 import 'package:mojo/bindings.dart' as bindings;
 import 'package:mojo/core.dart' as core;
+import 'package:mojo/mojo/service_provider.mojom.dart' as service_provider_mojom;
 import 'package:mojo_services/mojo/ui/layouts.mojom.dart' as layouts_mojom;
 import 'package:mojo_services/mojo/ui/views.mojom.dart' as views_mojom;
 
 
 
+class ViewTreeToken extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int value = 0;
+
+  ViewTreeToken() : super(kVersions.last.size);
+
+  static ViewTreeToken deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static ViewTreeToken decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewTreeToken result = new ViewTreeToken();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.value = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(value, 8);
+  }
+
+  String toString() {
+    return "ViewTreeToken("
+           "value: $value" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["value"] = value;
+    return map;
+  }
+}
+
+
 class _ViewTreeOnLayoutParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(8, 0)
@@ -254,6 +322,72 @@
 }
 
 
+class _ViewTreeHostGetServiceProviderParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Object serviceProvider = null;
+
+  _ViewTreeHostGetServiceProviderParams() : super(kVersions.last.size);
+
+  static _ViewTreeHostGetServiceProviderParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _ViewTreeHostGetServiceProviderParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewTreeHostGetServiceProviderParams result = new _ViewTreeHostGetServiceProviderParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.serviceProvider = decoder0.decodeInterfaceRequest(8, false, service_provider_mojom.ServiceProviderStub.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInterfaceRequest(serviceProvider, 8, false);
+  }
+
+  String toString() {
+    return "_ViewTreeHostGetServiceProviderParams("
+           "serviceProvider: $serviceProvider" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
 class _ViewTreeHostRequestLayoutParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(8, 0)
@@ -847,13 +981,15 @@
   int get version => 0;
 }
 
-const int _ViewTreeHost_requestLayoutName = 0;
-const int _ViewTreeHost_setRootName = 1;
-const int _ViewTreeHost_resetRootName = 2;
-const int _ViewTreeHost_layoutRootName = 3;
+const int _ViewTreeHost_getServiceProviderName = 0;
+const int _ViewTreeHost_requestLayoutName = 1;
+const int _ViewTreeHost_setRootName = 2;
+const int _ViewTreeHost_resetRootName = 3;
+const int _ViewTreeHost_layoutRootName = 4;
 
 abstract class ViewTreeHost {
   static const String serviceName = null;
+  void getServiceProvider(Object serviceProvider);
   void requestLayout();
   void setRoot(int rootKey, views_mojom.ViewToken rootViewToken);
   void resetRoot();
@@ -916,6 +1052,15 @@
   _ViewTreeHostProxyImpl _proxyImpl;
 
   _ViewTreeHostProxyCalls(this._proxyImpl);
+    void getServiceProvider(Object serviceProvider) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewTreeHostGetServiceProviderParams();
+      params.serviceProvider = serviceProvider;
+      _proxyImpl.sendMessage(params, _ViewTreeHost_getServiceProviderName);
+    }
     void requestLayout() {
       if (!_proxyImpl.isBound) {
         _proxyImpl.proxyError("The Proxy is closed.");
@@ -1046,6 +1191,11 @@
     }
     assert(_impl != null);
     switch (message.header.type) {
+      case _ViewTreeHost_getServiceProviderName:
+        var params = _ViewTreeHostGetServiceProviderParams.deserialize(
+            message.payload);
+        _impl.getServiceProvider(params.serviceProvider);
+        break;
       case _ViewTreeHost_requestLayoutName:
         var params = _ViewTreeHostRequestLayoutParams.deserialize(
             message.payload);
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/views.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/views.mojom.dart
index 33c3d13..3b79c70 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/ui/views.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/views.mojom.dart
@@ -9,6 +9,7 @@
 import 'package:mojo/bindings.dart' as bindings;
 import 'package:mojo/core.dart' as core;
 import 'package:mojo/mojo/service_provider.mojom.dart' as service_provider_mojom;
+import 'package:mojo_services/mojo/gfx/composition/scenes.mojom.dart' as scenes_mojom;
 import 'package:mojo_services/mojo/ui/layouts.mojom.dart' as layouts_mojom;
 
 
@@ -161,7 +162,7 @@
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(16, 0)
   ];
-  layouts_mojom.ViewLayoutInfo info = null;
+  layouts_mojom.ViewLayoutResult result = null;
 
   ViewOnLayoutResponseParams() : super(kVersions.last.size);
 
@@ -201,7 +202,7 @@
     if (mainDataHeader.version >= 0) {
       
       var decoder1 = decoder0.decodePointer(8, false);
-      result.info = layouts_mojom.ViewLayoutInfo.decode(decoder1);
+      result.result = layouts_mojom.ViewLayoutResult.decode(decoder1);
     }
     return result;
   }
@@ -209,17 +210,17 @@
   void encode(bindings.Encoder encoder) {
     var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
     
-    encoder0.encodeStruct(info, 8, false);
+    encoder0.encodeStruct(result, 8, false);
   }
 
   String toString() {
     return "ViewOnLayoutResponseParams("
-           "info: $info" ")";
+           "result: $result" ")";
   }
 
   Map toJson() {
     Map map = new Map();
-    map["info"] = info;
+    map["result"] = result;
     return map;
   }
 }
@@ -416,6 +417,72 @@
 }
 
 
+class _ViewHostCreateSceneParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Object scene = null;
+
+  _ViewHostCreateSceneParams() : super(kVersions.last.size);
+
+  static _ViewHostCreateSceneParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static _ViewHostCreateSceneParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewHostCreateSceneParams result = new _ViewHostCreateSceneParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.scene = decoder0.decodeInterfaceRequest(8, false, scenes_mojom.SceneStub.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInterfaceRequest(scene, 8, false);
+  }
+
+  String toString() {
+    return "_ViewHostCreateSceneParams("
+           "scene: $scene" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
 class _ViewHostRequestLayoutParams extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(8, 0)
@@ -947,9 +1014,9 @@
   }
 
 
-  ViewOnLayoutResponseParams _ViewOnLayoutResponseParamsFactory(layouts_mojom.ViewLayoutInfo info) {
+  ViewOnLayoutResponseParams _ViewOnLayoutResponseParamsFactory(layouts_mojom.ViewLayoutResult result) {
     var mojo_factory_result = new ViewOnLayoutResponseParams();
-    mojo_factory_result.info = info;
+    mojo_factory_result.result = result;
     return mojo_factory_result;
   }
   ViewOnChildUnavailableResponseParams _ViewOnChildUnavailableResponseParamsFactory() {
@@ -1031,14 +1098,16 @@
 }
 
 const int _ViewHost_getServiceProviderName = 0;
-const int _ViewHost_requestLayoutName = 1;
-const int _ViewHost_addChildName = 2;
-const int _ViewHost_removeChildName = 3;
-const int _ViewHost_layoutChildName = 4;
+const int _ViewHost_createSceneName = 1;
+const int _ViewHost_requestLayoutName = 2;
+const int _ViewHost_addChildName = 3;
+const int _ViewHost_removeChildName = 4;
+const int _ViewHost_layoutChildName = 5;
 
 abstract class ViewHost {
   static const String serviceName = null;
   void getServiceProvider(Object serviceProvider);
+  void createScene(Object scene);
   void requestLayout();
   void addChild(int childKey, ViewToken childViewToken);
   void removeChild(int childKey);
@@ -1110,6 +1179,15 @@
       params.serviceProvider = serviceProvider;
       _proxyImpl.sendMessage(params, _ViewHost_getServiceProviderName);
     }
+    void createScene(Object scene) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewHostCreateSceneParams();
+      params.scene = scene;
+      _proxyImpl.sendMessage(params, _ViewHost_createSceneName);
+    }
     void requestLayout() {
       if (!_proxyImpl.isBound) {
         _proxyImpl.proxyError("The Proxy is closed.");
@@ -1247,6 +1325,11 @@
             message.payload);
         _impl.getServiceProvider(params.serviceProvider);
         break;
+      case _ViewHost_createSceneName:
+        var params = _ViewHostCreateSceneParams.deserialize(
+            message.payload);
+        _impl.createScene(params.scene);
+        break;
       case _ViewHost_requestLayoutName:
         var params = _ViewHostRequestLayoutParams.deserialize(
             message.payload);
diff --git a/mojo/services/mojo_services.gni b/mojo/services/mojo_services.gni
index 5b04f49..39672de 100644
--- a/mojo/services/mojo_services.gni
+++ b/mojo/services/mojo_services.gni
@@ -46,6 +46,7 @@
   "//mojo/services/surfaces/interfaces",
   "//mojo/services/terminal/interfaces",
   "//mojo/services/tracing/interfaces",
+  "//mojo/services/ui/input/interfaces",
   "//mojo/services/ui/views/interfaces",
   "//mojo/services/url_response_disk_cache/interfaces",
   "//mojo/services/vanadium/security/interfaces",
diff --git a/mojo/services/ui/input/interfaces/BUILD.gn b/mojo/services/ui/input/interfaces/BUILD.gn
new file mode 100644
index 0000000..d122c96
--- /dev/null
+++ b/mojo/services/ui/input/interfaces/BUILD.gn
@@ -0,0 +1,19 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "input_connection.mojom",
+    "input_dispatcher.mojom",
+  ]
+
+  import_dirs = [ get_path_info("../../../", "abspath") ]
+
+  deps = [
+    "../../../input_events/interfaces",
+  ]
+}
diff --git a/mojo/services/ui/input/interfaces/input_connection.mojom b/mojo/services/ui/input/interfaces/input_connection.mojom
new file mode 100644
index 0000000..2746cb7
--- /dev/null
+++ b/mojo/services/ui/input/interfaces/input_connection.mojom
@@ -0,0 +1,35 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+// TODO(jeffbrown): Redesign input event representation later.
+import "mojo/services/input_events/interfaces/input_events.mojom";
+
+// The input connection service allows a view to receive input events
+// such as key presses and pointer movements.
+
+// This service can be retrieved from the |ViewHost| service provider.
+//
+// TODO(jeffbrown): Elaborate this.  Particularly need to think about
+// how to handle focus and gestures.
+[ServiceName="mojo::ui::InputConnection"]
+interface InputConnection {
+  // Sets the listener for receiving input events.
+  //
+  // If |listener| is null, then the previously set listener is removed.
+  SetListener(InputListener? listener);
+};
+
+// An interface applications may implement to receive events from an
+// input connection.
+interface InputListener {
+  // Called to deliver an input event.
+  //
+  // The |consumed| result should indicate whether the input event was
+  // consumed by the connection.  If |consumed| is false, the system may
+  // apply default behavior of its own in response to the event.
+  OnEvent(mojo.Event event) => (bool consumed);
+};
diff --git a/mojo/services/ui/input/interfaces/input_dispatcher.mojom b/mojo/services/ui/input/interfaces/input_dispatcher.mojom
new file mode 100644
index 0000000..75caef5
--- /dev/null
+++ b/mojo/services/ui/input/interfaces/input_dispatcher.mojom
@@ -0,0 +1,40 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+// TODO(jeffbrown): Redesign input event representation later.
+import "mojo/services/input_events/interfaces/input_events.mojom";
+
+// The input dispatcher service allows the component which owns and controls
+// a view tree to dispatch input events to the views that it contains.
+//
+// This service can be retrieved from the |ViewTreeHost| service provider.
+//
+// TODO(jeffbrown): Elaborate how input devices are registered with the
+// dispatcher so that applications can query their capabilities and distinguish
+// input coming from multiple sources concurrently.
+[ServiceName="mojo::ui::InputDispatcher"]
+interface InputDispatcher {
+  // Dispatches an event through the view tree.  The dispatcher will deliver
+  // the event to the appropriate views based on the current state of the
+  // tree, such as focus and the structure of the scene graph.
+  //
+  // The dispatcher expects the stream of events that it receives to be
+  // internally consistent.  For example, each pointer down must be matched
+  // by a corresponding pointer up.
+  //
+  // It is an error to supply an inconsistent stream events to the dispatcher;
+  // the connection will be closed.
+  //
+  // TODO(jeffbrown): Is this the right policy?  It would certainly help
+  // diagnose faults earlier.
+  // TODO(jeffbrown): Decide whether there should be a way to track the
+  // progress of input events.  For testing purposes it is often desirable
+  // to block the test until a sequence of events have been delivered and
+  // handled.  However these same mechanisms have proven to be brittle in
+  // the past so it might be better to solve the problem some other way.
+  DispatchEvent(mojo.Event event);
+};
diff --git a/mojo/services/ui/views/cpp/BUILD.gn b/mojo/services/ui/views/cpp/BUILD.gn
new file mode 100644
index 0000000..ada87b3
--- /dev/null
+++ b/mojo/services/ui/views/cpp/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
+
+mojo_sdk_source_set("cpp") {
+  restrict_external_deps = false
+  public_configs = [ "../../../public/build/config:mojo_services" ]
+  sources = [
+    "formatting.cc",
+    "formatting.h",
+  ]
+
+  deps = [
+    "../../../geometry/cpp",
+    "../../../gfx/composition/cpp",
+    "../interfaces",
+  ]
+}
diff --git a/mojo/services/ui/views/cpp/formatting.cc b/mojo/services/ui/views/cpp/formatting.cc
new file mode 100644
index 0000000..4e888bb
--- /dev/null
+++ b/mojo/services/ui/views/cpp/formatting.cc
@@ -0,0 +1,54 @@
+// 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 "mojo/services/ui/views/cpp/formatting.h"
+
+#include <ostream>
+
+namespace mojo {
+namespace ui {
+
+std::ostream& operator<<(std::ostream& os, const mojo::ui::ViewToken& value) {
+  return os << "{value=" << value.value << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewTreeToken& value) {
+  return os << "{value=" << value.value << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::BoxConstraints& value) {
+  return os << "{min_width=" << value.min_width
+            << ", max_width=" << value.max_width
+            << ", min_height=" << value.min_height
+            << ", max_height=" << value.max_height << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutParams& value) {
+  return os << "{constraints=" << value.constraints
+            << ", device_pixel_ratio=" << value.device_pixel_ratio << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutInfo& value) {
+  return os << "{size=" << value.size << ", scene_token=" << value.scene_token
+            << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutResult& value) {
+  return os << "{size=" << value.size << "}";
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewAssociateInfo& value) {
+  return os << "{view_service_names=" << value.view_service_names
+            << ", view_tree_service_names=" << value.view_tree_service_names
+            << "}";
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/services/ui/views/cpp/formatting.h b/mojo/services/ui/views/cpp/formatting.h
new file mode 100644
index 0000000..547a701
--- /dev/null
+++ b/mojo/services/ui/views/cpp/formatting.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef MOJO_SERVICES_UI_VIEWS_CPP_FORMATTING_H_
+#define MOJO_SERVICES_UI_VIEWS_CPP_FORMATTING_H_
+
+#include <iosfwd>
+
+#include "mojo/public/cpp/bindings/formatting.h"
+#include "mojo/services/geometry/cpp/formatting.h"
+#include "mojo/services/gfx/composition/cpp/formatting.h"
+#include "mojo/services/ui/views/interfaces/view_associates.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+
+namespace mojo {
+namespace ui {
+
+std::ostream& operator<<(std::ostream& os, const mojo::ui::ViewToken& value);
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewTreeToken& value);
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::BoxConstraints& value);
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutParams& value);
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutInfo& value);
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewLayoutResult& value);
+
+std::ostream& operator<<(std::ostream& os,
+                         const mojo::ui::ViewAssociateInfo& value);
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_UI_VIEWS_CPP_FORMATTING_H_
diff --git a/mojo/services/ui/views/interfaces/BUILD.gn b/mojo/services/ui/views/interfaces/BUILD.gn
index 747722d..a81eab7 100644
--- a/mojo/services/ui/views/interfaces/BUILD.gn
+++ b/mojo/services/ui/views/interfaces/BUILD.gn
@@ -8,6 +8,7 @@
 mojom("interfaces") {
   sources = [
     "layouts.mojom",
+    "view_associates.mojom",
     "view_manager.mojom",
     "view_provider.mojom",
     "view_trees.mojom",
@@ -18,6 +19,6 @@
 
   deps = [
     "../../../geometry/interfaces",
-    "../../../surfaces/interfaces:surface_id",
+    "../../../gfx/composition/interfaces",
   ]
 }
diff --git a/mojo/services/ui/views/interfaces/layouts.mojom b/mojo/services/ui/views/interfaces/layouts.mojom
index 7a6a177..f9831cc 100644
--- a/mojo/services/ui/views/interfaces/layouts.mojom
+++ b/mojo/services/ui/views/interfaces/layouts.mojom
@@ -6,7 +6,7 @@
 module mojo.ui;
 
 import "mojo/services/geometry/interfaces/geometry.mojom";
-import "mojo/services/surfaces/interfaces/surface_id.mojom";
+import "mojo/services/gfx/composition/interfaces/scene_token.mojom";
 
 // Box constraints for layout.
 //
@@ -65,9 +65,15 @@
 
 // Layout information for a view.
 struct ViewLayoutInfo {
-  // The view's surface id for composition by the parent.
-  mojo.SurfaceId surface_id;
+  // The view's scene token for composition by the parent.
+  mojo.gfx.composition.SceneToken scene_token;
 
   // The actual size of the view in device pixels.
   mojo.Size size;
 };
+
+// Result of a view layout request.
+struct ViewLayoutResult {
+  // The actual size of the view in device pixels.
+  mojo.Size size;
+};
diff --git a/mojo/services/ui/views/interfaces/view_associates.mojom b/mojo/services/ui/views/interfaces/view_associates.mojom
new file mode 100644
index 0000000..6dec347
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/view_associates.mojom
@@ -0,0 +1,79 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/public/interfaces/application/service_provider.mojom";
+import "mojo/services/ui/views/interfaces/views.mojom";
+import "mojo/services/ui/views/interfaces/view_trees.mojom";
+
+// View associates are trusted components that are attached to a view manager
+// instance with the purpose of offering additional services to views and
+// view trees registered beyond the basic operations performed by the
+// view manager itself.  Associates may be used to implement input,
+// accessibility, editing, and other capabilities.
+//
+// Associates are coupled to a view manager instance for the entire life
+// of that view manager.  Associates cannot be dynamically added or removed
+// since applications rely on the services that they offer and expect them
+// to be available for the lifetime of their views.  Moreover, all views and
+// view trees registered with a particular view manager instance share
+// the same set of associates.
+//
+// This mechanism is designed to avoid a potential explosion in complexity
+// if all features which depend on the state of views were implemented
+// in one place.
+//
+// TODO(jeffbrown): In the current implementation, the view manager binds
+// to a hard coded set of associates at start up time which can be overridden
+// from the command-line.  We should find a better way to register associates
+// once we decide how the system as a whole should be initialized.
+[ServiceName="mojo::ui::ViewAssociate"]
+interface ViewAssociate {
+  // Connects to the associate.
+  //
+  // The |inspector| provides a means for the view associate to query state
+  // from the view manager.
+  //
+  // The associate must return information about the services that it
+  // offers in |info|.
+  Connect(ViewInspector inspector) => (ViewAssociateInfo info);
+
+  // Asks the associate to provide the view service identified by
+  // |interface_name| through the message |pipe| endpoint supplied by
+  // the caller.  If the host is not willing or able to provide the requested
+  // service, it should close the |pipe|.
+  //
+  // The |view_token| is the token of the view which requested the service.
+  ConnectToViewService(ViewToken view_token, string service_name,
+      handle<message_pipe> pipe);
+
+  // Asks the associate to provide the view tree service identified by
+  // |interface_name| through the message |pipe| endpoint supplied by
+  // the caller.  If the host is not willing or able to provide the requested
+  // service, it should close the |pipe|.
+  //
+  // The |view_tree_token| is the token of the view tree which requested
+  // the service.
+  ConnectToViewTreeService(ViewTreeToken view_tree_token,
+      string service_name, handle<message_pipe> pipe);
+};
+
+// Provides information about the services offered by an associate.
+struct ViewAssociateInfo {
+  // The names of view services offered by the associate.
+  // May be null if none.
+  array<string>? view_service_names;
+
+  // The names of view tree services offered by the associate.
+  // May be null if none.
+  array<string>? view_tree_service_names;
+};
+
+// Provides a view associate with the ability to inspect and perform operations
+// on the contents of views and view trees.
+interface ViewInspector {
+
+};
diff --git a/mojo/services/ui/views/interfaces/view_manager.mojom b/mojo/services/ui/views/interfaces/view_manager.mojom
index febf24a..66f3861 100644
--- a/mojo/services/ui/views/interfaces/view_manager.mojom
+++ b/mojo/services/ui/views/interfaces/view_manager.mojom
@@ -6,8 +6,12 @@
 module mojo.ui;
 
 import "mojo/services/ui/views/interfaces/views.mojom";
+import "mojo/services/ui/views/interfaces/view_associates.mojom";
 import "mojo/services/ui/views/interfaces/view_trees.mojom";
 
+// Maximum length for a view or view tree label.
+const uint32 kLabelMaxLength = 32;
+
 // The view manager is a service which manages trees of views.
 //
 // Before a view can be added to the view tree, it must first be registered
@@ -31,11 +35,15 @@
   // eventually be passed back through the container's view host interface
   // as an argument to AddChild().
   //
+  // The |label| is an optional name to associate with the view for
+  // diagnostic purposes.  The label will be truncated if it is longer
+  // than |kLabelMaxLength|.
+  //
   // To unregister the view and cause it to be removed from the view tree,
   // simply close the |view| and/or |view_host| message pipes.
   RegisterView(mojo.ui.View view,
-               mojo.ui.ViewHost& view_host) =>
-                   (mojo.ui.ViewToken view_token);
+               mojo.ui.ViewHost& view_host,
+               string? label) => (mojo.ui.ViewToken view_token);
 
   // Registers a view tree with the view manager.
   //
@@ -43,8 +51,16 @@
   // with the views it contains.  The view tree host is private to the view
   // and should not be shared with anyone else.
   //
+  // The |label| is an optional name to associate with the view tree for
+  // diagnostic purposes.  The label will be truncated if it is longer
+  // than |kLabelMaxLength|.
+  //
+  // The |view_tree_token| is used as a transferable reference which can
+  // be passed to trusted services to reference the view tree.
+  //
   // To unregister the view tree simply close the |view_tree| and/or
   // |view_tree_host| message pipes.
   RegisterViewTree(mojo.ui.ViewTree view_tree,
-                   mojo.ui.ViewTreeHost& view_tree_host) => ();
+                   mojo.ui.ViewTreeHost& view_tree_host,
+                   string? label) => (mojo.ui.ViewTreeToken view_tree_token);
 };
diff --git a/mojo/services/ui/views/interfaces/view_trees.mojom b/mojo/services/ui/views/interfaces/view_trees.mojom
index ac3439c..ff7a426 100644
--- a/mojo/services/ui/views/interfaces/view_trees.mojom
+++ b/mojo/services/ui/views/interfaces/view_trees.mojom
@@ -5,9 +5,25 @@
 [DartPackage="mojo_services"]
 module mojo.ui;
 
+import "mojo/public/interfaces/application/service_provider.mojom";
 import "mojo/services/ui/views/interfaces/layouts.mojom";
 import "mojo/services/ui/views/interfaces/views.mojom";
 
+// A view tree token is an opaque transferable reference to a view tree.
+//
+// The ViewManager provides each view tree with a unique view tree token when
+// it is registered.  The token can subsequently be passed to other
+// applications and used as a way to refer to the tree.
+//
+// View tree tokens should be kept secret and should only be shared with
+// trusted services.
+//
+// TODO(jeffbrown): This implementation is a temporary placeholder until
+// we extend Mojo to provide a way to create tokens which cannot be forged.
+struct ViewTreeToken {
+  uint32 value;
+};
+
 // A view tree is a top-level container for a hierarchy of views.
 //
 // A view tree must registered with the view manager before it can be shown.
@@ -46,6 +62,14 @@
 // ViewManager.  To unregister the view tree, close its view tree
 // and/or view tree host message pipes.
 interface ViewTreeHost {
+  // Gets a service provider to access services which are associated with
+  // the view tree such as input, accessibility and editing capabilities.
+  // The view tree service provider is private to the view tree and should
+  // not be shared with anyone else.
+  //
+  // See |mojo.ui.InputDispatcher|.
+  GetServiceProvider(mojo.ServiceProvider& service_provider);
+
   // Requests that the view tree's OnLayout() method be called to compute a
   // new layout due to a change in the view tree's layout information.
   RequestLayout();
diff --git a/mojo/services/ui/views/interfaces/views.mojom b/mojo/services/ui/views/interfaces/views.mojom
index 49ca841..9dbfd31 100644
--- a/mojo/services/ui/views/interfaces/views.mojom
+++ b/mojo/services/ui/views/interfaces/views.mojom
@@ -6,6 +6,7 @@
 module mojo.ui;
 
 import "mojo/public/interfaces/application/service_provider.mojom";
+import "mojo/services/gfx/composition/interfaces/scenes.mojom";
 import "mojo/services/ui/views/interfaces/layouts.mojom";
 
 // A view token is an opaque transferable reference to a view.
@@ -69,14 +70,14 @@
   //
   // Recursive layout happens in any of the following circumstances:
   //
-  //   1. If the resulting surface has changed since the last layout.
+  //   1. If the resulting scene has changed since the last layout.
   //   2. If the resulting size has changed since the last layout.
   //
-  // It is an error to return a malformed |info| which does not satisfy
+  // It is an error to return a malformed |result| which does not satisfy
   // the requested |layout_params|, such as by returning a size which
   // exceeds the requested constraints; the view's connection will be closed.
   OnLayout(mojo.ui.ViewLayoutParams layout_params,
-    array<uint32> children_needing_layout) => (mojo.ui.ViewLayoutInfo info);
+    array<uint32> children_needing_layout) => (mojo.ui.ViewLayoutResult result);
 
   // Called when a child view has become unavailable.
   //
@@ -103,8 +104,21 @@
   // the view such as input, accessibility and editing capabilities.
   // The view service provider is private to the view and should not be
   // shared with anyone else.
+  //
+  // See |mojo.ui.InputConnection|.
   GetServiceProvider(mojo.ServiceProvider& service_provider);
 
+  // Creates the view's scene, replacing any previous scene the view
+  // might have had.
+  //
+  // The |scene| is used to supply content for the scene.  The scene pipe
+  // is private to the scene and should not be shared with anyone else.
+  //
+  // To destroy the scene, simply close the |scene| message pipe.
+  //
+  // See also: |mojo.gfx.composition.Compositor.CreateScene()|.
+  CreateScene(mojo.gfx.composition.Scene& scene);
+
   // Requests that the view's OnLayout() method be called to compute a
   // new layout due to a change in the view's layout information.
   RequestLayout();
diff --git a/services/ui/BUILD.gn b/services/ui/BUILD.gn
index 9480d5e..7c3ea55 100644
--- a/services/ui/BUILD.gn
+++ b/services/ui/BUILD.gn
@@ -4,6 +4,7 @@
 
 group("ui") {
   deps = [
+    "//services/ui/input_manager",
     "//services/ui/launcher",
     "//services/ui/view_manager",
   ]
diff --git a/services/ui/README.md b/services/ui/README.md
index cccc9c3..e67b89f 100644
--- a/services/ui/README.md
+++ b/services/ui/README.md
@@ -1,4 +1,4 @@
 # Mozart UI System
 
-This directory contains Mozart, an implementation of a composable view
-management system named after the classical composer.
+This directory contains the Mozart UI System, an implementation of a
+composable view management system named after the classical composer.
diff --git a/services/ui/input_manager/BUILD.gn b/services/ui/input_manager/BUILD.gn
new file mode 100644
index 0000000..b869f33
--- /dev/null
+++ b/services/ui/input_manager/BUILD.gn
@@ -0,0 +1,34 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("input_manager") {
+  output_name = "input_manager_service"
+
+  sources = [
+    "input_associate.cc",
+    "input_associate.h",
+    "input_manager_app.cc",
+    "input_manager_app.h",
+    "main.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/common:tracing_impl",
+    "//mojo/converters/geometry",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/geometry/cpp",
+    "//mojo/services/gfx/composition/cpp",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/input/interfaces",
+    "//mojo/services/ui/views/cpp",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
diff --git a/services/ui/input_manager/README.md b/services/ui/input_manager/README.md
new file mode 100644
index 0000000..bcce55f
--- /dev/null
+++ b/services/ui/input_manager/README.md
@@ -0,0 +1,8 @@
+# Mozart Input Manager
+
+This directory contains an Associate
+
+It doesn't make sense to run this application stand-alone since it
+doesn't have any UI of its own to display.  Instead, use the Mozart
+Launcher or some other application to launch and embed the UI of some
+other application using the view manager.
diff --git a/services/ui/input_manager/input_associate.cc b/services/ui/input_manager/input_associate.cc
new file mode 100644
index 0000000..7d739bc
--- /dev/null
+++ b/services/ui/input_manager/input_associate.cc
@@ -0,0 +1,101 @@
+// 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 "services/ui/input_manager/input_associate.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace input_manager {
+
+InputAssociate::InputAssociate() {}
+
+InputAssociate::~InputAssociate() {}
+
+void InputAssociate::Connect(mojo::ui::ViewInspectorPtr inspector,
+                             const ConnectCallback& callback) {
+  DCHECK(inspector);  // checked by mojom
+
+  auto info = mojo::ui::ViewAssociateInfo::New();
+  info->view_service_names.push_back(mojo::ui::InputConnection::Name_);
+  info->view_tree_service_names.push_back(mojo::ui::InputDispatcher::Name_);
+  callback.Run(info.Pass());
+}
+
+void InputAssociate::ConnectToViewService(
+    mojo::ui::ViewTokenPtr view_token,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  DCHECK(view_token);  // checked by mojom
+
+  if (service_name == mojo::ui::InputConnection::Name_) {
+    input_connections_.AddBinding(
+        new InputConnectionImpl(this, view_token.Pass()),
+        mojo::MakeRequest<mojo::ui::InputConnection>(client_handle.Pass()));
+  }
+}
+
+void InputAssociate::ConnectToViewTreeService(
+    mojo::ui::ViewTreeTokenPtr view_tree_token,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  DCHECK(view_tree_token);  // checked by mojom
+
+  if (service_name == mojo::ui::InputDispatcher::Name_) {
+    input_dispatchers_.AddBinding(
+        new InputDispatcherImpl(this, view_tree_token.Pass()),
+        mojo::MakeRequest<mojo::ui::InputDispatcher>(client_handle.Pass()));
+  }
+}
+
+void InputAssociate::SetListener(mojo::ui::ViewToken* view_token,
+                                 mojo::ui::InputListenerPtr listener) {
+  // TODO(jeffbrown): This simple hack just hooks up the first listener
+  // ever seen.
+  listener_ = listener.Pass();
+}
+
+void InputAssociate::DispatchEvent(mojo::ui::ViewTreeToken* view_tree_token,
+                                   mojo::EventPtr event) {
+  if (listener_)
+    listener_->OnEvent(
+        event.Pass(),
+        base::Bind(&InputAssociate::OnEventFinished, base::Unretained(this)));
+}
+
+void InputAssociate::OnEventFinished(bool handled) {
+  // TODO: detect ANRs
+}
+
+InputAssociate::InputConnectionImpl::InputConnectionImpl(
+    InputAssociate* associate,
+    mojo::ui::ViewTokenPtr view_token)
+    : associate_(associate), view_token_(view_token.Pass()) {
+  DCHECK(associate_);
+  DCHECK(view_token_);
+}
+
+InputAssociate::InputConnectionImpl::~InputConnectionImpl() {}
+
+void InputAssociate::InputConnectionImpl::SetListener(
+    mojo::ui::InputListenerPtr listener) {
+  associate_->SetListener(view_token_.get(), listener.Pass());
+}
+
+InputAssociate::InputDispatcherImpl::InputDispatcherImpl(
+    InputAssociate* associate,
+    mojo::ui::ViewTreeTokenPtr view_tree_token)
+    : associate_(associate), view_tree_token_(view_tree_token.Pass()) {
+  DCHECK(associate_);
+  DCHECK(view_tree_token_);
+}
+
+InputAssociate::InputDispatcherImpl::~InputDispatcherImpl() {}
+
+void InputAssociate::InputDispatcherImpl::DispatchEvent(mojo::EventPtr event) {
+  associate_->DispatchEvent(view_tree_token_.get(), event.Pass());
+}
+
+}  // namespace input_manager
diff --git a/services/ui/input_manager/input_associate.h b/services/ui/input_manager/input_associate.h
new file mode 100644
index 0000000..7f2fd7e
--- /dev/null
+++ b/services/ui/input_manager/input_associate.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef SERVICES_UI_INPUT_MANAGER_INPUT_ASSOCIATE_IMPL_H_
+#define SERVICES_UI_INPUT_MANAGER_INPUT_ASSOCIATE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/common/strong_binding_set.h"
+#include "mojo/services/ui/input/interfaces/input_connection.mojom.h"
+#include "mojo/services/ui/input/interfaces/input_dispatcher.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_associates.mojom.h"
+
+namespace input_manager {
+
+// InputManager's ViewAssociate interface implementation.
+class InputAssociate : public mojo::ui::ViewAssociate {
+ public:
+  InputAssociate();
+  ~InputAssociate() override;
+
+ private:
+  // InputConnection implementation.
+  // Binds incoming requests to the relevant view token.
+  class InputConnectionImpl : public mojo::ui::InputConnection {
+   public:
+    InputConnectionImpl(InputAssociate* associate,
+                        mojo::ui::ViewTokenPtr view_token);
+    ~InputConnectionImpl() override;
+
+   private:
+    void SetListener(mojo::ui::InputListenerPtr listener) override;
+
+    InputAssociate* const associate_;
+    mojo::ui::ViewTokenPtr view_token_;
+
+    DISALLOW_COPY_AND_ASSIGN(InputConnectionImpl);
+  };
+
+  // InputDispatcher implementation.
+  // Binds incoming requests to the relevant view token.
+  class InputDispatcherImpl : public mojo::ui::InputDispatcher {
+   public:
+    InputDispatcherImpl(InputAssociate* associate,
+                        mojo::ui::ViewTreeTokenPtr view_tree_token);
+    ~InputDispatcherImpl() override;
+
+   private:
+    void DispatchEvent(mojo::EventPtr event) override;
+
+    InputAssociate* const associate_;
+    mojo::ui::ViewTreeTokenPtr view_tree_token_;
+
+    DISALLOW_COPY_AND_ASSIGN(InputDispatcherImpl);
+  };
+
+  // |ViewAssociate|:
+  void Connect(mojo::ui::ViewInspectorPtr inspector,
+               const ConnectCallback& callback) override;
+  void ConnectToViewService(
+      mojo::ui::ViewTokenPtr view_token,
+      const mojo::String& service_name,
+      mojo::ScopedMessagePipeHandle client_handle) override;
+  void ConnectToViewTreeService(
+      mojo::ui::ViewTreeTokenPtr view_tree_token,
+      const mojo::String& service_name,
+      mojo::ScopedMessagePipeHandle client_handle) override;
+
+  // Incoming service calls.
+  void SetListener(mojo::ui::ViewToken* view_token,
+                   mojo::ui::InputListenerPtr listener);
+  void DispatchEvent(mojo::ui::ViewTreeToken* view_tree_token,
+                     mojo::EventPtr event);
+
+  // Callbacks.
+  void OnEventFinished(bool handled);
+
+  mojo::StrongBindingSet<mojo::ui::InputConnection> input_connections_;
+  mojo::StrongBindingSet<mojo::ui::InputDispatcher> input_dispatchers_;
+
+  mojo::ui::InputListenerPtr listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputAssociate);
+};
+
+}  // namespace input_manager
+
+#endif  // SERVICES_UI_INPUT_MANAGER_INPUT_ASSOCIATE_IMPL_H_
diff --git a/services/ui/input_manager/input_manager_app.cc b/services/ui/input_manager/input_manager_app.cc
new file mode 100644
index 0000000..9c5db14
--- /dev/null
+++ b/services/ui/input_manager/input_manager_app.cc
@@ -0,0 +1,47 @@
+// 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 "services/ui/input_manager/input_manager_app.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "services/ui/input_manager/input_associate.h"
+
+namespace input_manager {
+
+InputManagerApp::InputManagerApp() : app_impl_(nullptr) {}
+
+InputManagerApp::~InputManagerApp() {}
+
+void InputManagerApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(app_impl_->args());
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+
+  tracing_.Initialize(app_impl);
+}
+
+bool InputManagerApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::ui::ViewAssociate>(this);
+  return true;
+}
+
+void InputManagerApp::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::ui::ViewAssociate> request) {
+  input_associates.AddBinding(new InputAssociate(), request.Pass());
+}
+
+}  // namespace input_manager
diff --git a/services/ui/input_manager/input_manager_app.h b/services/ui/input_manager/input_manager_app.h
new file mode 100644
index 0000000..70ac70d
--- /dev/null
+++ b/services/ui/input_manager/input_manager_app.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef SERVICES_UI_INPUT_MANAGER_INPUT_MANAGER_APP_H_
+#define SERVICES_UI_INPUT_MANAGER_INPUT_MANAGER_APP_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "mojo/common/strong_binding_set.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/ui/views/interfaces/view_associates.mojom.h"
+
+namespace input_manager {
+
+// Input manager application entry point.
+class InputManagerApp : public mojo::ApplicationDelegate,
+                        public mojo::InterfaceFactory<mojo::ui::ViewAssociate> {
+ public:
+  InputManagerApp();
+  ~InputManagerApp() override;
+
+ private:
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app_impl) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // |InterfaceFactory<ViewAssociate>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ui::ViewAssociate> request) override;
+
+  mojo::ApplicationImpl* app_impl_;
+  mojo::TracingImpl tracing_;
+
+  mojo::StrongBindingSet<mojo::ui::ViewAssociate> input_associates;
+
+  DISALLOW_COPY_AND_ASSIGN(InputManagerApp);
+};
+
+}  // namespace input_manager
+
+#endif  // SERVICES_UI_INPUT_MANAGER_INPUT_MANAGER_APP_H_
diff --git a/services/ui/input_manager/main.cc b/services/ui/input_manager/main.cc
new file mode 100644
index 0000000..286f898
--- /dev/null
+++ b/services/ui/input_manager/main.cc
@@ -0,0 +1,12 @@
+// 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 "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "services/ui/input_manager/input_manager_app.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new input_manager::InputManagerApp);
+  return runner.Run(application_request);
+}
diff --git a/services/ui/launcher/BUILD.gn b/services/ui/launcher/BUILD.gn
index e85fda3..554c08e 100644
--- a/services/ui/launcher/BUILD.gn
+++ b/services/ui/launcher/BUILD.gn
@@ -20,9 +20,11 @@
     "//mojo/common:tracing_impl",
     "//mojo/environment:chromium",
     "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/gfx/composition/cpp",
+    "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/native_viewport/interfaces",
-    "//mojo/services/surfaces/cpp",
-    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/ui/input/interfaces",
+    "//mojo/services/ui/views/cpp",
     "//mojo/services/ui/views/interfaces",
   ]
 }
diff --git a/services/ui/launcher/launcher_app.cc b/services/ui/launcher/launcher_app.cc
index 07e796f..55a58be 100644
--- a/services/ui/launcher/launcher_app.cc
+++ b/services/ui/launcher/launcher_app.cc
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/ui/launcher/launcher_app.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "mojo/application/application_runner_chromium.h"
 #include "mojo/common/tracing_impl.h"
 #include "mojo/public/c/system/main.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/services/surfaces/cpp/surfaces_utils.h"
-#include "mojo/services/surfaces/interfaces/quads.mojom.h"
-#include "services/ui/launcher/launcher_app.h"
 #include "services/ui/launcher/launcher_view_tree.h"
 
 namespace launcher {
@@ -20,31 +21,55 @@
 
 LauncherApp::~LauncherApp() {}
 
-void LauncherApp::Initialize(mojo::ApplicationImpl* app) {
-  app_impl_ = app;
-  tracing_.Initialize(app);
+void LauncherApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(app_impl_->args());
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+
+  tracing_.Initialize(app_impl_);
   TRACE_EVENT0("launcher", __func__);
 
-  if (app->args().size() != 2) {
+  if (command_line->GetArgs().size() != 1) {
     LOG(ERROR) << "Invalid arguments.\n\n"
                   "Usage: mojo_shell \"mojo:launcher <app url>\"";
-    app->Terminate();
+    app_impl_->Terminate();
     return;
   }
 
+  app_impl_->ConnectToService("mojo:compositor_service", &compositor_);
+  compositor_.set_connection_error_handler(base::Bind(
+      &LauncherApp::OnCompositorConnectionError, base::Unretained(this)));
+
+  app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_);
+  view_manager_.set_connection_error_handler(base::Bind(
+      &LauncherApp::OnViewManagerConnectionError, base::Unretained(this)));
+
   InitViewport();
-  LaunchClient(app->args()[1]);
+  LaunchClient(command_line->GetArgs()[0]);
+}
+
+void LauncherApp::OnCompositorConnectionError() {
+  LOG(ERROR) << "Exiting due to compositor connection error.";
+  Shutdown();
+}
+
+void LauncherApp::OnViewManagerConnectionError() {
+  LOG(ERROR) << "Exiting due to view manager connection error.";
+  Shutdown();
 }
 
 void LauncherApp::InitViewport() {
-  app_impl_->ConnectToService("mojo:native_viewport_service",
-                              &viewport_service_);
-  viewport_service_.set_connection_error_handler(base::Bind(
+  app_impl_->ConnectToService("mojo:native_viewport_service", &viewport_);
+  viewport_.set_connection_error_handler(base::Bind(
       &LauncherApp::OnViewportConnectionError, base::Unretained(this)));
 
   mojo::NativeViewportEventDispatcherPtr dispatcher;
   viewport_event_dispatcher_binding_.Bind(GetProxy(&dispatcher));
-  viewport_service_->SetEventDispatcher(dispatcher.Pass());
+  viewport_->SetEventDispatcher(dispatcher.Pass());
 
   // Match the Nexus 5 aspect ratio initially.
   auto size = mojo::Size::New();
@@ -52,29 +77,26 @@
   size->height = 640;
 
   auto requested_configuration = mojo::SurfaceConfiguration::New();
-  viewport_service_->Create(
+  viewport_->Create(
       size.Clone(), requested_configuration.Pass(),
       base::Bind(&LauncherApp::OnViewportCreated, base::Unretained(this)));
 }
 
 void LauncherApp::OnViewportConnectionError() {
   LOG(ERROR) << "Exiting due to viewport connection error.";
-  app_impl_->Terminate();
+  Shutdown();
 }
 
 void LauncherApp::OnViewportCreated(mojo::ViewportMetricsPtr metrics) {
-  viewport_service_->Show();
+  viewport_->Show();
+
   mojo::ContextProviderPtr context_provider;
-  viewport_service_->GetContextProvider(GetProxy(&context_provider));
+  viewport_->GetContextProvider(GetProxy(&context_provider));
 
-  mojo::DisplayFactoryPtr display_factory;
-  app_impl_->ConnectToService("mojo:surfaces_service", &display_factory);
-
-  mojo::DisplayPtr display;
-  display_factory->Create(context_provider.Pass(), nullptr, GetProxy(&display));
-
-  view_tree_.reset(
-      new LauncherViewTree(app_impl_, display.Pass(), metrics.Pass()));
+  view_tree_.reset(new LauncherViewTree(
+      compositor_.get(), view_manager_.get(), context_provider.Pass(),
+      metrics.Pass(),
+      base::Bind(&LauncherApp::Shutdown, base::Unretained(this))));
   UpdateClientView();
   RequestUpdatedViewportMetrics();
 }
@@ -87,8 +109,8 @@
 }
 
 void LauncherApp::RequestUpdatedViewportMetrics() {
-  viewport_service_->RequestMetrics(base::Bind(
-      &LauncherApp::OnViewportMetricsChanged, base::Unretained(this)));
+  viewport_->RequestMetrics(base::Bind(&LauncherApp::OnViewportMetricsChanged,
+                                       base::Unretained(this)));
 }
 
 void LauncherApp::OnEvent(mojo::EventPtr event,
@@ -112,7 +134,7 @@
 
 void LauncherApp::OnClientConnectionError() {
   LOG(ERROR) << "Exiting due to client application connection error.";
-  app_impl_->Terminate();
+  Shutdown();
 }
 
 void LauncherApp::OnClientViewCreated(mojo::ui::ViewTokenPtr view_token) {
@@ -125,4 +147,8 @@
     view_tree_->SetRoot(client_view_token_.Clone());
 }
 
+void LauncherApp::Shutdown() {
+  app_impl_->Terminate();
+}
+
 }  // namespace launcher
diff --git a/services/ui/launcher/launcher_app.h b/services/ui/launcher/launcher_app.h
index d614956..b6b5c2d 100644
--- a/services/ui/launcher/launcher_app.h
+++ b/services/ui/launcher/launcher_app.h
@@ -9,8 +9,9 @@
 
 #include "mojo/common/tracing_impl.h"
 #include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/gfx/composition/interfaces/compositor.mojom.h"
 #include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
-#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
 #include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
 
 namespace launcher {
@@ -25,30 +26,36 @@
 
  private:
   // |ApplicationDelegate|:
-  void Initialize(mojo::ApplicationImpl* app) override;
+  void Initialize(mojo::ApplicationImpl* app_impl) override;
 
   // |NativeViewportEventDispatcher|:
   void OnEvent(mojo::EventPtr event,
                const mojo::Callback<void()>& callback) override;
 
+  void OnCompositorConnectionError();
+  void OnViewManagerConnectionError();
+
   void InitViewport();
   void OnViewportConnectionError();
   void OnViewportCreated(mojo::ViewportMetricsPtr metrics);
   void OnViewportMetricsChanged(mojo::ViewportMetricsPtr metrics);
   void RequestUpdatedViewportMetrics();
 
-  void OnViewManagerConnectionError();
-
   void LaunchClient(std::string app_url);
   void OnClientConnectionError();
   void OnClientViewCreated(mojo::ui::ViewTokenPtr view_token);
 
   void UpdateClientView();
 
+  void Shutdown();
+
   mojo::ApplicationImpl* app_impl_;
   mojo::TracingImpl tracing_;
 
-  mojo::NativeViewportPtr viewport_service_;
+  mojo::gfx::composition::CompositorPtr compositor_;
+  mojo::ui::ViewManagerPtr view_manager_;
+
+  mojo::NativeViewportPtr viewport_;
   mojo::Binding<NativeViewportEventDispatcher>
       viewport_event_dispatcher_binding_;
 
diff --git a/services/ui/launcher/launcher_view_tree.cc b/services/ui/launcher/launcher_view_tree.cc
index 052b3c0..1c2beb6 100644
--- a/services/ui/launcher/launcher_view_tree.cc
+++ b/services/ui/launcher/launcher_view_tree.cc
@@ -2,32 +2,68 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/bind.h"
 #include "services/ui/launcher/launcher_view_tree.h"
 
+#include "base/bind.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/services/gfx/composition/cpp/formatting.h"
+#include "mojo/services/ui/views/cpp/formatting.h"
+
 namespace launcher {
 
-LauncherViewTree::LauncherViewTree(mojo::ApplicationImpl* app_impl,
-                                   mojo::DisplayPtr display,
-                                   mojo::ViewportMetricsPtr viewport_metrics)
-    : display_(display.Pass()),
-      viewport_metrics_(viewport_metrics.Pass()),
-      binding_(this),
-      root_key_(0),
-      frame_scheduled_(false),
-      frame_pending_(false) {
-  app_impl->ConnectToService("mojo:view_manager_service", &view_manager_);
-  view_manager_.set_connection_error_handler(base::Bind(
-      &LauncherViewTree::OnViewManagerConnectionError, base::Unretained(this)));
+constexpr uint32_t kViewSceneResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+constexpr uint32_t kViewNodeId = 1;
+constexpr uint32_t kFallbackNodeId = 2;
 
+LauncherViewTree::LauncherViewTree(
+    mojo::gfx::composition::Compositor* compositor,
+    mojo::ui::ViewManager* view_manager,
+    mojo::ContextProviderPtr context_provider,
+    mojo::ViewportMetricsPtr viewport_metrics,
+    const base::Closure& shutdown_callback)
+    : compositor_(compositor),
+      view_manager_(view_manager),
+      context_provider_(context_provider.Pass()),
+      viewport_metrics_(viewport_metrics.Pass()),
+      shutdown_callback_(shutdown_callback),
+      scene_listener_binding_(this),
+      view_tree_binding_(this) {
+  // Create the renderer.
+  compositor_->CreateRenderer(context_provider_.Pass(), GetProxy(&renderer_),
+                              "Launcher");
+  renderer_.set_connection_error_handler(base::Bind(
+      &LauncherViewTree::OnRendererConnectionError, base::Unretained(this)));
+
+  // Create the root scene.
+  compositor_->CreateScene(
+      mojo::GetProxy(&scene_), "Launcher",
+      base::Bind(&LauncherViewTree::OnSceneRegistered, base::Unretained(this)));
+  mojo::gfx::composition::SceneListenerPtr scene_listener;
+  scene_listener_binding_.Bind(mojo::GetProxy(&scene_listener));
+  scene_->SetListener(scene_listener.Pass());
+  scene_.set_connection_error_handler(base::Bind(
+      &LauncherViewTree::OnSceneConnectionError, base::Unretained(this)));
+
+  // Register the view tree.
   mojo::ui::ViewTreePtr view_tree;
-  binding_.Bind(mojo::GetProxy(&view_tree));
+  view_tree_binding_.Bind(mojo::GetProxy(&view_tree));
   view_manager_->RegisterViewTree(
-      view_tree.Pass(), mojo::GetProxy(&view_tree_host_),
+      view_tree.Pass(), mojo::GetProxy(&view_tree_host_), "Launcher",
       base::Bind(&LauncherViewTree::OnViewTreeRegistered,
                  base::Unretained(this)));
+  view_tree_host_.set_connection_error_handler(base::Bind(
+      &LauncherViewTree::OnViewTreeConnectionError, base::Unretained(this)));
 
-  ScheduleFrame();
+  // Get view tree services.
+  mojo::ServiceProviderPtr view_tree_service_provider;
+  view_tree_host_->GetServiceProvider(
+      mojo::GetProxy(&view_tree_service_provider));
+  mojo::ConnectToService<mojo::ui::InputDispatcher>(
+      view_tree_service_provider.get(), &input_dispatcher_);
+  input_dispatcher_.set_connection_error_handler(
+      base::Bind(&LauncherViewTree::OnInputDispatcherConnectionError,
+                 base::Unretained(this)));
 }
 
 LauncherViewTree::~LauncherViewTree() {}
@@ -45,17 +81,53 @@
     mojo::ViewportMetricsPtr viewport_metrics) {
   viewport_metrics_ = viewport_metrics.Pass();
   view_tree_host_->RequestLayout();
+  SetRootScene();
 }
 
 void LauncherViewTree::DispatchEvent(mojo::EventPtr event) {
-  // TODO(jeffbrown): Support input dispatch.
+  if (input_dispatcher_)
+    input_dispatcher_->DispatchEvent(event.Pass());
 }
 
-void LauncherViewTree::OnViewManagerConnectionError() {
-  LOG(ERROR) << "View manager connection error.";
+void LauncherViewTree::OnRendererConnectionError() {
+  LOG(ERROR) << "Renderer connection error.";
+  Shutdown();
 }
 
-void LauncherViewTree::OnViewTreeRegistered() {}
+void LauncherViewTree::OnSceneConnectionError() {
+  LOG(ERROR) << "Scene connection error.";
+  Shutdown();
+}
+
+void LauncherViewTree::OnViewTreeConnectionError() {
+  LOG(ERROR) << "View tree connection error.";
+  Shutdown();
+}
+
+void LauncherViewTree::OnInputDispatcherConnectionError() {
+  // This isn't considered a fatal error right now since it is still useful
+  // to be able to test a view system that has graphics but no input.
+  LOG(WARNING) << "Input dispatcher connection error, input will not work.";
+  input_dispatcher_.reset();
+}
+
+void LauncherViewTree::OnSceneRegistered(
+    mojo::gfx::composition::SceneTokenPtr scene_token) {
+  DVLOG(1) << "OnSceneRegistered: scene_token=" << scene_token;
+  scene_token_ = scene_token.Pass();
+  SetRootScene();
+}
+
+void LauncherViewTree::OnViewTreeRegistered(
+    mojo::ui::ViewTreeTokenPtr view_tree_token) {
+  DVLOG(1) << "OnViewTreeRegistered: view_tree_token=" << view_tree_token;
+}
+
+void LauncherViewTree::OnResourceUnavailable(
+    uint32_t resource_id,
+    const OnResourceUnavailableCallback& callback) {
+  LOG(ERROR) << "Resource lost: resource_id=" << resource_id;
+}
 
 void LauncherViewTree::OnLayout(const OnLayoutCallback& callback) {
   LayoutRoot();
@@ -66,9 +138,8 @@
     uint32_t root_key,
     const OnRootUnavailableCallback& callback) {
   if (root_key_ == root_key) {
-    // TODO(jeffbrown): We should probably shut down the launcher.
     LOG(ERROR) << "Root view terminated unexpectedly.";
-    SetRoot(mojo::ui::ViewTokenPtr());
+    Shutdown();
   }
   callback.Run();
 }
@@ -97,63 +168,74 @@
 
   DVLOG(1) << "Root layout: size.width=" << info->size->width
            << ", size.height=" << info->size->height
-           << ", surface_id.id_namespace=" << info->surface_id->id_namespace
-           << ", surface_id.local=" << info->surface_id->local;
+           << ", scene_token.value=" << info->scene_token->value;
 
   root_layout_info_ = info.Pass();
-  ScheduleFrame();
+  PublishFrame();
 }
 
-void LauncherViewTree::ScheduleFrame() {
-  frame_scheduled_ = true;
-  FinishFrame();
+void LauncherViewTree::SetRootScene() {
+  if (scene_token_) {
+    mojo::Rect viewport;
+    viewport.width = viewport_metrics_->size->width;
+    viewport.height = viewport_metrics_->size->height;
+    scene_version_++;
+    renderer_->SetRootScene(scene_token_.Clone(), scene_version_,
+                            viewport.Clone());
+    PublishFrame();
+  }
 }
 
-void LauncherViewTree::FinishFrame() {
-  if (!frame_scheduled_ || frame_pending_)
-    return;
-  frame_scheduled_ = false;
-
-  mojo::FramePtr frame = mojo::Frame::New();
-  frame->resources.resize(0u);
-
+void LauncherViewTree::PublishFrame() {
   mojo::Rect bounds;
   bounds.width = viewport_metrics_->size->width;
   bounds.height = viewport_metrics_->size->height;
-  mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
-  pass->shared_quad_states.push_back(
-      mojo::CreateDefaultSQS(*viewport_metrics_->size));
 
-  mojo::QuadPtr quad = mojo::Quad::New();
-  quad->rect = bounds.Clone();
-  quad->opaque_rect = bounds.Clone();
-  quad->visible_rect = bounds.Clone();
-  quad->shared_quad_state_index = 0u;
+  auto update = mojo::gfx::composition::SceneUpdate::New();
 
   if (root_layout_info_) {
-    quad->material = mojo::Material::SURFACE_CONTENT;
-    quad->surface_quad_state = mojo::SurfaceQuadState::New();
-    quad->surface_quad_state->surface = root_layout_info_->surface_id.Clone();
+    auto view_resource = mojo::gfx::composition::Resource::New();
+    view_resource->set_scene(mojo::gfx::composition::SceneResource::New());
+    view_resource->get_scene()->scene_token =
+        root_layout_info_->scene_token.Clone();
+    update->resources.insert(kViewSceneResourceId, view_resource.Pass());
+
+    auto view_node = mojo::gfx::composition::Node::New();
+    view_node->op = mojo::gfx::composition::NodeOp::New();
+    view_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New());
+    view_node->op->get_scene()->scene_resource_id = kViewSceneResourceId;
+    update->nodes.insert(kViewNodeId, view_node.Pass());
   } else {
-    quad->material = mojo::Material::SOLID_COLOR;
-    quad->solid_color_quad_state = mojo::SolidColorQuadState::New();
-    quad->solid_color_quad_state->color = mojo::Color::New();
-    quad->solid_color_quad_state->color->rgba = 0xffff0000;
+    update->resources.insert(kViewSceneResourceId, nullptr);
+    update->nodes.insert(kViewNodeId, nullptr);
   }
 
-  pass->quads.push_back(quad.Pass());
-  frame->passes.push_back(pass.Pass());
+  auto fallback_node = mojo::gfx::composition::Node::New();
+  fallback_node->op = mojo::gfx::composition::NodeOp::New();
+  fallback_node->op->set_rect(mojo::gfx::composition::RectNodeOp::New());
+  fallback_node->op->get_rect()->content_rect = bounds.Clone();
+  fallback_node->op->get_rect()->color = mojo::gfx::composition::Color::New();
+  fallback_node->op->get_rect()->color->red = 255;
+  fallback_node->op->get_rect()->color->alpha = 255;
+  update->nodes.insert(kFallbackNodeId, fallback_node.Pass());
 
-  frame_pending_ = true;
-  display_->SubmitFrame(
-      frame.Pass(),
-      base::Bind(&LauncherViewTree::OnFrameSubmitted, base::Unretained(this)));
+  auto root_node = mojo::gfx::composition::Node::New();
+  root_node->combinator = mojo::gfx::composition::Node::Combinator::FALLBACK;
+  if (root_layout_info_) {
+    root_node->child_node_ids.push_back(kViewNodeId);
+  }
+  root_node->child_node_ids.push_back(kFallbackNodeId);
+  update->nodes.insert(kRootNodeId, root_node.Pass());
+
+  auto metadata = mojo::gfx::composition::SceneMetadata::New();
+  metadata->version = scene_version_;
+
+  scene_->Update(update.Pass());
+  scene_->Publish(metadata.Pass());
 }
 
-void LauncherViewTree::OnFrameSubmitted() {
-  DCHECK(frame_pending_);
-  frame_pending_ = false;
-  FinishFrame();
+void LauncherViewTree::Shutdown() {
+  shutdown_callback_.Run();
 }
 
 }  // namespace launcher
diff --git a/services/ui/launcher/launcher_view_tree.h b/services/ui/launcher/launcher_view_tree.h
index 58ab563..4c4a303 100644
--- a/services/ui/launcher/launcher_view_tree.h
+++ b/services/ui/launcher/launcher_view_tree.h
@@ -5,23 +5,24 @@
 #ifndef SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
 #define SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
 
+#include "base/callback.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/application/application_impl.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/gfx/composition/interfaces/compositor.mojom.h"
 #include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
-#include "mojo/services/surfaces/cpp/surfaces_utils.h"
-#include "mojo/services/surfaces/interfaces/display.mojom.h"
-#include "mojo/services/surfaces/interfaces/quads.mojom.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
+#include "mojo/services/ui/input/interfaces/input_dispatcher.mojom.h"
 #include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
 
 namespace launcher {
 
-class LauncherViewTree : public mojo::ui::ViewTree {
+class LauncherViewTree : public mojo::ui::ViewTree,
+                         public mojo::gfx::composition::SceneListener {
  public:
-  LauncherViewTree(mojo::ApplicationImpl* app_impl,
-                   mojo::DisplayPtr display,
-                   mojo::ViewportMetricsPtr viewport_metrics);
+  LauncherViewTree(mojo::gfx::composition::Compositor* compositor,
+                   mojo::ui::ViewManager* view_manager,
+                   mojo::ContextProviderPtr context_provider,
+                   mojo::ViewportMetricsPtr viewport_metrics,
+                   const base::Closure& shutdown_callback);
 
   ~LauncherViewTree() override;
 
@@ -30,35 +31,55 @@
   void DispatchEvent(mojo::EventPtr event);
 
  private:
+  // |SceneListener|:
+  void OnResourceUnavailable(
+      uint32_t resource_id,
+      const OnResourceUnavailableCallback& callback) override;
+
   // |ViewTree|:
   void OnLayout(const OnLayoutCallback& callback) override;
   void OnRootUnavailable(uint32_t root_key,
                          const OnRootUnavailableCallback& callback) override;
 
-  void OnViewManagerConnectionError();
-  void OnViewTreeRegistered();
+  void OnRendererConnectionError();
+  void OnSceneConnectionError();
+  void OnViewTreeConnectionError();
+  void OnInputDispatcherConnectionError();
+
+  void OnSceneRegistered(mojo::gfx::composition::SceneTokenPtr scene_token);
+  void OnViewTreeRegistered(mojo::ui::ViewTreeTokenPtr view_tree_token);
 
   void LayoutRoot();
   void OnLayoutResult(mojo::ui::ViewLayoutInfoPtr info);
 
-  void ScheduleFrame();
-  void FinishFrame();
-  void OnFrameSubmitted();
+  void SetRootScene();
 
-  mojo::ui::ViewManagerPtr view_manager_;
-  mojo::DisplayPtr display_;
+  void PublishFrame();
+
+  void Shutdown();
+
+  mojo::gfx::composition::Compositor* compositor_;
+  mojo::ui::ViewManager* view_manager_;
+
+  mojo::ContextProviderPtr context_provider_;
   mojo::ViewportMetricsPtr viewport_metrics_;
-  mojo::Binding<mojo::ui::ViewTree> binding_;
+  base::Closure shutdown_callback_;
 
+  mojo::Binding<mojo::gfx::composition::SceneListener> scene_listener_binding_;
+  mojo::Binding<mojo::ui::ViewTree> view_tree_binding_;
+
+  mojo::gfx::composition::ScenePtr scene_;
+  mojo::gfx::composition::SceneTokenPtr scene_token_;
+  uint32_t scene_version_ = 1u;
+
+  mojo::gfx::composition::RendererPtr renderer_;
   mojo::ui::ViewTreeHostPtr view_tree_host_;
+  mojo::ui::InputDispatcherPtr input_dispatcher_;
 
   mojo::ui::ViewTokenPtr root_;
-  uint32_t root_key_;
+  uint32_t root_key_ = 0u;
   mojo::ui::ViewLayoutInfoPtr root_layout_info_;
 
-  bool frame_scheduled_;
-  bool frame_pending_;
-
   DISALLOW_COPY_AND_ASSIGN(LauncherViewTree);
 };
 
diff --git a/services/ui/view_manager/BUILD.gn b/services/ui/view_manager/BUILD.gn
index 833582b..0a14325 100644
--- a/services/ui/view_manager/BUILD.gn
+++ b/services/ui/view_manager/BUILD.gn
@@ -10,8 +10,8 @@
 
   sources = [
     "main.cc",
-    "surface_manager.cc",
-    "surface_manager.h",
+    "view_associate_table.cc",
+    "view_associate_table.h",
     "view_host_impl.cc",
     "view_host_impl.h",
     "view_layout_request.cc",
@@ -35,11 +35,13 @@
     "//mojo/application",
     "//mojo/common",
     "//mojo/common:tracing_impl",
-    "//mojo/environment:chromium",
     "//mojo/converters/geometry",
+    "//mojo/environment:chromium",
     "//mojo/public/cpp/bindings:bindings",
-    "//mojo/services/surfaces/cpp",
-    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/geometry/cpp",
+    "//mojo/services/gfx/composition/cpp",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/views/cpp",
     "//mojo/services/ui/views/interfaces",
   ]
 }
diff --git a/services/ui/view_manager/surface_manager.cc b/services/ui/view_manager/surface_manager.cc
deleted file mode 100644
index 24ec899..0000000
--- a/services/ui/view_manager/surface_manager.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 "base/bind.h"
-#include "base/bind_helpers.h"
-#include "mojo/services/surfaces/cpp/surfaces_utils.h"
-#include "mojo/services/surfaces/interfaces/quads.mojom.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
-#include "services/ui/view_manager/surface_manager.h"
-
-namespace view_manager {
-
-SurfaceManager::SurfaceManager(mojo::SurfacePtr surfaces)
-    : surfaces_(surfaces.Pass()), surface_namespace_(0u) {}
-
-SurfaceManager::~SurfaceManager() {}
-
-mojo::SurfaceIdPtr SurfaceManager::CreateWrappedSurface(
-    mojo::SurfaceId* inner_surface_id) {
-  return inner_surface_id->Clone();
-}
-
-void SurfaceManager::DestroySurface(mojo::SurfaceIdPtr surface_id) {
-  // surfaces_->DestroySurface(surface_id->local);
-}
-
-}  // namespace view_manager
diff --git a/services/ui/view_manager/surface_manager.h b/services/ui/view_manager/surface_manager.h
deleted file mode 100644
index d5d2068..0000000
--- a/services/ui/view_manager/surface_manager.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.
-
-#ifndef SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_
-#define SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_
-
-#include "base/macros.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
-
-namespace view_manager {
-
-// Manages surfaces on behalf of the view manager.
-class SurfaceManager {
- public:
-  explicit SurfaceManager(mojo::SurfacePtr surfaces);
-  ~SurfaceManager();
-
-  mojo::SurfaceIdPtr CreateWrappedSurface(mojo::SurfaceId* inner_surface_id);
-
-  void DestroySurface(mojo::SurfaceIdPtr surface_id);
-
- private:
-  void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace);
-
-  mojo::SurfacePtr surfaces_;
-  uint32_t surface_namespace_;
-
-  DISALLOW_COPY_AND_ASSIGN(SurfaceManager);
-};
-
-}  // namespace view_manager
-
-#endif  // SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_
diff --git a/services/ui/view_manager/view_associate_table.cc b/services/ui/view_manager/view_associate_table.cc
new file mode 100644
index 0000000..bcfca3b
--- /dev/null
+++ b/services/ui/view_manager/view_associate_table.cc
@@ -0,0 +1,143 @@
+// 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 "services/ui/view_manager/view_associate_table.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "mojo/services/ui/views/cpp/formatting.h"
+
+namespace view_manager {
+
+template <typename T>
+static bool Contains(const mojo::Array<T>& array, const T& value) {
+  return std::find(array.storage().cbegin(), array.storage().cend(), value) !=
+         array.storage().cend();
+}
+
+ViewAssociateTable::ViewAssociateTable() {}
+
+ViewAssociateTable::~ViewAssociateTable() {}
+
+void ViewAssociateTable::ConnectAssociates(
+    mojo::ApplicationImpl* app_impl,
+    mojo::ui::ViewInspector* inspector,
+    const std::vector<std::string>& urls,
+    const AssociateConnectionErrorCallback& connection_error_callback) {
+  DCHECK(app_impl);
+  DCHECK(inspector);
+
+  for (auto& url : urls) {
+    DVLOG(1) << "Connecting to view associate: url=" << url;
+    associates_.emplace_back(new AssociateData(url, inspector));
+    AssociateData* data = associates_.back().get();
+
+    app_impl->ConnectToService(url, &data->associate);
+    data->associate.set_connection_error_handler(
+        base::Bind(connection_error_callback, url));
+
+    mojo::ui::ViewInspectorPtr inspector;
+    data->inspector_binding.Bind(mojo::GetProxy(&inspector));
+    data->associate->Connect(
+        inspector.Pass(),
+        base::Bind(&ViewAssociateTable::OnConnected, base::Unretained(this),
+                   pending_connection_count_));
+
+    pending_connection_count_++;
+  }
+}
+
+void ViewAssociateTable::ConnectToViewService(
+    mojo::ui::ViewTokenPtr view_token,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  if (pending_connection_count_) {
+    deferred_work_.push_back(
+        base::Bind(&ViewAssociateTable::ConnectToViewService,
+                   base::Unretained(this), base::Passed(view_token.Pass()),
+                   service_name, base::Passed(client_handle.Pass())));
+    return;
+  }
+
+  for (auto& data : associates_) {
+    DCHECK(data->info);
+    if (Contains(data->info->view_service_names, service_name)) {
+      DVLOG(2) << "Connecting to view service: view_token=" << view_token
+               << ", service_name=" << service_name
+               << ", associate_url=" << data->url;
+      DCHECK(data->associate);
+      data->associate->ConnectToViewService(view_token.Pass(), service_name,
+                                            client_handle.Pass());
+      return;
+    }
+  }
+
+  DVLOG(2) << "Requested view service not available: view_token=" << view_token
+           << ", service_name=" << service_name;
+  // Allow pipe to be closed as an indication of failure.
+}
+
+void ViewAssociateTable::ConnectToViewTreeService(
+    mojo::ui::ViewTreeTokenPtr view_tree_token,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  if (pending_connection_count_) {
+    deferred_work_.push_back(
+        base::Bind(&ViewAssociateTable::ConnectToViewTreeService,
+                   base::Unretained(this), base::Passed(view_tree_token.Pass()),
+                   service_name, base::Passed(client_handle.Pass())));
+    return;
+  }
+
+  for (auto& data : associates_) {
+    DCHECK(data->info);
+    if (Contains(data->info->view_tree_service_names, service_name)) {
+      DVLOG(2) << "Connecting to view tree service: view_tree_token="
+               << view_tree_token << ", service_name=" << service_name
+               << ", associate_url=" << data->url;
+      DCHECK(data->associate);
+      data->associate->ConnectToViewTreeService(
+          view_tree_token.Pass(), service_name, client_handle.Pass());
+      return;
+    }
+  }
+
+  DVLOG(2) << "Requested view tree service not available: view_tree_token="
+           << view_tree_token << ", service_name=" << service_name;
+  // Allow pipe to be closed as an indication of failure.
+}
+
+void ViewAssociateTable::OnConnected(uint32_t index,
+                                     mojo::ui::ViewAssociateInfoPtr info) {
+  DCHECK(info);
+  DCHECK(pending_connection_count_);
+  DCHECK(!associates_[index]->info);
+
+  DVLOG(1) << "Connected to view associate: url=" << associates_[index]->url
+           << ", info=" << info;
+  associates_[index]->info = info.Pass();
+
+  pending_connection_count_--;
+  if (!pending_connection_count_)
+    CompleteDeferredWork();
+}
+
+void ViewAssociateTable::CompleteDeferredWork() {
+  DCHECK(!pending_connection_count_);
+
+  for (auto& work : deferred_work_)
+    work.Run();
+  deferred_work_.clear();
+}
+
+ViewAssociateTable::AssociateData::AssociateData(
+    const std::string& url,
+    mojo::ui::ViewInspector* inspector)
+    : url(url), inspector_binding(inspector) {}
+
+ViewAssociateTable::AssociateData::~AssociateData() {}
+
+}  // namespace view_manager
diff --git a/services/ui/view_manager/view_associate_table.h b/services/ui/view_manager/view_associate_table.h
new file mode 100644
index 0000000..9cdc71b
--- /dev/null
+++ b/services/ui/view_manager/view_associate_table.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_ASSOCIATE_TABLE_H_
+#define SERVICES_UI_VIEW_MANAGER_VIEW_ASSOCIATE_TABLE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "mojo/common/binding_set.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/ui/views/interfaces/view_associates.mojom.h"
+
+namespace view_manager {
+
+// Maintains a table of all connected view associates.
+class ViewAssociateTable {
+ public:
+  using AssociateConnectionErrorCallback =
+      base::Callback<void(const std::string&)>;
+
+  ViewAssociateTable();
+  ~ViewAssociateTable();
+
+  // Begins connecting to the view associates.
+  // Invokes |connection_error_callback| if an associate connection fails
+  // and provides the associate's url.
+  void ConnectAssociates(
+      mojo::ApplicationImpl* app_impl,
+      mojo::ui::ViewInspector* inspector,
+      const std::vector<std::string>& urls,
+      const AssociateConnectionErrorCallback& connection_error_callback);
+
+  // Connects to services offered by the view associates.
+  void ConnectToViewService(mojo::ui::ViewTokenPtr view_token,
+                            const mojo::String& service_name,
+                            mojo::ScopedMessagePipeHandle client_handle);
+  void ConnectToViewTreeService(mojo::ui::ViewTreeTokenPtr view_tree_token,
+                                const mojo::String& service_name,
+                                mojo::ScopedMessagePipeHandle client_handle);
+
+  void OnConnected(uint32_t index, mojo::ui::ViewAssociateInfoPtr info);
+
+  void CompleteDeferredWork();
+
+ private:
+  struct AssociateData {
+    AssociateData(const std::string& url, mojo::ui::ViewInspector* inspector);
+    ~AssociateData();
+
+    const std::string url;
+    mojo::ui::ViewAssociatePtr associate;
+    mojo::ui::ViewAssociateInfoPtr info;
+    mojo::Binding<mojo::ui::ViewInspector> inspector_binding;
+  };
+
+  std::vector<std::unique_ptr<AssociateData>> associates_;
+
+  uint32_t pending_connection_count_ = 0u;
+  std::vector<base::Closure> deferred_work_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewAssociateTable);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_UI_VIEW_MANAGER_VIEW_ASSOCIATE_TABLE_H_
diff --git a/services/ui/view_manager/view_host_impl.cc b/services/ui/view_manager/view_host_impl.cc
index fef8f0b..976644e 100644
--- a/services/ui/view_manager/view_host_impl.cc
+++ b/services/ui/view_manager/view_host_impl.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/ui/view_manager/view_host_impl.h"
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "services/ui/view_manager/view_host_impl.h"
 
 namespace view_manager {
 
@@ -19,8 +20,13 @@
 ViewHostImpl::~ViewHostImpl() {}
 
 void ViewHostImpl::GetServiceProvider(
-    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) {
-  state_->GetServiceProvider(service_provider.Pass());
+    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request) {
+  service_provider_bindings_.AddBinding(this, service_provider_request.Pass());
+}
+
+void ViewHostImpl::CreateScene(
+    mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene) {
+  registry_->CreateScene(state_, scene.Pass());
 }
 
 void ViewHostImpl::RequestLayout() {
@@ -50,4 +56,10 @@
                          base::Bind(&RunLayoutChildCallback, callback));
 }
 
+void ViewHostImpl::ConnectToService(
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  registry_->ConnectToViewService(state_, service_name, client_handle.Pass());
+}
+
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_host_impl.h b/services/ui/view_manager/view_host_impl.h
index 274d1f1..907c8f1 100644
--- a/services/ui/view_manager/view_host_impl.h
+++ b/services/ui/view_manager/view_host_impl.h
@@ -6,6 +6,7 @@
 #define SERVICES_UI_VIEW_MANAGER_VIEW_HOST_IMPL_H_
 
 #include "base/macros.h"
+#include "mojo/common/binding_set.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/services/ui/views/interfaces/views.mojom.h"
 #include "services/ui/view_manager/view_registry.h"
@@ -15,7 +16,7 @@
 
 // ViewHost interface implementation.
 // This object is owned by its associated ViewState.
-class ViewHostImpl : public mojo::ui::ViewHost {
+class ViewHostImpl : public mojo::ui::ViewHost, public mojo::ServiceProvider {
  public:
   ViewHostImpl(ViewRegistry* registry,
                ViewState* state,
@@ -28,8 +29,10 @@
 
  private:
   // |ViewHost|:
-  void GetServiceProvider(
-      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) override;
+  void GetServiceProvider(mojo::InterfaceRequest<mojo::ServiceProvider>
+                              service_provider_request) override;
+  void CreateScene(
+      mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene) override;
   void RequestLayout() override;
   void AddChild(uint32_t child_key,
                 mojo::ui::ViewTokenPtr child_view_token) override;
@@ -38,9 +41,14 @@
                    mojo::ui::ViewLayoutParamsPtr child_layout_params,
                    const LayoutChildCallback& callback) override;
 
+  // |ServiceProvider|:
+  void ConnectToService(const mojo::String& service_name,
+                        mojo::ScopedMessagePipeHandle client_handle) override;
+
   ViewRegistry* const registry_;
   ViewState* const state_;
   mojo::Binding<mojo::ui::ViewHost> binding_;
+  mojo::BindingSet<mojo::ServiceProvider> service_provider_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewHostImpl);
 };
diff --git a/services/ui/view_manager/view_layout_request.cc b/services/ui/view_manager/view_layout_request.cc
index cbec836..9ff591c 100644
--- a/services/ui/view_manager/view_layout_request.cc
+++ b/services/ui/view_manager/view_layout_request.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/logging.h"
 #include "services/ui/view_manager/view_layout_request.h"
 
+#include "base/logging.h"
+
 namespace view_manager {
 
 ViewLayoutRequest::ViewLayoutRequest(
@@ -23,11 +24,11 @@
   callbacks_.emplace_back(callback);
 }
 
-void ViewLayoutRequest::DispatchLayoutInfo(mojo::ui::ViewLayoutInfo* info) {
+void ViewLayoutRequest::DispatchLayoutInfo(mojo::ui::ViewLayoutInfoPtr info) {
   DCHECK(!was_dispatched_);
   was_dispatched_ = true;
   for (const auto& callback : callbacks_)
-    callback.Run(info ? info->Clone() : nullptr);
+    callback.Run(info ? info.Clone() : nullptr);
 }
 
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_layout_request.h b/services/ui/view_manager/view_layout_request.h
index 8091d2f..e2a63ab 100644
--- a/services/ui/view_manager/view_layout_request.h
+++ b/services/ui/view_manager/view_layout_request.h
@@ -27,7 +27,9 @@
 
   // Gets the layout parameters for this request.
   // Does not confer ownership.
-  mojo::ui::ViewLayoutParams* layout_params() { return layout_params_.get(); }
+  const mojo::ui::ViewLayoutParams* layout_params() const {
+    return layout_params_.get();
+  }
 
   // Gets the layout parameters for this request and takes ownership.
   mojo::ui::ViewLayoutParamsPtr TakeLayoutParams() {
@@ -44,7 +46,7 @@
   // Sends the layout information to each client.
   // Must be invoked exactly once before destroying the request to prevent
   // dangling callbacks.
-  void DispatchLayoutInfo(mojo::ui::ViewLayoutInfo* info);
+  void DispatchLayoutInfo(mojo::ui::ViewLayoutInfoPtr info);
 
   // True if the request has been issued to the view.
   // False if it is still pending in the queue.
diff --git a/services/ui/view_manager/view_manager_app.cc b/services/ui/view_manager/view_manager_app.cc
index 2190590..98c545c 100644
--- a/services/ui/view_manager/view_manager_app.cc
+++ b/services/ui/view_manager/view_manager_app.cc
@@ -2,13 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/ui/view_manager/view_manager_app.h"
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "mojo/application/application_runner_chromium.h"
 #include "mojo/common/tracing_impl.h"
 #include "mojo/public/c/system/main.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "services/ui/view_manager/view_manager_app.h"
 #include "services/ui/view_manager/view_manager_impl.h"
 
 namespace view_manager {
@@ -19,13 +25,36 @@
 
 void ViewManagerApp::Initialize(mojo::ApplicationImpl* app_impl) {
   app_impl_ = app_impl;
-  tracing_.Initialize(app_impl);
 
-  mojo::SurfacePtr surfaces;
-  app_impl->ConnectToService("mojo:surfaces_service", &surfaces);
-  surface_manager_.reset(new SurfaceManager(surfaces.Pass()));
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(app_impl_->args());
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
 
-  registry_.reset(new ViewRegistry(surface_manager_.get()));
+  tracing_.Initialize(app_impl_);
+
+  // Connect to compositor.
+  mojo::gfx::composition::CompositorPtr compositor;
+  app_impl_->ConnectToService("mojo:compositor_service", &compositor);
+  compositor.set_connection_error_handler(base::Bind(
+      &ViewManagerApp::OnCompositorConnectionError, base::Unretained(this)));
+
+  // Create the registry.
+  registry_.reset(new ViewRegistry(compositor.Pass()));
+
+  // Connect to associates.
+  // TODO(jeffbrown): Consider making the launcher register associates
+  // with the view manager or perhaps per view tree.
+  std::vector<std::string> associate_urls = command_line->GetArgs();
+  if (associate_urls.empty()) {
+    // TODO(jeffbrown): Replace this hardcoded list.
+    associate_urls.push_back("mojo:input_manager_service");
+  }
+  registry_->ConnectAssociates(
+      app_impl_, associate_urls,
+      base::Bind(&ViewManagerApp::OnAssociateConnectionError,
+                 base::Unretained(this)));
 }
 
 bool ViewManagerApp::ConfigureIncomingConnection(
@@ -37,8 +66,23 @@
 void ViewManagerApp::Create(
     mojo::ApplicationConnection* connection,
     mojo::InterfaceRequest<mojo::ui::ViewManager> request) {
-  view_managers.AddBinding(new ViewManagerImpl(registry_.get()),
-                           request.Pass());
+  DCHECK(registry_);
+  view_managers_.AddBinding(new ViewManagerImpl(registry_.get()),
+                            request.Pass());
+}
+
+void ViewManagerApp::OnCompositorConnectionError() {
+  LOG(ERROR) << "Exiting due to compositor connection error.";
+  Shutdown();
+}
+
+void ViewManagerApp::OnAssociateConnectionError(const std::string& url) {
+  LOG(ERROR) << "Exiting due to view associate connection error: url=" << url;
+  Shutdown();
+}
+
+void ViewManagerApp::Shutdown() {
+  app_impl_->Terminate();
 }
 
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_manager_app.h b/services/ui/view_manager/view_manager_app.h
index ca37dea..96cbab4 100644
--- a/services/ui/view_manager/view_manager_app.h
+++ b/services/ui/view_manager/view_manager_app.h
@@ -8,12 +8,11 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
 #include "mojo/common/strong_binding_set.h"
 #include "mojo/common/tracing_impl.h"
 #include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/gfx/composition/interfaces/compositor.mojom.h"
 #include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
-#include "services/ui/view_manager/surface_manager.h"
 #include "services/ui/view_manager/view_registry.h"
 
 namespace view_manager {
@@ -35,12 +34,16 @@
   void Create(mojo::ApplicationConnection* connection,
               mojo::InterfaceRequest<mojo::ui::ViewManager> request) override;
 
+  void OnCompositorConnectionError();
+  void OnAssociateConnectionError(const std::string& url);
+
+  void Shutdown();
+
   mojo::ApplicationImpl* app_impl_;
   mojo::TracingImpl tracing_;
 
-  mojo::StrongBindingSet<mojo::ui::ViewManager> view_managers;
+  mojo::StrongBindingSet<mojo::ui::ViewManager> view_managers_;
   std::unique_ptr<ViewRegistry> registry_;
-  std::unique_ptr<SurfaceManager> surface_manager_;  // must come after registry
 
   DISALLOW_COPY_AND_ASSIGN(ViewManagerApp);
 };
diff --git a/services/ui/view_manager/view_manager_impl.cc b/services/ui/view_manager/view_manager_impl.cc
index 38cf975..fa66868 100644
--- a/services/ui/view_manager/view_manager_impl.cc
+++ b/services/ui/view_manager/view_manager_impl.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/view_manager/view_host_impl.h"
 #include "services/ui/view_manager/view_manager_impl.h"
+
+#include "services/ui/view_manager/view_host_impl.h"
 #include "services/ui/view_manager/view_tree_host_impl.h"
 
 namespace view_manager {
@@ -16,18 +17,21 @@
 void ViewManagerImpl::RegisterView(
     mojo::ui::ViewPtr view,
     mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request,
+    const mojo::String& label,
     const RegisterViewCallback& callback) {
   mojo::ui::ViewTokenPtr view_token =
-      registry_->RegisterView(view.Pass(), view_host_request.Pass());
+      registry_->RegisterView(view.Pass(), view_host_request.Pass(), label);
   callback.Run(view_token.Pass());
 }
 
 void ViewManagerImpl::RegisterViewTree(
     mojo::ui::ViewTreePtr view_tree,
     mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request,
+    const mojo::String& label,
     const RegisterViewTreeCallback& callback) {
-  registry_->RegisterViewTree(view_tree.Pass(), view_tree_host_request.Pass());
-  callback.Run();
+  mojo::ui::ViewTreeTokenPtr view_tree_token = registry_->RegisterViewTree(
+      view_tree.Pass(), view_tree_host_request.Pass(), label);
+  callback.Run(view_tree_token.Pass());
 }
 
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_manager_impl.h b/services/ui/view_manager/view_manager_impl.h
index db84e4f..89c463f 100644
--- a/services/ui/view_manager/view_manager_impl.h
+++ b/services/ui/view_manager/view_manager_impl.h
@@ -6,7 +6,6 @@
 #define SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_IMPL_H_
 
 #include "base/macros.h"
-#include "mojo/common/strong_binding_set.h"
 #include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
 #include "services/ui/view_manager/view_registry.h"
 
@@ -23,10 +22,12 @@
   void RegisterView(
       mojo::ui::ViewPtr view,
       mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request,
+      const mojo::String& label,
       const RegisterViewCallback& callback) override;
   void RegisterViewTree(
       mojo::ui::ViewTreePtr view_tree,
       mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request,
+      const mojo::String& label,
       const RegisterViewTreeCallback& callback) override;
 
   ViewRegistry* registry_;
diff --git a/services/ui/view_manager/view_registry.cc b/services/ui/view_manager/view_registry.cc
index 9c691d9..a92c7f1 100644
--- a/services/ui/view_manager/view_registry.cc
+++ b/services/ui/view_manager/view_registry.cc
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/ui/view_manager/view_registry.h"
+
 #include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "services/ui/view_manager/surface_manager.h"
+#include "mojo/services/ui/views/cpp/formatting.h"
 #include "services/ui/view_manager/view_host_impl.h"
-#include "services/ui/view_manager/view_registry.h"
 #include "services/ui/view_manager/view_tree_host_impl.h"
 
 namespace view_manager {
@@ -22,72 +23,35 @@
          params->device_pixel_ratio > 0;
 }
 
-static std::ostream& operator<<(std::ostream& os, const mojo::Size* size) {
-  return size
-             ? os << "{width=" << size->width << ", height=" << size->height
-                  << "}"
-             : os << "{null}";
-}
-
-static std::ostream& operator<<(std::ostream& os,
-                                const mojo::SurfaceId* surface_id) {
-  return surface_id
-             ? os << "{id_namespace=" << surface_id->id_namespace
-                  << ", local=" << surface_id->local << "}"
-             : os << "{null}";
-}
-
-static std::ostream& operator<<(std::ostream& os,
-                                const mojo::ui::ViewToken* token) {
-  return token ? os << "{token=" << token->value << "}" : os << "{null}";
-}
-
-static std::ostream& operator<<(std::ostream& os, const ViewState* view_state) {
-  return view_state ? os << "{token=" << view_state->view_token_value() << "}"
-                    : os << "{null}";
-}
-
-static std::ostream& operator<<(std::ostream& os,
-                                const mojo::ui::BoxConstraints* constraints) {
-  return constraints
-             ? os << "{min_width=" << constraints->min_width
-                  << ", max_width=" << constraints->max_width
-                  << ", min_height=" << constraints->min_height
-                  << ", max_height=" << constraints->max_height << "}"
-             : os << "{null}";
-};
-
-static std::ostream& operator<<(std::ostream& os,
-                                const mojo::ui::ViewLayoutParams* params) {
-  return params
-             ? os << "{constraints=" << params->constraints.get()
-                  << ", device_pixel_ratio=" << params->device_pixel_ratio
-                  << "}"
-             : os << "{null}";
-}
-
-static std::ostream& operator<<(std::ostream& os,
-                                const mojo::ui::ViewLayoutInfo* info) {
-  return info
-             ? os << "{size=" << info->size.get()
-                  << ", surface_id=" << info->surface_id.get() << "}"
-             : os << "{null}";
-}
-
-ViewRegistry::ViewRegistry(SurfaceManager* surface_manager)
-    : surface_manager_(surface_manager), next_view_token_value_(1u) {}
+ViewRegistry::ViewRegistry(mojo::gfx::composition::CompositorPtr compositor)
+    : compositor_(compositor.Pass()) {}
 
 ViewRegistry::~ViewRegistry() {}
 
+void ViewRegistry::ConnectAssociates(
+    mojo::ApplicationImpl* app_impl,
+    const std::vector<std::string>& urls,
+    const AssociateConnectionErrorCallback& connection_error_callback) {
+  associate_table_.ConnectAssociates(app_impl, this, urls,
+                                     connection_error_callback);
+}
+
 mojo::ui::ViewTokenPtr ViewRegistry::RegisterView(
     mojo::ui::ViewPtr view,
-    mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request) {
+    mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request,
+    const mojo::String& label) {
   DCHECK(view);
-  uint32_t view_token_value = next_view_token_value_++;
-  DCHECK(!FindView(view_token_value));
+
+  auto view_token = mojo::ui::ViewToken::New();
+  view_token->value = next_view_token_value_++;
+  CHECK(view_token->value);
+  CHECK(!FindView(view_token->value));
 
   // Create the state and bind host to it.
-  ViewState* view_state = new ViewState(view.Pass(), view_token_value);
+  std::string sanitized_label =
+      label.get().substr(0, mojo::ui::kLabelMaxLength);
+  ViewState* view_state =
+      new ViewState(view.Pass(), view_token.Pass(), sanitized_label);
   ViewHostImpl* view_host =
       new ViewHostImpl(this, view_state, view_host_request.Pass());
   view_state->set_view_host(view_host);
@@ -99,11 +63,9 @@
                  view_state));
 
   // Add to registry and return token.
-  views_by_token_.insert({view_token_value, view_state});
-  mojo::ui::ViewTokenPtr token = mojo::ui::ViewToken::New();
-  token->value = view_state->view_token_value();
+  views_by_token_.insert({view_state->view_token()->value, view_state});
   DVLOG(1) << "RegisterView: view=" << view_state;
-  return token;
+  return view_state->view_token()->Clone();
 }
 
 void ViewRegistry::OnViewConnectionError(ViewState* view_state) {
@@ -121,17 +83,26 @@
   HijackView(view_state);
 
   // Remove from registry.
-  views_by_token_.erase(view_state->view_token_value());
+  views_by_token_.erase(view_state->view_token()->value);
   delete view_state;
 }
 
-void ViewRegistry::RegisterViewTree(
+mojo::ui::ViewTreeTokenPtr ViewRegistry::RegisterViewTree(
     mojo::ui::ViewTreePtr view_tree,
-    mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request) {
+    mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request,
+    const mojo::String& label) {
   DCHECK(view_tree);
 
+  auto view_tree_token = mojo::ui::ViewTreeToken::New();
+  view_tree_token->value = next_view_tree_token_value_++;
+  CHECK(view_tree_token->value);
+  CHECK(!FindViewTree(view_tree_token->value));
+
   // Create the state and bind host to it.
-  ViewTreeState* tree_state = new ViewTreeState(view_tree.Pass());
+  std::string sanitized_label =
+      label.get().substr(0, mojo::ui::kLabelMaxLength);
+  ViewTreeState* tree_state = new ViewTreeState(
+      view_tree.Pass(), view_tree_token.Pass(), sanitized_label);
   ViewTreeHostImpl* tree_host =
       new ViewTreeHostImpl(this, tree_state, view_tree_host_request.Pass());
   tree_state->set_view_tree_host(tree_host);
@@ -143,8 +114,10 @@
                  base::Unretained(this), tree_state));
 
   // Add to registry.
-  view_trees_.push_back(tree_state);
+  view_trees_by_token_.insert(
+      {tree_state->view_tree_token()->value, tree_state});
   DVLOG(1) << "RegisterViewTree: tree=" << tree_state;
+  return tree_state->view_tree_token()->Clone();
 }
 
 void ViewRegistry::OnViewTreeConnectionError(ViewTreeState* tree_state) {
@@ -163,12 +136,36 @@
     UnlinkRoot(tree_state);
 
   // Remove from registry.
-  view_trees_.erase(std::find_if(
-      view_trees_.begin(), view_trees_.end(),
-      [tree_state](ViewTreeState* other) { return tree_state == other; }));
+  view_trees_by_token_.erase(tree_state->view_tree_token()->value);
   delete tree_state;
 }
 
+void ViewRegistry::CreateScene(
+    ViewState* view_state,
+    mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene) {
+  DCHECK(IsViewStateRegisteredDebug(view_state));
+  DVLOG(1) << "CreateScene: view=" << view_state;
+
+  compositor_->CreateScene(
+      scene.Pass(), view_state->label(),
+      base::Bind(&ViewRegistry::OnSceneCreated, base::Unretained(this),
+                 view_state->GetWeakPtr()));
+}
+
+void ViewRegistry::OnSceneCreated(
+    base::WeakPtr<ViewState> view_state_weak,
+    mojo::gfx::composition::SceneTokenPtr scene_token) {
+  DCHECK(scene_token);
+  ViewState* view_state = view_state_weak.get();
+  if (view_state) {
+    DVLOG(1) << "OnSceneCreated: scene_token=" << scene_token;
+
+    view_state->set_scene_token(scene_token.Pass());
+    view_state->set_scene_changed_since_last_report(true);
+    InvalidateLayout(view_state);
+  }
+}
+
 void ViewRegistry::RequestLayout(ViewState* view_state) {
   DCHECK(IsViewStateRegisteredDebug(view_state));
   DVLOG(1) << "RequestLayout: view=" << view_state;
@@ -182,14 +179,14 @@
   DCHECK(IsViewStateRegisteredDebug(parent_state));
   DCHECK(child_view_token);
   DVLOG(1) << "AddChild: parent=" << parent_state << ", child_key=" << child_key
-           << ", child=" << child_view_token.get();
+           << ", child=" << child_view_token;
 
   // Check for duplicate children.
   if (parent_state->children().find(child_key) !=
       parent_state->children().end()) {
     LOG(ERROR) << "View attempted to add a child with a duplicate key: "
                << "parent=" << parent_state << ", child_key=" << child_key
-               << ", child=" << child_view_token.get();
+               << ", child=" << child_view_token;
     UnregisterView(parent_state);
     return;
   }
@@ -240,13 +237,13 @@
   DCHECK(child_layout_params->constraints);
   DVLOG(1) << "LayoutChild: parent=" << parent_state
            << ", child_key=" << child_key
-           << ", child_layout_params=" << child_layout_params.get();
+           << ", child_layout_params=" << child_layout_params;
 
   // Check whether the layout parameters are well-formed.
   if (!AreViewLayoutParamsValid(child_layout_params.get())) {
     LOG(ERROR) << "View provided invalid child layout parameters: "
                << "parent=" << parent_state << ", child_key=" << child_key
-               << ", child_layout_params=" << child_layout_params.get();
+               << ", child_layout_params=" << child_layout_params;
     UnregisterView(parent_state);
     callback.Run(nullptr);
     return;
@@ -257,7 +254,7 @@
   if (child_it == parent_state->children().end()) {
     LOG(ERROR) << "View attempted to layout a child with an invalid key: "
                << "parent=" << parent_state << ", child_key=" << child_key
-               << ", child_layout_params=" << child_layout_params.get();
+               << ", child_layout_params=" << child_layout_params;
     UnregisterView(parent_state);
     callback.Run(nullptr);
     return;
@@ -266,6 +263,16 @@
   SetLayout(child_it->second, child_layout_params.Pass(), callback);
 }
 
+void ViewRegistry::ConnectToViewService(
+    ViewState* view_state,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  DCHECK(IsViewStateRegisteredDebug(view_state));
+
+  associate_table_.ConnectToViewService(view_state->view_token()->Clone(),
+                                        service_name, client_handle.Pass());
+}
+
 void ViewRegistry::RequestLayout(ViewTreeState* tree_state) {
   DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
   DVLOG(1) << "RequestLayout: tree=" << tree_state;
@@ -279,7 +286,7 @@
   DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
   DCHECK(root_view_token);
   DVLOG(1) << "SetRoot: tree=" << tree_state << ", root_key=" << root_key
-           << ", root=" << root_view_token.get();
+           << ", root=" << root_view_token;
 
   // Check whether the desired root view still exists.
   // Using a non-existent root view still succeeds but the view manager will
@@ -310,13 +317,13 @@
   DCHECK(root_layout_params);
   DCHECK(root_layout_params->constraints);
   DVLOG(1) << "LayoutRoot: tree=" << tree_state
-           << ", root_layout_params=" << root_layout_params.get();
+           << ", root_layout_params=" << root_layout_params;
 
   // Check whether the layout parameters are well-formed.
   if (!AreViewLayoutParamsValid(root_layout_params.get())) {
     LOG(ERROR) << "View tree provided invalid root layout parameters: "
                << "tree=" << tree_state
-               << ", root_layout_params=" << root_layout_params.get();
+               << ", root_layout_params=" << root_layout_params;
     UnregisterViewTree(tree_state);
     callback.Run(nullptr);
     return;
@@ -327,8 +334,7 @@
   if (!tree_state->explicit_root()) {
     LOG(ERROR) << "View tree attempted to layout the rout without having "
                   "set one first: tree="
-               << tree_state
-               << ", root_layout_params=" << root_layout_params.get();
+               << tree_state << ", root_layout_params=" << root_layout_params;
     UnregisterViewTree(tree_state);
     callback.Run(nullptr);
     return;
@@ -344,8 +350,19 @@
   SetLayout(tree_state->root(), root_layout_params.Pass(), callback);
 }
 
-ViewState* ViewRegistry::FindView(uint32_t view_token) {
-  auto it = views_by_token_.find(view_token);
+void ViewRegistry::ConnectToViewTreeService(
+    ViewTreeState* tree_state,
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
+
+  associate_table_.ConnectToViewTreeService(
+      tree_state->view_tree_token()->Clone(), service_name,
+      client_handle.Pass());
+}
+
+ViewState* ViewRegistry::FindView(uint32_t view_token_value) {
+  auto it = views_by_token_.find(view_token_value);
   return it != views_by_token_.end() ? it->second : nullptr;
 }
 
@@ -357,9 +374,8 @@
          parent_state->children().end());
   DCHECK(IsViewStateRegisteredDebug(child_state));
 
-  DVLOG(2) << "Added child " << child_key << " {"
-           << child_state->view_token_value() << "} to parent {"
-           << parent_state->view_token_value() << "}";
+  DVLOG(2) << "Added child " << child_key << " {" << child_state->label()
+           << "} to parent {" << parent_state->label() << "}";
 
   parent_state->children().insert({child_key, child_state});
   child_state->SetParent(parent_state, child_key);
@@ -377,7 +393,7 @@
          parent_state->children().end());
 
   DVLOG(2) << "Added unavailable child " << child_key << " to parent {"
-           << parent_state->view_token_value() << "}";
+           << parent_state->label() << "}";
 
   parent_state->children().insert({child_key, nullptr});
   SendChildUnavailable(parent_state, child_key);
@@ -394,8 +410,8 @@
   DCHECK(child_it->second);
 
   DVLOG(2) << "Marked unavailable child " << child_key << " {"
-           << child_it->second->view_token_value() << "} from parent {"
-           << parent_state->view_token_value() << "}";
+           << child_it->second->label() << "} from parent {"
+           << parent_state->label() << "}";
 
   ResetStateWhenUnlinking(child_it->second);
   child_it->second->ResetContainer();
@@ -416,13 +432,13 @@
   ViewState* child_state = child_it->second;
   if (child_state) {
     DVLOG(2) << "Removed child " << child_state->key() << " {"
-             << child_state->view_token_value() << "} from parent {"
-             << parent_state->view_token_value() << "}";
+             << child_state->label() << "} from parent {"
+             << parent_state->label() << "}";
     ResetStateWhenUnlinking(child_it->second);
     child_state->ResetContainer();
   } else {
     DVLOG(2) << "Removed unavailable child " << child_it->first
-             << "} from parent {" << parent_state->view_token_value() << "}";
+             << "} from parent {" << parent_state->label() << "}";
   }
   parent_state->children().erase(child_it);
 
@@ -432,6 +448,11 @@
   InvalidateLayout(parent_state);
 }
 
+ViewTreeState* ViewRegistry::FindViewTree(uint32_t view_tree_token_value) {
+  auto it = view_trees_by_token_.find(view_tree_token_value);
+  return it != view_trees_by_token_.end() ? it->second : nullptr;
+}
+
 void ViewRegistry::LinkRoot(ViewTreeState* tree_state,
                             ViewState* root_state,
                             uint32_t root_key) {
@@ -441,7 +462,7 @@
   DCHECK(!root_state->parent());
 
   DVLOG(2) << "Linked view tree root " << root_key << " {"
-           << root_state->view_token_value() << "}";
+           << root_state->label() << "}";
 
   tree_state->SetRoot(root_state, root_key);
 
@@ -456,7 +477,7 @@
   DCHECK(tree_state->root());
 
   DVLOG(2) << "Unlinked view tree root " << tree_state->root()->key() << " {"
-           << tree_state->root()->view_token_value() << "}";
+           << tree_state->root()->label() << "}";
 
   ResetStateWhenUnlinking(tree_state->root());
   tree_state->ResetRoot();
@@ -486,7 +507,7 @@
   if (view_state->layout_params() &&
       (view_state->pending_layout_requests().empty() ||
        view_state->pending_layout_requests().back()->issued())) {
-    EnqueueLayoutRequest(view_state, view_state->layout_params().Clone());
+    EnqueueLayoutRequest(view_state, view_state->layout_params()->Clone());
     IssueNextViewLayoutRequest(view_state);
   }
 }
@@ -519,11 +540,15 @@
   // Check whether the currently cached layout parameters are the same
   // and we already have a result and we have no pending layout requests.
   if (view_state->pending_layout_requests().empty() &&
-      view_state->layout_params() && view_state->layout_info() &&
+      view_state->layout_params() &&
       view_state->layout_params()->Equals(*layout_params)) {
-    DVLOG(2) << "Layout cache hit";
-    callback.Run(view_state->layout_info().Clone());
-    return;
+    mojo::ui::ViewLayoutInfoPtr info = view_state->CreateLayoutInfo();
+    if (info) {
+      DVLOG(2) << "Layout cache hit";
+      view_state->set_scene_changed_since_last_report(false);
+      callback.Run(info.Pass());
+      return;
+    }
   }
 
   // Check whether the layout parameters are different from the most
@@ -584,11 +609,6 @@
   if (view_state->parent()) {
     view_state->parent()->children_needing_layout().erase(view_state->key());
   }
-
-  // Clean up child's recorded state for the parent or tree.
-  if (view_state->wrapped_surface()) {
-    surface_manager_->DestroySurface(view_state->wrapped_surface().Pass());
-  }
 }
 
 void ViewRegistry::SendChildUnavailable(ViewState* parent_state,
@@ -617,8 +637,7 @@
   DCHECK(view_state->pending_layout_requests().front()->issued());
 
   // TODO: Detect ANRs
-  DVLOG(1) << "SendViewLayoutRequest: view.token="
-           << view_state->view_token_value();
+  DVLOG(1) << "SendViewLayoutRequest: view.token=" << view_state->label();
   view_state->view()->OnLayout(
       view_state->pending_layout_requests().front()->layout_params()->Clone(),
       mojo::Array<uint32_t>::From(view_state->children_needing_layout()),
@@ -647,9 +666,8 @@
 }
 
 void ViewRegistry::OnViewLayoutResult(base::WeakPtr<ViewState> view_state_weak,
-                                      mojo::ui::ViewLayoutInfoPtr info) {
-  DCHECK(info);
-  DCHECK(info->surface_id);  // checked by mojom
+                                      mojo::ui::ViewLayoutResultPtr result) {
+  DCHECK(result);
 
   ViewState* view_state = view_state_weak.get();
   if (!view_state)
@@ -664,16 +682,15 @@
       view_state->pending_layout_requests().begin());
 
   DVLOG(1) << "OnViewLayoutResult: view=" << view_state
-           << ", params=" << request->layout_params()
-           << ", info=" << info.get();
+           << ", params=" << request->layout_params() << ", result=" << result;
 
   // Validate the layout info.
   if (!IsSizeInBounds(request->layout_params()->constraints.get(),
-                      info->size.get())) {
+                      result->size.get())) {
     LOG(ERROR) << "View returned invalid size in its layout info: "
                << "view=" << view_state
                << ", params=" << request->layout_params()
-               << ", info=" << info.get();
+               << ", result=" << result;
     UnregisterView(view_state);
     return;
   }
@@ -681,26 +698,21 @@
   // Assume the parent or root will not see the new layout information if
   // there are no callbacks so we need to inform it when things change.
   const bool size_changed =
-      !view_state->layout_info() ||
-      !view_state->layout_info()->size->Equals(*info->size);
-  const bool surface_changed =
-      !view_state->layout_info() ||
-      !view_state->layout_info()->surface_id->Equals(*info->surface_id);
+      !view_state->layout_result() ||
+      !view_state->layout_result()->size->Equals(*result->size);
   const bool recurse =
-      !request->has_callbacks() && (surface_changed || size_changed);
+      !request->has_callbacks() &&
+      (size_changed || view_state->scene_changed_since_last_report());
 
-  view_state->layout_params() = request->TakeLayoutParams().Pass();
-  view_state->layout_info() = info.Pass();
+  view_state->set_layout_params(request->TakeLayoutParams().Pass());
+  view_state->set_layout_result(result.Pass());
 
-  if (surface_changed) {
-    if (view_state->wrapped_surface())
-      surface_manager_->DestroySurface(view_state->wrapped_surface().Pass());
-    view_state->wrapped_surface() = surface_manager_->CreateWrappedSurface(
-        view_state->layout_info()->surface_id.get());
+  mojo::ui::ViewLayoutInfoPtr info = view_state->CreateLayoutInfo();
+  if (info) {
+    view_state->set_scene_changed_since_last_report(false);
+    request->DispatchLayoutInfo(info.Pass());
   }
 
-  request->DispatchLayoutInfo(view_state->layout_info().get());
-
   if (recurse) {
     if (view_state->parent()) {
       InvalidateLayoutForChild(view_state->parent(), view_state->key());
diff --git a/services/ui/view_manager/view_registry.h b/services/ui/view_manager/view_registry.h
index e4585d6..c056209 100644
--- a/services/ui/view_manager/view_registry.h
+++ b/services/ui/view_manager/view_registry.h
@@ -6,40 +6,58 @@
 #define SERVICES_UI_VIEW_MANAGER_VIEW_REGISTRY_H_
 
 #include <unordered_map>
-#include <vector>
 
 #include "base/macros.h"
+#include "mojo/services/gfx/composition/interfaces/compositor.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_associates.mojom.h"
 #include "mojo/services/ui/views/interfaces/view_trees.mojom.h"
 #include "mojo/services/ui/views/interfaces/views.mojom.h"
+#include "services/ui/view_manager/view_associate_table.h"
 #include "services/ui/view_manager/view_layout_request.h"
 #include "services/ui/view_manager/view_state.h"
 #include "services/ui/view_manager/view_tree_state.h"
 
 namespace view_manager {
 
-class SurfaceManager;
-
 // Maintains a registry of the state of all views.
 // All ViewState objects are owned by the registry.
-class ViewRegistry {
+class ViewRegistry : public mojo::ui::ViewInspector {
  public:
-  explicit ViewRegistry(SurfaceManager* surface_manager);
-  ~ViewRegistry();
+  using AssociateConnectionErrorCallback =
+      ViewAssociateTable::AssociateConnectionErrorCallback;
+
+  explicit ViewRegistry(mojo::gfx::composition::CompositorPtr compositor);
+  ~ViewRegistry() override;
+
+  // Begins connecting to the view associates.
+  // Invokes |connection_error_callback| if an associate connection fails
+  // and provides the associate's url.
+  void ConnectAssociates(
+      mojo::ApplicationImpl* app_impl,
+      const std::vector<std::string>& urls,
+      const AssociateConnectionErrorCallback& connection_error_callback);
 
   // VIEW MANAGER REQUESTS
 
   // Registers a view and returns its ViewToken.
   mojo::ui::ViewTokenPtr RegisterView(
       mojo::ui::ViewPtr view,
-      mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request);
+      mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request,
+      const mojo::String& label);
 
   // Registers a view tree.
-  void RegisterViewTree(
+  mojo::ui::ViewTreeTokenPtr RegisterViewTree(
       mojo::ui::ViewTreePtr view_tree,
-      mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request);
+      mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request,
+      const mojo::String& label);
 
   // VIEW HOST REQUESTS
 
+  // Creates a scene for the view, replacing its current scene.
+  // Destroys |view_state| if an error occurs.
+  void CreateScene(ViewState* view_state,
+                   mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene);
+
   // Requests layout.
   // Destroys |view_state| if an error occurs.
   void RequestLayout(ViewState* view_state);
@@ -61,6 +79,12 @@
                    mojo::ui::ViewLayoutParamsPtr child_layout_params,
                    const ViewLayoutCallback& callback);
 
+  // Connects to a view service.
+  // Destroys |view_state| if an error occurs.
+  void ConnectToViewService(ViewState* view_state,
+                            const mojo::String& service_name,
+                            mojo::ScopedMessagePipeHandle client_handle);
+
   // VIEW TREE HOST REQUESTS
 
   // Requests layout.
@@ -83,6 +107,12 @@
                   mojo::ui::ViewLayoutParamsPtr root_layout_params,
                   const ViewLayoutCallback& callback);
 
+  // Connects to a view service.
+  // Destroys |view_state| if an error occurs.
+  void ConnectToViewTreeService(ViewTreeState* tree_state,
+                                const mojo::String& service_name,
+                                mojo::ScopedMessagePipeHandle client_handle);
+
  private:
   // LIFETIME
 
@@ -93,7 +123,7 @@
 
   // TREE MANIPULATION
 
-  ViewState* FindView(uint32_t view_token);
+  ViewState* FindView(uint32_t view_token_value);
   void LinkChild(ViewState* parent_state,
                  uint32_t child_key,
                  ViewState* child_state);
@@ -101,6 +131,8 @@
   void MarkChildAsUnavailable(ViewState* parent_state, uint32_t child_key);
   void UnlinkChild(ViewState* parent_state,
                    ViewState::ChildrenMap::iterator child_it);
+
+  ViewTreeState* FindViewTree(uint32_t view_tree_token_value);
   void LinkRoot(ViewTreeState* tree_state,
                 ViewState* root_state,
                 uint32_t root_key);
@@ -124,6 +156,11 @@
   void IssueNextViewLayoutRequest(ViewState* view_state);
   void IssueNextViewTreeLayoutRequest(ViewTreeState* tree_state);
 
+  // SCENE MANAGEMENT
+
+  void OnSceneCreated(base::WeakPtr<ViewState> view_state_weak,
+                      mojo::gfx::composition::SceneTokenPtr scene_token);
+
   // SIGNALING
 
   void SendChildUnavailable(ViewState* parent_state, uint32_t child_key);
@@ -131,25 +168,24 @@
   void SendViewLayoutRequest(ViewState* view_state);
   void SendViewTreeLayoutRequest(ViewTreeState* tree_state);
   void OnViewLayoutResult(base::WeakPtr<ViewState> view_state_weak,
-                          mojo::ui::ViewLayoutInfoPtr info);
+                          mojo::ui::ViewLayoutResultPtr result);
   void OnViewTreeLayoutResult(base::WeakPtr<ViewTreeState> tree_state_weak);
 
   bool IsViewStateRegisteredDebug(ViewState* view_state) {
-    return view_state && FindView(view_state->view_token_value());
+    return view_state && FindView(view_state->view_token()->value);
   }
 
   bool IsViewTreeStateRegisteredDebug(ViewTreeState* tree_state) {
-    return tree_state && std::any_of(view_trees_.begin(), view_trees_.end(),
-                                     [tree_state](ViewTreeState* other) {
-                                       return tree_state == other;
-                                     });
+    return tree_state && FindViewTree(tree_state->view_tree_token()->value);
   }
 
-  SurfaceManager* surface_manager_;
+  mojo::gfx::composition::CompositorPtr compositor_;
+  ViewAssociateTable associate_table_;
 
-  uint32_t next_view_token_value_;
+  uint32_t next_view_token_value_ = 1u;
+  uint32_t next_view_tree_token_value_ = 1u;
   std::unordered_map<uint32_t, ViewState*> views_by_token_;
-  std::vector<ViewTreeState*> view_trees_;
+  std::unordered_map<uint32_t, ViewTreeState*> view_trees_by_token_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewRegistry);
 };
diff --git a/services/ui/view_manager/view_state.cc b/services/ui/view_manager/view_state.cc
index cfccc56..643f6f5 100644
--- a/services/ui/view_manager/view_state.cc
+++ b/services/ui/view_manager/view_state.cc
@@ -2,20 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/logging.h"
 #include "services/ui/view_manager/view_state.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "services/ui/view_manager/view_tree_state.h"
 
 namespace view_manager {
 
-ViewState::ViewState(mojo::ui::ViewPtr view, uint32_t view_token_value)
+ViewState::ViewState(mojo::ui::ViewPtr view,
+                     mojo::ui::ViewTokenPtr view_token,
+                     const std::string& label)
     : view_(view.Pass()),
-      view_token_value_(view_token_value),
-      tree_(nullptr),
-      parent_(nullptr),
-      key_(0),
+      view_token_(view_token.Pass()),
+      label_(label),
       weak_factory_(this) {
   DCHECK(view_);
+  DCHECK(view_token_);
 }
 
 ViewState::~ViewState() {}
@@ -49,7 +52,30 @@
   SetTreeUnchecked(nullptr);
 }
 
-void ViewState::GetServiceProvider(
-    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) {}
+mojo::ui::ViewLayoutInfoPtr ViewState::CreateLayoutInfo() {
+  if (!layout_result_ || !scene_token_)
+    return nullptr;
+
+  auto info = mojo::ui::ViewLayoutInfo::New();
+  info->size = layout_result_->size.Clone();
+  info->scene_token = scene_token_.Clone();
+  return info;
+}
+
+const std::string& ViewState::FormattedLabel() {
+  if (formatted_label_cache_.empty()) {
+    formatted_label_cache_ =
+        label_.empty()
+            ? base::StringPrintf("<%d>", view_token_->value)
+            : base::StringPrintf("<%d:%s>", view_token_->value, label_.c_str());
+  }
+  return formatted_label_cache_;
+}
+
+std::ostream& operator<<(std::ostream& os, ViewState* view_state) {
+  if (!view_state)
+    return os << "null";
+  return os << view_state->FormattedLabel();
+}
 
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_state.h b/services/ui/view_manager/view_state.h
index 1f1d2a8..2919f91 100644
--- a/services/ui/view_manager/view_state.h
+++ b/services/ui/view_manager/view_state.h
@@ -7,11 +7,13 @@
 
 #include <memory>
 #include <set>
+#include <string>
 #include <unordered_map>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "mojo/services/ui/views/cpp/formatting.h"
 #include "mojo/services/ui/views/interfaces/views.mojom.h"
 #include "services/ui/view_manager/view_layout_request.h"
 
@@ -25,7 +27,9 @@
  public:
   using ChildrenMap = std::unordered_map<uint32_t, ViewState*>;
 
-  ViewState(mojo::ui::ViewPtr view, uint32_t view_token_value);
+  ViewState(mojo::ui::ViewPtr view,
+            mojo::ui::ViewTokenPtr view_token,
+            const std::string& label);
   ~ViewState();
 
   base::WeakPtr<ViewState> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
@@ -34,8 +38,9 @@
   // Caller does not obtain ownership of the view.
   mojo::ui::View* view() const { return view_.get(); }
 
-  // Gets the view token value used to refer to this view globally.
-  uint32_t view_token_value() const { return view_token_value_; }
+  // Gets the token used to refer to this view globally.
+  // Caller does not obtain ownership of the token.
+  mojo::ui::ViewToken* view_token() const { return view_token_.get(); }
 
   // Sets the associated host implementation and takes ownership of it.
   void set_view_host(mojo::ui::ViewHost* host) { view_host_.reset(host); }
@@ -66,10 +71,6 @@
   // Resets the parent view state and tree pointers to null.
   void ResetContainer();
 
-  // Gets the view's service provider.
-  void GetServiceProvider(
-      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider);
-
   // The map of children, indexed by child key.
   // Child view state may be null if the child with the given key has
   // become unavailable but not yet removed.
@@ -88,41 +89,70 @@
 
   // The layout parameters most recently processed by the view,
   // or null if none.  These parameters are preserved across reparenting.
-  mojo::ui::ViewLayoutParamsPtr& layout_params() { return layout_params_; }
+  mojo::ui::ViewLayoutParams* layout_params() { return layout_params_.get(); }
+  void set_layout_params(mojo::ui::ViewLayoutParamsPtr layout_params) {
+    layout_params_ = layout_params.Pass();
+  }
 
-  // The layout information most recently provided by the view in
+  // The layout result most recently provided by the view in
   // response to the value of |layout_params|, or null if none.  These
   // results are preserved across reparenting.
-  mojo::ui::ViewLayoutInfoPtr& layout_info() { return layout_info_; }
+  mojo::ui::ViewLayoutResult* layout_result() { return layout_result_.get(); }
+  void set_layout_result(mojo::ui::ViewLayoutResultPtr layout_result) {
+    layout_result_ = layout_result.Pass();
+  }
 
-  // The id of the Surface which the view manager itself created to wrap the
-  // view's own Surface, or null if none.  The wrapped Surface is destroyed
-  // when the view is reparented so that the old parent can no longer embed
-  // the view's actual content.
-  mojo::SurfaceIdPtr& wrapped_surface() { return wrapped_surface_; }
+  // The current scene token, or null if none.
+  mojo::gfx::composition::SceneToken* scene_token() {
+    return scene_token_.get();
+  }
+  void set_scene_token(mojo::gfx::composition::SceneTokenPtr scene_token) {
+    scene_token_ = scene_token.Pass();
+  }
+
+  // True if the scene changed since the last time the layout was reported
+  // to the parent or tree.
+  bool scene_changed_since_last_report() {
+    return scene_changed_since_last_report_;
+  }
+  void set_scene_changed_since_last_report(bool value) {
+    scene_changed_since_last_report_ = value;
+  }
+
+  // Creates layout information to return to the parent or tree.
+  // Returns null if unavailable.
+  mojo::ui::ViewLayoutInfoPtr CreateLayoutInfo();
+
+  const std::string& label() { return label_; }
+  const std::string& FormattedLabel();
 
  private:
   void SetTreeUnchecked(ViewTreeState* tree);
 
   mojo::ui::ViewPtr view_;
-  const uint32_t view_token_value_;
+  mojo::ui::ViewTokenPtr view_token_;
+  const std::string label_;
+  std::string formatted_label_cache_;
 
   std::unique_ptr<mojo::ui::ViewHost> view_host_;
-  ViewTreeState* tree_;
-  ViewState* parent_;
-  uint32_t key_;
+  ViewTreeState* tree_ = nullptr;
+  ViewState* parent_ = nullptr;
+  uint32_t key_ = 0u;
   ChildrenMap children_;
   std::set<uint32_t> children_needing_layout_;
   std::vector<std::unique_ptr<ViewLayoutRequest>> pending_layout_requests_;
   mojo::ui::ViewLayoutParamsPtr layout_params_;
-  mojo::ui::ViewLayoutInfoPtr layout_info_;
-  mojo::SurfaceIdPtr wrapped_surface_;
+  mojo::ui::ViewLayoutResultPtr layout_result_;
+  mojo::gfx::composition::SceneTokenPtr scene_token_;
+  bool scene_changed_since_last_report_ = false;
 
   base::WeakPtrFactory<ViewState> weak_factory_;  // must be last
 
   DISALLOW_COPY_AND_ASSIGN(ViewState);
 };
 
+std::ostream& operator<<(std::ostream& os, ViewState* view_state);
+
 }  // namespace view_manager
 
 #endif  // SERVICES_UI_VIEW_MANAGER_VIEW_STATE_H_
diff --git a/services/ui/view_manager/view_tree_host_impl.cc b/services/ui/view_manager/view_tree_host_impl.cc
index e970a69..654b411 100644
--- a/services/ui/view_manager/view_tree_host_impl.cc
+++ b/services/ui/view_manager/view_tree_host_impl.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/ui/view_manager/view_tree_host_impl.h"
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "services/ui/view_manager/view_tree_host_impl.h"
 
 namespace view_manager {
 
@@ -18,6 +19,11 @@
 
 ViewTreeHostImpl::~ViewTreeHostImpl() {}
 
+void ViewTreeHostImpl::GetServiceProvider(
+    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) {
+  service_provider_bindings_.AddBinding(this, service_provider.Pass());
+}
+
 void ViewTreeHostImpl::RequestLayout() {
   registry_->RequestLayout(state_);
 }
@@ -44,4 +50,11 @@
                         base::Bind(&RunLayoutRootCallback, callback));
 }
 
+void ViewTreeHostImpl::ConnectToService(
+    const mojo::String& service_name,
+    mojo::ScopedMessagePipeHandle client_handle) {
+  registry_->ConnectToViewTreeService(state_, service_name,
+                                      client_handle.Pass());
+}
+
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_tree_host_impl.h b/services/ui/view_manager/view_tree_host_impl.h
index 9677d81..017ee88 100644
--- a/services/ui/view_manager/view_tree_host_impl.h
+++ b/services/ui/view_manager/view_tree_host_impl.h
@@ -6,6 +6,7 @@
 #define SERVICES_UI_VIEW_MANAGER_VIEW_TREE_HOST_IMPL_H_
 
 #include "base/macros.h"
+#include "mojo/common/binding_set.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/services/ui/views/interfaces/view_trees.mojom.h"
 #include "services/ui/view_manager/view_registry.h"
@@ -15,7 +16,8 @@
 
 // ViewTreeHost interface implementation.
 // This object is owned by its associated ViewTreeState.
-class ViewTreeHostImpl : public mojo::ui::ViewTreeHost {
+class ViewTreeHostImpl : public mojo::ui::ViewTreeHost,
+                         public mojo::ServiceProvider {
  public:
   ViewTreeHostImpl(
       ViewRegistry* registry,
@@ -30,6 +32,8 @@
 
  private:
   // |ViewTreeHost|:
+  void GetServiceProvider(
+      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) override;
   void RequestLayout() override;
   void SetRoot(uint32_t root_key,
                mojo::ui::ViewTokenPtr root_view_token) override;
@@ -37,9 +41,14 @@
   void LayoutRoot(mojo::ui::ViewLayoutParamsPtr root_layout_params,
                   const LayoutRootCallback& callback) override;
 
+  // |ServiceProvider|:
+  void ConnectToService(const mojo::String& service_name,
+                        mojo::ScopedMessagePipeHandle client_handle) override;
+
   ViewRegistry* const registry_;
   ViewTreeState* const state_;
   mojo::Binding<mojo::ui::ViewTreeHost> binding_;
+  mojo::BindingSet<mojo::ServiceProvider> service_provider_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewTreeHostImpl);
 };
diff --git a/services/ui/view_manager/view_tree_state.cc b/services/ui/view_manager/view_tree_state.cc
index e236c08..4a69f2c 100644
--- a/services/ui/view_manager/view_tree_state.cc
+++ b/services/ui/view_manager/view_tree_state.cc
@@ -2,19 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/logging.h"
 #include "services/ui/view_manager/view_tree_state.h"
 
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+
 namespace view_manager {
 
-ViewTreeState::ViewTreeState(mojo::ui::ViewTreePtr view_tree)
+ViewTreeState::ViewTreeState(mojo::ui::ViewTreePtr view_tree,
+                             mojo::ui::ViewTreeTokenPtr view_tree_token,
+                             const std::string& label)
     : view_tree_(view_tree.Pass()),
-      root_(nullptr),
-      explicit_root_(false),
-      layout_request_pending_(false),
-      layout_request_issued_(false),
+      view_tree_token_(view_tree_token.Pass()),
+      label_(label),
       weak_factory_(this) {
   DCHECK(view_tree_);
+  DCHECK(view_tree_token_);
 }
 
 ViewTreeState::~ViewTreeState() {}
@@ -35,4 +38,20 @@
   root_ = nullptr;
 }
 
+const std::string& ViewTreeState::FormattedLabel() {
+  if (formatted_label_cache_.empty()) {
+    formatted_label_cache_ =
+        label_.empty() ? base::StringPrintf("<%d>", view_tree_token_->value)
+                       : base::StringPrintf("<%d:%s>", view_tree_token_->value,
+                                            label_.c_str());
+  }
+  return formatted_label_cache_;
+}
+
+std::ostream& operator<<(std::ostream& os, ViewTreeState* view_tree_state) {
+  if (!view_tree_state)
+    return os << "null";
+  return os << view_tree_state->FormattedLabel();
+}
+
 }  // namespace view_manager
diff --git a/services/ui/view_manager/view_tree_state.h b/services/ui/view_manager/view_tree_state.h
index abcbad9..a6ea845 100644
--- a/services/ui/view_manager/view_tree_state.h
+++ b/services/ui/view_manager/view_tree_state.h
@@ -7,12 +7,15 @@
 
 #include <memory>
 #include <set>
+#include <string>
 #include <unordered_map>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "mojo/common/binding_set.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/ui/views/cpp/formatting.h"
 #include "mojo/services/ui/views/interfaces/view_trees.mojom.h"
 #include "services/ui/view_manager/view_state.h"
 
@@ -22,7 +25,9 @@
 // This object is owned by the ViewRegistry that created it.
 class ViewTreeState {
  public:
-  explicit ViewTreeState(mojo::ui::ViewTreePtr view_tree);
+  explicit ViewTreeState(mojo::ui::ViewTreePtr view_tree,
+                         mojo::ui::ViewTreeTokenPtr view_tree_token,
+                         const std::string& label);
   ~ViewTreeState();
 
   base::WeakPtr<ViewTreeState> GetWeakPtr() {
@@ -33,6 +38,12 @@
   // Caller does not obtain ownership of the view.
   mojo::ui::ViewTree* view_tree() const { return view_tree_.get(); }
 
+  // Gets the token used to refer to this view tree globally.
+  // Caller does not obtain ownership of the token.
+  mojo::ui::ViewTreeToken* view_tree_token() const {
+    return view_tree_token_.get();
+  }
+
   // Sets the associated host implementation and takes ownership of it.
   void set_view_tree_host(mojo::ui::ViewTreeHost* host) {
     view_tree_host_.reset(host);
@@ -68,20 +79,28 @@
   bool layout_request_issued() const { return layout_request_issued_; }
   void set_layout_request_issued(bool value) { layout_request_issued_ = value; }
 
+  const std::string& label() { return label_; }
+  const std::string& FormattedLabel();
+
  private:
   mojo::ui::ViewTreePtr view_tree_;
+  mojo::ui::ViewTreeTokenPtr view_tree_token_;
+  const std::string label_;
+  std::string formatted_label_cache_;
 
   std::unique_ptr<mojo::ui::ViewTreeHost> view_tree_host_;
-  ViewState* root_;
-  bool explicit_root_;
-  bool layout_request_pending_;
-  bool layout_request_issued_;
+  ViewState* root_ = nullptr;
+  bool explicit_root_ = false;
+  bool layout_request_pending_ = false;
+  bool layout_request_issued_ = false;
 
   base::WeakPtrFactory<ViewTreeState> weak_factory_;  // must be last
 
   DISALLOW_COPY_AND_ASSIGN(ViewTreeState);
 };
 
+std::ostream& operator<<(std::ostream& os, ViewTreeState* view_tree_state);
+
 }  // namespace view_manager
 
 #endif  // SERVICES_UI_VIEW_MANAGER_VIEW_TREE_STATE_H_