Revert "Delete the ViewManager and WindowManager services."

This reverts commit d03eeb6d9f59ffd4e44ea890877837d69536cc1e.

Reverting unreviewed changes.

TBR=jeffbrown@google.com

Review URL: https://codereview.chromium.org/1536713004 .
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index ddc4b93..44ca966 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -17,7 +17,9 @@
     "//examples/dart",
     "//examples/echo",
     "//examples/echo_terminal",
+    "//examples/embedded_app",
     "//examples/forwarding_content_handler",
+    "//examples/ganesh_app",
     "//examples/http_handler",
     "//examples/indirect_service",
     "//examples/native_run_app",
diff --git a/examples/embedded_app/BUILD.gn b/examples/embedded_app/BUILD.gn
new file mode 100644
index 0000000..64d667f
--- /dev/null
+++ b/examples/embedded_app/BUILD.gn
@@ -0,0 +1,24 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+
+mojo_native_application("embedded_app") {
+  sources = [
+    "embedded_app.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//examples/bitmap_uploader",
+    "//mojo/application",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/navigation/interfaces",
+    "//mojo/services/view_manager/cpp",
+    "//skia:skia",
+    "//url",
+  ]
+}
diff --git a/examples/embedded_app/embedded_app.cc b/examples/embedded_app/embedded_app.cc
new file mode 100644
index 0000000..e8e2bf4
--- /dev/null
+++ b/examples/embedded_app/embedded_app.cc
@@ -0,0 +1,118 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "examples/bitmap_uploader/bitmap_uploader.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/navigation/interfaces/navigation.mojom.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "mojo/services/view_manager/cpp/view_manager.h"
+#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace examples {
+
+const SkColor kColors[] = {SK_ColorYELLOW, SK_ColorRED, SK_ColorGREEN,
+                           SK_ColorMAGENTA};
+
+struct Window {
+  Window(View* root, ServiceProviderPtr embedder_service_provider, Shell* shell)
+      : root(root),
+        embedder_service_provider(embedder_service_provider.Pass()),
+        bitmap_uploader(root) {
+    bitmap_uploader.Init(shell);
+  }
+
+  View* root;
+  ServiceProviderPtr embedder_service_provider;
+  BitmapUploader bitmap_uploader;
+};
+
+class EmbeddedApp
+    : public ApplicationDelegate,
+      public ViewManagerDelegate,
+      public ViewObserver {
+ public:
+  EmbeddedApp() : shell_(nullptr) { url::AddStandardScheme("mojo"); }
+  ~EmbeddedApp() override {}
+
+ private:
+
+  // Overridden from ApplicationDelegate:
+  void Initialize(ApplicationImpl* app) override {
+    shell_ = app->shell();
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  // Overridden from ViewManagerDelegate:
+  void OnEmbed(View* root,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services) override {
+    root->AddObserver(this);
+    Window* window = new Window(root, exposed_services.Pass(), shell_);
+    windows_[root->id()] = window;
+    window->bitmap_uploader.SetColor(
+        kColors[next_color_++ % arraysize(kColors)]);
+  }
+  void OnViewManagerDisconnected(ViewManager* view_manager) override {
+    base::MessageLoop::current()->Quit();
+  }
+
+  // Overridden from ViewObserver:
+  void OnViewDestroyed(View* view) override {
+    DCHECK(windows_.find(view->id()) != windows_.end());
+    windows_.erase(view->id());
+  }
+  void OnViewInputEvent(View* view, const EventPtr& event) override {
+    if (event->action == EventType::POINTER_UP &&
+        (static_cast<uint32_t>(event->flags) &
+         static_cast<uint32_t>(EventFlags::LEFT_MOUSE_BUTTON))) {
+      URLRequestPtr request(URLRequest::New());
+      request->url = "http://www.aaronboodman.com/z_dropbox/test.html";
+      NavigatorHostPtr navigator_host;
+      ConnectToService(windows_[view->id()]->embedder_service_provider.get(),
+                       &navigator_host);
+      navigator_host->RequestNavigate(Target::SOURCE_NODE, request.Pass());
+    }
+  }
+
+  Shell* shell_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  typedef std::map<Id, Window*> WindowMap;
+  WindowMap windows_;
+
+  int next_color_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddedApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::EmbeddedApp);
+  return runner.Run(application_request);
+}
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 4df674a..5302355 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -69,6 +69,7 @@
     "//mojo/public/cpp/bindings/tests:versioning_apptests",
     "//mojo/services/files/c:apptests",
     "//mojo/services/files/cpp:files_impl_apptests",
+    "//mojo/services/view_manager/cpp/tests:mojo_view_manager_lib_unittests",
     "//mojo/tools:message_generator",
   ]
 
diff --git a/mojo/dart/packages/mojo_services/BUILD.gn b/mojo/dart/packages/mojo_services/BUILD.gn
index 35caff7..2a3b602 100644
--- a/mojo/dart/packages/mojo_services/BUILD.gn
+++ b/mojo/dart/packages/mojo_services/BUILD.gn
@@ -6,80 +6,83 @@
 import("//mojo/services/mojo_services.gni")
 
 # TODO(johnmccutchan): Automate this.
-# for i in `find lib/|grep mojom.dart|sort`;do echo "  \"$i\",";done
+# for i in `find lib/|grep mojom.dart`;do echo \"$i\",;done
 bindings_sources = [
-  "lib/activity/activity.mojom.dart",
-  "lib/authentication/authentication.mojom.dart",
-  "lib/contacts/contacts.mojom.dart",
-  "lib/http_server/http_request.mojom.dart",
-  "lib/http_server/http_response.mojom.dart",
-  "lib/http_server/http_server_factory.mojom.dart",
-  "lib/http_server/http_server.mojom.dart",
-  "lib/icu_data/icu_data.mojom.dart",
-  "lib/input/input.mojom.dart",
-  "lib/keyboard/keyboard.mojom.dart",
-  "lib/mojo/asset_bundle/asset_bundle.mojom.dart",
-  "lib/mojo/authenticating_url_loader_interceptor_meta_factory.mojom.dart",
-  "lib/mojo/camera.mojom.dart",
-  "lib/mojo/clipboard.mojom.dart",
-  "lib/mojo/command_buffer.mojom.dart",
-  "lib/mojo/content_handler.mojom.dart",
-  "lib/mojo/context_provider.mojom.dart",
-  "lib/mojo/cookie_store.mojom.dart",
-  "lib/mojo/device_info.mojom.dart",
-  "lib/mojo/display.mojom.dart",
-  "lib/mojo/files/directory.mojom.dart",
-  "lib/mojo/files/file.mojom.dart",
-  "lib/mojo/files/files.mojom.dart",
-  "lib/mojo/files/ioctl.mojom.dart",
-  "lib/mojo/files/ioctl_terminal.mojom.dart",
-  "lib/mojo/files/types.mojom.dart",
-  "lib/mojo/geocoder.mojom.dart",
-  "lib/mojo/geometry.mojom.dart",
-  "lib/mojo/gpu_capabilities.mojom.dart",
   "lib/mojo/gpu.mojom.dart",
-  "lib/mojo/host_resolver.mojom.dart",
-  "lib/mojo/http_connection.mojom.dart",
-  "lib/mojo/http_message.mojom.dart",
-  "lib/mojo/http_server.mojom.dart",
-  "lib/mojo/input_event_constants.mojom.dart",
-  "lib/mojo/input_events.mojom.dart",
-  "lib/mojo/input_key_codes.mojom.dart",
-  "lib/mojo/location.mojom.dart",
-  "lib/mojo/location_service.mojom.dart",
-  "lib/mojo/native_viewport.mojom.dart",
-  "lib/mojo/navigation.mojom.dart",
-  "lib/mojo/net_address.mojom.dart",
-  "lib/mojo/network_service.mojom.dart",
+  "lib/mojo/clipboard.mojom.dart",
+  "lib/mojo/view_manager_constants.mojom.dart",
   "lib/mojo/quads.mojom.dart",
-  "lib/mojo/service_registry.mojom.dart",
-  "lib/mojo/sharing.mojom.dart",
-  "lib/mojo/surface_id.mojom.dart",
-  "lib/mojo/surfaces.mojom.dart",
-  "lib/mojo/tcp_bound_socket.mojom.dart",
-  "lib/mojo/tcp_connected_socket.mojom.dart",
-  "lib/mojo/tcp_server_socket.mojom.dart",
-  "lib/mojo/terminal/terminal_client.mojom.dart",
-  "lib/mojo/terminal/terminal.mojom.dart",
-  "lib/mojo/udp_socket.mojom.dart",
-  "lib/mojo/ui/layouts.mojom.dart",
-  "lib/mojo/ui/view_manager.mojom.dart",
+  "lib/mojo/navigation.mojom.dart",
+  "lib/mojo/display.mojom.dart",
+  "lib/mojo/authenticating_url_loader_interceptor_meta_factory.mojom.dart",
+  "lib/mojo/window_manager_internal.mojom.dart",
+  "lib/mojo/url_loader.mojom.dart",
+  "lib/mojo/content_handler.mojom.dart",
+  "lib/mojo/window_manager.mojom.dart",
+  "lib/mojo/geocoder.mojom.dart",
+  "lib/mojo/host_resolver.mojom.dart",
+  "lib/mojo/command_buffer.mojom.dart",
+  "lib/mojo/http_server.mojom.dart",
+  "lib/mojo/files/file.mojom.dart",
+  "lib/mojo/files/ioctl_terminal.mojom.dart",
+  "lib/mojo/files/directory.mojom.dart",
+  "lib/mojo/files/files.mojom.dart",
+  "lib/mojo/files/types.mojom.dart",
+  "lib/mojo/files/ioctl.mojom.dart",
+  "lib/mojo/asset_bundle/asset_bundle.mojom.dart",
+  "lib/mojo/net_address.mojom.dart",
   "lib/mojo/ui/view_provider.mojom.dart",
   "lib/mojo/ui/views.mojom.dart",
   "lib/mojo/ui/view_trees.mojom.dart",
-  "lib/mojo/url_loader_interceptor.mojom.dart",
-  "lib/mojo/url_loader.mojom.dart",
-  "lib/mojo/url_response_disk_cache.mojom.dart",
-  "lib/mojo/viewport_parameter_listener.mojom.dart",
+  "lib/mojo/ui/layouts.mojom.dart",
+  "lib/mojo/ui/view_manager.mojom.dart",
   "lib/mojo/web_socket.mojom.dart",
-  "lib/native_support/process.mojom.dart",
-  "lib/nfc/nfc.mojom.dart",
-  "lib/notifications/notifications.mojom.dart",
-  "lib/prediction/prediction.mojom.dart",
-  "lib/sensors/sensors.mojom.dart",
-  "lib/speech_recognizer/speech_recognizer.mojom.dart",
+  "lib/mojo/url_loader_interceptor.mojom.dart",
+  "lib/mojo/viewport_parameter_listener.mojom.dart",
+  "lib/mojo/location.mojom.dart",
+  "lib/mojo/service_registry.mojom.dart",
+  "lib/mojo/network_service.mojom.dart",
+  "lib/mojo/http_message.mojom.dart",
+  "lib/mojo/cookie_store.mojom.dart",
+  "lib/mojo/input_events.mojom.dart",
+  "lib/mojo/udp_socket.mojom.dart",
+  "lib/mojo/http_connection.mojom.dart",
+  "lib/mojo/native_viewport.mojom.dart",
+  "lib/mojo/tcp_connected_socket.mojom.dart",
+  "lib/mojo/input_key_codes.mojom.dart",
+  "lib/mojo/sharing.mojom.dart",
+  "lib/mojo/gpu_capabilities.mojom.dart",
+  "lib/mojo/context_provider.mojom.dart",
+  "lib/mojo/animations.mojom.dart",
+  "lib/mojo/surface_id.mojom.dart",
+  "lib/mojo/tcp_server_socket.mojom.dart",
+  "lib/mojo/view_manager.mojom.dart",
+  "lib/mojo/url_response_disk_cache.mojom.dart",
+  "lib/mojo/terminal/terminal.mojom.dart",
+  "lib/mojo/terminal/terminal_client.mojom.dart",
+  "lib/mojo/geometry.mojom.dart",
+  "lib/mojo/tcp_bound_socket.mojom.dart",
+  "lib/mojo/input_event_constants.mojom.dart",
+  "lib/mojo/surfaces.mojom.dart",
+  "lib/mojo/device_info.mojom.dart",
+  "lib/mojo/camera.mojom.dart",
+  "lib/mojo/location_service.mojom.dart",
+  "lib/contacts/contacts.mojom.dart",
+  "lib/authentication/authentication.mojom.dart",
   "lib/tracing/tracing.mojom.dart",
-  "lib/vsync/vsync.mojom.dart",
+  "lib/input/input.mojom.dart",
+  "lib/prediction/prediction.mojom.dart",
+  "lib/nfc/nfc.mojom.dart",
+  "lib/native_support/process.mojom.dart",
+  "lib/speech_recognizer/speech_recognizer.mojom.dart",
+  "lib/http_server/http_server.mojom.dart",
+  "lib/http_server/http_request.mojom.dart",
+  "lib/http_server/http_server_factory.mojom.dart",
+  "lib/http_server/http_response.mojom.dart",
+  "lib/keyboard/keyboard.mojom.dart",
+  "lib/notifications/notifications.mojom.dart",
+  "lib/icu_data/icu_data.mojom.dart",
+  "lib/sensors/sensors.mojom.dart",
 ]
 
 dart_pkg("mojo_services") {
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/animations.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/animations.mojom.dart
new file mode 100644
index 0000000..4040c28
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/animations.mojom.dart
@@ -0,0 +1,511 @@
+// 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 animations_mojom;
+
+import 'dart:async';
+
+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;
+
+class AnimationTweenType extends bindings.MojoEnum {
+  static const AnimationTweenType linear = const AnimationTweenType._(0);
+  static const AnimationTweenType easeIn = const AnimationTweenType._(1);
+  static const AnimationTweenType easeOut = const AnimationTweenType._(2);
+  static const AnimationTweenType easeInOut = const AnimationTweenType._(3);
+
+  const AnimationTweenType._(int v) : super(v);
+
+  static const Map<String, AnimationTweenType> valuesMap = const {
+    "linear": linear,
+    "easeIn": easeIn,
+    "easeOut": easeOut,
+    "easeInOut": easeInOut,
+  };
+  static const List<AnimationTweenType> values = const [
+    linear,
+    easeIn,
+    easeOut,
+    easeInOut,
+  ];
+
+  static AnimationTweenType valueOf(String name) => valuesMap[name];
+
+  factory AnimationTweenType(int v) {
+    switch (v) {
+      case 0:
+        return linear;
+      case 1:
+        return easeIn;
+      case 2:
+        return easeOut;
+      case 3:
+        return easeInOut;
+      default:
+        return null;
+    }
+  }
+
+  static AnimationTweenType decode(bindings.Decoder decoder0, int offset) {
+    int v = decoder0.decodeUint32(offset);
+    AnimationTweenType result = new AnimationTweenType(v);
+    if (result == null) {
+      throw new bindings.MojoCodecError(
+          'Bad value $v for enum AnimationTweenType.');
+    }
+    return result;
+  }
+
+  String toString() {
+    switch(this) {
+      case linear:
+        return 'AnimationTweenType.linear';
+      case easeIn:
+        return 'AnimationTweenType.easeIn';
+      case easeOut:
+        return 'AnimationTweenType.easeOut';
+      case easeInOut:
+        return 'AnimationTweenType.easeInOut';
+    }
+  }
+
+  int toJson() => mojoEnumValue;
+}
+
+class AnimationProperty extends bindings.MojoEnum {
+  static const AnimationProperty none = const AnimationProperty._(0);
+  static const AnimationProperty opacity = const AnimationProperty._(1);
+  static const AnimationProperty transform = const AnimationProperty._(2);
+
+  const AnimationProperty._(int v) : super(v);
+
+  static const Map<String, AnimationProperty> valuesMap = const {
+    "none": none,
+    "opacity": opacity,
+    "transform": transform,
+  };
+  static const List<AnimationProperty> values = const [
+    none,
+    opacity,
+    transform,
+  ];
+
+  static AnimationProperty valueOf(String name) => valuesMap[name];
+
+  factory AnimationProperty(int v) {
+    switch (v) {
+      case 0:
+        return none;
+      case 1:
+        return opacity;
+      case 2:
+        return transform;
+      default:
+        return null;
+    }
+  }
+
+  static AnimationProperty decode(bindings.Decoder decoder0, int offset) {
+    int v = decoder0.decodeUint32(offset);
+    AnimationProperty result = new AnimationProperty(v);
+    if (result == null) {
+      throw new bindings.MojoCodecError(
+          'Bad value $v for enum AnimationProperty.');
+    }
+    return result;
+  }
+
+  String toString() {
+    switch(this) {
+      case none:
+        return 'AnimationProperty.none';
+      case opacity:
+        return 'AnimationProperty.opacity';
+      case transform:
+        return 'AnimationProperty.transform';
+    }
+  }
+
+  int toJson() => mojoEnumValue;
+}
+
+
+
+class AnimationValue extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  double floatValue = 0.0;
+  geometry_mojom.Transform transform = null;
+
+  AnimationValue() : super(kVersions.last.size);
+
+  static AnimationValue 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 AnimationValue decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AnimationValue result = new AnimationValue();
+
+    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.floatValue = decoder0.decodeFloat(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.transform = geometry_mojom.Transform.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeFloat(floatValue, 8);
+    
+    encoder0.encodeStruct(transform, 16, false);
+  }
+
+  String toString() {
+    return "AnimationValue("
+           "floatValue: $floatValue" ", "
+           "transform: $transform" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["floatValue"] = floatValue;
+    map["transform"] = transform;
+    return map;
+  }
+}
+
+
+class AnimationElement extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(40, 0)
+  ];
+  AnimationProperty property = null;
+  AnimationTweenType tweenType = null;
+  int duration = 0;
+  AnimationValue startValue = null;
+  AnimationValue targetValue = null;
+
+  AnimationElement() : super(kVersions.last.size);
+
+  static AnimationElement 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 AnimationElement decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AnimationElement result = new AnimationElement();
+
+    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.property = AnimationProperty.decode(decoder0, 8);
+        if (result.property == null) {
+          throw new bindings.MojoCodecError(
+            'Trying to decode null union for non-nullable AnimationProperty.');
+        }
+    }
+    if (mainDataHeader.version >= 0) {
+      
+        result.tweenType = AnimationTweenType.decode(decoder0, 12);
+        if (result.tweenType == null) {
+          throw new bindings.MojoCodecError(
+            'Trying to decode null union for non-nullable AnimationTweenType.');
+        }
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.duration = decoder0.decodeInt64(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(24, true);
+      result.startValue = AnimationValue.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(32, true);
+      result.targetValue = AnimationValue.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeEnum(property, 8);
+    
+    encoder0.encodeEnum(tweenType, 12);
+    
+    encoder0.encodeInt64(duration, 16);
+    
+    encoder0.encodeStruct(startValue, 24, true);
+    
+    encoder0.encodeStruct(targetValue, 32, true);
+  }
+
+  String toString() {
+    return "AnimationElement("
+           "property: $property" ", "
+           "tweenType: $tweenType" ", "
+           "duration: $duration" ", "
+           "startValue: $startValue" ", "
+           "targetValue: $targetValue" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["property"] = property;
+    map["tweenType"] = tweenType;
+    map["duration"] = duration;
+    map["startValue"] = startValue;
+    map["targetValue"] = targetValue;
+    return map;
+  }
+}
+
+
+class AnimationSequence extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int cycleCount = 0;
+  List<AnimationElement> elements = null;
+
+  AnimationSequence() : super(kVersions.last.size);
+
+  static AnimationSequence 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 AnimationSequence decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AnimationSequence result = new AnimationSequence();
+
+    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.cycleCount = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.elements = new List<AnimationElement>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+          result.elements[i1] = AnimationElement.decode(decoder2);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(cycleCount, 8);
+    
+    if (elements == null) {
+      encoder0.encodeNullPointer(16, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(elements.length, 16, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < elements.length; ++i0) {
+        
+        encoder1.encodeStruct(elements[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "AnimationSequence("
+           "cycleCount: $cycleCount" ", "
+           "elements: $elements" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["cycleCount"] = cycleCount;
+    map["elements"] = elements;
+    return map;
+  }
+}
+
+
+class AnimationGroup extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  List<AnimationSequence> sequences = null;
+
+  AnimationGroup() : super(kVersions.last.size);
+
+  static AnimationGroup 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 AnimationGroup decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AnimationGroup result = new AnimationGroup();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.sequences = new List<AnimationSequence>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+          result.sequences[i1] = AnimationSequence.decode(decoder2);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    if (sequences == null) {
+      encoder0.encodeNullPointer(16, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(sequences.length, 16, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < sequences.length; ++i0) {
+        
+        encoder1.encodeStruct(sequences[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "AnimationGroup("
+           "viewId: $viewId" ", "
+           "sequences: $sequences" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["sequences"] = sequences;
+    return map;
+  }
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/view_manager.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/view_manager.mojom.dart
new file mode 100644
index 0000000..575992c
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/view_manager.mojom.dart
@@ -0,0 +1,4628 @@
+// 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_manager_mojom;
+
+import 'dart:async';
+
+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/input_events.mojom.dart' as input_events_mojom;
+import 'package:mojo/mojo/service_provider.mojom.dart' as service_provider_mojom;
+import 'package:mojo_services/mojo/native_viewport.mojom.dart' as native_viewport_mojom;
+import 'package:mojo_services/mojo/surface_id.mojom.dart' as surface_id_mojom;
+import 'package:mojo_services/mojo/view_manager_constants.mojom.dart' as view_manager_constants_mojom;
+
+class ErrorCode extends bindings.MojoEnum {
+  static const ErrorCode none = const ErrorCode._(0);
+  static const ErrorCode valueInUse = const ErrorCode._(1);
+  static const ErrorCode illegalArgument = const ErrorCode._(2);
+
+  const ErrorCode._(int v) : super(v);
+
+  static const Map<String, ErrorCode> valuesMap = const {
+    "none": none,
+    "valueInUse": valueInUse,
+    "illegalArgument": illegalArgument,
+  };
+  static const List<ErrorCode> values = const [
+    none,
+    valueInUse,
+    illegalArgument,
+  ];
+
+  static ErrorCode valueOf(String name) => valuesMap[name];
+
+  factory ErrorCode(int v) {
+    switch (v) {
+      case 0:
+        return none;
+      case 1:
+        return valueInUse;
+      case 2:
+        return illegalArgument;
+      default:
+        return null;
+    }
+  }
+
+  static ErrorCode decode(bindings.Decoder decoder0, int offset) {
+    int v = decoder0.decodeUint32(offset);
+    ErrorCode result = new ErrorCode(v);
+    if (result == null) {
+      throw new bindings.MojoCodecError(
+          'Bad value $v for enum ErrorCode.');
+    }
+    return result;
+  }
+
+  String toString() {
+    switch(this) {
+      case none:
+        return 'ErrorCode.none';
+      case valueInUse:
+        return 'ErrorCode.valueInUse';
+      case illegalArgument:
+        return 'ErrorCode.illegalArgument';
+    }
+  }
+
+  int toJson() => mojoEnumValue;
+}
+
+
+
+class ViewData extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(48, 0)
+  ];
+  int parentId = 0;
+  int viewId = 0;
+  geometry_mojom.Rect bounds = null;
+  Map<String, List<int>> properties = null;
+  bool visible = false;
+  bool drawn = false;
+  native_viewport_mojom.ViewportMetrics viewportMetrics = null;
+
+  ViewData() : super(kVersions.last.size);
+
+  static ViewData 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 ViewData decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewData result = new ViewData();
+
+    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.parentId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.viewId = decoder0.decodeUint32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.bounds = geometry_mojom.Rect.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(24, false);
+      {
+        decoder1.decodeDataHeaderForMap();
+        List<String> keys0;
+        List<List<int>> values0;
+        {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize, false);
+          {
+            var si2 = decoder2.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+            keys0 = new List<String>(si2.numElements);
+            for (int i2 = 0; i2 < si2.numElements; ++i2) {
+              
+              keys0[i2] = decoder2.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i2, false);
+            }
+          }
+        }
+        {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, false);
+          {
+            var si2 = decoder2.decodeDataHeaderForPointerArray(keys0.length);
+            values0 = new List<List<int>>(si2.numElements);
+            for (int i2 = 0; i2 < si2.numElements; ++i2) {
+              
+              values0[i2] = decoder2.decodeUint8Array(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i2, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+            }
+          }
+        }
+        result.properties = new Map<String, List<int>>.fromIterables(
+            keys0, values0);
+      }
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.visible = decoder0.decodeBool(32, 0);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.drawn = decoder0.decodeBool(32, 1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(40, false);
+      result.viewportMetrics = native_viewport_mojom.ViewportMetrics.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(parentId, 8);
+    
+    encoder0.encodeUint32(viewId, 12);
+    
+    encoder0.encodeStruct(bounds, 16, false);
+    
+    if (properties == null) {
+      encoder0.encodeNullPointer(24, false);
+    } else {
+      var encoder1 = encoder0.encoderForMap(24);
+      int size0 = properties.length;
+      var keys0 = properties.keys.toList();
+      var values0 = properties.values.toList();
+      
+      {
+        var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
+        for (int i1 = 0; i1 < keys0.length; ++i1) {
+          
+          encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+      
+      {
+        var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
+        for (int i1 = 0; i1 < values0.length; ++i1) {
+          
+          encoder2.encodeUint8Array(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+        }
+      }
+    }
+    
+    encoder0.encodeBool(visible, 32, 0);
+    
+    encoder0.encodeBool(drawn, 32, 1);
+    
+    encoder0.encodeStruct(viewportMetrics, 40, false);
+  }
+
+  String toString() {
+    return "ViewData("
+           "parentId: $parentId" ", "
+           "viewId: $viewId" ", "
+           "bounds: $bounds" ", "
+           "properties: $properties" ", "
+           "visible: $visible" ", "
+           "drawn: $drawn" ", "
+           "viewportMetrics: $viewportMetrics" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["parentId"] = parentId;
+    map["viewId"] = viewId;
+    map["bounds"] = bounds;
+    map["properties"] = properties;
+    map["visible"] = visible;
+    map["drawn"] = drawn;
+    map["viewportMetrics"] = viewportMetrics;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceCreateViewParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _ViewManagerServiceCreateViewParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceCreateViewParams 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 _ViewManagerServiceCreateViewParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceCreateViewParams result = new _ViewManagerServiceCreateViewParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceCreateViewParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceCreateViewResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  ErrorCode errorCode = null;
+
+  ViewManagerServiceCreateViewResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceCreateViewResponseParams 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 ViewManagerServiceCreateViewResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceCreateViewResponseParams result = new ViewManagerServiceCreateViewResponseParams();
+
+    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.errorCode = ErrorCode.decode(decoder0, 8);
+        if (result.errorCode == null) {
+          throw new bindings.MojoCodecError(
+            'Trying to decode null union for non-nullable ErrorCode.');
+        }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeEnum(errorCode, 8);
+  }
+
+  String toString() {
+    return "ViewManagerServiceCreateViewResponseParams("
+           "errorCode: $errorCode" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["errorCode"] = errorCode;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceDeleteViewParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _ViewManagerServiceDeleteViewParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceDeleteViewParams 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 _ViewManagerServiceDeleteViewParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceDeleteViewParams result = new _ViewManagerServiceDeleteViewParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceDeleteViewParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceDeleteViewResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceDeleteViewResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceDeleteViewResponseParams 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 ViewManagerServiceDeleteViewResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceDeleteViewResponseParams result = new ViewManagerServiceDeleteViewResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceDeleteViewResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceSetViewBoundsParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  geometry_mojom.Rect bounds = null;
+
+  _ViewManagerServiceSetViewBoundsParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceSetViewBoundsParams 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 _ViewManagerServiceSetViewBoundsParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceSetViewBoundsParams result = new _ViewManagerServiceSetViewBoundsParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.bounds = geometry_mojom.Rect.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeStruct(bounds, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceSetViewBoundsParams("
+           "viewId: $viewId" ", "
+           "bounds: $bounds" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["bounds"] = bounds;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceSetViewBoundsResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceSetViewBoundsResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceSetViewBoundsResponseParams 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 ViewManagerServiceSetViewBoundsResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceSetViewBoundsResponseParams result = new ViewManagerServiceSetViewBoundsResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceSetViewBoundsResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceSetViewVisibilityParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+  bool visible = false;
+
+  _ViewManagerServiceSetViewVisibilityParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceSetViewVisibilityParams 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 _ViewManagerServiceSetViewVisibilityParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceSetViewVisibilityParams result = new _ViewManagerServiceSetViewVisibilityParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.visible = decoder0.decodeBool(12, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeBool(visible, 12, 0);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceSetViewVisibilityParams("
+           "viewId: $viewId" ", "
+           "visible: $visible" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["visible"] = visible;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceSetViewVisibilityResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceSetViewVisibilityResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceSetViewVisibilityResponseParams 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 ViewManagerServiceSetViewVisibilityResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceSetViewVisibilityResponseParams result = new ViewManagerServiceSetViewVisibilityResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceSetViewVisibilityResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceSetViewPropertyParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  int viewId = 0;
+  String name = null;
+  List<int> value = null;
+
+  _ViewManagerServiceSetViewPropertyParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceSetViewPropertyParams 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 _ViewManagerServiceSetViewPropertyParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceSetViewPropertyParams result = new _ViewManagerServiceSetViewPropertyParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.name = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.value = decoder0.decodeUint8Array(24, bindings.kArrayNullable, bindings.kUnspecifiedArrayLength);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeString(name, 16, false);
+    
+    encoder0.encodeUint8Array(value, 24, bindings.kArrayNullable, bindings.kUnspecifiedArrayLength);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceSetViewPropertyParams("
+           "viewId: $viewId" ", "
+           "name: $name" ", "
+           "value: $value" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["name"] = name;
+    map["value"] = value;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceSetViewPropertyResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceSetViewPropertyResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceSetViewPropertyResponseParams 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 ViewManagerServiceSetViewPropertyResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceSetViewPropertyResponseParams result = new ViewManagerServiceSetViewPropertyResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceSetViewPropertyResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceAddViewParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int parent = 0;
+  int child = 0;
+
+  _ViewManagerServiceAddViewParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceAddViewParams 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 _ViewManagerServiceAddViewParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceAddViewParams result = new _ViewManagerServiceAddViewParams();
+
+    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.parent = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.child = decoder0.decodeUint32(12);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(parent, 8);
+    
+    encoder0.encodeUint32(child, 12);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceAddViewParams("
+           "parent: $parent" ", "
+           "child: $child" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["parent"] = parent;
+    map["child"] = child;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceAddViewResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceAddViewResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceAddViewResponseParams 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 ViewManagerServiceAddViewResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceAddViewResponseParams result = new ViewManagerServiceAddViewResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceAddViewResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceRemoveViewFromParentParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _ViewManagerServiceRemoveViewFromParentParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceRemoveViewFromParentParams 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 _ViewManagerServiceRemoveViewFromParentParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceRemoveViewFromParentParams result = new _ViewManagerServiceRemoveViewFromParentParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceRemoveViewFromParentParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceRemoveViewFromParentResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceRemoveViewFromParentResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceRemoveViewFromParentResponseParams 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 ViewManagerServiceRemoveViewFromParentResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceRemoveViewFromParentResponseParams result = new ViewManagerServiceRemoveViewFromParentResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceRemoveViewFromParentResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceReorderViewParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  int relativeViewId = 0;
+  view_manager_constants_mojom.OrderDirection direction = null;
+
+  _ViewManagerServiceReorderViewParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceReorderViewParams 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 _ViewManagerServiceReorderViewParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceReorderViewParams result = new _ViewManagerServiceReorderViewParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.relativeViewId = decoder0.decodeUint32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+        result.direction = view_manager_constants_mojom.OrderDirection.decode(decoder0, 16);
+        if (result.direction == null) {
+          throw new bindings.MojoCodecError(
+            'Trying to decode null union for non-nullable view_manager_constants_mojom.OrderDirection.');
+        }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeUint32(relativeViewId, 12);
+    
+    encoder0.encodeEnum(direction, 16);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceReorderViewParams("
+           "viewId: $viewId" ", "
+           "relativeViewId: $relativeViewId" ", "
+           "direction: $direction" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["relativeViewId"] = relativeViewId;
+    map["direction"] = direction;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceReorderViewResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceReorderViewResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceReorderViewResponseParams 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 ViewManagerServiceReorderViewResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceReorderViewResponseParams result = new ViewManagerServiceReorderViewResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceReorderViewResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceGetViewTreeParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _ViewManagerServiceGetViewTreeParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceGetViewTreeParams 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 _ViewManagerServiceGetViewTreeParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceGetViewTreeParams result = new _ViewManagerServiceGetViewTreeParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceGetViewTreeParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceGetViewTreeResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  List<ViewData> views = null;
+
+  ViewManagerServiceGetViewTreeResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceGetViewTreeResponseParams 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 ViewManagerServiceGetViewTreeResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceGetViewTreeResponseParams result = new ViewManagerServiceGetViewTreeResponseParams();
+
+    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);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.views = new List<ViewData>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+          result.views[i1] = ViewData.decode(decoder2);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    if (views == null) {
+      encoder0.encodeNullPointer(8, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(views.length, 8, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < views.length; ++i0) {
+        
+        encoder1.encodeStruct(views[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "ViewManagerServiceGetViewTreeResponseParams("
+           "views: $views" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["views"] = views;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceSetViewSurfaceIdParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  surface_id_mojom.SurfaceId surfaceId = null;
+
+  _ViewManagerServiceSetViewSurfaceIdParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceSetViewSurfaceIdParams 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 _ViewManagerServiceSetViewSurfaceIdParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceSetViewSurfaceIdParams result = new _ViewManagerServiceSetViewSurfaceIdParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.surfaceId = surface_id_mojom.SurfaceId.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeStruct(surfaceId, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceSetViewSurfaceIdParams("
+           "viewId: $viewId" ", "
+           "surfaceId: $surfaceId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["surfaceId"] = surfaceId;
+    return map;
+  }
+}
+
+
+class ViewManagerServiceSetViewSurfaceIdResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceSetViewSurfaceIdResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceSetViewSurfaceIdResponseParams 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 ViewManagerServiceSetViewSurfaceIdResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceSetViewSurfaceIdResponseParams result = new ViewManagerServiceSetViewSurfaceIdResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceSetViewSurfaceIdResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceEmbedUrlParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  String url = null;
+  int viewId = 0;
+  Object services = null;
+  Object exposedServices = null;
+
+  _ViewManagerServiceEmbedUrlParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceEmbedUrlParams 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 _ViewManagerServiceEmbedUrlParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceEmbedUrlParams result = new _ViewManagerServiceEmbedUrlParams();
+
+    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.url = decoder0.decodeString(8, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.viewId = decoder0.decodeUint32(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.services = decoder0.decodeInterfaceRequest(20, true, service_provider_mojom.ServiceProviderStub.newFromEndpoint);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.exposedServices = decoder0.decodeServiceInterface(24, true, service_provider_mojom.ServiceProviderProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeString(url, 8, false);
+    
+    encoder0.encodeUint32(viewId, 16);
+    
+    encoder0.encodeInterfaceRequest(services, 20, true);
+    
+    encoder0.encodeInterface(exposedServices, 24, true);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceEmbedUrlParams("
+           "url: $url" ", "
+           "viewId: $viewId" ", "
+           "services: $services" ", "
+           "exposedServices: $exposedServices" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class ViewManagerServiceEmbedUrlResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceEmbedUrlResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceEmbedUrlResponseParams 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 ViewManagerServiceEmbedUrlResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceEmbedUrlResponseParams result = new ViewManagerServiceEmbedUrlResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceEmbedUrlResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServiceEmbedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  Object client = null;
+
+  _ViewManagerServiceEmbedParams() : super(kVersions.last.size);
+
+  static _ViewManagerServiceEmbedParams 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 _ViewManagerServiceEmbedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServiceEmbedParams result = new _ViewManagerServiceEmbedParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.client = decoder0.decodeServiceInterface(12, false, ViewManagerClientProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeInterface(client, 12, false);
+  }
+
+  String toString() {
+    return "_ViewManagerServiceEmbedParams("
+           "viewId: $viewId" ", "
+           "client: $client" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class ViewManagerServiceEmbedResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServiceEmbedResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServiceEmbedResponseParams 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 ViewManagerServiceEmbedResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServiceEmbedResponseParams result = new ViewManagerServiceEmbedResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServiceEmbedResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerServicePerformActionParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  String action = null;
+
+  _ViewManagerServicePerformActionParams() : super(kVersions.last.size);
+
+  static _ViewManagerServicePerformActionParams 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 _ViewManagerServicePerformActionParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerServicePerformActionParams result = new _ViewManagerServicePerformActionParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.action = decoder0.decodeString(16, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeString(action, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerServicePerformActionParams("
+           "viewId: $viewId" ", "
+           "action: $action" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["action"] = action;
+    return map;
+  }
+}
+
+
+class ViewManagerServicePerformActionResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerServicePerformActionResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerServicePerformActionResponseParams 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 ViewManagerServicePerformActionResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerServicePerformActionResponseParams result = new ViewManagerServicePerformActionResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerServicePerformActionResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnEmbedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(56, 0)
+  ];
+  int connectionId = 0;
+  Object services = null;
+  String embedderUrl = null;
+  ViewData root = null;
+  Object viewManagerService = null;
+  Object exposedServices = null;
+  core.MojoMessagePipeEndpoint windowManagerPipe = null;
+
+  _ViewManagerClientOnEmbedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnEmbedParams 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 _ViewManagerClientOnEmbedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnEmbedParams result = new _ViewManagerClientOnEmbedParams();
+
+    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.connectionId = decoder0.decodeUint16(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.services = decoder0.decodeInterfaceRequest(12, true, service_provider_mojom.ServiceProviderStub.newFromEndpoint);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.embedderUrl = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(24, false);
+      result.root = ViewData.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.viewManagerService = decoder0.decodeServiceInterface(32, true, ViewManagerServiceProxy.newFromEndpoint);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.exposedServices = decoder0.decodeServiceInterface(40, true, service_provider_mojom.ServiceProviderProxy.newFromEndpoint);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.windowManagerPipe = decoder0.decodeMessagePipeHandle(48, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint16(connectionId, 8);
+    
+    encoder0.encodeInterfaceRequest(services, 12, true);
+    
+    encoder0.encodeString(embedderUrl, 16, false);
+    
+    encoder0.encodeStruct(root, 24, false);
+    
+    encoder0.encodeInterface(viewManagerService, 32, true);
+    
+    encoder0.encodeInterface(exposedServices, 40, true);
+    
+    encoder0.encodeMessagePipeHandle(windowManagerPipe, 48, false);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnEmbedParams("
+           "connectionId: $connectionId" ", "
+           "services: $services" ", "
+           "embedderUrl: $embedderUrl" ", "
+           "root: $root" ", "
+           "viewManagerService: $viewManagerService" ", "
+           "exposedServices: $exposedServices" ", "
+           "windowManagerPipe: $windowManagerPipe" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _ViewManagerClientOnEmbeddedAppDisconnectedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int view = 0;
+
+  _ViewManagerClientOnEmbeddedAppDisconnectedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnEmbeddedAppDisconnectedParams 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 _ViewManagerClientOnEmbeddedAppDisconnectedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnEmbeddedAppDisconnectedParams result = new _ViewManagerClientOnEmbeddedAppDisconnectedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnEmbeddedAppDisconnectedParams("
+           "view: $view" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewBoundsChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  int view = 0;
+  geometry_mojom.Rect oldBounds = null;
+  geometry_mojom.Rect newBounds = null;
+
+  _ViewManagerClientOnViewBoundsChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewBoundsChangedParams 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 _ViewManagerClientOnViewBoundsChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewBoundsChangedParams result = new _ViewManagerClientOnViewBoundsChangedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.oldBounds = geometry_mojom.Rect.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(24, false);
+      result.newBounds = geometry_mojom.Rect.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeStruct(oldBounds, 16, false);
+    
+    encoder0.encodeStruct(newBounds, 24, false);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewBoundsChangedParams("
+           "view: $view" ", "
+           "oldBounds: $oldBounds" ", "
+           "newBounds: $newBounds" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["oldBounds"] = oldBounds;
+    map["newBounds"] = newBounds;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewViewportMetricsChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  native_viewport_mojom.ViewportMetrics oldMetrics = null;
+  native_viewport_mojom.ViewportMetrics newMetrics = null;
+
+  _ViewManagerClientOnViewViewportMetricsChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewViewportMetricsChangedParams 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 _ViewManagerClientOnViewViewportMetricsChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewViewportMetricsChangedParams result = new _ViewManagerClientOnViewViewportMetricsChangedParams();
+
+    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.oldMetrics = native_viewport_mojom.ViewportMetrics.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.newMetrics = native_viewport_mojom.ViewportMetrics.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(oldMetrics, 8, false);
+    
+    encoder0.encodeStruct(newMetrics, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewViewportMetricsChangedParams("
+           "oldMetrics: $oldMetrics" ", "
+           "newMetrics: $newMetrics" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["oldMetrics"] = oldMetrics;
+    map["newMetrics"] = newMetrics;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewHierarchyChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  int view = 0;
+  int newParent = 0;
+  int oldParent = 0;
+  List<ViewData> views = null;
+
+  _ViewManagerClientOnViewHierarchyChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewHierarchyChangedParams 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 _ViewManagerClientOnViewHierarchyChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewHierarchyChangedParams result = new _ViewManagerClientOnViewHierarchyChangedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.newParent = decoder0.decodeUint32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.oldParent = decoder0.decodeUint32(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(24, false);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.views = new List<ViewData>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+          result.views[i1] = ViewData.decode(decoder2);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeUint32(newParent, 12);
+    
+    encoder0.encodeUint32(oldParent, 16);
+    
+    if (views == null) {
+      encoder0.encodeNullPointer(24, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(views.length, 24, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < views.length; ++i0) {
+        
+        encoder1.encodeStruct(views[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewHierarchyChangedParams("
+           "view: $view" ", "
+           "newParent: $newParent" ", "
+           "oldParent: $oldParent" ", "
+           "views: $views" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["newParent"] = newParent;
+    map["oldParent"] = oldParent;
+    map["views"] = views;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewReorderedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  int relativeViewId = 0;
+  view_manager_constants_mojom.OrderDirection direction = null;
+
+  _ViewManagerClientOnViewReorderedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewReorderedParams 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 _ViewManagerClientOnViewReorderedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewReorderedParams result = new _ViewManagerClientOnViewReorderedParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.relativeViewId = decoder0.decodeUint32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+        result.direction = view_manager_constants_mojom.OrderDirection.decode(decoder0, 16);
+        if (result.direction == null) {
+          throw new bindings.MojoCodecError(
+            'Trying to decode null union for non-nullable view_manager_constants_mojom.OrderDirection.');
+        }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeUint32(relativeViewId, 12);
+    
+    encoder0.encodeEnum(direction, 16);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewReorderedParams("
+           "viewId: $viewId" ", "
+           "relativeViewId: $relativeViewId" ", "
+           "direction: $direction" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["relativeViewId"] = relativeViewId;
+    map["direction"] = direction;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewDeletedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int view = 0;
+
+  _ViewManagerClientOnViewDeletedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewDeletedParams 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 _ViewManagerClientOnViewDeletedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewDeletedParams result = new _ViewManagerClientOnViewDeletedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewDeletedParams("
+           "view: $view" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewVisibilityChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int view = 0;
+  bool visible = false;
+
+  _ViewManagerClientOnViewVisibilityChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewVisibilityChangedParams 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 _ViewManagerClientOnViewVisibilityChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewVisibilityChangedParams result = new _ViewManagerClientOnViewVisibilityChangedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.visible = decoder0.decodeBool(12, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeBool(visible, 12, 0);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewVisibilityChangedParams("
+           "view: $view" ", "
+           "visible: $visible" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["visible"] = visible;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewDrawnStateChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int view = 0;
+  bool drawn = false;
+
+  _ViewManagerClientOnViewDrawnStateChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewDrawnStateChangedParams 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 _ViewManagerClientOnViewDrawnStateChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewDrawnStateChangedParams result = new _ViewManagerClientOnViewDrawnStateChangedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.drawn = decoder0.decodeBool(12, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeBool(drawn, 12, 0);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewDrawnStateChangedParams("
+           "view: $view" ", "
+           "drawn: $drawn" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["drawn"] = drawn;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewSharedPropertyChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  int view = 0;
+  String name = null;
+  List<int> newData = null;
+
+  _ViewManagerClientOnViewSharedPropertyChangedParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewSharedPropertyChangedParams 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 _ViewManagerClientOnViewSharedPropertyChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewSharedPropertyChangedParams result = new _ViewManagerClientOnViewSharedPropertyChangedParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.name = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.newData = decoder0.decodeUint8Array(24, bindings.kArrayNullable, bindings.kUnspecifiedArrayLength);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeString(name, 16, false);
+    
+    encoder0.encodeUint8Array(newData, 24, bindings.kArrayNullable, bindings.kUnspecifiedArrayLength);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewSharedPropertyChangedParams("
+           "view: $view" ", "
+           "name: $name" ", "
+           "newData: $newData" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["name"] = name;
+    map["newData"] = newData;
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnViewInputEventParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int view = 0;
+  input_events_mojom.Event event = null;
+
+  _ViewManagerClientOnViewInputEventParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnViewInputEventParams 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 _ViewManagerClientOnViewInputEventParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnViewInputEventParams result = new _ViewManagerClientOnViewInputEventParams();
+
+    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.view = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.event = input_events_mojom.Event.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(view, 8);
+    
+    encoder0.encodeStruct(event, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnViewInputEventParams("
+           "view: $view" ", "
+           "event: $event" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["view"] = view;
+    map["event"] = event;
+    return map;
+  }
+}
+
+
+class ViewManagerClientOnViewInputEventResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(8, 0)
+  ];
+
+  ViewManagerClientOnViewInputEventResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerClientOnViewInputEventResponseParams 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 ViewManagerClientOnViewInputEventResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerClientOnViewInputEventResponseParams result = new ViewManagerClientOnViewInputEventResponseParams();
+
+    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.');
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    encoder.getStructEncoderAtOffset(kVersions.last);
+  }
+
+  String toString() {
+    return "ViewManagerClientOnViewInputEventResponseParams("")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    return map;
+  }
+}
+
+
+class _ViewManagerClientOnPerformActionParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  String action = null;
+
+  _ViewManagerClientOnPerformActionParams() : super(kVersions.last.size);
+
+  static _ViewManagerClientOnPerformActionParams 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 _ViewManagerClientOnPerformActionParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _ViewManagerClientOnPerformActionParams result = new _ViewManagerClientOnPerformActionParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.action = decoder0.decodeString(16, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeString(action, 16, false);
+  }
+
+  String toString() {
+    return "_ViewManagerClientOnPerformActionParams("
+           "viewId: $viewId" ", "
+           "action: $action" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["action"] = action;
+    return map;
+  }
+}
+
+
+class ViewManagerClientOnPerformActionResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  ViewManagerClientOnPerformActionResponseParams() : super(kVersions.last.size);
+
+  static ViewManagerClientOnPerformActionResponseParams 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 ViewManagerClientOnPerformActionResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ViewManagerClientOnPerformActionResponseParams result = new ViewManagerClientOnPerformActionResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "ViewManagerClientOnPerformActionResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+const int _ViewManagerService_createViewName = 0;
+const int _ViewManagerService_deleteViewName = 1;
+const int _ViewManagerService_setViewBoundsName = 2;
+const int _ViewManagerService_setViewVisibilityName = 3;
+const int _ViewManagerService_setViewPropertyName = 4;
+const int _ViewManagerService_addViewName = 5;
+const int _ViewManagerService_removeViewFromParentName = 6;
+const int _ViewManagerService_reorderViewName = 7;
+const int _ViewManagerService_getViewTreeName = 8;
+const int _ViewManagerService_setViewSurfaceIdName = 9;
+const int _ViewManagerService_embedUrlName = 10;
+const int _ViewManagerService_embedName = 11;
+const int _ViewManagerService_performActionName = 12;
+
+abstract class ViewManagerService {
+  static const String serviceName = "mojo::ViewManagerService";
+  dynamic createView(int viewId,[Function responseFactory = null]);
+  dynamic deleteView(int viewId,[Function responseFactory = null]);
+  dynamic setViewBounds(int viewId,geometry_mojom.Rect bounds,[Function responseFactory = null]);
+  dynamic setViewVisibility(int viewId,bool visible,[Function responseFactory = null]);
+  dynamic setViewProperty(int viewId,String name,List<int> value,[Function responseFactory = null]);
+  dynamic addView(int parent,int child,[Function responseFactory = null]);
+  dynamic removeViewFromParent(int viewId,[Function responseFactory = null]);
+  dynamic reorderView(int viewId,int relativeViewId,view_manager_constants_mojom.OrderDirection direction,[Function responseFactory = null]);
+  dynamic getViewTree(int viewId,[Function responseFactory = null]);
+  dynamic setViewSurfaceId(int viewId,surface_id_mojom.SurfaceId surfaceId,[Function responseFactory = null]);
+  dynamic embedUrl(String url,int viewId,Object services,Object exposedServices,[Function responseFactory = null]);
+  dynamic embed(int viewId,Object client,[Function responseFactory = null]);
+  dynamic performAction(int viewId,String action,[Function responseFactory = null]);
+}
+
+
+class _ViewManagerServiceProxyImpl extends bindings.Proxy {
+  _ViewManagerServiceProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _ViewManagerServiceProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _ViewManagerServiceProxyImpl.unbound() : super.unbound();
+
+  static _ViewManagerServiceProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _ViewManagerServiceProxyImpl"));
+    return new _ViewManagerServiceProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _ViewManagerService_createViewName:
+        var r = ViewManagerServiceCreateViewResponseParams.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;
+      case _ViewManagerService_deleteViewName:
+        var r = ViewManagerServiceDeleteViewResponseParams.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;
+      case _ViewManagerService_setViewBoundsName:
+        var r = ViewManagerServiceSetViewBoundsResponseParams.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;
+      case _ViewManagerService_setViewVisibilityName:
+        var r = ViewManagerServiceSetViewVisibilityResponseParams.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;
+      case _ViewManagerService_setViewPropertyName:
+        var r = ViewManagerServiceSetViewPropertyResponseParams.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;
+      case _ViewManagerService_addViewName:
+        var r = ViewManagerServiceAddViewResponseParams.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;
+      case _ViewManagerService_removeViewFromParentName:
+        var r = ViewManagerServiceRemoveViewFromParentResponseParams.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;
+      case _ViewManagerService_reorderViewName:
+        var r = ViewManagerServiceReorderViewResponseParams.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;
+      case _ViewManagerService_getViewTreeName:
+        var r = ViewManagerServiceGetViewTreeResponseParams.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;
+      case _ViewManagerService_setViewSurfaceIdName:
+        var r = ViewManagerServiceSetViewSurfaceIdResponseParams.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;
+      case _ViewManagerService_embedUrlName:
+        var r = ViewManagerServiceEmbedUrlResponseParams.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;
+      case _ViewManagerService_embedName:
+        var r = ViewManagerServiceEmbedResponseParams.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;
+      case _ViewManagerService_performActionName:
+        var r = ViewManagerServicePerformActionResponseParams.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 "_ViewManagerServiceProxyImpl($superString)";
+  }
+}
+
+
+class _ViewManagerServiceProxyCalls implements ViewManagerService {
+  _ViewManagerServiceProxyImpl _proxyImpl;
+
+  _ViewManagerServiceProxyCalls(this._proxyImpl);
+    dynamic createView(int viewId,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceCreateViewParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_createViewName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic deleteView(int viewId,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceDeleteViewParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_deleteViewName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic setViewBounds(int viewId,geometry_mojom.Rect bounds,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceSetViewBoundsParams();
+      params.viewId = viewId;
+      params.bounds = bounds;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_setViewBoundsName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic setViewVisibility(int viewId,bool visible,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceSetViewVisibilityParams();
+      params.viewId = viewId;
+      params.visible = visible;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_setViewVisibilityName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic setViewProperty(int viewId,String name,List<int> value,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceSetViewPropertyParams();
+      params.viewId = viewId;
+      params.name = name;
+      params.value = value;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_setViewPropertyName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic addView(int parent,int child,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceAddViewParams();
+      params.parent = parent;
+      params.child = child;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_addViewName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic removeViewFromParent(int viewId,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceRemoveViewFromParentParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_removeViewFromParentName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic reorderView(int viewId,int relativeViewId,view_manager_constants_mojom.OrderDirection direction,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceReorderViewParams();
+      params.viewId = viewId;
+      params.relativeViewId = relativeViewId;
+      params.direction = direction;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_reorderViewName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic getViewTree(int viewId,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceGetViewTreeParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_getViewTreeName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic setViewSurfaceId(int viewId,surface_id_mojom.SurfaceId surfaceId,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceSetViewSurfaceIdParams();
+      params.viewId = viewId;
+      params.surfaceId = surfaceId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_setViewSurfaceIdName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic embedUrl(String url,int viewId,Object services,Object exposedServices,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceEmbedUrlParams();
+      params.url = url;
+      params.viewId = viewId;
+      params.services = services;
+      params.exposedServices = exposedServices;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_embedUrlName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic embed(int viewId,Object client,[Function responseFactory = null]) {
+      var params = new _ViewManagerServiceEmbedParams();
+      params.viewId = viewId;
+      params.client = client;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_embedName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic performAction(int viewId,String action,[Function responseFactory = null]) {
+      var params = new _ViewManagerServicePerformActionParams();
+      params.viewId = viewId;
+      params.action = action;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerService_performActionName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+}
+
+
+class ViewManagerServiceProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  ViewManagerService ptr;
+
+  ViewManagerServiceProxy(_ViewManagerServiceProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ViewManagerServiceProxyCalls(proxyImpl);
+
+  ViewManagerServiceProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _ViewManagerServiceProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ViewManagerServiceProxyCalls(impl);
+  }
+
+  ViewManagerServiceProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _ViewManagerServiceProxyImpl.fromHandle(handle) {
+    ptr = new _ViewManagerServiceProxyCalls(impl);
+  }
+
+  ViewManagerServiceProxy.unbound() :
+      impl = new _ViewManagerServiceProxyImpl.unbound() {
+    ptr = new _ViewManagerServiceProxyCalls(impl);
+  }
+
+  factory ViewManagerServiceProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    ViewManagerServiceProxy p = new ViewManagerServiceProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static ViewManagerServiceProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewManagerServiceProxy"));
+    return new ViewManagerServiceProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => ViewManagerService.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 "ViewManagerServiceProxy($impl)";
+  }
+}
+
+
+class ViewManagerServiceStub extends bindings.Stub {
+  ViewManagerService _impl = null;
+
+  ViewManagerServiceStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ViewManagerServiceStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ViewManagerServiceStub.unbound() : super.unbound();
+
+  static ViewManagerServiceStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewManagerServiceStub"));
+    return new ViewManagerServiceStub.fromEndpoint(endpoint);
+  }
+
+
+  ViewManagerServiceCreateViewResponseParams _ViewManagerServiceCreateViewResponseParamsFactory(ErrorCode errorCode) {
+    var mojo_factory_result = new ViewManagerServiceCreateViewResponseParams();
+    mojo_factory_result.errorCode = errorCode;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceDeleteViewResponseParams _ViewManagerServiceDeleteViewResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceDeleteViewResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceSetViewBoundsResponseParams _ViewManagerServiceSetViewBoundsResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceSetViewBoundsResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceSetViewVisibilityResponseParams _ViewManagerServiceSetViewVisibilityResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceSetViewVisibilityResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceSetViewPropertyResponseParams _ViewManagerServiceSetViewPropertyResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceSetViewPropertyResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceAddViewResponseParams _ViewManagerServiceAddViewResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceAddViewResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceRemoveViewFromParentResponseParams _ViewManagerServiceRemoveViewFromParentResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceRemoveViewFromParentResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceReorderViewResponseParams _ViewManagerServiceReorderViewResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceReorderViewResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceGetViewTreeResponseParams _ViewManagerServiceGetViewTreeResponseParamsFactory(List<ViewData> views) {
+    var mojo_factory_result = new ViewManagerServiceGetViewTreeResponseParams();
+    mojo_factory_result.views = views;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceSetViewSurfaceIdResponseParams _ViewManagerServiceSetViewSurfaceIdResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceSetViewSurfaceIdResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceEmbedUrlResponseParams _ViewManagerServiceEmbedUrlResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceEmbedUrlResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServiceEmbedResponseParams _ViewManagerServiceEmbedResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServiceEmbedResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  ViewManagerServicePerformActionResponseParams _ViewManagerServicePerformActionResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerServicePerformActionResponseParams();
+    mojo_factory_result.success = success;
+    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 _ViewManagerService_createViewName:
+        var params = _ViewManagerServiceCreateViewParams.deserialize(
+            message.payload);
+        var response = _impl.createView(params.viewId,_ViewManagerServiceCreateViewResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_createViewName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_createViewName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_deleteViewName:
+        var params = _ViewManagerServiceDeleteViewParams.deserialize(
+            message.payload);
+        var response = _impl.deleteView(params.viewId,_ViewManagerServiceDeleteViewResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_deleteViewName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_deleteViewName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_setViewBoundsName:
+        var params = _ViewManagerServiceSetViewBoundsParams.deserialize(
+            message.payload);
+        var response = _impl.setViewBounds(params.viewId,params.bounds,_ViewManagerServiceSetViewBoundsResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_setViewBoundsName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_setViewBoundsName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_setViewVisibilityName:
+        var params = _ViewManagerServiceSetViewVisibilityParams.deserialize(
+            message.payload);
+        var response = _impl.setViewVisibility(params.viewId,params.visible,_ViewManagerServiceSetViewVisibilityResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_setViewVisibilityName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_setViewVisibilityName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_setViewPropertyName:
+        var params = _ViewManagerServiceSetViewPropertyParams.deserialize(
+            message.payload);
+        var response = _impl.setViewProperty(params.viewId,params.name,params.value,_ViewManagerServiceSetViewPropertyResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_setViewPropertyName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_setViewPropertyName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_addViewName:
+        var params = _ViewManagerServiceAddViewParams.deserialize(
+            message.payload);
+        var response = _impl.addView(params.parent,params.child,_ViewManagerServiceAddViewResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_addViewName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_addViewName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_removeViewFromParentName:
+        var params = _ViewManagerServiceRemoveViewFromParentParams.deserialize(
+            message.payload);
+        var response = _impl.removeViewFromParent(params.viewId,_ViewManagerServiceRemoveViewFromParentResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_removeViewFromParentName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_removeViewFromParentName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_reorderViewName:
+        var params = _ViewManagerServiceReorderViewParams.deserialize(
+            message.payload);
+        var response = _impl.reorderView(params.viewId,params.relativeViewId,params.direction,_ViewManagerServiceReorderViewResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_reorderViewName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_reorderViewName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_getViewTreeName:
+        var params = _ViewManagerServiceGetViewTreeParams.deserialize(
+            message.payload);
+        var response = _impl.getViewTree(params.viewId,_ViewManagerServiceGetViewTreeResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_getViewTreeName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_getViewTreeName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_setViewSurfaceIdName:
+        var params = _ViewManagerServiceSetViewSurfaceIdParams.deserialize(
+            message.payload);
+        var response = _impl.setViewSurfaceId(params.viewId,params.surfaceId,_ViewManagerServiceSetViewSurfaceIdResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_setViewSurfaceIdName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_setViewSurfaceIdName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_embedUrlName:
+        var params = _ViewManagerServiceEmbedUrlParams.deserialize(
+            message.payload);
+        var response = _impl.embedUrl(params.url,params.viewId,params.services,params.exposedServices,_ViewManagerServiceEmbedUrlResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_embedUrlName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_embedUrlName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_embedName:
+        var params = _ViewManagerServiceEmbedParams.deserialize(
+            message.payload);
+        var response = _impl.embed(params.viewId,params.client,_ViewManagerServiceEmbedResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_embedName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_embedName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerService_performActionName:
+        var params = _ViewManagerServicePerformActionParams.deserialize(
+            message.payload);
+        var response = _impl.performAction(params.viewId,params.action,_ViewManagerServicePerformActionResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerService_performActionName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerService_performActionName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  ViewManagerService get impl => _impl;
+  set impl(ViewManagerService d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ViewManagerServiceStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int _ViewManagerClient_onEmbedName = 0;
+const int _ViewManagerClient_onEmbeddedAppDisconnectedName = 1;
+const int _ViewManagerClient_onViewBoundsChangedName = 2;
+const int _ViewManagerClient_onViewViewportMetricsChangedName = 3;
+const int _ViewManagerClient_onViewHierarchyChangedName = 4;
+const int _ViewManagerClient_onViewReorderedName = 5;
+const int _ViewManagerClient_onViewDeletedName = 6;
+const int _ViewManagerClient_onViewVisibilityChangedName = 7;
+const int _ViewManagerClient_onViewDrawnStateChangedName = 8;
+const int _ViewManagerClient_onViewSharedPropertyChangedName = 9;
+const int _ViewManagerClient_onViewInputEventName = 10;
+const int _ViewManagerClient_onPerformActionName = 11;
+
+abstract class ViewManagerClient {
+  static const String serviceName = "mojo::ViewManagerClient";
+  void onEmbed(int connectionId, String embedderUrl, ViewData root, Object viewManagerService, Object services, Object exposedServices, core.MojoMessagePipeEndpoint windowManagerPipe);
+  void onEmbeddedAppDisconnected(int view);
+  void onViewBoundsChanged(int view, geometry_mojom.Rect oldBounds, geometry_mojom.Rect newBounds);
+  void onViewViewportMetricsChanged(native_viewport_mojom.ViewportMetrics oldMetrics, native_viewport_mojom.ViewportMetrics newMetrics);
+  void onViewHierarchyChanged(int view, int newParent, int oldParent, List<ViewData> views);
+  void onViewReordered(int viewId, int relativeViewId, view_manager_constants_mojom.OrderDirection direction);
+  void onViewDeleted(int view);
+  void onViewVisibilityChanged(int view, bool visible);
+  void onViewDrawnStateChanged(int view, bool drawn);
+  void onViewSharedPropertyChanged(int view, String name, List<int> newData);
+  dynamic onViewInputEvent(int view,input_events_mojom.Event event,[Function responseFactory = null]);
+  dynamic onPerformAction(int viewId,String action,[Function responseFactory = null]);
+}
+
+
+class _ViewManagerClientProxyImpl extends bindings.Proxy {
+  _ViewManagerClientProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _ViewManagerClientProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _ViewManagerClientProxyImpl.unbound() : super.unbound();
+
+  static _ViewManagerClientProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _ViewManagerClientProxyImpl"));
+    return new _ViewManagerClientProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _ViewManagerClient_onViewInputEventName:
+        var r = ViewManagerClientOnViewInputEventResponseParams.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;
+      case _ViewManagerClient_onPerformActionName:
+        var r = ViewManagerClientOnPerformActionResponseParams.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 "_ViewManagerClientProxyImpl($superString)";
+  }
+}
+
+
+class _ViewManagerClientProxyCalls implements ViewManagerClient {
+  _ViewManagerClientProxyImpl _proxyImpl;
+
+  _ViewManagerClientProxyCalls(this._proxyImpl);
+    void onEmbed(int connectionId, String embedderUrl, ViewData root, Object viewManagerService, Object services, Object exposedServices, core.MojoMessagePipeEndpoint windowManagerPipe) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnEmbedParams();
+      params.connectionId = connectionId;
+      params.embedderUrl = embedderUrl;
+      params.root = root;
+      params.viewManagerService = viewManagerService;
+      params.services = services;
+      params.exposedServices = exposedServices;
+      params.windowManagerPipe = windowManagerPipe;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onEmbedName);
+    }
+    void onEmbeddedAppDisconnected(int view) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnEmbeddedAppDisconnectedParams();
+      params.view = view;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onEmbeddedAppDisconnectedName);
+    }
+    void onViewBoundsChanged(int view, geometry_mojom.Rect oldBounds, geometry_mojom.Rect newBounds) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewBoundsChangedParams();
+      params.view = view;
+      params.oldBounds = oldBounds;
+      params.newBounds = newBounds;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewBoundsChangedName);
+    }
+    void onViewViewportMetricsChanged(native_viewport_mojom.ViewportMetrics oldMetrics, native_viewport_mojom.ViewportMetrics newMetrics) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewViewportMetricsChangedParams();
+      params.oldMetrics = oldMetrics;
+      params.newMetrics = newMetrics;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewViewportMetricsChangedName);
+    }
+    void onViewHierarchyChanged(int view, int newParent, int oldParent, List<ViewData> views) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewHierarchyChangedParams();
+      params.view = view;
+      params.newParent = newParent;
+      params.oldParent = oldParent;
+      params.views = views;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewHierarchyChangedName);
+    }
+    void onViewReordered(int viewId, int relativeViewId, view_manager_constants_mojom.OrderDirection direction) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewReorderedParams();
+      params.viewId = viewId;
+      params.relativeViewId = relativeViewId;
+      params.direction = direction;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewReorderedName);
+    }
+    void onViewDeleted(int view) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewDeletedParams();
+      params.view = view;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewDeletedName);
+    }
+    void onViewVisibilityChanged(int view, bool visible) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewVisibilityChangedParams();
+      params.view = view;
+      params.visible = visible;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewVisibilityChangedName);
+    }
+    void onViewDrawnStateChanged(int view, bool drawn) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewDrawnStateChangedParams();
+      params.view = view;
+      params.drawn = drawn;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewDrawnStateChangedName);
+    }
+    void onViewSharedPropertyChanged(int view, String name, List<int> newData) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _ViewManagerClientOnViewSharedPropertyChangedParams();
+      params.view = view;
+      params.name = name;
+      params.newData = newData;
+      _proxyImpl.sendMessage(params, _ViewManagerClient_onViewSharedPropertyChangedName);
+    }
+    dynamic onViewInputEvent(int view,input_events_mojom.Event event,[Function responseFactory = null]) {
+      var params = new _ViewManagerClientOnViewInputEventParams();
+      params.view = view;
+      params.event = event;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerClient_onViewInputEventName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic onPerformAction(int viewId,String action,[Function responseFactory = null]) {
+      var params = new _ViewManagerClientOnPerformActionParams();
+      params.viewId = viewId;
+      params.action = action;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _ViewManagerClient_onPerformActionName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+}
+
+
+class ViewManagerClientProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  ViewManagerClient ptr;
+
+  ViewManagerClientProxy(_ViewManagerClientProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ViewManagerClientProxyCalls(proxyImpl);
+
+  ViewManagerClientProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _ViewManagerClientProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ViewManagerClientProxyCalls(impl);
+  }
+
+  ViewManagerClientProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _ViewManagerClientProxyImpl.fromHandle(handle) {
+    ptr = new _ViewManagerClientProxyCalls(impl);
+  }
+
+  ViewManagerClientProxy.unbound() :
+      impl = new _ViewManagerClientProxyImpl.unbound() {
+    ptr = new _ViewManagerClientProxyCalls(impl);
+  }
+
+  factory ViewManagerClientProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    ViewManagerClientProxy p = new ViewManagerClientProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static ViewManagerClientProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewManagerClientProxy"));
+    return new ViewManagerClientProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => ViewManagerClient.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 "ViewManagerClientProxy($impl)";
+  }
+}
+
+
+class ViewManagerClientStub extends bindings.Stub {
+  ViewManagerClient _impl = null;
+
+  ViewManagerClientStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ViewManagerClientStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ViewManagerClientStub.unbound() : super.unbound();
+
+  static ViewManagerClientStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ViewManagerClientStub"));
+    return new ViewManagerClientStub.fromEndpoint(endpoint);
+  }
+
+
+  ViewManagerClientOnViewInputEventResponseParams _ViewManagerClientOnViewInputEventResponseParamsFactory() {
+    var mojo_factory_result = new ViewManagerClientOnViewInputEventResponseParams();
+    return mojo_factory_result;
+  }
+  ViewManagerClientOnPerformActionResponseParams _ViewManagerClientOnPerformActionResponseParamsFactory(bool success) {
+    var mojo_factory_result = new ViewManagerClientOnPerformActionResponseParams();
+    mojo_factory_result.success = success;
+    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 _ViewManagerClient_onEmbedName:
+        var params = _ViewManagerClientOnEmbedParams.deserialize(
+            message.payload);
+        _impl.onEmbed(params.connectionId, params.embedderUrl, params.root, params.viewManagerService, params.services, params.exposedServices, params.windowManagerPipe);
+        break;
+      case _ViewManagerClient_onEmbeddedAppDisconnectedName:
+        var params = _ViewManagerClientOnEmbeddedAppDisconnectedParams.deserialize(
+            message.payload);
+        _impl.onEmbeddedAppDisconnected(params.view);
+        break;
+      case _ViewManagerClient_onViewBoundsChangedName:
+        var params = _ViewManagerClientOnViewBoundsChangedParams.deserialize(
+            message.payload);
+        _impl.onViewBoundsChanged(params.view, params.oldBounds, params.newBounds);
+        break;
+      case _ViewManagerClient_onViewViewportMetricsChangedName:
+        var params = _ViewManagerClientOnViewViewportMetricsChangedParams.deserialize(
+            message.payload);
+        _impl.onViewViewportMetricsChanged(params.oldMetrics, params.newMetrics);
+        break;
+      case _ViewManagerClient_onViewHierarchyChangedName:
+        var params = _ViewManagerClientOnViewHierarchyChangedParams.deserialize(
+            message.payload);
+        _impl.onViewHierarchyChanged(params.view, params.newParent, params.oldParent, params.views);
+        break;
+      case _ViewManagerClient_onViewReorderedName:
+        var params = _ViewManagerClientOnViewReorderedParams.deserialize(
+            message.payload);
+        _impl.onViewReordered(params.viewId, params.relativeViewId, params.direction);
+        break;
+      case _ViewManagerClient_onViewDeletedName:
+        var params = _ViewManagerClientOnViewDeletedParams.deserialize(
+            message.payload);
+        _impl.onViewDeleted(params.view);
+        break;
+      case _ViewManagerClient_onViewVisibilityChangedName:
+        var params = _ViewManagerClientOnViewVisibilityChangedParams.deserialize(
+            message.payload);
+        _impl.onViewVisibilityChanged(params.view, params.visible);
+        break;
+      case _ViewManagerClient_onViewDrawnStateChangedName:
+        var params = _ViewManagerClientOnViewDrawnStateChangedParams.deserialize(
+            message.payload);
+        _impl.onViewDrawnStateChanged(params.view, params.drawn);
+        break;
+      case _ViewManagerClient_onViewSharedPropertyChangedName:
+        var params = _ViewManagerClientOnViewSharedPropertyChangedParams.deserialize(
+            message.payload);
+        _impl.onViewSharedPropertyChanged(params.view, params.name, params.newData);
+        break;
+      case _ViewManagerClient_onViewInputEventName:
+        var params = _ViewManagerClientOnViewInputEventParams.deserialize(
+            message.payload);
+        var response = _impl.onViewInputEvent(params.view,params.event,_ViewManagerClientOnViewInputEventResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerClient_onViewInputEventName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerClient_onViewInputEventName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _ViewManagerClient_onPerformActionName:
+        var params = _ViewManagerClientOnPerformActionParams.deserialize(
+            message.payload);
+        var response = _impl.onPerformAction(params.viewId,params.action,_ViewManagerClientOnPerformActionResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _ViewManagerClient_onPerformActionName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _ViewManagerClient_onPerformActionName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  ViewManagerClient get impl => _impl;
+  set impl(ViewManagerClient d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ViewManagerClientStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/view_manager_constants.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/view_manager_constants.mojom.dart
new file mode 100644
index 0000000..7591fba
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/view_manager_constants.mojom.dart
@@ -0,0 +1,63 @@
+// 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_manager_constants_mojom;
+
+import 'dart:async';
+
+import 'package:mojo/bindings.dart' as bindings;
+import 'package:mojo/core.dart' as core;
+
+class OrderDirection extends bindings.MojoEnum {
+  static const OrderDirection above = const OrderDirection._(1);
+  static const OrderDirection below = const OrderDirection._(2);
+
+  const OrderDirection._(int v) : super(v);
+
+  static const Map<String, OrderDirection> valuesMap = const {
+    "above": above,
+    "below": below,
+  };
+  static const List<OrderDirection> values = const [
+    above,
+    below,
+  ];
+
+  static OrderDirection valueOf(String name) => valuesMap[name];
+
+  factory OrderDirection(int v) {
+    switch (v) {
+      case 1:
+        return above;
+      case 2:
+        return below;
+      default:
+        return null;
+    }
+  }
+
+  static OrderDirection decode(bindings.Decoder decoder0, int offset) {
+    int v = decoder0.decodeUint32(offset);
+    OrderDirection result = new OrderDirection(v);
+    if (result == null) {
+      throw new bindings.MojoCodecError(
+          'Bad value $v for enum OrderDirection.');
+    }
+    return result;
+  }
+
+  String toString() {
+    switch(this) {
+      case above:
+        return 'OrderDirection.above';
+      case below:
+        return 'OrderDirection.below';
+    }
+  }
+
+  int toJson() => mojoEnumValue;
+}
+
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/window_manager.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/window_manager.mojom.dart
new file mode 100644
index 0000000..8ad38af
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/window_manager.mojom.dart
@@ -0,0 +1,1455 @@
+// 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 window_manager_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;
+import 'package:mojo/mojo/service_provider.mojom.dart' as service_provider_mojom;
+
+
+
+class _WindowManagerEmbedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  String url = null;
+  Object services = null;
+  Object exposedServices = null;
+
+  _WindowManagerEmbedParams() : super(kVersions.last.size);
+
+  static _WindowManagerEmbedParams 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 _WindowManagerEmbedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerEmbedParams result = new _WindowManagerEmbedParams();
+
+    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.url = decoder0.decodeString(8, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.services = decoder0.decodeInterfaceRequest(16, true, service_provider_mojom.ServiceProviderStub.newFromEndpoint);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.exposedServices = decoder0.decodeServiceInterface(20, true, service_provider_mojom.ServiceProviderProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeString(url, 8, false);
+    
+    encoder0.encodeInterfaceRequest(services, 16, true);
+    
+    encoder0.encodeInterface(exposedServices, 20, true);
+  }
+
+  String toString() {
+    return "_WindowManagerEmbedParams("
+           "url: $url" ", "
+           "services: $services" ", "
+           "exposedServices: $exposedServices" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _WindowManagerSetCaptureParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _WindowManagerSetCaptureParams() : super(kVersions.last.size);
+
+  static _WindowManagerSetCaptureParams 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 _WindowManagerSetCaptureParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerSetCaptureParams result = new _WindowManagerSetCaptureParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerSetCaptureParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class WindowManagerSetCaptureResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  WindowManagerSetCaptureResponseParams() : super(kVersions.last.size);
+
+  static WindowManagerSetCaptureResponseParams 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 WindowManagerSetCaptureResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    WindowManagerSetCaptureResponseParams result = new WindowManagerSetCaptureResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "WindowManagerSetCaptureResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _WindowManagerFocusWindowParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _WindowManagerFocusWindowParams() : super(kVersions.last.size);
+
+  static _WindowManagerFocusWindowParams 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 _WindowManagerFocusWindowParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerFocusWindowParams result = new _WindowManagerFocusWindowParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerFocusWindowParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class WindowManagerFocusWindowResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  WindowManagerFocusWindowResponseParams() : super(kVersions.last.size);
+
+  static WindowManagerFocusWindowResponseParams 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 WindowManagerFocusWindowResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    WindowManagerFocusWindowResponseParams result = new WindowManagerFocusWindowResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "WindowManagerFocusWindowResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _WindowManagerActivateWindowParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _WindowManagerActivateWindowParams() : super(kVersions.last.size);
+
+  static _WindowManagerActivateWindowParams 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 _WindowManagerActivateWindowParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerActivateWindowParams result = new _WindowManagerActivateWindowParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerActivateWindowParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+
+class WindowManagerActivateWindowResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool success = false;
+
+  WindowManagerActivateWindowResponseParams() : super(kVersions.last.size);
+
+  static WindowManagerActivateWindowResponseParams 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 WindowManagerActivateWindowResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    WindowManagerActivateWindowResponseParams result = new WindowManagerActivateWindowResponseParams();
+
+    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.success = decoder0.decodeBool(8, 0);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeBool(success, 8, 0);
+  }
+
+  String toString() {
+    return "WindowManagerActivateWindowResponseParams("
+           "success: $success" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["success"] = success;
+    return map;
+  }
+}
+
+
+class _WindowManagerGetFocusedAndActiveViewsParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Object observer = null;
+
+  _WindowManagerGetFocusedAndActiveViewsParams() : super(kVersions.last.size);
+
+  static _WindowManagerGetFocusedAndActiveViewsParams 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 _WindowManagerGetFocusedAndActiveViewsParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerGetFocusedAndActiveViewsParams result = new _WindowManagerGetFocusedAndActiveViewsParams();
+
+    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.observer = decoder0.decodeServiceInterface(8, true, WindowManagerObserverProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInterface(observer, 8, true);
+  }
+
+  String toString() {
+    return "_WindowManagerGetFocusedAndActiveViewsParams("
+           "observer: $observer" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class WindowManagerGetFocusedAndActiveViewsResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int captureViewId = 0;
+  int focusedViewId = 0;
+  int activeViewId = 0;
+
+  WindowManagerGetFocusedAndActiveViewsResponseParams() : super(kVersions.last.size);
+
+  static WindowManagerGetFocusedAndActiveViewsResponseParams 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 WindowManagerGetFocusedAndActiveViewsResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    WindowManagerGetFocusedAndActiveViewsResponseParams result = new WindowManagerGetFocusedAndActiveViewsResponseParams();
+
+    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.captureViewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.focusedViewId = decoder0.decodeUint32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.activeViewId = decoder0.decodeUint32(16);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(captureViewId, 8);
+    
+    encoder0.encodeUint32(focusedViewId, 12);
+    
+    encoder0.encodeUint32(activeViewId, 16);
+  }
+
+  String toString() {
+    return "WindowManagerGetFocusedAndActiveViewsResponseParams("
+           "captureViewId: $captureViewId" ", "
+           "focusedViewId: $focusedViewId" ", "
+           "activeViewId: $activeViewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["captureViewId"] = captureViewId;
+    map["focusedViewId"] = focusedViewId;
+    map["activeViewId"] = activeViewId;
+    return map;
+  }
+}
+
+
+class _WindowManagerObserverOnCaptureChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int captureViewId = 0;
+
+  _WindowManagerObserverOnCaptureChangedParams() : super(kVersions.last.size);
+
+  static _WindowManagerObserverOnCaptureChangedParams 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 _WindowManagerObserverOnCaptureChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerObserverOnCaptureChangedParams result = new _WindowManagerObserverOnCaptureChangedParams();
+
+    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.captureViewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(captureViewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerObserverOnCaptureChangedParams("
+           "captureViewId: $captureViewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["captureViewId"] = captureViewId;
+    return map;
+  }
+}
+
+
+class _WindowManagerObserverOnFocusChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int focusedViewId = 0;
+
+  _WindowManagerObserverOnFocusChangedParams() : super(kVersions.last.size);
+
+  static _WindowManagerObserverOnFocusChangedParams 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 _WindowManagerObserverOnFocusChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerObserverOnFocusChangedParams result = new _WindowManagerObserverOnFocusChangedParams();
+
+    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.focusedViewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(focusedViewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerObserverOnFocusChangedParams("
+           "focusedViewId: $focusedViewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["focusedViewId"] = focusedViewId;
+    return map;
+  }
+}
+
+
+class _WindowManagerObserverOnActiveWindowChangedParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int focusedViewId = 0;
+
+  _WindowManagerObserverOnActiveWindowChangedParams() : super(kVersions.last.size);
+
+  static _WindowManagerObserverOnActiveWindowChangedParams 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 _WindowManagerObserverOnActiveWindowChangedParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerObserverOnActiveWindowChangedParams result = new _WindowManagerObserverOnActiveWindowChangedParams();
+
+    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.focusedViewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(focusedViewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerObserverOnActiveWindowChangedParams("
+           "focusedViewId: $focusedViewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["focusedViewId"] = focusedViewId;
+    return map;
+  }
+}
+
+const int _WindowManager_embedName = 0;
+const int _WindowManager_setCaptureName = 1;
+const int _WindowManager_focusWindowName = 2;
+const int _WindowManager_activateWindowName = 3;
+const int _WindowManager_getFocusedAndActiveViewsName = 4;
+
+abstract class WindowManager {
+  static const String serviceName = "mojo::WindowManager";
+  void embed(String url, Object services, Object exposedServices);
+  dynamic setCapture(int viewId,[Function responseFactory = null]);
+  dynamic focusWindow(int viewId,[Function responseFactory = null]);
+  dynamic activateWindow(int viewId,[Function responseFactory = null]);
+  dynamic getFocusedAndActiveViews(Object observer,[Function responseFactory = null]);
+}
+
+
+class _WindowManagerProxyImpl extends bindings.Proxy {
+  _WindowManagerProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _WindowManagerProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _WindowManagerProxyImpl.unbound() : super.unbound();
+
+  static _WindowManagerProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _WindowManagerProxyImpl"));
+    return new _WindowManagerProxyImpl.fromEndpoint(endpoint);
+  }
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _WindowManager_setCaptureName:
+        var r = WindowManagerSetCaptureResponseParams.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;
+      case _WindowManager_focusWindowName:
+        var r = WindowManagerFocusWindowResponseParams.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;
+      case _WindowManager_activateWindowName:
+        var r = WindowManagerActivateWindowResponseParams.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;
+      case _WindowManager_getFocusedAndActiveViewsName:
+        var r = WindowManagerGetFocusedAndActiveViewsResponseParams.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 "_WindowManagerProxyImpl($superString)";
+  }
+}
+
+
+class _WindowManagerProxyCalls implements WindowManager {
+  _WindowManagerProxyImpl _proxyImpl;
+
+  _WindowManagerProxyCalls(this._proxyImpl);
+    void embed(String url, Object services, Object exposedServices) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerEmbedParams();
+      params.url = url;
+      params.services = services;
+      params.exposedServices = exposedServices;
+      _proxyImpl.sendMessage(params, _WindowManager_embedName);
+    }
+    dynamic setCapture(int viewId,[Function responseFactory = null]) {
+      var params = new _WindowManagerSetCaptureParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _WindowManager_setCaptureName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic focusWindow(int viewId,[Function responseFactory = null]) {
+      var params = new _WindowManagerFocusWindowParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _WindowManager_focusWindowName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic activateWindow(int viewId,[Function responseFactory = null]) {
+      var params = new _WindowManagerActivateWindowParams();
+      params.viewId = viewId;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _WindowManager_activateWindowName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    dynamic getFocusedAndActiveViews(Object observer,[Function responseFactory = null]) {
+      var params = new _WindowManagerGetFocusedAndActiveViewsParams();
+      params.observer = observer;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _WindowManager_getFocusedAndActiveViewsName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+}
+
+
+class WindowManagerProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  WindowManager ptr;
+
+  WindowManagerProxy(_WindowManagerProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _WindowManagerProxyCalls(proxyImpl);
+
+  WindowManagerProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _WindowManagerProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _WindowManagerProxyCalls(impl);
+  }
+
+  WindowManagerProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _WindowManagerProxyImpl.fromHandle(handle) {
+    ptr = new _WindowManagerProxyCalls(impl);
+  }
+
+  WindowManagerProxy.unbound() :
+      impl = new _WindowManagerProxyImpl.unbound() {
+    ptr = new _WindowManagerProxyCalls(impl);
+  }
+
+  factory WindowManagerProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    WindowManagerProxy p = new WindowManagerProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static WindowManagerProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerProxy"));
+    return new WindowManagerProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => WindowManager.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 "WindowManagerProxy($impl)";
+  }
+}
+
+
+class WindowManagerStub extends bindings.Stub {
+  WindowManager _impl = null;
+
+  WindowManagerStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  WindowManagerStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  WindowManagerStub.unbound() : super.unbound();
+
+  static WindowManagerStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerStub"));
+    return new WindowManagerStub.fromEndpoint(endpoint);
+  }
+
+
+  WindowManagerSetCaptureResponseParams _WindowManagerSetCaptureResponseParamsFactory(bool success) {
+    var mojo_factory_result = new WindowManagerSetCaptureResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  WindowManagerFocusWindowResponseParams _WindowManagerFocusWindowResponseParamsFactory(bool success) {
+    var mojo_factory_result = new WindowManagerFocusWindowResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  WindowManagerActivateWindowResponseParams _WindowManagerActivateWindowResponseParamsFactory(bool success) {
+    var mojo_factory_result = new WindowManagerActivateWindowResponseParams();
+    mojo_factory_result.success = success;
+    return mojo_factory_result;
+  }
+  WindowManagerGetFocusedAndActiveViewsResponseParams _WindowManagerGetFocusedAndActiveViewsResponseParamsFactory(int captureViewId, int focusedViewId, int activeViewId) {
+    var mojo_factory_result = new WindowManagerGetFocusedAndActiveViewsResponseParams();
+    mojo_factory_result.captureViewId = captureViewId;
+    mojo_factory_result.focusedViewId = focusedViewId;
+    mojo_factory_result.activeViewId = activeViewId;
+    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 _WindowManager_embedName:
+        var params = _WindowManagerEmbedParams.deserialize(
+            message.payload);
+        _impl.embed(params.url, params.services, params.exposedServices);
+        break;
+      case _WindowManager_setCaptureName:
+        var params = _WindowManagerSetCaptureParams.deserialize(
+            message.payload);
+        var response = _impl.setCapture(params.viewId,_WindowManagerSetCaptureResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _WindowManager_setCaptureName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _WindowManager_setCaptureName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _WindowManager_focusWindowName:
+        var params = _WindowManagerFocusWindowParams.deserialize(
+            message.payload);
+        var response = _impl.focusWindow(params.viewId,_WindowManagerFocusWindowResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _WindowManager_focusWindowName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _WindowManager_focusWindowName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _WindowManager_activateWindowName:
+        var params = _WindowManagerActivateWindowParams.deserialize(
+            message.payload);
+        var response = _impl.activateWindow(params.viewId,_WindowManagerActivateWindowResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _WindowManager_activateWindowName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _WindowManager_activateWindowName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case _WindowManager_getFocusedAndActiveViewsName:
+        var params = _WindowManagerGetFocusedAndActiveViewsParams.deserialize(
+            message.payload);
+        var response = _impl.getFocusedAndActiveViews(params.observer,_WindowManagerGetFocusedAndActiveViewsResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _WindowManager_getFocusedAndActiveViewsName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _WindowManager_getFocusedAndActiveViewsName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  WindowManager get impl => _impl;
+  set impl(WindowManager d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "WindowManagerStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int _WindowManagerObserver_onCaptureChangedName = 0;
+const int _WindowManagerObserver_onFocusChangedName = 1;
+const int _WindowManagerObserver_onActiveWindowChangedName = 2;
+
+abstract class WindowManagerObserver {
+  static const String serviceName = null;
+  void onCaptureChanged(int captureViewId);
+  void onFocusChanged(int focusedViewId);
+  void onActiveWindowChanged(int focusedViewId);
+}
+
+
+class _WindowManagerObserverProxyImpl extends bindings.Proxy {
+  _WindowManagerObserverProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _WindowManagerObserverProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _WindowManagerObserverProxyImpl.unbound() : super.unbound();
+
+  static _WindowManagerObserverProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _WindowManagerObserverProxyImpl"));
+    return new _WindowManagerObserverProxyImpl.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 "_WindowManagerObserverProxyImpl($superString)";
+  }
+}
+
+
+class _WindowManagerObserverProxyCalls implements WindowManagerObserver {
+  _WindowManagerObserverProxyImpl _proxyImpl;
+
+  _WindowManagerObserverProxyCalls(this._proxyImpl);
+    void onCaptureChanged(int captureViewId) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerObserverOnCaptureChangedParams();
+      params.captureViewId = captureViewId;
+      _proxyImpl.sendMessage(params, _WindowManagerObserver_onCaptureChangedName);
+    }
+    void onFocusChanged(int focusedViewId) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerObserverOnFocusChangedParams();
+      params.focusedViewId = focusedViewId;
+      _proxyImpl.sendMessage(params, _WindowManagerObserver_onFocusChangedName);
+    }
+    void onActiveWindowChanged(int focusedViewId) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerObserverOnActiveWindowChangedParams();
+      params.focusedViewId = focusedViewId;
+      _proxyImpl.sendMessage(params, _WindowManagerObserver_onActiveWindowChangedName);
+    }
+}
+
+
+class WindowManagerObserverProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  WindowManagerObserver ptr;
+
+  WindowManagerObserverProxy(_WindowManagerObserverProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _WindowManagerObserverProxyCalls(proxyImpl);
+
+  WindowManagerObserverProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _WindowManagerObserverProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _WindowManagerObserverProxyCalls(impl);
+  }
+
+  WindowManagerObserverProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _WindowManagerObserverProxyImpl.fromHandle(handle) {
+    ptr = new _WindowManagerObserverProxyCalls(impl);
+  }
+
+  WindowManagerObserverProxy.unbound() :
+      impl = new _WindowManagerObserverProxyImpl.unbound() {
+    ptr = new _WindowManagerObserverProxyCalls(impl);
+  }
+
+  factory WindowManagerObserverProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    WindowManagerObserverProxy p = new WindowManagerObserverProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static WindowManagerObserverProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerObserverProxy"));
+    return new WindowManagerObserverProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => WindowManagerObserver.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 "WindowManagerObserverProxy($impl)";
+  }
+}
+
+
+class WindowManagerObserverStub extends bindings.Stub {
+  WindowManagerObserver _impl = null;
+
+  WindowManagerObserverStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  WindowManagerObserverStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  WindowManagerObserverStub.unbound() : super.unbound();
+
+  static WindowManagerObserverStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerObserverStub"));
+    return new WindowManagerObserverStub.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 _WindowManagerObserver_onCaptureChangedName:
+        var params = _WindowManagerObserverOnCaptureChangedParams.deserialize(
+            message.payload);
+        _impl.onCaptureChanged(params.captureViewId);
+        break;
+      case _WindowManagerObserver_onFocusChangedName:
+        var params = _WindowManagerObserverOnFocusChangedParams.deserialize(
+            message.payload);
+        _impl.onFocusChanged(params.focusedViewId);
+        break;
+      case _WindowManagerObserver_onActiveWindowChangedName:
+        var params = _WindowManagerObserverOnActiveWindowChangedParams.deserialize(
+            message.payload);
+        _impl.onActiveWindowChanged(params.focusedViewId);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  WindowManagerObserver get impl => _impl;
+  set impl(WindowManagerObserver d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "WindowManagerObserverStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/window_manager_internal.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/window_manager_internal.mojom.dart
new file mode 100644
index 0000000..1dea692
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/window_manager_internal.mojom.dart
@@ -0,0 +1,753 @@
+// 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 window_manager_internal_mojom;
+
+import 'dart:async';
+
+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/input_events.mojom.dart' as input_events_mojom;
+
+
+
+class _WindowManagerInternalCreateWindowManagerForViewManagerClientParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int connectionId = 0;
+  core.MojoMessagePipeEndpoint windowManagerPipe = null;
+
+  _WindowManagerInternalCreateWindowManagerForViewManagerClientParams() : super(kVersions.last.size);
+
+  static _WindowManagerInternalCreateWindowManagerForViewManagerClientParams 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 _WindowManagerInternalCreateWindowManagerForViewManagerClientParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerInternalCreateWindowManagerForViewManagerClientParams result = new _WindowManagerInternalCreateWindowManagerForViewManagerClientParams();
+
+    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.connectionId = decoder0.decodeUint16(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.windowManagerPipe = decoder0.decodeMessagePipeHandle(12, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint16(connectionId, 8);
+    
+    encoder0.encodeMessagePipeHandle(windowManagerPipe, 12, false);
+  }
+
+  String toString() {
+    return "_WindowManagerInternalCreateWindowManagerForViewManagerClientParams("
+           "connectionId: $connectionId" ", "
+           "windowManagerPipe: $windowManagerPipe" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _WindowManagerInternalSetViewManagerClientParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  core.MojoMessagePipeEndpoint viewManagerClientRequest = null;
+
+  _WindowManagerInternalSetViewManagerClientParams() : super(kVersions.last.size);
+
+  static _WindowManagerInternalSetViewManagerClientParams 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 _WindowManagerInternalSetViewManagerClientParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerInternalSetViewManagerClientParams result = new _WindowManagerInternalSetViewManagerClientParams();
+
+    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.viewManagerClientRequest = decoder0.decodeMessagePipeHandle(8, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeMessagePipeHandle(viewManagerClientRequest, 8, false);
+  }
+
+  String toString() {
+    return "_WindowManagerInternalSetViewManagerClientParams("
+           "viewManagerClientRequest: $viewManagerClientRequest" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class _WindowManagerInternalClientDispatchInputEventToViewParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int viewId = 0;
+  input_events_mojom.Event event = null;
+
+  _WindowManagerInternalClientDispatchInputEventToViewParams() : super(kVersions.last.size);
+
+  static _WindowManagerInternalClientDispatchInputEventToViewParams 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 _WindowManagerInternalClientDispatchInputEventToViewParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerInternalClientDispatchInputEventToViewParams result = new _WindowManagerInternalClientDispatchInputEventToViewParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      result.event = input_events_mojom.Event.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+    
+    encoder0.encodeStruct(event, 16, false);
+  }
+
+  String toString() {
+    return "_WindowManagerInternalClientDispatchInputEventToViewParams("
+           "viewId: $viewId" ", "
+           "event: $event" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    map["event"] = event;
+    return map;
+  }
+}
+
+
+class _WindowManagerInternalClientSetViewportSizeParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  geometry_mojom.Size size = null;
+
+  _WindowManagerInternalClientSetViewportSizeParams() : super(kVersions.last.size);
+
+  static _WindowManagerInternalClientSetViewportSizeParams 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 _WindowManagerInternalClientSetViewportSizeParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerInternalClientSetViewportSizeParams result = new _WindowManagerInternalClientSetViewportSizeParams();
+
+    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 "_WindowManagerInternalClientSetViewportSizeParams("
+           "size: $size" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["size"] = size;
+    return map;
+  }
+}
+
+
+class _WindowManagerInternalClientCloneAndAnimateParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int viewId = 0;
+
+  _WindowManagerInternalClientCloneAndAnimateParams() : super(kVersions.last.size);
+
+  static _WindowManagerInternalClientCloneAndAnimateParams 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 _WindowManagerInternalClientCloneAndAnimateParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _WindowManagerInternalClientCloneAndAnimateParams result = new _WindowManagerInternalClientCloneAndAnimateParams();
+
+    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.viewId = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(viewId, 8);
+  }
+
+  String toString() {
+    return "_WindowManagerInternalClientCloneAndAnimateParams("
+           "viewId: $viewId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["viewId"] = viewId;
+    return map;
+  }
+}
+
+const int _WindowManagerInternal_createWindowManagerForViewManagerClientName = 0;
+const int _WindowManagerInternal_setViewManagerClientName = 1;
+
+abstract class WindowManagerInternal {
+  static const String serviceName = "mojo::WindowManagerInternal";
+  void createWindowManagerForViewManagerClient(int connectionId, core.MojoMessagePipeEndpoint windowManagerPipe);
+  void setViewManagerClient(core.MojoMessagePipeEndpoint viewManagerClientRequest);
+}
+
+
+class _WindowManagerInternalProxyImpl extends bindings.Proxy {
+  _WindowManagerInternalProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _WindowManagerInternalProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _WindowManagerInternalProxyImpl.unbound() : super.unbound();
+
+  static _WindowManagerInternalProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _WindowManagerInternalProxyImpl"));
+    return new _WindowManagerInternalProxyImpl.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 "_WindowManagerInternalProxyImpl($superString)";
+  }
+}
+
+
+class _WindowManagerInternalProxyCalls implements WindowManagerInternal {
+  _WindowManagerInternalProxyImpl _proxyImpl;
+
+  _WindowManagerInternalProxyCalls(this._proxyImpl);
+    void createWindowManagerForViewManagerClient(int connectionId, core.MojoMessagePipeEndpoint windowManagerPipe) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerInternalCreateWindowManagerForViewManagerClientParams();
+      params.connectionId = connectionId;
+      params.windowManagerPipe = windowManagerPipe;
+      _proxyImpl.sendMessage(params, _WindowManagerInternal_createWindowManagerForViewManagerClientName);
+    }
+    void setViewManagerClient(core.MojoMessagePipeEndpoint viewManagerClientRequest) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerInternalSetViewManagerClientParams();
+      params.viewManagerClientRequest = viewManagerClientRequest;
+      _proxyImpl.sendMessage(params, _WindowManagerInternal_setViewManagerClientName);
+    }
+}
+
+
+class WindowManagerInternalProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  WindowManagerInternal ptr;
+
+  WindowManagerInternalProxy(_WindowManagerInternalProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _WindowManagerInternalProxyCalls(proxyImpl);
+
+  WindowManagerInternalProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _WindowManagerInternalProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _WindowManagerInternalProxyCalls(impl);
+  }
+
+  WindowManagerInternalProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _WindowManagerInternalProxyImpl.fromHandle(handle) {
+    ptr = new _WindowManagerInternalProxyCalls(impl);
+  }
+
+  WindowManagerInternalProxy.unbound() :
+      impl = new _WindowManagerInternalProxyImpl.unbound() {
+    ptr = new _WindowManagerInternalProxyCalls(impl);
+  }
+
+  factory WindowManagerInternalProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    WindowManagerInternalProxy p = new WindowManagerInternalProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static WindowManagerInternalProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerInternalProxy"));
+    return new WindowManagerInternalProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => WindowManagerInternal.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 "WindowManagerInternalProxy($impl)";
+  }
+}
+
+
+class WindowManagerInternalStub extends bindings.Stub {
+  WindowManagerInternal _impl = null;
+
+  WindowManagerInternalStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  WindowManagerInternalStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  WindowManagerInternalStub.unbound() : super.unbound();
+
+  static WindowManagerInternalStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerInternalStub"));
+    return new WindowManagerInternalStub.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 _WindowManagerInternal_createWindowManagerForViewManagerClientName:
+        var params = _WindowManagerInternalCreateWindowManagerForViewManagerClientParams.deserialize(
+            message.payload);
+        _impl.createWindowManagerForViewManagerClient(params.connectionId, params.windowManagerPipe);
+        break;
+      case _WindowManagerInternal_setViewManagerClientName:
+        var params = _WindowManagerInternalSetViewManagerClientParams.deserialize(
+            message.payload);
+        _impl.setViewManagerClient(params.viewManagerClientRequest);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  WindowManagerInternal get impl => _impl;
+  set impl(WindowManagerInternal d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "WindowManagerInternalStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int _WindowManagerInternalClient_dispatchInputEventToViewName = 0;
+const int _WindowManagerInternalClient_setViewportSizeName = 1;
+const int _WindowManagerInternalClient_cloneAndAnimateName = 2;
+
+abstract class WindowManagerInternalClient {
+  static const String serviceName = "mojo::WindowManagerInternalClient";
+  void dispatchInputEventToView(int viewId, input_events_mojom.Event event);
+  void setViewportSize(geometry_mojom.Size size);
+  void cloneAndAnimate(int viewId);
+}
+
+
+class _WindowManagerInternalClientProxyImpl extends bindings.Proxy {
+  _WindowManagerInternalClientProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _WindowManagerInternalClientProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _WindowManagerInternalClientProxyImpl.unbound() : super.unbound();
+
+  static _WindowManagerInternalClientProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _WindowManagerInternalClientProxyImpl"));
+    return new _WindowManagerInternalClientProxyImpl.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 "_WindowManagerInternalClientProxyImpl($superString)";
+  }
+}
+
+
+class _WindowManagerInternalClientProxyCalls implements WindowManagerInternalClient {
+  _WindowManagerInternalClientProxyImpl _proxyImpl;
+
+  _WindowManagerInternalClientProxyCalls(this._proxyImpl);
+    void dispatchInputEventToView(int viewId, input_events_mojom.Event event) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerInternalClientDispatchInputEventToViewParams();
+      params.viewId = viewId;
+      params.event = event;
+      _proxyImpl.sendMessage(params, _WindowManagerInternalClient_dispatchInputEventToViewName);
+    }
+    void setViewportSize(geometry_mojom.Size size) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerInternalClientSetViewportSizeParams();
+      params.size = size;
+      _proxyImpl.sendMessage(params, _WindowManagerInternalClient_setViewportSizeName);
+    }
+    void cloneAndAnimate(int viewId) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
+      var params = new _WindowManagerInternalClientCloneAndAnimateParams();
+      params.viewId = viewId;
+      _proxyImpl.sendMessage(params, _WindowManagerInternalClient_cloneAndAnimateName);
+    }
+}
+
+
+class WindowManagerInternalClientProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  WindowManagerInternalClient ptr;
+
+  WindowManagerInternalClientProxy(_WindowManagerInternalClientProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _WindowManagerInternalClientProxyCalls(proxyImpl);
+
+  WindowManagerInternalClientProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _WindowManagerInternalClientProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _WindowManagerInternalClientProxyCalls(impl);
+  }
+
+  WindowManagerInternalClientProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _WindowManagerInternalClientProxyImpl.fromHandle(handle) {
+    ptr = new _WindowManagerInternalClientProxyCalls(impl);
+  }
+
+  WindowManagerInternalClientProxy.unbound() :
+      impl = new _WindowManagerInternalClientProxyImpl.unbound() {
+    ptr = new _WindowManagerInternalClientProxyCalls(impl);
+  }
+
+  factory WindowManagerInternalClientProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    WindowManagerInternalClientProxy p = new WindowManagerInternalClientProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static WindowManagerInternalClientProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerInternalClientProxy"));
+    return new WindowManagerInternalClientProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => WindowManagerInternalClient.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 "WindowManagerInternalClientProxy($impl)";
+  }
+}
+
+
+class WindowManagerInternalClientStub extends bindings.Stub {
+  WindowManagerInternalClient _impl = null;
+
+  WindowManagerInternalClientStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  WindowManagerInternalClientStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  WindowManagerInternalClientStub.unbound() : super.unbound();
+
+  static WindowManagerInternalClientStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For WindowManagerInternalClientStub"));
+    return new WindowManagerInternalClientStub.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 _WindowManagerInternalClient_dispatchInputEventToViewName:
+        var params = _WindowManagerInternalClientDispatchInputEventToViewParams.deserialize(
+            message.payload);
+        _impl.dispatchInputEventToView(params.viewId, params.event);
+        break;
+      case _WindowManagerInternalClient_setViewportSizeName:
+        var params = _WindowManagerInternalClientSetViewportSizeParams.deserialize(
+            message.payload);
+        _impl.setViewportSize(params.size);
+        break;
+      case _WindowManagerInternalClient_cloneAndAnimateName:
+        var params = _WindowManagerInternalClientCloneAndAnimateParams.deserialize(
+            message.payload);
+        _impl.cloneAndAnimate(params.viewId);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  WindowManagerInternalClient get impl => _impl;
+  set impl(WindowManagerInternalClient d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "WindowManagerInternalClientStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojo/nacl/BUILD.gn b/mojo/nacl/BUILD.gn
index 98d2fe3..0d62e53 100644
--- a/mojo/nacl/BUILD.gn
+++ b/mojo/nacl/BUILD.gn
@@ -31,6 +31,9 @@
       "//services/files:apptests",
       "//services/http_server",
       "//services/http_server:apptests",
+      "//services/view_manager:mojo_view_manager_client_apptests",
+      "//services/view_manager:view_manager_service_apptests",
+      "//services/window_manager:window_manager_apptests",
       "//shell:apptests",
     ]
   }
diff --git a/mojo/services/mojo_services.gni b/mojo/services/mojo_services.gni
index d7b3e1b..2ec90e7 100644
--- a/mojo/services/mojo_services.gni
+++ b/mojo/services/mojo_services.gni
@@ -44,5 +44,7 @@
   "//mojo/services/ui/views/interfaces",
   "//mojo/services/url_response_disk_cache/interfaces",
   "//mojo/services/vanadium/security/interfaces",
+  "//mojo/services/view_manager/interfaces",
   "//mojo/services/vsync/interfaces",
+  "//mojo/services/window_manager/interfaces",
 ]
diff --git a/mojo/services/view_manager/cpp/BUILD.gn b/mojo/services/view_manager/cpp/BUILD.gn
new file mode 100644
index 0000000..156c437
--- /dev/null
+++ b/mojo/services/view_manager/cpp/BUILD.gn
@@ -0,0 +1,61 @@
+# 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.
+
+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 = [
+    "lib/view.cc",
+    "lib/view_manager_client_factory.cc",
+    "lib/view_manager_client_impl.cc",
+    "lib/view_manager_client_impl.h",
+    "lib/view_manager_context.cc",
+    "lib/view_observer.cc",
+    "lib/view_private.cc",
+    "lib/view_private.h",
+    "view.h",
+    "view_manager.h",
+    "view_manager_client_factory.h",
+    "view_manager_context.h",
+    "view_manager_delegate.cc",
+    "view_manager_delegate.h",
+    "view_observer.h",
+    "view_property.h",
+    "view_tracker.cc",
+    "view_tracker.h",
+  ]
+
+  public_deps = [
+    ":common",
+  ]
+
+  deps = [
+    "../interfaces",
+    "../../geometry/interfaces",
+    "../../input_events/interfaces",
+    "../../surfaces/interfaces:surface_id",
+    "../../window_manager/interfaces",
+    "//base",
+  ]
+
+  mojo_sdk_deps = [
+    "mojo/public/cpp/application",
+    "mojo/public/cpp/bindings:bindings",
+    "mojo/public/cpp/system",
+    "mojo/public/interfaces/application",
+  ]
+}
+
+source_set("common") {
+  configs += [ "../../public/build/config:mojo_services" ]
+
+  sources = [
+    "keys.cc",
+    "keys.h",
+    "types.h",
+  ]
+}
diff --git a/mojo/services/view_manager/cpp/keys.cc b/mojo/services/view_manager/cpp/keys.cc
new file mode 100644
index 0000000..b9a5d94
--- /dev/null
+++ b/mojo/services/view_manager/cpp/keys.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 "view_manager/cpp/keys.h"
+
+namespace mojo {
+
+extern const char kViewManagerKeyWantsTouchEvents[] =
+    "view-manager-key-wants-touch-events";
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/keys.h b/mojo/services/view_manager/cpp/keys.h
new file mode 100644
index 0000000..890657f
--- /dev/null
+++ b/mojo/services/view_manager/cpp/keys.h
@@ -0,0 +1,14 @@
+// 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_VIEW_MANAGER_CPP_KEYS_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_KEYS_H_
+
+namespace mojo {
+
+extern const char kViewManagerKeyWantsTouchEvents[];
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_KEYS_H_
diff --git a/mojo/services/view_manager/cpp/lib/view.cc b/mojo/services/view_manager/cpp/lib/view.cc
new file mode 100644
index 0000000..aa4b30e
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view.cc
@@ -0,0 +1,581 @@
+// 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.
+
+#include "view_manager/cpp/view.h"
+
+#include <set>
+#include <string>
+
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "view_manager/cpp/lib/view_manager_client_impl.h"
+#include "view_manager/cpp/lib/view_private.h"
+#include "view_manager/cpp/view_observer.h"
+#include "view_manager/cpp/view_tracker.h"
+
+namespace mojo {
+
+namespace {
+
+void NotifyViewTreeChangeAtReceiver(
+    View* receiver,
+    const ViewObserver::TreeChangeParams& params,
+    bool change_applied) {
+  ViewObserver::TreeChangeParams local_params = params;
+  local_params.receiver = receiver;
+  if (change_applied) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(receiver).observers(),
+                      OnTreeChanged(local_params));
+  } else {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(receiver).observers(),
+                      OnTreeChanging(local_params));
+  }
+}
+
+void NotifyViewTreeChangeUp(View* start_at,
+                            const ViewObserver::TreeChangeParams& params,
+                            bool change_applied) {
+  for (View* current = start_at; current; current = current->parent())
+    NotifyViewTreeChangeAtReceiver(current, params, change_applied);
+}
+
+void NotifyViewTreeChangeDown(View* start_at,
+                              const ViewObserver::TreeChangeParams& params,
+                              bool change_applied) {
+  NotifyViewTreeChangeAtReceiver(start_at, params, change_applied);
+  View::Children::const_iterator it = start_at->children().begin();
+  for (; it != start_at->children().end(); ++it)
+    NotifyViewTreeChangeDown(*it, params, change_applied);
+}
+
+void NotifyViewTreeChange(const ViewObserver::TreeChangeParams& params,
+                          bool change_applied) {
+  NotifyViewTreeChangeDown(params.target, params, change_applied);
+  if (params.old_parent)
+    NotifyViewTreeChangeUp(params.old_parent, params, change_applied);
+  if (params.new_parent)
+    NotifyViewTreeChangeUp(params.new_parent, params, change_applied);
+}
+
+class ScopedTreeNotifier {
+ public:
+  ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) {
+    params_.target = target;
+    params_.old_parent = old_parent;
+    params_.new_parent = new_parent;
+    NotifyViewTreeChange(params_, false);
+  }
+  ~ScopedTreeNotifier() { NotifyViewTreeChange(params_, true); }
+
+ private:
+  ViewObserver::TreeChangeParams params_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
+};
+
+void RemoveChildImpl(View* child, View::Children* children) {
+  View::Children::iterator it =
+      std::find(children->begin(), children->end(), child);
+  if (it != children->end()) {
+    children->erase(it);
+    ViewPrivate(child).ClearParent();
+  }
+}
+
+class ScopedOrderChangedNotifier {
+ public:
+  ScopedOrderChangedNotifier(View* view,
+                             View* relative_view,
+                             OrderDirection direction)
+      : view_(view), relative_view_(relative_view), direction_(direction) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
+                      OnViewReordering(view_, relative_view_, direction_));
+  }
+  ~ScopedOrderChangedNotifier() {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
+                      OnViewReordered(view_, relative_view_, direction_));
+  }
+
+ private:
+  View* view_;
+  View* relative_view_;
+  OrderDirection direction_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
+};
+
+// Returns true if the order actually changed.
+bool ReorderImpl(View::Children* children,
+                 View* view,
+                 View* relative,
+                 OrderDirection direction) {
+  DCHECK(relative);
+  DCHECK_NE(view, relative);
+  DCHECK_EQ(view->parent(), relative->parent());
+
+  const size_t child_i =
+      std::find(children->begin(), children->end(), view) - children->begin();
+  const size_t target_i =
+      std::find(children->begin(), children->end(), relative) -
+      children->begin();
+  if ((direction == OrderDirection::ABOVE && child_i == target_i + 1) ||
+      (direction == OrderDirection::BELOW && child_i + 1 == target_i)) {
+    return false;
+  }
+
+  ScopedOrderChangedNotifier notifier(view, relative, direction);
+
+  const size_t dest_i = direction == OrderDirection::ABOVE
+                            ? (child_i < target_i ? target_i : target_i + 1)
+                            : (child_i < target_i ? target_i - 1 : target_i);
+  children->erase(children->begin() + child_i);
+  children->insert(children->begin() + dest_i, view);
+
+  return true;
+}
+
+class ScopedSetBoundsNotifier {
+ public:
+  ScopedSetBoundsNotifier(View* view,
+                          const Rect& old_bounds,
+                          const Rect& new_bounds)
+      : view_(view), old_bounds_(old_bounds), new_bounds_(new_bounds) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
+                      OnViewBoundsChanging(view_, old_bounds_, new_bounds_));
+  }
+  ~ScopedSetBoundsNotifier() {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
+                      OnViewBoundsChanged(view_, old_bounds_, new_bounds_));
+  }
+
+ private:
+  View* view_;
+  const Rect old_bounds_;
+  const Rect new_bounds_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
+};
+
+// Some operations are only permitted in the connection that created the view.
+bool OwnsView(ViewManager* manager, View* view) {
+  return !manager ||
+         static_cast<ViewManagerClientImpl*>(manager)->OwnsView(view->id());
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// View, public:
+
+void View::Destroy() {
+  if (!OwnsView(manager_, this))
+    return;
+
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_);
+  while (!children_.empty()) {
+    View* child = children_.front();
+    if (!OwnsView(manager_, child)) {
+      ViewPrivate(child).ClearParent();
+      children_.erase(children_.begin());
+    } else {
+      child->Destroy();
+      DCHECK(std::find(children_.begin(), children_.end(), child) ==
+             children_.end());
+    }
+  }
+  LocalDestroy();
+}
+
+void View::SetBounds(const Rect& bounds) {
+  if (!OwnsView(manager_, this))
+    return;
+
+  if (bounds_.Equals(bounds))
+    return;
+
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds);
+  LocalSetBounds(bounds_, bounds);
+}
+
+void View::SetVisible(bool value) {
+  if (visible_ == value)
+    return;
+
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->SetVisible(id_, value);
+  LocalSetVisible(value);
+}
+
+void View::SetSharedProperty(const std::string& name,
+                             const std::vector<uint8_t>* value) {
+  std::vector<uint8_t> old_value;
+  std::vector<uint8_t>* old_value_ptr = nullptr;
+  auto it = properties_.find(name);
+  if (it != properties_.end()) {
+    old_value = it->second;
+    old_value_ptr = &old_value;
+
+    if (value && old_value == *value)
+      return;
+  } else if (!value) {
+    // This property isn't set in |properties_| and |value| is NULL, so there's
+    // no change.
+    return;
+  }
+
+  if (value) {
+    properties_[name] = *value;
+  } else if (it != properties_.end()) {
+    properties_.erase(it);
+  }
+
+  // TODO: add test coverage of this (450303).
+  if (manager_) {
+    Array<uint8_t> transport_value;
+    if (value) {
+      transport_value.resize(value->size());
+      if (value->size())
+        memcpy(&transport_value.front(), &(value->front()), value->size());
+    }
+    static_cast<ViewManagerClientImpl*>(manager_)
+        ->SetProperty(id_, name, transport_value.Pass());
+  }
+
+  FOR_EACH_OBSERVER(
+      ViewObserver, observers_,
+      OnViewSharedPropertyChanged(this, name, old_value_ptr, value));
+}
+
+bool View::IsDrawn() const {
+  if (!visible_)
+    return false;
+  return parent_ ? parent_->IsDrawn() : drawn_;
+}
+
+void View::AddObserver(ViewObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void View::RemoveObserver(ViewObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+const View* View::GetRoot() const {
+  const View* root = this;
+  for (const View* parent = this; parent; parent = parent->parent())
+    root = parent;
+  return root;
+}
+
+void View::AddChild(View* child) {
+  // TODO(beng): not necessarily valid to all connections, but possibly to the
+  //             embeddee in an embedder-embeddee relationship.
+  if (manager_)
+    CHECK_EQ(child->view_manager(), manager_);
+  LocalAddChild(child);
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_);
+}
+
+void View::RemoveChild(View* child) {
+  // TODO(beng): not necessarily valid to all connections, but possibly to the
+  //             embeddee in an embedder-embeddee relationship.
+  if (manager_)
+    CHECK_EQ(child->view_manager(), manager_);
+  LocalRemoveChild(child);
+  if (manager_) {
+    static_cast<ViewManagerClientImpl*>(manager_)
+        ->RemoveChild(child->id(), id_);
+  }
+}
+
+void View::MoveToFront() {
+  if (!parent_ || parent_->children_.back() == this)
+    return;
+  Reorder(parent_->children_.back(), OrderDirection::ABOVE);
+}
+
+void View::MoveToBack() {
+  if (!parent_ || parent_->children_.front() == this)
+    return;
+  Reorder(parent_->children_.front(), OrderDirection::BELOW);
+}
+
+void View::Reorder(View* relative, OrderDirection direction) {
+  if (!LocalReorder(relative, direction))
+    return;
+  if (manager_) {
+    static_cast<ViewManagerClientImpl*>(manager_)
+        ->Reorder(id_, relative->id(), direction);
+  }
+}
+
+bool View::Contains(View* child) const {
+  if (!child)
+    return false;
+  if (child == this)
+    return true;
+  if (manager_)
+    CHECK_EQ(child->view_manager(), manager_);
+  for (View* p = child->parent(); p; p = p->parent()) {
+    if (p == this)
+      return true;
+  }
+  return false;
+}
+
+View* View::GetChildById(Id id) {
+  if (id == id_)
+    return this;
+  // TODO(beng): this could be improved depending on how we decide to own views.
+  Children::const_iterator it = children_.begin();
+  for (; it != children_.end(); ++it) {
+    View* view = (*it)->GetChildById(id);
+    if (view)
+      return view;
+  }
+  return NULL;
+}
+
+void View::SetSurfaceId(SurfaceIdPtr id) {
+  if (manager_) {
+    static_cast<ViewManagerClientImpl*>(manager_)->SetSurfaceId(id_, id.Pass());
+  }
+}
+
+void View::SetFocus() {
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_);
+}
+
+void View::Embed(const String& url) {
+  if (PrepareForEmbed())
+    static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_);
+}
+
+void View::Embed(const String& url,
+                 InterfaceRequest<ServiceProvider> services,
+                 ServiceProviderPtr exposed_services) {
+  if (PrepareForEmbed()) {
+    static_cast<ViewManagerClientImpl*>(manager_)
+        ->Embed(url, id_, services.Pass(), exposed_services.Pass());
+  }
+}
+
+void View::Embed(ViewManagerClientPtr client) {
+  if (PrepareForEmbed())
+    static_cast<ViewManagerClientImpl*>(manager_)->Embed(id_, client.Pass());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, protected:
+
+namespace {
+
+ViewportMetricsPtr CreateEmptyViewportMetrics() {
+  ViewportMetricsPtr metrics = ViewportMetrics::New();
+  metrics->size = Size::New();
+  return metrics;
+}
+
+}  // namespace
+
+View::View()
+    : manager_(NULL),
+      id_(static_cast<Id>(-1)),
+      parent_(NULL),
+      viewport_metrics_(CreateEmptyViewportMetrics()),
+      visible_(true),
+      drawn_(false) {}
+
+View::~View() {
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
+  if (parent_)
+    parent_->LocalRemoveChild(this);
+
+  // We may still have children. This can happen if the embedder destroys the
+  // root while we're still alive.
+  while (!children_.empty()) {
+    View* child = children_.front();
+    LocalRemoveChild(child);
+    DCHECK(children_.empty() || children_.front() != child);
+  }
+
+  // TODO(beng): It'd be better to do this via a destruction observer in the
+  //             ViewManagerClientImpl.
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
+
+  // Clear properties.
+  for (auto& pair : prop_map_) {
+    if (pair.second.deallocator)
+      (*pair.second.deallocator)(pair.second.value);
+  }
+  prop_map_.clear();
+
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, private:
+
+View::View(ViewManager* manager, Id id)
+    : manager_(manager),
+      id_(id),
+      parent_(nullptr),
+      viewport_metrics_(CreateEmptyViewportMetrics()),
+      visible_(false),
+      drawn_(false) {}
+
+int64 View::SetLocalPropertyInternal(const void* key,
+                                     const char* name,
+                                     PropertyDeallocator deallocator,
+                                     int64 value,
+                                     int64 default_value) {
+  int64 old = GetLocalPropertyInternal(key, default_value);
+  if (value == default_value) {
+    prop_map_.erase(key);
+  } else {
+    Value prop_value;
+    prop_value.name = name;
+    prop_value.value = value;
+    prop_value.deallocator = deallocator;
+    prop_map_[key] = prop_value;
+  }
+  FOR_EACH_OBSERVER(ViewObserver, observers_,
+                    OnViewLocalPropertyChanged(this, key, old));
+  return old;
+}
+
+int64 View::GetLocalPropertyInternal(const void* key,
+                                     int64 default_value) const {
+  std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
+  if (iter == prop_map_.end())
+    return default_value;
+  return iter->second.value;
+}
+
+void View::LocalDestroy() {
+  delete this;
+}
+
+void View::LocalAddChild(View* child) {
+  ScopedTreeNotifier notifier(child, child->parent(), this);
+  if (child->parent())
+    RemoveChildImpl(child, &child->parent_->children_);
+  children_.push_back(child);
+  child->parent_ = this;
+}
+
+void View::LocalRemoveChild(View* child) {
+  DCHECK_EQ(this, child->parent());
+  ScopedTreeNotifier notifier(child, this, NULL);
+  RemoveChildImpl(child, &children_);
+}
+
+bool View::LocalReorder(View* relative, OrderDirection direction) {
+  return ReorderImpl(&parent_->children_, this, relative, direction);
+}
+
+void View::LocalSetBounds(const Rect& old_bounds, const Rect& new_bounds) {
+  DCHECK(old_bounds.x == bounds_.x);
+  DCHECK(old_bounds.y == bounds_.y);
+  DCHECK(old_bounds.width == bounds_.width);
+  DCHECK(old_bounds.height == bounds_.height);
+  ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
+  bounds_ = new_bounds;
+}
+
+void View::LocalSetViewportMetrics(const ViewportMetrics& old_metrics,
+                                   const ViewportMetrics& new_metrics) {
+  // TODO(eseidel): We could check old_metrics against viewport_metrics_.
+  viewport_metrics_ = new_metrics.Clone();
+  FOR_EACH_OBSERVER(
+      ViewObserver, observers_,
+      OnViewViewportMetricsChanged(this, old_metrics, new_metrics));
+}
+
+void View::LocalSetDrawn(bool value) {
+  if (drawn_ == value)
+    return;
+
+  // As IsDrawn() is derived from |visible_| and |drawn_|, only send drawn
+  // notification is the value of IsDrawn() is really changing.
+  if (IsDrawn() == value) {
+    drawn_ = value;
+    return;
+  }
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanging(this));
+  drawn_ = value;
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this));
+}
+
+void View::LocalSetVisible(bool visible) {
+  if (visible_ == visible)
+    return;
+
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this));
+  visible_ = visible;
+  NotifyViewVisibilityChanged(this);
+}
+
+void View::NotifyViewVisibilityChanged(View* target) {
+  if (!NotifyViewVisibilityChangedDown(target)) {
+    return;  // |this| has been deleted.
+  }
+  NotifyViewVisibilityChangedUp(target);
+}
+
+bool View::NotifyViewVisibilityChangedAtReceiver(View* target) {
+  // |this| may be deleted during a call to OnViewVisibilityChanged() on one
+  // of the observers. We create an local observer for that. In that case we
+  // exit without further access to any members.
+  ViewTracker tracker;
+  tracker.Add(this);
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(target));
+  return tracker.Contains(this);
+}
+
+bool View::NotifyViewVisibilityChangedDown(View* target) {
+  if (!NotifyViewVisibilityChangedAtReceiver(target))
+    return false;  // |this| was deleted.
+  std::set<const View*> child_already_processed;
+  bool child_destroyed = false;
+  do {
+    child_destroyed = false;
+    for (View::Children::const_iterator it = children_.begin();
+         it != children_.end(); ++it) {
+      if (!child_already_processed.insert(*it).second)
+        continue;
+      if (!(*it)->NotifyViewVisibilityChangedDown(target)) {
+        // |*it| was deleted, |it| is invalid and |children_| has changed.  We
+        // exit the current for-loop and enter a new one.
+        child_destroyed = true;
+        break;
+      }
+    }
+  } while (child_destroyed);
+  return true;
+}
+
+void View::NotifyViewVisibilityChangedUp(View* target) {
+  // Start with the parent as we already notified |this|
+  // in NotifyViewVisibilityChangedDown.
+  for (View* view = parent(); view; view = view->parent()) {
+    bool ret = view->NotifyViewVisibilityChangedAtReceiver(target);
+    DCHECK(ret);
+  }
+}
+
+bool View::PrepareForEmbed() {
+  if (!OwnsView(manager_, this))
+    return false;
+
+  while (!children_.empty())
+    RemoveChild(children_[0]);
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_manager_client_factory.cc b/mojo/services/view_manager/cpp/lib/view_manager_client_factory.cc
new file mode 100644
index 0000000..73a06a1
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_manager_client_factory.cc
@@ -0,0 +1,40 @@
+// 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.
+
+#include "view_manager/cpp/view_manager_client_factory.h"
+
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "view_manager/cpp/lib/view_manager_client_impl.h"
+
+namespace mojo {
+
+ViewManagerClientFactory::ViewManagerClientFactory(
+    Shell* shell,
+    ViewManagerDelegate* delegate)
+    : shell_(shell), delegate_(delegate) {}
+
+ViewManagerClientFactory::~ViewManagerClientFactory() {}
+
+// static
+ViewManagerClient* ViewManagerClientFactory::WeakBindViewManagerToPipe(
+    InterfaceRequest<ViewManagerClient> request,
+    ViewManagerServicePtr view_manager_service,
+    Shell* shell,
+    ViewManagerDelegate* delegate) {
+  const bool delete_on_error = false;
+  auto client = new ViewManagerClientImpl(delegate, shell, request.Pass(),
+                                          delete_on_error);
+  client->SetViewManagerService(view_manager_service.Pass());
+  return client;
+}
+
+// InterfaceFactory<ViewManagerClient> implementation.
+void ViewManagerClientFactory::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<ViewManagerClient> request) {
+  const bool delete_on_error = true;
+  new ViewManagerClientImpl(delegate_, shell_, request.Pass(), delete_on_error);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_manager_client_impl.cc b/mojo/services/view_manager/cpp/lib/view_manager_client_impl.cc
new file mode 100644
index 0000000..6852f66
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_manager_client_impl.cc
@@ -0,0 +1,494 @@
+// 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.
+
+#include "view_manager/cpp/lib/view_manager_client_impl.h"
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "view_manager/cpp/lib/view_private.h"
+#include "view_manager/cpp/util.h"
+#include "view_manager/cpp/view_manager_delegate.h"
+#include "view_manager/cpp/view_observer.h"
+
+namespace mojo {
+
+Id MakeTransportId(ConnectionSpecificId connection_id,
+                   ConnectionSpecificId local_id) {
+  return (connection_id << 16) | local_id;
+}
+
+// Helper called to construct a local view object from transport data.
+View* AddViewToViewManager(ViewManagerClientImpl* client,
+                           View* parent,
+                           const ViewDataPtr& view_data) {
+  // We don't use the ctor that takes a ViewManager here, since it will call
+  // back to the service and attempt to create a new view.
+  View* view = ViewPrivate::LocalCreate();
+  ViewPrivate private_view(view);
+  private_view.set_view_manager(client);
+  private_view.set_id(view_data->view_id);
+  private_view.set_visible(view_data->visible);
+  private_view.set_drawn(view_data->drawn);
+  private_view.LocalSetViewportMetrics(ViewportMetrics(),
+                                       *view_data->viewport_metrics);
+  private_view.set_properties(
+      view_data->properties.To<std::map<std::string, std::vector<uint8_t>>>());
+  client->AddView(view);
+  private_view.LocalSetBounds(Rect(), *view_data->bounds);
+  if (parent)
+    ViewPrivate(parent).LocalAddChild(view);
+  return view;
+}
+
+View* BuildViewTree(ViewManagerClientImpl* client,
+                    const Array<ViewDataPtr>& views,
+                    View* initial_parent) {
+  std::vector<View*> parents;
+  View* root = NULL;
+  View* last_view = NULL;
+  if (initial_parent)
+    parents.push_back(initial_parent);
+  for (size_t i = 0; i < views.size(); ++i) {
+    if (last_view && views[i]->parent_id == last_view->id()) {
+      parents.push_back(last_view);
+    } else if (!parents.empty()) {
+      while (parents.back()->id() != views[i]->parent_id)
+        parents.pop_back();
+    }
+    View* view = AddViewToViewManager(
+        client, !parents.empty() ? parents.back() : NULL, views[i]);
+    if (!last_view)
+      root = view;
+    last_view = view;
+  }
+  return root;
+}
+
+// Responsible for removing a root from the ViewManager when that view is
+// destroyed.
+class RootObserver : public ViewObserver {
+ public:
+  explicit RootObserver(View* root) : root_(root) {}
+  ~RootObserver() override {}
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(view, root_);
+    static_cast<ViewManagerClientImpl*>(root_->view_manager())
+        ->RootDestroyed(root_);
+    view->RemoveObserver(this);
+    delete this;
+  }
+
+  View* root_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RootObserver);
+};
+
+ViewManagerClientImpl::ViewManagerClientImpl(
+    ViewManagerDelegate* delegate,
+    Shell* shell,
+    InterfaceRequest<ViewManagerClient> request,
+    bool delete_on_error)
+    : connection_id_(0),
+      next_id_(1),
+      delegate_(delegate),
+      root_(nullptr),
+      capture_view_(nullptr),
+      focused_view_(nullptr),
+      activated_view_(nullptr),
+      wm_observer_binding_(this),
+      binding_(this, request.Pass()) {
+  if (delete_on_error)
+    binding_.set_connection_error_handler([this]() { delete this; });
+}
+
+ViewManagerClientImpl::~ViewManagerClientImpl() {
+  std::vector<View*> non_owned;
+  while (!views_.empty()) {
+    IdToViewMap::iterator it = views_.begin();
+    if (OwnsView(it->second->id())) {
+      it->second->Destroy();
+    } else {
+      non_owned.push_back(it->second);
+      views_.erase(it);
+    }
+  }
+  // Delete the non-owned views last. In the typical case these are roots. The
+  // exception is the window manager, which may know aboutother random views
+  // that it doesn't own.
+  // NOTE: we manually delete as we're a friend.
+  for (size_t i = 0; i < non_owned.size(); ++i)
+    delete non_owned[i];
+
+  delegate_->OnViewManagerDisconnected(this);
+}
+
+void ViewManagerClientImpl::DestroyView(Id view_id) {
+  DCHECK(service_);
+  service_->DeleteView(view_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::AddChild(Id child_id, Id parent_id) {
+  DCHECK(service_);
+  service_->AddView(parent_id, child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) {
+  DCHECK(service_);
+  service_->RemoveViewFromParent(child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Reorder(Id view_id,
+                                    Id relative_view_id,
+                                    OrderDirection direction) {
+  DCHECK(service_);
+  service_->ReorderView(view_id, relative_view_id, direction,
+                        ActionCompletedCallback());
+}
+
+bool ViewManagerClientImpl::OwnsView(Id id) const {
+  return HiWord(id) == connection_id_;
+}
+
+void ViewManagerClientImpl::SetBounds(Id view_id, const Rect& bounds) {
+  DCHECK(service_);
+  service_->SetViewBounds(view_id, bounds.Clone(), ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetSurfaceId(Id view_id, SurfaceIdPtr surface_id) {
+  DCHECK(service_);
+  if (surface_id.is_null())
+    return;
+  service_->SetViewSurfaceId(view_id, surface_id.Pass(),
+                             ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetFocus(Id view_id) {
+  // In order for us to get here we had to have exposed a view, which implies we
+  // got a connection.
+  DCHECK(service_);
+  service_->PerformAction(view_id, "focus", ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetVisible(Id view_id, bool visible) {
+  DCHECK(service_);
+  service_->SetViewVisibility(view_id, visible, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetProperty(Id view_id,
+                                        const std::string& name,
+                                        const std::vector<uint8_t>& data) {
+  DCHECK(service_);
+  service_->SetViewProperty(view_id, String(name), Array<uint8_t>::From(data),
+                            ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Embed(const String& url, Id view_id) {
+  Embed(url, view_id, nullptr, nullptr);
+}
+
+void ViewManagerClientImpl::Embed(const String& url,
+                                  Id view_id,
+                                  InterfaceRequest<ServiceProvider> services,
+                                  ServiceProviderPtr exposed_services) {
+  DCHECK(service_);
+  service_->EmbedUrl(url, view_id, services.Pass(), exposed_services.Pass(),
+                     ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Embed(Id view_id, ViewManagerClientPtr client) {
+  DCHECK(service_);
+  service_->Embed(view_id, client.Pass(), ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::AddView(View* view) {
+  DCHECK(views_.find(view->id()) == views_.end());
+  views_[view->id()] = view;
+}
+
+void ViewManagerClientImpl::RemoveView(Id view_id) {
+  if (focused_view_ && focused_view_->id() == view_id)
+    OnFocusChanged(0);
+  if (capture_view_ && capture_view_->id() == view_id)
+    OnCaptureChanged(0);
+  if (activated_view_ && activated_view_->id() == view_id)
+    OnActiveWindowChanged(0);
+
+  IdToViewMap::iterator it = views_.find(view_id);
+  if (it != views_.end())
+    views_.erase(it);
+}
+
+void ViewManagerClientImpl::SetViewManagerService(
+    ViewManagerServicePtr service) {
+  DCHECK(!service_);
+  DCHECK(service);
+  service_ = service.Pass();
+}
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManager implementation:
+
+Id ViewManagerClientImpl::CreateViewOnServer() {
+  DCHECK(service_);
+  const Id view_id = MakeTransportId(connection_id_, ++next_id_);
+  service_->CreateView(view_id, [this](ErrorCode code) {
+    OnActionCompleted(code == ErrorCode::NONE);
+  });
+  return view_id;
+}
+
+const std::string& ViewManagerClientImpl::GetEmbedderURL() const {
+  return creator_url_;
+}
+
+View* ViewManagerClientImpl::GetRoot() {
+  return root_;
+}
+
+View* ViewManagerClientImpl::GetViewById(Id id) {
+  IdToViewMap::const_iterator it = views_.find(id);
+  return it != views_.end() ? it->second : NULL;
+}
+
+View* ViewManagerClientImpl::GetFocusedView() {
+  return focused_view_;
+}
+
+View* ViewManagerClientImpl::CreateView() {
+  View* view = new View(this, CreateViewOnServer());
+  AddView(view);
+  return view;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManagerClient implementation:
+
+void ViewManagerClientImpl::OnEmbed(
+    ConnectionSpecificId connection_id,
+    const String& creator_url,
+    ViewDataPtr root_data,
+    ViewManagerServicePtr view_manager_service,
+    InterfaceRequest<ServiceProvider> services,
+    ServiceProviderPtr exposed_services,
+    ScopedMessagePipeHandle window_manager_pipe) {
+  if (view_manager_service) {
+    DCHECK(!service_);
+    service_ = view_manager_service.Pass();
+  }
+  connection_id_ = connection_id;
+  creator_url_ = String::From(creator_url);
+
+  DCHECK(!root_);
+  root_ = AddViewToViewManager(this, nullptr, root_data);
+  root_->AddObserver(new RootObserver(root_));
+
+  window_manager_.Bind(
+      InterfacePtrInfo<WindowManager>(window_manager_pipe.Pass(), 0u));
+  WindowManagerObserverPtr observer;
+  wm_observer_binding_.Bind(GetProxy(&observer));
+  // binding to |this| is safe here as |window_manager_| is bound to our
+  // lifetime.
+  window_manager_->GetFocusedAndActiveViews(
+      observer.Pass(),
+      [this](uint32_t capture_view_id, uint32_t focused_view_id,
+             uint32_t active_view_id) {
+        if (GetViewById(capture_view_id) != capture_view_)
+          OnCaptureChanged(capture_view_id);
+        if (GetViewById(focused_view_id) != focused_view_)
+          OnFocusChanged(focused_view_id);
+        if (GetViewById(active_view_id) != activated_view_)
+          OnActiveWindowChanged(active_view_id);
+      });
+
+  delegate_->OnEmbed(root_, services.Pass(), exposed_services.Pass());
+}
+
+void ViewManagerClientImpl::OnEmbeddedAppDisconnected(Id view_id) {
+  View* view = GetViewById(view_id);
+  if (view) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view).observers(),
+                      OnViewEmbeddedAppDisconnected(view));
+  }
+}
+
+void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id,
+                                                RectPtr old_bounds,
+                                                RectPtr new_bounds) {
+  View* view = GetViewById(view_id);
+  ViewPrivate(view).LocalSetBounds(*old_bounds, *new_bounds);
+}
+
+namespace {
+
+void SetViewportMetricsOnDecendants(View* root,
+                                    const ViewportMetrics& old_metrics,
+                                    const ViewportMetrics& new_metrics) {
+  ViewPrivate(root).LocalSetViewportMetrics(old_metrics, new_metrics);
+  const View::Children& children = root->children();
+  for (size_t i = 0; i < children.size(); ++i)
+    SetViewportMetricsOnDecendants(children[i], old_metrics, new_metrics);
+}
+}
+
+void ViewManagerClientImpl::OnViewViewportMetricsChanged(
+    ViewportMetricsPtr old_metrics,
+    ViewportMetricsPtr new_metrics) {
+  View* view = GetRoot();
+  if (view)
+    SetViewportMetricsOnDecendants(view, *old_metrics, *new_metrics);
+}
+
+void ViewManagerClientImpl::OnViewHierarchyChanged(
+    Id view_id,
+    Id new_parent_id,
+    Id old_parent_id,
+    mojo::Array<ViewDataPtr> views) {
+  View* initial_parent = views.size() ? GetViewById(views[0]->parent_id) : NULL;
+
+  BuildViewTree(this, views, initial_parent);
+
+  View* new_parent = GetViewById(new_parent_id);
+  View* old_parent = GetViewById(old_parent_id);
+  View* view = GetViewById(view_id);
+  if (new_parent)
+    ViewPrivate(new_parent).LocalAddChild(view);
+  else
+    ViewPrivate(old_parent).LocalRemoveChild(view);
+}
+
+void ViewManagerClientImpl::OnViewReordered(Id view_id,
+                                            Id relative_view_id,
+                                            OrderDirection direction) {
+  View* view = GetViewById(view_id);
+  View* relative_view = GetViewById(relative_view_id);
+  if (view && relative_view)
+    ViewPrivate(view).LocalReorder(relative_view, direction);
+}
+
+void ViewManagerClientImpl::OnViewDeleted(Id view_id) {
+  View* view = GetViewById(view_id);
+  if (view)
+    ViewPrivate(view).LocalDestroy();
+}
+
+void ViewManagerClientImpl::OnViewVisibilityChanged(Id view_id, bool visible) {
+  // TODO(sky): there is a race condition here. If this client and another
+  // client change the visibility at the same time the wrong value may be set.
+  // Deal with this some how.
+  View* view = GetViewById(view_id);
+  if (view)
+    ViewPrivate(view).LocalSetVisible(visible);
+}
+
+void ViewManagerClientImpl::OnViewDrawnStateChanged(Id view_id, bool drawn) {
+  View* view = GetViewById(view_id);
+  if (view)
+    ViewPrivate(view).LocalSetDrawn(drawn);
+}
+
+void ViewManagerClientImpl::OnViewSharedPropertyChanged(
+    Id view_id,
+    const String& name,
+    Array<uint8_t> new_data) {
+  View* view = GetViewById(view_id);
+  if (view) {
+    std::vector<uint8_t> data;
+    std::vector<uint8_t>* data_ptr = NULL;
+    if (!new_data.is_null()) {
+      data = new_data.To<std::vector<uint8_t>>();
+      data_ptr = &data;
+    }
+
+    view->SetSharedProperty(name, data_ptr);
+  }
+}
+
+void ViewManagerClientImpl::OnViewInputEvent(
+    Id view_id,
+    EventPtr event,
+    const Callback<void()>& ack_callback) {
+  View* view = GetViewById(view_id);
+  if (view) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view).observers(),
+                      OnViewInputEvent(view, event));
+  }
+  ack_callback.Run();
+}
+
+void ViewManagerClientImpl::OnPerformAction(
+    Id view_id,
+    const String& name,
+    const Callback<void(bool)>& callback) {
+  View* view = GetViewById(view_id);
+  callback.Run(delegate_->OnPerformAction(view, name));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, WindowManagerObserver implementation:
+
+void ViewManagerClientImpl::OnCaptureChanged(Id capture_view_id) {
+  View* gained_capture = GetViewById(capture_view_id);
+  View* lost_capture = capture_view_;
+  if (lost_capture) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(lost_capture).observers(),
+                      OnViewFocusChanged(gained_capture, lost_capture));
+  }
+  capture_view_ = gained_capture;
+  if (gained_capture) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(gained_capture).observers(),
+                      OnViewFocusChanged(gained_capture, lost_capture));
+  }
+}
+
+void ViewManagerClientImpl::OnFocusChanged(Id focused_view_id) {
+  View* focused = GetViewById(focused_view_id);
+  View* blurred = focused_view_;
+  if (blurred) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(blurred).observers(),
+                      OnViewFocusChanged(focused, blurred));
+  }
+  focused_view_ = focused;
+  if (focused) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(focused).observers(),
+                      OnViewFocusChanged(focused, blurred));
+  }
+}
+
+void ViewManagerClientImpl::OnActiveWindowChanged(Id active_view_id) {
+  View* activated = GetViewById(active_view_id);
+  View* deactivated = activated_view_;
+  if (deactivated) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(deactivated).observers(),
+                      OnViewActivationChanged(activated, deactivated));
+  }
+  activated_view_ = activated;
+  if (activated) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(activated).observers(),
+                      OnViewActivationChanged(activated, deactivated));
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, private:
+
+void ViewManagerClientImpl::RootDestroyed(View* root) {
+  DCHECK_EQ(root, root_);
+  root_ = nullptr;
+}
+
+void ViewManagerClientImpl::OnActionCompleted(bool success) {
+  if (!change_acked_callback_.is_null())
+    change_acked_callback_.Run();
+}
+
+Callback<void(bool)> ViewManagerClientImpl::ActionCompletedCallback() {
+  return [this](bool success) { OnActionCompleted(success); };
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_manager_client_impl.h b/mojo/services/view_manager/cpp/lib/view_manager_client_impl.h
new file mode 100644
index 0000000..906a7a6
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_manager_client_impl.h
@@ -0,0 +1,164 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "view_manager/cpp/types.h"
+#include "view_manager/cpp/view.h"
+#include "view_manager/cpp/view_manager.h"
+#include "view_manager/interfaces/view_manager.mojom.h"
+#include "window_manager/interfaces/window_manager.mojom.h"
+
+namespace mojo {
+class Shell;
+class ViewManager;
+class ViewManagerDelegate;
+class ViewManagerTransaction;
+
+// Manages the connection with the View Manager service.
+class ViewManagerClientImpl : public ViewManager,
+                              public ViewManagerClient,
+                              public WindowManagerObserver {
+ public:
+  ViewManagerClientImpl(ViewManagerDelegate* delegate,
+                        Shell* shell,
+                        InterfaceRequest<ViewManagerClient> request,
+                        bool delete_on_error);
+  ~ViewManagerClientImpl() override;
+
+  bool connected() const { return !!service_; }
+  ConnectionSpecificId connection_id() const { return connection_id_; }
+
+  // API exposed to the view implementations that pushes local changes to the
+  // service.
+  void DestroyView(Id view_id);
+
+  // These methods take TransportIds. For views owned by the current connection,
+  // the connection id high word can be zero. In all cases, the TransportId 0x1
+  // refers to the root view.
+  void AddChild(Id child_id, Id parent_id);
+  void RemoveChild(Id child_id, Id parent_id);
+
+  void Reorder(Id view_id, Id relative_view_id, OrderDirection direction);
+
+  // Returns true if the specified view was created by this connection.
+  bool OwnsView(Id id) const;
+
+  void SetBounds(Id view_id, const Rect& bounds);
+  void SetSurfaceId(Id view_id, SurfaceIdPtr surface_id);
+  void SetFocus(Id view_id);
+  void SetVisible(Id view_id, bool visible);
+  void SetProperty(Id view_id,
+                   const std::string& name,
+                   const std::vector<uint8_t>& data);
+
+  void Embed(const String& url, Id view_id);
+  void Embed(const String& url,
+             Id view_id,
+             InterfaceRequest<ServiceProvider> services,
+             ServiceProviderPtr exposed_services);
+  void Embed(Id view_id, ViewManagerClientPtr client);
+
+  void set_change_acked_callback(const Callback<void(void)>& callback) {
+    change_acked_callback_ = callback;
+  }
+  void ClearChangeAckedCallback() { change_acked_callback_.reset(); }
+
+  // Start/stop tracking views. While tracked, they can be retrieved via
+  // ViewManager::GetViewById.
+  void AddView(View* view);
+  void RemoveView(Id view_id);
+
+  void SetViewManagerService(ViewManagerServicePtr service);
+
+ private:
+  friend class RootObserver;
+
+  typedef std::map<Id, View*> IdToViewMap;
+
+  Id CreateViewOnServer();
+
+  // Overridden from ViewManager:
+  const std::string& GetEmbedderURL() const override;
+  View* GetRoot() override;
+  View* GetViewById(Id id) override;
+  View* GetFocusedView() override;
+  View* CreateView() override;
+
+  // Overridden from ViewManagerClient:
+  void OnEmbed(ConnectionSpecificId connection_id,
+               const String& creator_url,
+               ViewDataPtr root,
+               ViewManagerServicePtr view_manager_service,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services,
+               ScopedMessagePipeHandle window_manager_pipe) override;
+  void OnEmbeddedAppDisconnected(Id view_id) override;
+  void OnViewBoundsChanged(Id view_id,
+                           RectPtr old_bounds,
+                           RectPtr new_bounds) override;
+  void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
+                                    ViewportMetricsPtr new_metrics) override;
+  void OnViewHierarchyChanged(Id view_id,
+                              Id new_parent_id,
+                              Id old_parent_id,
+                              Array<ViewDataPtr> views) override;
+  void OnViewReordered(Id view_id,
+                       Id relative_view_id,
+                       OrderDirection direction) override;
+  void OnViewDeleted(Id view_id) override;
+  void OnViewVisibilityChanged(Id view_id, bool visible) override;
+  void OnViewDrawnStateChanged(Id view_id, bool drawn) override;
+  void OnViewSharedPropertyChanged(Id view_id,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override;
+  void OnViewInputEvent(Id view_id,
+                        EventPtr event,
+                        const Callback<void()>& callback) override;
+  void OnPerformAction(Id view_id,
+                       const String& name,
+                       const Callback<void(bool)>& callback) override;
+
+  // Overridden from WindowManagerObserver:
+  void OnCaptureChanged(Id capture_view_id) override;
+  void OnFocusChanged(Id focused_view_id) override;
+  void OnActiveWindowChanged(Id focused_view_id) override;
+
+  void RootDestroyed(View* root);
+
+  void OnActionCompleted(bool success);
+
+  Callback<void(bool)> ActionCompletedCallback();
+
+  ConnectionSpecificId connection_id_;
+  ConnectionSpecificId next_id_;
+
+  std::string creator_url_;
+
+  Callback<void(void)> change_acked_callback_;
+
+  ViewManagerDelegate* delegate_;
+
+  View* root_;
+
+  IdToViewMap views_;
+
+  View* capture_view_;
+  View* focused_view_;
+  View* activated_view_;
+
+  WindowManagerPtr window_manager_;
+  Binding<WindowManagerObserver> wm_observer_binding_;
+
+  Binding<ViewManagerClient> binding_;
+  ViewManagerServicePtr service_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
diff --git a/mojo/services/view_manager/cpp/lib/view_manager_context.cc b/mojo/services/view_manager/cpp/lib/view_manager_context.cc
new file mode 100644
index 0000000..adef60a
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_manager_context.cc
@@ -0,0 +1,45 @@
+// 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.
+
+#include "view_manager/cpp/view_manager_context.h"
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "window_manager/interfaces/window_manager.mojom.h"
+
+namespace mojo {
+class ApplicationImpl;
+
+class ViewManagerContext::InternalState {
+ public:
+  explicit InternalState(ApplicationImpl* application_impl) {
+    application_impl->ConnectToService("mojo:window_manager", &wm_);
+  }
+  ~InternalState() {}
+
+  WindowManager* wm() { return wm_.get(); }
+
+ private:
+  WindowManagerPtr wm_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InternalState);
+};
+
+ViewManagerContext::ViewManagerContext(ApplicationImpl* application_impl)
+    : state_(new InternalState(application_impl)) {}
+ViewManagerContext::~ViewManagerContext() {
+  delete state_;
+}
+
+void ViewManagerContext::Embed(const String& url) {
+  Embed(url, nullptr, nullptr);
+}
+
+void ViewManagerContext::Embed(const String& url,
+                               InterfaceRequest<ServiceProvider> services,
+                               ServiceProviderPtr exposed_services) {
+  state_->wm()->Embed(url, services.Pass(), exposed_services.Pass());
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_observer.cc b/mojo/services/view_manager/cpp/lib/view_observer.cc
new file mode 100644
index 0000000..a72b514
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_observer.cc
@@ -0,0 +1,18 @@
+// 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.
+
+#include "view_manager/cpp/view_observer.h"
+
+namespace mojo {
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewObserver, public:
+
+ViewObserver::TreeChangeParams::TreeChangeParams()
+    : target(nullptr),
+      old_parent(nullptr),
+      new_parent(nullptr),
+      receiver(nullptr) {}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_private.cc b/mojo/services/view_manager/cpp/lib/view_private.cc
new file mode 100644
index 0000000..3f9d25c
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_private.cc
@@ -0,0 +1,20 @@
+// 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.
+
+#include "view_manager/cpp/lib/view_private.h"
+
+namespace mojo {
+
+ViewPrivate::ViewPrivate(View* view) : view_(view) {
+  CHECK(view);
+}
+
+ViewPrivate::~ViewPrivate() {}
+
+// static
+View* ViewPrivate::LocalCreate() {
+  return new View;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/lib/view_private.h b/mojo/services/view_manager/cpp/lib/view_private.h
new file mode 100644
index 0000000..731c678
--- /dev/null
+++ b/mojo/services/view_manager/cpp/lib/view_private.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_PRIVATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_PRIVATE_H_
+
+#include "view_manager/cpp/view.h"
+
+namespace mojo {
+
+// This class is a friend of a View and contains functions to mutate internal
+// state of View.
+class ViewPrivate {
+ public:
+  explicit ViewPrivate(View* view);
+  ~ViewPrivate();
+
+  // Creates and returns a new View. Caller owns the return value.
+  static View* LocalCreate();
+
+  base::ObserverList<ViewObserver>* observers() { return &view_->observers_; }
+
+  void ClearParent() { view_->parent_ = NULL; }
+
+  void set_visible(bool visible) { view_->visible_ = visible; }
+
+  void set_drawn(bool drawn) { view_->drawn_ = drawn; }
+
+  void set_id(Id id) { view_->id_ = id; }
+
+  void set_view_manager(ViewManager* manager) { view_->manager_ = manager; }
+
+  void set_properties(const std::map<std::string, std::vector<uint8_t>>& data) {
+    view_->properties_ = data;
+  }
+
+  void LocalSetViewportMetrics(const ViewportMetrics& old_metrics,
+                               const ViewportMetrics& new_metrics) {
+    view_->LocalSetViewportMetrics(new_metrics, new_metrics);
+  }
+
+  void LocalDestroy() { view_->LocalDestroy(); }
+  void LocalAddChild(View* child) { view_->LocalAddChild(child); }
+  void LocalRemoveChild(View* child) { view_->LocalRemoveChild(child); }
+  void LocalReorder(View* relative, OrderDirection direction) {
+    view_->LocalReorder(relative, direction);
+  }
+  void LocalSetBounds(const Rect& old_bounds, const Rect& new_bounds) {
+    view_->LocalSetBounds(old_bounds, new_bounds);
+  }
+  void LocalSetDrawn(bool drawn) { view_->LocalSetDrawn(drawn); }
+  void LocalSetVisible(bool visible) { view_->LocalSetVisible(visible); }
+
+ private:
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewPrivate);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_LIB_VIEW_PRIVATE_H_
diff --git a/mojo/services/view_manager/cpp/tests/BUILD.gn b/mojo/services/view_manager/cpp/tests/BUILD.gn
new file mode 100644
index 0000000..744074b
--- /dev/null
+++ b/mojo/services/view_manager/cpp/tests/BUILD.gn
@@ -0,0 +1,37 @@
+# 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.
+
+import("//build/config/ui.gni")
+import("//testing/test.gni")
+
+test("mojo_view_manager_lib_unittests") {
+  sources = [
+    "run_all_unittests.cc",
+    "view_manager_test_suite.cc",
+    "view_manager_test_suite.h",
+    "view_unittest.cc",
+  ]
+
+  deps = [
+    "../../../geometry/cpp",
+    "../../../geometry/interfaces",
+    "//base",
+    "//base/test:test_support",
+
+    # TODO(vtl): These non-public deps are illegal here. This should be
+    # converted to an apptest.
+    "//mojo/application",
+    "//mojo/edk/base_edk",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/application",
+    "//mojo/public/cpp/system",
+    "//mojo/services/view_manager/cpp",
+    "//testing/gtest",
+  ]
+
+  if (use_x11) {
+    deps += [ "//ui/gfx/x" ]
+  }
+}
diff --git a/mojo/services/view_manager/cpp/tests/run_all_unittests.cc b/mojo/services/view_manager/cpp/tests/run_all_unittests.cc
new file mode 100644
index 0000000..a3d5d1e
--- /dev/null
+++ b/mojo/services/view_manager/cpp/tests/run_all_unittests.cc
@@ -0,0 +1,15 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "mojo/services/view_manager/cpp/tests/view_manager_test_suite.h"
+
+int main(int argc, char** argv) {
+  mojo::ViewManagerTestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/services/view_manager/cpp/tests/view_manager_test_suite.cc b/mojo/services/view_manager/cpp/tests/view_manager_test_suite.cc
new file mode 100644
index 0000000..3614965
--- /dev/null
+++ b/mojo/services/view_manager/cpp/tests/view_manager_test_suite.cc
@@ -0,0 +1,33 @@
+// 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.
+
+#include "mojo/services/view_manager/cpp/tests/view_manager_test_suite.h"
+
+#include "base/i18n/icu_util.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_connection.h"
+#endif
+
+namespace mojo {
+
+ViewManagerTestSuite::ViewManagerTestSuite(int argc, char** argv)
+    : TestSuite(argc, argv) {}
+
+ViewManagerTestSuite::~ViewManagerTestSuite() {}
+
+void ViewManagerTestSuite::Initialize() {
+#if defined(USE_X11)
+  // Each test ends up creating a new thread for the native viewport service.
+  // In other words we'll use X on different threads, so tell it that.
+  gfx::InitializeThreadedX11();
+#endif
+
+  base::TestSuite::Initialize();
+
+  // base::TestSuite and ViewsInit both try to load icu. That's ok for tests.
+  base::i18n::AllowMultipleInitializeCallsForTesting();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/tests/view_manager_test_suite.h b/mojo/services/view_manager/cpp/tests/view_manager_test_suite.h
new file mode 100644
index 0000000..547efc2
--- /dev/null
+++ b/mojo/services/view_manager/cpp/tests/view_manager_test_suite.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class ViewManagerTestSuite : public base::TestSuite {
+ public:
+  ViewManagerTestSuite(int argc, char** argv);
+  ~ViewManagerTestSuite() override;
+
+ protected:
+  void Initialize() override;
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_
diff --git a/mojo/services/view_manager/cpp/tests/view_unittest.cc b/mojo/services/view_manager/cpp/tests/view_unittest.cc
new file mode 100644
index 0000000..8c7d6dd
--- /dev/null
+++ b/mojo/services/view_manager/cpp/tests/view_unittest.cc
@@ -0,0 +1,874 @@
+// 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.
+
+#include "mojo/services/view_manager/cpp/view.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/services/view_manager/cpp/lib/view_private.h"
+#include "mojo/services/view_manager/cpp/util.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "mojo/services/view_manager/cpp/view_property.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+// View ------------------------------------------------------------------------
+
+typedef testing::Test ViewTest;
+
+// Subclass with public ctor/dtor.
+class TestView : public View {
+ public:
+  TestView() { ViewPrivate(this).set_id(1); }
+  ~TestView() {}
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+TEST_F(ViewTest, AddChild) {
+  TestView v1;
+  TestView v11;
+  v1.AddChild(&v11);
+  EXPECT_EQ(1U, v1.children().size());
+}
+
+TEST_F(ViewTest, RemoveChild) {
+  TestView v1;
+  TestView v11;
+  v1.AddChild(&v11);
+  EXPECT_EQ(1U, v1.children().size());
+  v1.RemoveChild(&v11);
+  EXPECT_EQ(0U, v1.children().size());
+}
+
+TEST_F(ViewTest, Reparent) {
+  TestView v1;
+  TestView v2;
+  TestView v11;
+  v1.AddChild(&v11);
+  EXPECT_EQ(1U, v1.children().size());
+  v2.AddChild(&v11);
+  EXPECT_EQ(1U, v2.children().size());
+  EXPECT_EQ(0U, v1.children().size());
+}
+
+TEST_F(ViewTest, Contains) {
+  TestView v1;
+
+  // Direct descendant.
+  TestView v11;
+  v1.AddChild(&v11);
+  EXPECT_TRUE(v1.Contains(&v11));
+
+  // Indirect descendant.
+  TestView v111;
+  v11.AddChild(&v111);
+  EXPECT_TRUE(v1.Contains(&v111));
+}
+
+TEST_F(ViewTest, GetChildById) {
+  TestView v1;
+  ViewPrivate(&v1).set_id(1);
+  TestView v11;
+  ViewPrivate(&v11).set_id(11);
+  v1.AddChild(&v11);
+  TestView v111;
+  ViewPrivate(&v111).set_id(111);
+  v11.AddChild(&v111);
+
+  // Find direct & indirect descendents.
+  EXPECT_EQ(&v11, v1.GetChildById(v11.id()));
+  EXPECT_EQ(&v111, v1.GetChildById(v111.id()));
+}
+
+TEST_F(ViewTest, DrawnAndVisible) {
+  TestView v1;
+  EXPECT_TRUE(v1.visible());
+  EXPECT_FALSE(v1.IsDrawn());
+
+  ViewPrivate(&v1).set_drawn(true);
+
+  TestView v11;
+  v1.AddChild(&v11);
+  EXPECT_TRUE(v11.visible());
+  EXPECT_TRUE(v11.IsDrawn());
+
+  v1.RemoveChild(&v11);
+  EXPECT_TRUE(v11.visible());
+  EXPECT_FALSE(v11.IsDrawn());
+}
+
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(int, kIntKey, -2);
+DEFINE_VIEW_PROPERTY_KEY(const char*, kStringKey, "squeamish");
+}
+
+TEST_F(ViewTest, Property) {
+  TestView v;
+
+  // Non-existent properties should return the default values.
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+
+  // A set property value should be returned again (even if it's the default
+  // value).
+  v.SetLocalProperty(kIntKey, INT_MAX);
+  EXPECT_EQ(INT_MAX, v.GetLocalProperty(kIntKey));
+  v.SetLocalProperty(kIntKey, -2);
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  v.SetLocalProperty(kIntKey, INT_MIN);
+  EXPECT_EQ(INT_MIN, v.GetLocalProperty(kIntKey));
+
+  v.SetLocalProperty(kStringKey, static_cast<const char*>(NULL));
+  EXPECT_EQ(NULL, v.GetLocalProperty(kStringKey));
+  v.SetLocalProperty(kStringKey, "squeamish");
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+  v.SetLocalProperty(kStringKey, "ossifrage");
+  EXPECT_EQ(std::string("ossifrage"), v.GetLocalProperty(kStringKey));
+
+  // ClearProperty should restore the default value.
+  v.ClearLocalProperty(kIntKey);
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  v.ClearLocalProperty(kStringKey);
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+}
+
+namespace {
+
+class TestProperty {
+ public:
+  TestProperty() {}
+  virtual ~TestProperty() { last_deleted_ = this; }
+  static TestProperty* last_deleted() { return last_deleted_; }
+
+ private:
+  static TestProperty* last_deleted_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestProperty);
+};
+
+TestProperty* TestProperty::last_deleted_ = NULL;
+
+DEFINE_OWNED_VIEW_PROPERTY_KEY(TestProperty, kOwnedKey, NULL);
+
+}  // namespace
+
+TEST_F(ViewTest, OwnedProperty) {
+  TestProperty* p3 = NULL;
+  {
+    TestView v;
+    EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
+    TestProperty* p1 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p1);
+    EXPECT_EQ(p1, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(NULL, TestProperty::last_deleted());
+
+    TestProperty* p2 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p2);
+    EXPECT_EQ(p2, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p1, TestProperty::last_deleted());
+
+    v.ClearLocalProperty(kOwnedKey);
+    EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p2, TestProperty::last_deleted());
+
+    p3 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p3);
+    EXPECT_EQ(p3, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p2, TestProperty::last_deleted());
+  }
+
+  EXPECT_EQ(p3, TestProperty::last_deleted());
+}
+
+// ViewObserver --------------------------------------------------------
+
+typedef testing::Test ViewObserverTest;
+
+bool TreeChangeParamsMatch(const ViewObserver::TreeChangeParams& lhs,
+                           const ViewObserver::TreeChangeParams& rhs) {
+  return lhs.target == rhs.target && lhs.old_parent == rhs.old_parent &&
+         lhs.new_parent == rhs.new_parent && lhs.receiver == rhs.receiver;
+}
+
+class TreeChangeObserver : public ViewObserver {
+ public:
+  explicit TreeChangeObserver(View* observee) : observee_(observee) {
+    observee_->AddObserver(this);
+  }
+  ~TreeChangeObserver() override { observee_->RemoveObserver(this); }
+
+  void Reset() { received_params_.clear(); }
+
+  const std::vector<TreeChangeParams>& received_params() {
+    return received_params_;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnTreeChanging(const TreeChangeParams& params) override {
+    received_params_.push_back(params);
+  }
+  void OnTreeChanged(const TreeChangeParams& params) override {
+    received_params_.push_back(params);
+  }
+
+  View* observee_;
+  std::vector<TreeChangeParams> received_params_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver);
+};
+
+// Adds/Removes v11 to v1.
+TEST_F(ViewObserverTest, TreeChange_SimpleAddRemove) {
+  TestView v1;
+  TreeChangeObserver o1(&v1);
+  EXPECT_TRUE(o1.received_params().empty());
+
+  TestView v11;
+  TreeChangeObserver o11(&v11);
+  EXPECT_TRUE(o11.received_params().empty());
+
+  // Add.
+
+  v1.AddChild(&v11);
+
+  EXPECT_EQ(2U, o1.received_params().size());
+  ViewObserver::TreeChangeParams p1;
+  p1.target = &v11;
+  p1.receiver = &v1;
+  p1.old_parent = NULL;
+  p1.new_parent = &v1;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+  EXPECT_EQ(2U, o11.received_params().size());
+  ViewObserver::TreeChangeParams p11 = p1;
+  p11.receiver = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+
+  o1.Reset();
+  o11.Reset();
+  EXPECT_TRUE(o1.received_params().empty());
+  EXPECT_TRUE(o11.received_params().empty());
+
+  // Remove.
+
+  v1.RemoveChild(&v11);
+
+  EXPECT_EQ(2U, o1.received_params().size());
+  p1.target = &v11;
+  p1.receiver = &v1;
+  p1.old_parent = &v1;
+  p1.new_parent = NULL;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+
+  EXPECT_EQ(2U, o11.received_params().size());
+  p11 = p1;
+  p11.receiver = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+}
+
+// Creates these two trees:
+// v1
+//  +- v11
+// v111
+//  +- v1111
+//  +- v1112
+// Then adds/removes v111 from v11.
+TEST_F(ViewObserverTest, TreeChange_NestedAddRemove) {
+  TestView v1, v11, v111, v1111, v1112;
+
+  // Root tree.
+  v1.AddChild(&v11);
+
+  // Tree to be attached.
+  v111.AddChild(&v1111);
+  v111.AddChild(&v1112);
+
+  TreeChangeObserver o1(&v1), o11(&v11), o111(&v111), o1111(&v1111),
+      o1112(&v1112);
+  ViewObserver::TreeChangeParams p1, p11, p111, p1111, p1112;
+
+  // Add.
+
+  v11.AddChild(&v111);
+
+  EXPECT_EQ(2U, o1.received_params().size());
+  p1.target = &v111;
+  p1.receiver = &v1;
+  p1.old_parent = NULL;
+  p1.new_parent = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+  EXPECT_EQ(2U, o11.received_params().size());
+  p11 = p1;
+  p11.receiver = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+
+  EXPECT_EQ(2U, o111.received_params().size());
+  p111 = p11;
+  p111.receiver = &v111;
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+
+  EXPECT_EQ(2U, o1111.received_params().size());
+  p1111 = p111;
+  p1111.receiver = &v1111;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
+
+  EXPECT_EQ(2U, o1112.received_params().size());
+  p1112 = p111;
+  p1112.receiver = &v1112;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
+
+  // Remove.
+  o1.Reset();
+  o11.Reset();
+  o111.Reset();
+  o1111.Reset();
+  o1112.Reset();
+  EXPECT_TRUE(o1.received_params().empty());
+  EXPECT_TRUE(o11.received_params().empty());
+  EXPECT_TRUE(o111.received_params().empty());
+  EXPECT_TRUE(o1111.received_params().empty());
+  EXPECT_TRUE(o1112.received_params().empty());
+
+  v11.RemoveChild(&v111);
+
+  EXPECT_EQ(2U, o1.received_params().size());
+  p1.target = &v111;
+  p1.receiver = &v1;
+  p1.old_parent = &v11;
+  p1.new_parent = NULL;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+
+  EXPECT_EQ(2U, o11.received_params().size());
+  p11 = p1;
+  p11.receiver = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+
+  EXPECT_EQ(2U, o111.received_params().size());
+  p111 = p11;
+  p111.receiver = &v111;
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+
+  EXPECT_EQ(2U, o1111.received_params().size());
+  p1111 = p111;
+  p1111.receiver = &v1111;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
+
+  EXPECT_EQ(2U, o1112.received_params().size());
+  p1112 = p111;
+  p1112.receiver = &v1112;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
+}
+
+TEST_F(ViewObserverTest, TreeChange_Reparent) {
+  TestView v1, v11, v12, v111;
+  v1.AddChild(&v11);
+  v1.AddChild(&v12);
+  v11.AddChild(&v111);
+
+  TreeChangeObserver o1(&v1), o11(&v11), o12(&v12), o111(&v111);
+
+  // Reparent.
+  v12.AddChild(&v111);
+
+  // v1 (root) should see both changing and changed notifications.
+  EXPECT_EQ(4U, o1.received_params().size());
+  ViewObserver::TreeChangeParams p1;
+  p1.target = &v111;
+  p1.receiver = &v1;
+  p1.old_parent = &v11;
+  p1.new_parent = &v12;
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+  // v11 should see changing notifications.
+  EXPECT_EQ(2U, o11.received_params().size());
+  ViewObserver::TreeChangeParams p11;
+  p11 = p1;
+  p11.receiver = &v11;
+  EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+
+  // v12 should see changed notifications.
+  EXPECT_EQ(2U, o12.received_params().size());
+  ViewObserver::TreeChangeParams p12;
+  p12 = p1;
+  p12.receiver = &v12;
+  EXPECT_TRUE(TreeChangeParamsMatch(p12, o12.received_params().back()));
+
+  // v111 should see both changing and changed notifications.
+  EXPECT_EQ(2U, o111.received_params().size());
+  ViewObserver::TreeChangeParams p111;
+  p111 = p1;
+  p111.receiver = &v111;
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+  EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+}
+
+namespace {
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+  struct Change {
+    View* view;
+    View* relative_view;
+    OrderDirection direction;
+  };
+  typedef std::vector<Change> Changes;
+
+  explicit OrderChangeObserver(View* observee) : observee_(observee) {
+    observee_->AddObserver(this);
+  }
+  ~OrderChangeObserver() override { observee_->RemoveObserver(this); }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes_.swap(changes);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewReordering(View* view,
+                        View* relative_view,
+                        OrderDirection direction) override {
+    OnViewReordered(view, relative_view, direction);
+  }
+
+  void OnViewReordered(View* view,
+                       View* relative_view,
+                       OrderDirection direction) override {
+    Change change;
+    change.view = view;
+    change.relative_view = relative_view;
+    change.direction = direction;
+    changes_.push_back(change);
+  }
+
+  View* observee_;
+  Changes changes_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, Order) {
+  TestView v1, v11, v12, v13;
+  v1.AddChild(&v11);
+  v1.AddChild(&v12);
+  v1.AddChild(&v13);
+
+  // Order: v11, v12, v13
+  EXPECT_EQ(3U, v1.children().size());
+  EXPECT_EQ(&v11, v1.children().front());
+  EXPECT_EQ(&v13, v1.children().back());
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 to front.
+    // Resulting order: v12, v13, v11
+    v11.MoveToFront();
+    EXPECT_EQ(&v12, v1.children().front());
+    EXPECT_EQ(&v11, v1.children().back());
+
+    OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ(&v11, changes[0].view);
+    EXPECT_EQ(&v13, changes[0].relative_view);
+    EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v13, changes[1].relative_view);
+    EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction);
+  }
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 to back.
+    // Resulting order: v11, v12, v13
+    v11.MoveToBack();
+    EXPECT_EQ(&v11, v1.children().front());
+    EXPECT_EQ(&v13, v1.children().back());
+
+    OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ(&v11, changes[0].view);
+    EXPECT_EQ(&v12, changes[0].relative_view);
+    EXPECT_EQ(OrderDirection::BELOW, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(OrderDirection::BELOW, changes[1].direction);
+  }
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 above v12.
+    // Resulting order: v12. v11, v13
+    v11.Reorder(&v12, OrderDirection::ABOVE);
+    EXPECT_EQ(&v12, v1.children().front());
+    EXPECT_EQ(&v13, v1.children().back());
+
+    OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ(&v11, changes[0].view);
+    EXPECT_EQ(&v12, changes[0].relative_view);
+    EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction);
+  }
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 below v12.
+    // Resulting order: v11, v12, v13
+    v11.Reorder(&v12, OrderDirection::BELOW);
+    EXPECT_EQ(&v11, v1.children().front());
+    EXPECT_EQ(&v13, v1.children().back());
+
+    OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ(&v11, changes[0].view);
+    EXPECT_EQ(&v12, changes[0].relative_view);
+    EXPECT_EQ(OrderDirection::BELOW, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(OrderDirection::BELOW, changes[1].direction);
+  }
+}
+
+namespace {
+
+typedef std::vector<std::string> Changes;
+
+std::string ViewIdToString(Id id) {
+  return (id == 0) ? "null"
+                   : base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+std::string RectToString(const Rect& rect) {
+  return base::StringPrintf("%d,%d %dx%d", rect.x, rect.y, rect.width,
+                            rect.height);
+}
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+  explicit BoundsChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~BoundsChangeObserver() override { view_->RemoveObserver(this); }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes.swap(changes_);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewBoundsChanging(View* view,
+                            const Rect& old_bounds,
+                            const Rect& new_bounds) override {
+    changes_.push_back(base::StringPrintf(
+        "view=%s old_bounds=%s new_bounds=%s phase=changing",
+        ViewIdToString(view->id()).c_str(), RectToString(old_bounds).c_str(),
+        RectToString(new_bounds).c_str()));
+  }
+  void OnViewBoundsChanged(View* view,
+                           const Rect& old_bounds,
+                           const Rect& new_bounds) override {
+    changes_.push_back(base::StringPrintf(
+        "view=%s old_bounds=%s new_bounds=%s phase=changed",
+        ViewIdToString(view->id()).c_str(), RectToString(old_bounds).c_str(),
+        RectToString(new_bounds).c_str()));
+  }
+
+  View* view_;
+  Changes changes_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, SetBounds) {
+  TestView v1;
+  {
+    BoundsChangeObserver observer(&v1);
+    Rect rect;
+    rect.width = rect.height = 100;
+    v1.SetBounds(rect);
+
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ(
+        "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changing",
+        changes[0]);
+    EXPECT_EQ(
+        "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changed",
+        changes[1]);
+  }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+  explicit VisibilityChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes.swap(changes_);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewVisibilityChanging(View* view) override {
+    changes_.push_back(
+        base::StringPrintf("view=%s phase=changing visibility=%s",
+                           ViewIdToString(view->id()).c_str(),
+                           view->visible() ? "true" : "false"));
+  }
+  void OnViewVisibilityChanged(View* view) override {
+    changes_.push_back(base::StringPrintf("view=%s phase=changed visibility=%s",
+                                          ViewIdToString(view->id()).c_str(),
+                                          view->visible() ? "true" : "false"));
+  }
+
+  View* view_;
+  Changes changes_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, SetVisible) {
+  TestView v1;
+  EXPECT_TRUE(v1.visible());
+  {
+    // Change visibility from true to false and make sure we get notifications.
+    VisibilityChangeObserver observer(&v1);
+    v1.SetVisible(false);
+
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(2U, changes.size());
+    EXPECT_EQ("view=0,1 phase=changing visibility=true", changes[0]);
+    EXPECT_EQ("view=0,1 phase=changed visibility=false", changes[1]);
+  }
+  {
+    // Set visible to existing value and verify no notifications.
+    VisibilityChangeObserver observer(&v1);
+    v1.SetVisible(false);
+    EXPECT_TRUE(observer.GetAndClearChanges().empty());
+  }
+}
+
+TEST_F(ViewObserverTest, SetVisibleParent) {
+  TestView parent;
+  ViewPrivate(&parent).set_id(1);
+  TestView child;
+  ViewPrivate(&child).set_id(2);
+  parent.AddChild(&child);
+  EXPECT_TRUE(parent.visible());
+  EXPECT_TRUE(child.visible());
+  {
+    // Change visibility from true to false and make sure we get notifications
+    // on the parent.
+    VisibilityChangeObserver observer(&parent);
+    child.SetVisible(false);
+
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(1U, changes.size());
+    EXPECT_EQ("view=0,2 phase=changed visibility=false", changes[0]);
+  }
+}
+
+TEST_F(ViewObserverTest, SetVisibleChild) {
+  TestView parent;
+  ViewPrivate(&parent).set_id(1);
+  TestView child;
+  ViewPrivate(&child).set_id(2);
+  parent.AddChild(&child);
+  EXPECT_TRUE(parent.visible());
+  EXPECT_TRUE(child.visible());
+  {
+    // Change visibility from true to false and make sure we get notifications
+    // on the child.
+    VisibilityChangeObserver observer(&child);
+    parent.SetVisible(false);
+
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(1U, changes.size());
+    EXPECT_EQ("view=0,1 phase=changed visibility=false", changes[0]);
+  }
+}
+
+namespace {
+
+class SharedPropertyChangeObserver : public ViewObserver {
+ public:
+  explicit SharedPropertyChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~SharedPropertyChangeObserver() override { view_->RemoveObserver(this); }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes.swap(changes_);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewSharedPropertyChanged(
+      View* view,
+      const std::string& name,
+      const std::vector<uint8_t>* old_data,
+      const std::vector<uint8_t>* new_data) override {
+    changes_.push_back(base::StringPrintf(
+        "view=%s shared property changed key=%s old_value=%s new_value=%s",
+        ViewIdToString(view->id()).c_str(), name.c_str(),
+        VectorToString(old_data).c_str(), VectorToString(new_data).c_str()));
+  }
+
+  std::string VectorToString(const std::vector<uint8_t>* data) {
+    if (!data)
+      return "NULL";
+    std::string s;
+    for (char c : *data)
+      s += c;
+    return s;
+  }
+
+  View* view_;
+  Changes changes_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(SharedPropertyChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, SetLocalProperty) {
+  TestView v1;
+  std::vector<uint8_t> one(1, '1');
+
+  {
+    // Change visibility from true to false and make sure we get notifications.
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", &one);
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(1U, changes.size());
+    EXPECT_EQ(
+        "view=0,1 shared property changed key=one old_value=NULL new_value=1",
+        changes[0]);
+    EXPECT_EQ(1U, v1.shared_properties().size());
+  }
+  {
+    // Set visible to existing value and verify no notifications.
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", &one);
+    EXPECT_TRUE(observer.GetAndClearChanges().empty());
+    EXPECT_EQ(1U, v1.shared_properties().size());
+  }
+  {
+    // Set the value to NULL to delete it.
+    // Change visibility from true to false and make sure we get notifications.
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", NULL);
+    Changes changes = observer.GetAndClearChanges();
+    ASSERT_EQ(1U, changes.size());
+    EXPECT_EQ(
+        "view=0,1 shared property changed key=one old_value=1 new_value=NULL",
+        changes[0]);
+    EXPECT_EQ(0U, v1.shared_properties().size());
+  }
+  {
+    // Setting a null property to null shouldn't update us.
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", NULL);
+    EXPECT_TRUE(observer.GetAndClearChanges().empty());
+    EXPECT_EQ(0U, v1.shared_properties().size());
+  }
+}
+
+namespace {
+
+typedef std::pair<const void*, intptr_t> PropertyChangeInfo;
+
+class LocalPropertyChangeObserver : public ViewObserver {
+ public:
+  explicit LocalPropertyChangeObserver(View* view)
+      : view_(view), property_key_(nullptr), old_property_value_(-1) {
+    view_->AddObserver(this);
+  }
+  ~LocalPropertyChangeObserver() override { view_->RemoveObserver(this); }
+
+  PropertyChangeInfo PropertyChangeInfoAndClear() {
+    PropertyChangeInfo result(property_key_, old_property_value_);
+    property_key_ = NULL;
+    old_property_value_ = -3;
+    return result;
+  }
+
+ private:
+  void OnViewLocalPropertyChanged(View* window,
+                                  const void* key,
+                                  intptr_t old) override {
+    property_key_ = key;
+    old_property_value_ = old;
+  }
+
+  View* view_;
+  const void* property_key_;
+  intptr_t old_property_value_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(LocalPropertyChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, LocalPropertyChanged) {
+  TestView v1;
+  LocalPropertyChangeObserver o(&v1);
+
+  static const ViewProperty<int> prop = {-2};
+
+  v1.SetLocalProperty(&prop, 1);
+  EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
+  v1.SetLocalProperty(&prop, -2);
+  EXPECT_EQ(PropertyChangeInfo(&prop, 1), o.PropertyChangeInfoAndClear());
+  v1.SetLocalProperty(&prop, 3);
+  EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
+  v1.ClearLocalProperty(&prop);
+  EXPECT_EQ(PropertyChangeInfo(&prop, 3), o.PropertyChangeInfoAndClear());
+
+  // Sanity check to see if |PropertyChangeInfoAndClear| really clears.
+  EXPECT_EQ(PropertyChangeInfo(reinterpret_cast<const void*>(NULL), -3),
+            o.PropertyChangeInfoAndClear());
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/types.h b/mojo/services/view_manager/cpp/types.h
new file mode 100644
index 0000000..3a2e5c8
--- /dev/null
+++ b/mojo/services/view_manager/cpp/types.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_TYPES_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_TYPES_H_
+
+#include <stdint.h>
+
+// Typedefs for the transport types. These typedefs match that of the mojom
+// file, see it for specifics.
+
+namespace mojo {
+
+// Used to identify views and change ids.
+typedef uint32_t Id;
+
+// Used to identify a connection as well as a connection specific view id. For
+// example, the Id for a view consists of the ConnectionSpecificId of the
+// connection and the ConnectionSpecificId of the view.
+typedef uint16_t ConnectionSpecificId;
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_TYPES_H_
diff --git a/mojo/services/view_manager/cpp/util.h b/mojo/services/view_manager/cpp/util.h
new file mode 100644
index 0000000..7e58ad1
--- /dev/null
+++ b/mojo/services/view_manager/cpp/util.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_UTIL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_UTIL_H_
+
+#include "view_manager/cpp/types.h"
+
+// TODO(beng): #$*&@#(@ MacOSX SDK!
+#if defined(HiWord)
+#undef HiWord
+#endif
+#if defined(LoWord)
+#undef LoWord
+#endif
+
+namespace mojo {
+
+inline uint16_t HiWord(uint32_t id) {
+  return static_cast<uint16_t>((id >> 16) & 0xFFFF);
+}
+
+inline uint16_t LoWord(uint32_t id) {
+  return static_cast<uint16_t>(id & 0xFFFF);
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_UTIL_H_
diff --git a/mojo/services/view_manager/cpp/view.h b/mojo/services/view_manager/cpp/view.h
new file mode 100644
index 0000000..ab294da
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view.h
@@ -0,0 +1,214 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "geometry/interfaces/geometry.mojom.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "surfaces/interfaces/surface_id.mojom.h"
+#include "view_manager/cpp/types.h"
+#include "view_manager/interfaces/view_manager.mojom.h"
+#include "view_manager/interfaces/view_manager_constants.mojom.h"
+
+namespace mojo {
+
+class ServiceProviderImpl;
+class View;
+class ViewManager;
+class ViewObserver;
+
+// Defined in view_property.h (which we do not include)
+template <typename T>
+struct ViewProperty;
+
+// Views are owned by the ViewManager.
+// TODO(beng): Right now, you'll have to implement a ViewObserver to track
+//             destruction and NULL any pointers you have.
+//             Investigate some kind of smart pointer or weak pointer for these.
+class View {
+ public:
+  using Children = std::vector<View*>;
+  using SharedProperties = std::map<std::string, std::vector<uint8_t>>;
+
+  // Destroys this view and all its children.
+  void Destroy();
+
+  ViewManager* view_manager() { return manager_; }
+
+  // Configuration.
+  Id id() const { return id_; }
+
+  // Geometric disposition.
+  const Rect& bounds() const { return bounds_; }
+  void SetBounds(const Rect& bounds);
+
+  // Visibility (also see IsDrawn()). When created views are hidden.
+  bool visible() const { return visible_; }
+  void SetVisible(bool value);
+
+  const ViewportMetrics& viewport_metrics() { return *viewport_metrics_; }
+
+  // Returns the set of string to bag of byte properties. These properties are
+  // shared with the view manager.
+  const SharedProperties& shared_properties() const { return properties_; }
+  // Sets a property. If |data| is null, this property is deleted.
+  void SetSharedProperty(const std::string& name,
+                         const std::vector<uint8_t>* data);
+
+  // Sets the |value| of the given window |property|. Setting to the default
+  // value (e.g., NULL) removes the property. The caller is responsible for the
+  // lifetime of any object set as a property on the View.
+  //
+  // These properties are not visible to the view manager.
+  template <typename T>
+  void SetLocalProperty(const ViewProperty<T>* property, T value);
+
+  // Returns the value of the given window |property|.  Returns the
+  // property-specific default value if the property was not previously set.
+  //
+  // These properties are only visible in the current process and are not
+  // shared with other mojo services.
+  template <typename T>
+  T GetLocalProperty(const ViewProperty<T>* property) const;
+
+  // Sets the |property| to its default value. Useful for avoiding a cast when
+  // setting to NULL.
+  //
+  // These properties are only visible in the current process and are not
+  // shared with other mojo services.
+  template <typename T>
+  void ClearLocalProperty(const ViewProperty<T>* property);
+
+  // Type of a function to delete a property that this view owns.
+  typedef void (*PropertyDeallocator)(int64_t value);
+
+  // A View is drawn if the View and all its ancestors are visible and the
+  // View is attached to the root.
+  bool IsDrawn() const;
+
+  // Observation.
+  void AddObserver(ViewObserver* observer);
+  void RemoveObserver(ViewObserver* observer);
+
+  // Tree.
+  View* parent() { return parent_; }
+  const View* parent() const { return parent_; }
+  const Children& children() const { return children_; }
+  View* GetRoot() {
+    return const_cast<View*>(const_cast<const View*>(this)->GetRoot());
+  }
+  const View* GetRoot() const;
+
+  void AddChild(View* child);
+  void RemoveChild(View* child);
+
+  void Reorder(View* relative, OrderDirection direction);
+  void MoveToFront();
+  void MoveToBack();
+
+  bool Contains(View* child) const;
+
+  View* GetChildById(Id id);
+
+  void SetSurfaceId(SurfaceIdPtr id);
+
+  // Focus.
+  void SetFocus();
+
+  // Embedding. See view_manager.mojom for details.
+  void Embed(const String& url);
+  void Embed(const String& url,
+             InterfaceRequest<ServiceProvider> services,
+             ServiceProviderPtr exposed_services);
+  void Embed(ViewManagerClientPtr client);
+
+ protected:
+  // This class is subclassed only by test classes that provide a public ctor.
+  View();
+  ~View();
+
+ private:
+  friend class ViewPrivate;
+  friend class ViewManagerClientImpl;
+
+  View(ViewManager* manager, Id id);
+
+  // Called by the public {Set,Get,Clear}Property functions.
+  int64_t SetLocalPropertyInternal(const void* key,
+                                   const char* name,
+                                   PropertyDeallocator deallocator,
+                                   int64_t value,
+                                   int64_t default_value);
+  int64_t GetLocalPropertyInternal(const void* key,
+                                   int64_t default_value) const;
+
+  void LocalDestroy();
+  void LocalAddChild(View* child);
+  void LocalRemoveChild(View* child);
+  // Returns true if the order actually changed.
+  bool LocalReorder(View* relative, OrderDirection direction);
+  void LocalSetBounds(const Rect& old_bounds, const Rect& new_bounds);
+  void LocalSetViewportMetrics(const ViewportMetrics& old_metrics,
+                               const ViewportMetrics& new_metrics);
+  void LocalSetDrawn(bool drawn);
+  void LocalSetVisible(bool visible);
+
+  // Methods implementing visibility change notifications. See ViewObserver
+  // for more details.
+  void NotifyViewVisibilityChanged(View* target);
+  // Notifies this view's observers. Returns false if |this| was deleted during
+  // the call (by an observer), otherwise true.
+  bool NotifyViewVisibilityChangedAtReceiver(View* target);
+  // Notifies this view and its child hierarchy. Returns false if |this| was
+  // deleted during the call (by an observer), otherwise true.
+  bool NotifyViewVisibilityChangedDown(View* target);
+  // Notifies this view and its parent hierarchy.
+  void NotifyViewVisibilityChangedUp(View* target);
+
+  // Returns true if embed is allowed for this node. If embedding is allowed all
+  // the children are removed.
+  bool PrepareForEmbed();
+
+  ViewManager* manager_;
+  Id id_;
+  View* parent_;
+  Children children_;
+
+  base::ObserverList<ViewObserver> observers_;
+
+  Rect bounds_;
+  ViewportMetricsPtr viewport_metrics_;
+
+  bool visible_;
+
+  SharedProperties properties_;
+
+  // Drawn state is derived from the visible state and the parent's visible
+  // state. This field is only used if the view has no parent (eg it's a root).
+  bool drawn_;
+
+  // Value struct to keep the name and deallocator for this property.
+  // Key cannot be used for this purpose because it can be char* or
+  // WindowProperty<>.
+  struct Value {
+    const char* name;
+    int64_t value;
+    PropertyDeallocator deallocator;
+  };
+
+  std::map<const void*, Value> prop_map_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(View);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_H_
diff --git a/mojo/services/view_manager/cpp/view_manager.h b/mojo/services/view_manager/cpp/view_manager.h
new file mode 100644
index 0000000..b2018ec
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_manager.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_H_
+
+#include <string>
+
+#include "view_manager/cpp/types.h"
+
+namespace mojo {
+class View;
+
+// Encapsulates a connection to the view manager service. A unique connection
+// is made every time an app is embedded.
+class ViewManager {
+ public:
+  // Returns the URL of the application that embedded this application.
+  virtual const std::string& GetEmbedderURL() const = 0;
+
+  // Returns the root of this connection.
+  virtual View* GetRoot() = 0;
+
+  // Returns a View known to this connection.
+  virtual View* GetViewById(Id id) = 0;
+
+  // Returns the focused view; null if focus is not yet known or another app is
+  // focused.
+  virtual View* GetFocusedView() = 0;
+
+  // Creates and returns a new View (which is owned by the ViewManager). Views
+  // are initially hidden, use SetVisible(true) to show.
+  virtual View* CreateView() = 0;
+
+ protected:
+  virtual ~ViewManager() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_H_
diff --git a/mojo/services/view_manager/cpp/view_manager_client_factory.h b/mojo/services/view_manager/cpp/view_manager_client_factory.h
new file mode 100644
index 0000000..f4e3cf8
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_manager_client_factory.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CLIENT_FACTORY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CLIENT_FACTORY_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "view_manager/interfaces/view_manager.mojom.h"
+
+namespace mojo {
+
+class ViewManagerDelegate;
+class Shell;
+
+// Add an instance of this class to an incoming connection to allow it to
+// instantiate ViewManagerClient implementations in response to
+// ViewManagerClient requests.
+class ViewManagerClientFactory : public InterfaceFactory<ViewManagerClient> {
+ public:
+  ViewManagerClientFactory(Shell* shell, ViewManagerDelegate* delegate);
+  ~ViewManagerClientFactory() override;
+
+  // Creates a ViewManagerClient from the supplied arguments. Returns ownership
+  // to the caller.
+  static ViewManagerClient* WeakBindViewManagerToPipe(
+      InterfaceRequest<ViewManagerClient> request,
+      ViewManagerServicePtr view_manager_service,
+      Shell* shell,
+      ViewManagerDelegate* delegate);
+
+  // InterfaceFactory<ViewManagerClient> implementation.
+  void Create(ApplicationConnection* connection,
+              InterfaceRequest<ViewManagerClient> request) override;
+
+ private:
+  Shell* shell_;
+  ViewManagerDelegate* delegate_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CLIENT_FACTORY_H_
diff --git a/mojo/services/view_manager/cpp/view_manager_context.h b/mojo/services/view_manager/cpp/view_manager_context.h
new file mode 100644
index 0000000..4065460
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_manager_context.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CONTEXT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CONTEXT_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class ApplicationImpl;
+
+class ViewManagerContext {
+ public:
+  explicit ViewManagerContext(ApplicationImpl* application_impl);
+  ~ViewManagerContext();
+
+  // Embed an application @ |url| at an appropriate View.
+  // The first time this method is called in the life time of the View Manager
+  // service the "appropriate View" is defined as being the service' root View.
+  // Subsequent times, the implementation of this method is delegated to the
+  // application embedded at the service root View. This application will have a
+  // specific definition of where within its View hierarchy to embed an
+  // un-parented URL.
+  // |services| encapsulates services offered by the embedder to the embedded
+  // app alongside this Embed() call. |exposed_services| provides a means for
+  // the embedder to connect to services exposed by the embedded app.
+  void Embed(const String& url);
+  void Embed(const String& url,
+             InterfaceRequest<ServiceProvider> services,
+             ServiceProviderPtr exposed_Services);
+
+ private:
+  class InternalState;
+  InternalState* state_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerContext);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_CONTEXT_H_
diff --git a/mojo/services/view_manager/cpp/view_manager_delegate.cc b/mojo/services/view_manager/cpp/view_manager_delegate.cc
new file mode 100644
index 0000000..959640d
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_manager_delegate.cc
@@ -0,0 +1,14 @@
+// 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 "view_manager/cpp/view_manager_delegate.h"
+
+namespace mojo {
+
+bool ViewManagerDelegate::OnPerformAction(View* view,
+                                          const std::string& action) {
+  return false;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/view_manager_delegate.h b/mojo/services/view_manager/cpp/view_manager_delegate.h
new file mode 100644
index 0000000..35775cd
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_manager_delegate.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_DELEGATE_H_
+
+#include <string>
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+class View;
+class ViewManager;
+
+// Interface implemented by an application using the view manager.
+class ViewManagerDelegate {
+ public:
+  // Called when the application implementing this interface is embedded at
+  // |root|. Every embed results in a new ViewManager and root View being
+  // created. |root| and it's corresponding ViewManager are valid until
+  // OnViewManagerDisconnected() is called with the same object.
+  //
+  // |services| exposes the services offered by the embedder to the delegate.
+  //
+  // |exposed_services| is an object that the delegate can add services to
+  // expose to the embedder.
+  //
+  // Note that if a different application is subsequently embedded at |root|,
+  // the pipes connecting |services| and |exposed_services| to the embedder and
+  // any services obtained from them are not broken and will continue to be
+  // valid.
+  virtual void OnEmbed(View* root,
+                       InterfaceRequest<ServiceProvider> services,
+                       ServiceProviderPtr exposed_services) = 0;
+
+  // Called when a connection to the view manager service is closed.
+  // |view_manager| is not valid after this function returns.
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) = 0;
+
+  // Asks the delegate to perform the specified action.
+  // TODO(sky): nuke! See comments in view_manager.mojom for details.
+  virtual bool OnPerformAction(View* view, const std::string& action);
+
+ protected:
+  virtual ~ViewManagerDelegate() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/view_manager/cpp/view_observer.h b/mojo/services/view_manager/cpp/view_observer.h
new file mode 100644
index 0000000..720a5b5
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_observer.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_OBSERVER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_OBSERVER_H_
+
+#include <vector>
+
+#include "input_events/interfaces/input_events.mojom.h"
+#include "view_manager/cpp/view.h"
+
+namespace mojo {
+
+class View;
+
+// A note on -ing and -ed suffixes:
+//
+// -ing methods are called before changes are applied to the local view model.
+// -ed methods are called after changes are applied to the local view model.
+//
+// If the change originated from another connection to the view manager, it's
+// possible that the change has already been applied to the service-side model
+// prior to being called, so for example in the case of OnViewDestroying(), it's
+// possible the view has already been destroyed on the service side.
+
+class ViewObserver {
+ public:
+  struct TreeChangeParams {
+    TreeChangeParams();
+    View* target;
+    View* old_parent;
+    View* new_parent;
+    View* receiver;
+  };
+
+  virtual void OnTreeChanging(const TreeChangeParams& params) {}
+  virtual void OnTreeChanged(const TreeChangeParams& params) {}
+
+  virtual void OnViewReordering(View* view,
+                                View* relative_view,
+                                OrderDirection direction) {}
+  virtual void OnViewReordered(View* view,
+                               View* relative_view,
+                               OrderDirection direction) {}
+
+  virtual void OnViewDestroying(View* view) {}
+  virtual void OnViewDestroyed(View* view) {}
+
+  virtual void OnViewBoundsChanging(View* view,
+                                    const Rect& old_bounds,
+                                    const Rect& new_bounds) {}
+  virtual void OnViewBoundsChanged(View* view,
+                                   const Rect& old_bounds,
+                                   const Rect& new_bounds) {}
+
+  virtual void OnViewViewportMetricsChanged(View* view,
+                                            const ViewportMetrics& old_bounds,
+                                            const ViewportMetrics& new_bounds) {
+  }
+
+  virtual void OnViewCaptureChanged(View* gained_capture, View* lost_capture) {}
+  virtual void OnViewFocusChanged(View* gained_focus, View* lost_focus) {}
+  virtual void OnViewActivationChanged(View* gained_active, View* lost_active) {
+  }
+
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) {}
+
+  virtual void OnViewVisibilityChanging(View* view) {}
+  virtual void OnViewVisibilityChanged(View* view) {}
+
+  // Invoked when this View's shared properties have changed. This can either
+  // be caused by SetSharedProperty() being called locally, or by us receiving
+  // a mojo message that this property has changed. If this property has been
+  // added, |old_data| is null. If this property was removed, |new_data| is
+  // null.
+  virtual void OnViewSharedPropertyChanged(
+      View* view,
+      const std::string& name,
+      const std::vector<uint8_t>* old_data,
+      const std::vector<uint8_t>* new_data) {}
+
+  // Invoked when SetProperty() or ClearProperty() is called on the window.
+  // |key| is either a WindowProperty<T>* (SetProperty, ClearProperty). Either
+  // way, it can simply be compared for equality with the property
+  // constant. |old| is the old property value, which must be cast to the
+  // appropriate type before use.
+  virtual void OnViewLocalPropertyChanged(View* view,
+                                          const void* key,
+                                          intptr_t old) {}
+
+  virtual void OnViewEmbeddedAppDisconnected(View* view) {}
+
+  // Sent when the drawn state changes. This is only sent for the root nodes
+  // when embedded.
+  virtual void OnViewDrawnChanging(View* view) {}
+  virtual void OnViewDrawnChanged(View* view) {}
+
+ protected:
+  virtual ~ViewObserver() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_OBSERVER_H_
diff --git a/mojo/services/view_manager/cpp/view_property.h b/mojo/services/view_manager/cpp/view_property.h
new file mode 100644
index 0000000..312b9bb
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_property.h
@@ -0,0 +1,139 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_PROPERTY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_PROPERTY_H_
+
+#include <stdint.h>
+
+// This header should be included by code that defines ViewProperties. It
+// should not be included by code that only gets and sets ViewProperties.
+//
+// To define a new ViewProperty:
+//
+//  #include "view_manager/cpp/view_property.h"
+//
+//  DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(FOO_EXPORT, MyType);
+//  namespace foo {
+//    // Use this to define an exported property that is premitive,
+//    // or a pointer you don't want automatically deleted.
+//    DEFINE_VIEW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+//    // Use this to define an exported property whose value is a heap
+//    // allocated object, and has to be owned and freed by the view.
+//    DEFINE_OWNED_VIEW_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, nullptr);
+//
+//    // Use this to define a non exported property that is primitive,
+//    // or a pointer you don't want to automatically deleted, and is used
+//    // only in a specific file. This will define the property in an unnamed
+//    // namespace which cannot be accessed from another file.
+//    DEFINE_LOCAL_VIEW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+//  }  // foo namespace
+//
+// To define a new type used for ViewProperty.
+//
+//  // outside all namespaces:
+//  DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(FOO_EXPORT, MyType)
+//
+// If a property type is not exported, use DECLARE_VIEW_PROPERTY_TYPE(MyType)
+// which is a shorthand for DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(, MyType).
+
+namespace mojo {
+namespace {
+
+// No single new-style cast works for every conversion to/from int64_t, so we
+// need this helper class. A third specialization is needed for bool because
+// MSVC warning C4800 (forcing value to bool) is not suppressed by an explicit
+// cast (!).
+template <typename T>
+class ViewPropertyCaster {
+ public:
+  static int64_t ToInt64(T x) { return static_cast<int64_t>(x); }
+  static T FromInt64(int64_t x) { return static_cast<T>(x); }
+};
+template <typename T>
+class ViewPropertyCaster<T*> {
+ public:
+  static int64_t ToInt64(T* x) { return reinterpret_cast<int64_t>(x); }
+  static T* FromInt64(int64_t x) { return reinterpret_cast<T*>(x); }
+};
+template <>
+class ViewPropertyCaster<bool> {
+ public:
+  static int64_t ToInt64(bool x) { return static_cast<int64_t>(x); }
+  static bool FromInt64(int64_t x) { return x != 0; }
+};
+
+}  // namespace
+
+template <typename T>
+struct ViewProperty {
+  T default_value;
+  const char* name;
+  View::PropertyDeallocator deallocator;
+};
+
+template <typename T>
+void View::SetLocalProperty(const ViewProperty<T>* property, T value) {
+  int64_t old = SetLocalPropertyInternal(
+      property, property->name,
+      value == property->default_value ? nullptr : property->deallocator,
+      ViewPropertyCaster<T>::ToInt64(value),
+      ViewPropertyCaster<T>::ToInt64(property->default_value));
+  if (property->deallocator &&
+      old != ViewPropertyCaster<T>::ToInt64(property->default_value)) {
+    (*property->deallocator)(old);
+  }
+}
+
+template <typename T>
+T View::GetLocalProperty(const ViewProperty<T>* property) const {
+  return ViewPropertyCaster<T>::FromInt64(GetLocalPropertyInternal(
+      property, ViewPropertyCaster<T>::ToInt64(property->default_value)));
+}
+
+template <typename T>
+void View::ClearLocalProperty(const ViewProperty<T>* property) {
+  SetLocalProperty(property, property->default_value);
+}
+
+}  // namespace mojo
+
+// Macros to instantiate the property getter/setter template functions.
+#define DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(EXPORT, T)                         \
+  template EXPORT void mojo::View::SetLocalProperty(                           \
+      const mojo::ViewProperty<T>*, T);                                        \
+  template EXPORT T mojo::View::GetLocalProperty(const mojo::ViewProperty<T>*) \
+      const;                                                                   \
+  template EXPORT void mojo::View::ClearLocalProperty(                         \
+      const mojo::ViewProperty<T>*);
+#define DECLARE_VIEW_PROPERTY_TYPE(T) DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(, T)
+
+#define DEFINE_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)                       \
+  COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64_t), property_type_too_large); \
+  namespace {                                                               \
+  const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr};  \
+  }                                                                         \
+  const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value;
+
+#define DEFINE_LOCAL_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)                 \
+  COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64_t), property_type_too_large); \
+  namespace {                                                               \
+  const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr};  \
+  const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value;               \
+  }
+
+#define DEFINE_OWNED_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)            \
+  namespace {                                                          \
+  void Deallocator##NAME(int64_t p) {                                  \
+    enum { type_must_be_complete = sizeof(TYPE) };                     \
+    delete mojo::ViewPropertyCaster<TYPE*>::FromInt64(p);              \
+  }                                                                    \
+  const mojo::ViewProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME,      \
+                                                  &Deallocator##NAME}; \
+  }                                                                    \
+  const mojo::ViewProperty<TYPE*>* const NAME = &NAME##_Value;
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_PROPERTY_H_
diff --git a/mojo/services/view_manager/cpp/view_tracker.cc b/mojo/services/view_manager/cpp/view_tracker.cc
new file mode 100644
index 0000000..35ab3d4
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_tracker.cc
@@ -0,0 +1,40 @@
+// 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.
+
+#include "view_manager/cpp/view_tracker.h"
+
+namespace mojo {
+
+ViewTracker::ViewTracker() {}
+
+ViewTracker::~ViewTracker() {
+  for (Views::iterator i = views_.begin(); i != views_.end(); ++i)
+    (*i)->RemoveObserver(this);
+}
+
+void ViewTracker::Add(View* view) {
+  if (views_.count(view))
+    return;
+
+  view->AddObserver(this);
+  views_.insert(view);
+}
+
+void ViewTracker::Remove(View* view) {
+  if (views_.count(view)) {
+    views_.erase(view);
+    view->RemoveObserver(this);
+  }
+}
+
+bool ViewTracker::Contains(View* view) {
+  return views_.count(view) > 0;
+}
+
+void ViewTracker::OnViewDestroying(View* view) {
+  DCHECK_GT(views_.count(view), 0u);
+  Remove(view);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/view_manager/cpp/view_tracker.h b/mojo/services/view_manager/cpp/view_tracker.h
new file mode 100644
index 0000000..572e535
--- /dev/null
+++ b/mojo/services/view_manager/cpp/view_tracker.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_TRACKER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_TRACKER_H_
+
+#include <stdint.h>
+#include <set>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "view_manager/cpp/view_observer.h"
+
+namespace mojo {
+
+class ViewTracker : public ViewObserver {
+ public:
+  using Views = std::set<View*>;
+
+  ViewTracker();
+  ~ViewTracker() override;
+
+  // Returns the set of views being observed.
+  const std::set<View*>& views() const { return views_; }
+
+  // Adds |view| to the set of Views being tracked.
+  void Add(View* view);
+
+  // Removes |view| from the set of views being tracked.
+  void Remove(View* view);
+
+  // Returns true if |view| was previously added and has not been removed or
+  // deleted.
+  bool Contains(View* view);
+
+  // ViewObserver overrides:
+  void OnViewDestroying(View* view) override;
+
+ private:
+  Views views_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CPP_VIEW_TRACKER_H_
diff --git a/mojo/services/view_manager/interfaces/BUILD.gn b/mojo/services/view_manager/interfaces/BUILD.gn
new file mode 100644
index 0000000..8d687a0
--- /dev/null
+++ b/mojo/services/view_manager/interfaces/BUILD.gn
@@ -0,0 +1,25 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "animations.mojom",
+    "view_manager.mojom",
+    "view_manager_constants.mojom",
+  ]
+
+  import_dirs = [ get_path_info("../../", "abspath") ]
+
+  mojo_sdk_deps = [ "mojo/public/interfaces/application" ]
+
+  deps = [
+    "../../geometry/interfaces",
+    "../../input_events/interfaces",
+    "../../native_viewport/interfaces",
+    "../../surfaces/interfaces:surface_id",
+  ]
+}
diff --git a/mojo/services/view_manager/interfaces/animations.mojom b/mojo/services/view_manager/interfaces/animations.mojom
new file mode 100644
index 0000000..56b1b01
--- /dev/null
+++ b/mojo/services/view_manager/interfaces/animations.mojom
@@ -0,0 +1,62 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "geometry/interfaces/geometry.mojom";
+
+enum AnimationTweenType {
+  LINEAR,
+  EASE_IN,
+  EASE_OUT,
+  EASE_IN_OUT,
+};
+
+enum AnimationProperty {
+  // Used for pausing.
+  NONE,
+  OPACITY,
+  TRANSFORM,
+};
+
+struct AnimationValue {
+  float float_value;
+  Transform transform;
+};
+
+// Identifies how a particular property should be animated between a start and
+// target value.
+struct AnimationElement {
+  AnimationProperty property;
+
+  // Duration is in microseconds.
+  int64 duration;
+
+  AnimationTweenType tween_type;
+
+  // If not specified the start value is taken from either the current value
+  // (for the first element) or the target_value of the previous element.
+  AnimationValue? start_value;
+
+  // target_value may be null when property is NONE.
+  AnimationValue? target_value;
+};
+
+// An AnimationSequence consists of a number of AnimationElements to animate.
+// Each element is animated serially.
+struct AnimationSequence {
+  // Number of times to run the sequence. Value of 0 means run until
+  // explicitly stopped.
+  uint32 cycle_count;
+
+  array<AnimationElement> elements;
+};
+
+// AnimationGroup identifies a view and a set of AnimationSequences to apply
+// to the view. Each sequence is run in parallel.
+struct AnimationGroup {
+  uint32 view_id;
+  array<AnimationSequence> sequences;
+};
diff --git a/mojo/services/view_manager/interfaces/view_manager.mojom b/mojo/services/view_manager/interfaces/view_manager.mojom
new file mode 100644
index 0000000..becf941
--- /dev/null
+++ b/mojo/services/view_manager/interfaces/view_manager.mojom
@@ -0,0 +1,233 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "geometry/interfaces/geometry.mojom";
+import "input_events/interfaces/input_events.mojom";
+import "mojo/public/interfaces/application/service_provider.mojom";
+import "native_viewport/interfaces/native_viewport.mojom";
+import "surfaces/interfaces/surface_id.mojom";
+import "view_manager/interfaces/view_manager_constants.mojom";
+
+struct ViewData {
+  uint32 parent_id;
+  uint32 view_id;
+  mojo.Rect bounds;
+  map<string, array<uint8>> properties;
+  // True if this view is visible. The view may not be drawn on screen (see
+  // drawn for specifics).
+  bool visible;
+  // True if this view is drawn on screen. A view is drawn if attached to the
+  // root and all ancestors (including this view) are visible.
+  bool drawn;
+  ViewportMetrics viewport_metrics;
+};
+
+enum ErrorCode {
+  NONE,
+  VALUE_IN_USE,
+  ILLEGAL_ARGUMENT,
+};
+
+// Views are identified by a uint32. The upper 16 bits are the connection id,
+// and the lower 16 the id assigned by the client.
+//
+// The root view is identified with a connection id of 0, and value of 1.
+[ServiceName="mojo::ViewManagerService"]
+interface ViewManagerService {
+  // Creates a new view with the specified id. It is up to the client to ensure
+  // the id is unique to the connection (the id need not be globally unique).
+  // Additionally the connection id (embedded in |view_id|) must match that of
+  // the connection.
+  // Errors:
+  //   ERROR_CODE_VALUE_IN_USE: a view already exists with the specified id.
+  //   ERROR_CODE_ILLEGAL_ARGUMENT: The connection part of |view_id| does not
+  //     match the connection id of the client.
+  //
+  // TODO(erg): Once we have default values in mojo, make this take a map of
+  // properties.
+  CreateView(uint32 view_id) => (ErrorCode error_code);
+
+  // Deletes a view. This does not recurse. No hierarchy change notifications
+  // are sent as a result of this. Only the connection that created the view can
+  // delete it.
+  DeleteView(uint32 view_id) => (bool success);
+
+  // Sets the specified bounds of the specified view.
+  SetViewBounds(uint32 view_id, mojo.Rect bounds) => (bool success);
+
+  // Sets the visibility of the specified view to |visible|. Connections are
+  // allowed to change the visibility of any view they have created, as well as
+  // any of their roots.
+  SetViewVisibility(uint32 view_id, bool visible) => (bool success);
+
+  // Sets an individual named property. Setting an individual property to null
+  // deletes the property.
+  SetViewProperty(uint32 view_id,
+                  string name,
+                  array<uint8>? value) => (bool success);
+
+  // Reparents a view.
+  // This fails for any of the following reasons:
+  // . |parent| or |child| does not identify a valid view.
+  // . |child| is an ancestor of |parent|.
+  // . |child| is already a child of |parent|.
+  //
+  // This may result in a connection getting OnViewDeleted(). See
+  // RemoveViewFromParent for details.
+  AddView(uint32 parent, uint32 child) => (bool success);
+
+  // Removes a view from its current parent. This fails if the view is not
+  // valid or the view already has no parent.
+  //
+  // Removing a view from a parent may result in OnViewDeleted() being sent to
+  // other connections. For example, connection A has views 1 and 2, with 2 a
+  // child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets
+  // OnViewDeleted(). This is done as view 2 is effectively no longer visible to
+  // connection B.
+  RemoveViewFromParent(uint32 view_id) => (bool success);
+
+  // Reorders a view in its parent, relative to |relative_view_id| according to
+  // |direction|.
+  // Only the connection that created the view's parent can reorder its
+  // children.
+  ReorderView(uint32 view_id,
+              uint32 relative_view_id,
+              OrderDirection direction) => (bool success);
+
+  // Returns the views comprising the tree starting at |view_id|. |view_id| is
+  // the first result in the return value, unless |view_id| is invalid, in which
+  // case an empty vector is returned. The views are visited using a depth first
+  // search (pre-order).
+  GetViewTree(uint32 view_id) => (array<ViewData> views);
+
+  // Shows the surface in the specified view.
+  SetViewSurfaceId(uint32 view_id, SurfaceId surface_id) => (bool success);
+
+  // A connection may grant access to a view from another connection by way of
+  // the embed functions. There are two variants of this call:
+  //
+  // . EmbedUrl: the ViewManager connects to the app at the supplied url and
+  //   asks it for a ViewManagerClient.
+  // . With the second variant a ViewManagerClient is directly supplied.
+  //
+  // In both cases the new ViewManagerClient is configured with a root of
+  // |view_id|.
+  //
+  // The caller must have created |view_id|. If not the request fails and the
+  // response is false.
+  //
+  // A view may only be a root of one connection at a time. Subsequent calls to
+  // Embed() for the same view result in the view being removed from the
+  // currently embedded app. The embedded app is told this by way of
+  // OnViewDeleted().
+  //
+  // The embedder can detect when the embedded app disconnects by way of
+  // OnEmbeddedAppDisconnected().
+  //
+  // When a connection embeds an app the connection no longer has priviledges
+  // to access or see any of the children of the view. If the view had existing
+  // children the children are removed. The one exception is the root
+  // connection.
+  //
+  // |services| encapsulates services offered by the embedder to the embedded
+  // app alongside this Embed() call. |exposed_services| provides a means for
+  // the embedder to connect to services exposed by the embedded app. Note that
+  // if a different app is subsequently embedded at |view_id| the
+  // ServiceProvider connections to its client in the embedded app and any
+  // services it provided are not broken and continue to be valid.
+  EmbedUrl(string url,
+           uint32 view_id,
+           ServiceProvider&? services,
+           ServiceProvider? exposed_services) => (bool success);
+  Embed(uint32 view_id, ViewManagerClient client) => (bool success);
+
+  // Requests the WindowManager to perform an action on the specified view.
+  // It's up to the WindowManager to decide what |action| is.
+  //
+  // TODO(sky): nuke this. This is here to guarantee the state of the
+  // WindowManager matches that of the ViewManager at the time the client
+  // invokes the function. When we can enforce ordering this won't be necessary.
+  PerformAction(uint32 view_id, string action) => (bool success);
+};
+
+// Changes to views are not sent to the connection that originated the
+// change. For example, if connection 1 changes the bounds of a view by calling
+// SetBounds(), connection 1 does not receive OnViewBoundsChanged().
+[ServiceName="mojo::ViewManagerClient"]
+interface ViewManagerClient {
+  // Invoked when the client application has been embedded at |root|.
+  // See Embed() on ViewManagerService for more details. |view_manager_service|
+  // will be a handle back to the view manager service, unless the connection is
+  // to the WindowManager in which case it will be null.
+  // |window_manager_pipe| is a pipe to the WindowManager.
+  OnEmbed(uint16 connection_id,
+          string embedder_url,
+          ViewData root,
+          ViewManagerService? view_manager_service,
+          ServiceProvider&? services,
+          ServiceProvider? exposed_services,
+          handle<message_pipe> window_manager_pipe);
+
+  // Invoked when the application embedded at |view| is disconnected.
+  OnEmbeddedAppDisconnected(uint32 view);
+
+  // Invoked when a view's bounds have changed.
+  OnViewBoundsChanged(uint32 view,
+                      mojo.Rect old_bounds,
+                      mojo.Rect new_bounds);
+
+  // Invoked when the viewport metrics for the view have changed.
+  // Clients are expected to propagate this to the view tree.
+  OnViewViewportMetricsChanged(mojo.ViewportMetrics old_metrics,
+                               mojo.ViewportMetrics new_metrics);
+
+  // Invoked when a change is done to the hierarchy. A value of 0 is used to
+  // identify a null view. For example, if the old_parent is NULL, 0 is
+  // supplied.
+  // |views| contains any views that are that the client has not been told
+  // about. This is not sent for hierarchy changes of views not known to this
+  // client or not attached to the tree.
+  OnViewHierarchyChanged(uint32 view,
+                         uint32 new_parent,
+                         uint32 old_parent,
+                         array<ViewData> views);
+
+  // Invoked when the order of views within a parent changes.
+  OnViewReordered(uint32 view_id,
+                  uint32 relative_view_id,
+                  OrderDirection direction);
+
+  // Invoked when a view is deleted.
+  OnViewDeleted(uint32 view);
+
+  // Invoked when the visibility of the specified view changes.
+  OnViewVisibilityChanged(uint32 view, bool visible);
+
+  // Invoked when a change to the visibility of |view| or one if it's ancestors
+  // is done such that the drawn state changes. This is only invoked for the
+  // top most view of a particular connection. For example, if you have the
+  // hierarchy: A -> B1 -> B2 (B2 is a child of B1 and B1 a child of A), B1/B2
+  // are from connection 2 and A from connection 1 with all views visible and
+  // drawn and the visiblity of A changes to false, then connection 2 is told
+  // the drawn state of B1 has changed (to false), but is not told anything
+  // about B2 as it's drawn state can be calculated from that of B1.
+  //
+  // NOTE: This is not invoked if OnViewVisibilityChanged() is invoked.
+  OnViewDrawnStateChanged(uint32 view, bool drawn);
+
+  // Invoked when a view property is changed. If this change is a removal,
+  // |new_data| is null.
+  OnViewSharedPropertyChanged(uint32 view, string name, array<uint8>? new_data);
+
+  // Invoked when an event is targeted at the specified view.
+  OnViewInputEvent(uint32 view, mojo.Event event) => ();
+
+  // Invoked solely on the WindowManager. See comments in PerformAction() above
+  // for details.
+  // TODO(sky): nuke this.
+  OnPerformAction(uint32 view_id, string action) => (bool success);
+};
diff --git a/mojo/services/view_manager/interfaces/view_manager_constants.mojom b/mojo/services/view_manager/interfaces/view_manager_constants.mojom
new file mode 100644
index 0000000..c18fee7
--- /dev/null
+++ b/mojo/services/view_manager/interfaces/view_manager_constants.mojom
@@ -0,0 +1,11 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+enum OrderDirection {
+  ABOVE = 1,
+  BELOW,
+};
diff --git a/mojo/services/window_manager/interfaces/BUILD.gn b/mojo/services/window_manager/interfaces/BUILD.gn
new file mode 100644
index 0000000..e5415a1
--- /dev/null
+++ b/mojo/services/window_manager/interfaces/BUILD.gn
@@ -0,0 +1,22 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "window_manager.mojom",
+    "window_manager_internal.mojom",
+  ]
+
+  import_dirs = [ get_path_info("../../", "abspath") ]
+
+  mojo_sdk_deps = [ "mojo/public/interfaces/application" ]
+
+  deps = [
+    "../../geometry/interfaces",
+    "../../input_events/interfaces",
+  ]
+}
diff --git a/mojo/services/window_manager/interfaces/window_manager.mojom b/mojo/services/window_manager/interfaces/window_manager.mojom
new file mode 100644
index 0000000..9203003
--- /dev/null
+++ b/mojo/services/window_manager/interfaces/window_manager.mojom
@@ -0,0 +1,38 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "input_events/interfaces/input_events.mojom";
+import "mojo/public/interfaces/application/service_provider.mojom";
+
+[ServiceName="mojo::WindowManager"]
+interface WindowManager {
+  // Requests the WindowManager to embed the app for |url| at an appropriate
+  // View. See ViewMangerService::Embed() for details on |services| and
+  // |exposed_services|.
+  Embed(string url,
+        ServiceProvider&? services,
+        ServiceProvider? exposed_services);
+
+  SetCapture(uint32 view_id) => (bool success);
+  FocusWindow(uint32 view_id) => (bool success);
+  ActivateWindow(uint32 view_id) => (bool success);
+
+  // Requests the current focus and activation state and an interface to observe
+  // future changes.
+  // If |observer| is not null capture, focus and activation updates will be
+  // sent to it.
+  GetFocusedAndActiveViews(WindowManagerObserver? observer)
+      => (uint32 capture_view_id,
+          uint32 focused_view_id,
+          uint32 active_view_id);
+};
+
+interface WindowManagerObserver {
+  OnCaptureChanged(uint32 capture_view_id);
+  OnFocusChanged(uint32 focused_view_id);
+  OnActiveWindowChanged(uint32 focused_view_id);
+};
diff --git a/mojo/services/window_manager/interfaces/window_manager_internal.mojom b/mojo/services/window_manager/interfaces/window_manager_internal.mojom
new file mode 100644
index 0000000..ac3254c
--- /dev/null
+++ b/mojo/services/window_manager/interfaces/window_manager_internal.mojom
@@ -0,0 +1,42 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "geometry/interfaces/geometry.mojom";
+import "input_events/interfaces/input_events.mojom";
+
+// WindowManagerInternal is an interface provided by the WindowManager
+// exclusively to the ViewManager.
+[ServiceName="mojo::WindowManagerInternal"]
+interface WindowManagerInternal {
+  // Creates a connection to the WindowManager specifically for a connection to
+  // the ViewManager. |connection_id| is the id of the connection to the
+  // ViewManager. See view_manager.mojom for details on the id.
+  CreateWindowManagerForViewManagerClient(
+      uint16 connection_id,
+      handle<message_pipe> window_manager_pipe);
+
+  SetViewManagerClient(handle<message_pipe> view_manager_client_request);
+};
+
+// WindowManagerInternalClient is an interface provide by the ViewManager
+// exclusively to the WindowManager. It provides functionality only available
+// to the WindowManager.
+[ServiceName="mojo::WindowManagerInternalClient"]
+interface WindowManagerInternalClient {
+  // Dispatches the specified input event to the specified view.
+  DispatchInputEventToView(uint32 view_id, mojo.Event event);
+
+  // Sets the native viewport size.
+  SetViewportSize(mojo.Size size);
+
+  // Clones the tree rooted at |view_id|. When the animation completes the clone
+  // is destroyed.
+  // TODO(sky): add actual animation.
+  // TODO(sky): I think this only makes sense when destroying (view is
+  // already visible), should it be named to indicate this?
+  CloneAndAnimate(uint32 view_id);
+};
diff --git a/services/BUILD.gn b/services/BUILD.gn
index e44f1c2..50ab043 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -52,10 +52,13 @@
     deps += [
       "//services/device_info",
       "//services/files",
+      "//services/kiosk_wm",
       "//services/ui",
       "//services/native_viewport",
       "//services/surfaces",
       "//services/url_response_disk_cache",
+      "//services/view_manager",
+      "//services/window_manager",
     ]
   }
 
@@ -90,6 +93,13 @@
     deps += [ "//services/notifications:apptests" ]
   }
 
+  if (is_linux || is_android) {
+    deps += [
+      "//services/window_manager:window_manager_apptests",
+      "//services/window_manager:window_manager_unittests",
+    ]
+  }
+
   # TODO(jamesr): We only support building V8 snapshot data on a linux host since it
   # needs a 32 bit toolchain and we don't have one configured for mac hosts.
   # TODO(cstout): javascript/v8 build support for fnl/musl
@@ -101,6 +111,9 @@
     deps += [
       "//services/files:apptests",
       "//services/url_response_disk_cache:tests",
+      "//services/view_manager:mojo_view_manager_client_apptests",
+      "//services/view_manager:view_manager_service_apptests",
+      "//services/view_manager:view_manager_service_unittests",
     ]
   }
 }
diff --git a/services/kiosk_wm/BUILD.gn b/services/kiosk_wm/BUILD.gn
new file mode 100644
index 0000000..0649962
--- /dev/null
+++ b/services/kiosk_wm/BUILD.gn
@@ -0,0 +1,37 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojo_native_application("kiosk_wm") {
+  sources = [
+    "kiosk_wm.cc",
+    "kiosk_wm.h",
+    "kiosk_wm_controller.cc",
+    "kiosk_wm_controller.h",
+    "main.cc",
+    "merged_service_provider.cc",
+    "merged_service_provider.h",
+    "navigator_host_impl.cc",
+    "navigator_host_impl.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common:common",
+    "//mojo/converters/geometry",
+    "//mojo/converters/input_events",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/input_events/interfaces",
+    "//mojo/services/navigation/interfaces",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/window_manager/interfaces:interfaces",
+    "//services/window_manager:lib",
+    "//ui/base",
+  ]
+}
diff --git a/services/kiosk_wm/README.md b/services/kiosk_wm/README.md
new file mode 100644
index 0000000..4c38010
--- /dev/null
+++ b/services/kiosk_wm/README.md
@@ -0,0 +1,6 @@
+A very simple window kiosk-like window manager.
+
+This can handle a single application at a time, any further calls to
+WindowManager::Embed will replace the existing application.
+
+Renders full-screen.  On linux defaults to a Nexus-5 aspect ratio.
\ No newline at end of file
diff --git a/services/kiosk_wm/kiosk_wm.cc b/services/kiosk_wm/kiosk_wm.cc
new file mode 100644
index 0000000..9d4f3ab
--- /dev/null
+++ b/services/kiosk_wm/kiosk_wm.cc
@@ -0,0 +1,44 @@
+// 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.
+
+#include "services/kiosk_wm/kiosk_wm.h"
+
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+#include "services/kiosk_wm/kiosk_wm_controller.h"
+#include "services/window_manager/window_manager_delegate.h"
+
+namespace kiosk_wm {
+
+KioskWM::KioskWM()
+    : window_manager_app_(new window_manager::WindowManagerApp(this)) {}
+
+KioskWM::~KioskWM() {}
+
+void KioskWM::Initialize(mojo::ApplicationImpl* app) {
+  window_manager_app_->Initialize(app);
+
+  // Format: --args-for="app_url default_url"
+  if (app->args().size() > 1) {
+    std::string default_url = app->args()[1];
+    mojo::WindowManagerPtr window_manager;
+    app->ConnectToService("mojo:kiosk_wm", &window_manager);
+    window_manager->Embed(default_url, nullptr, nullptr);
+  }
+}
+
+scoped_ptr<window_manager::WindowManagerController>
+KioskWM::CreateWindowManagerController(
+    mojo::ApplicationConnection* connection,
+    window_manager::WindowManagerRoot* wm_root) {
+  return scoped_ptr<window_manager::WindowManagerController>(
+      new KioskWMController(wm_root));
+}
+
+bool KioskWM::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  window_manager_app_->ConfigureIncomingConnection(connection);
+  return true;
+}
+
+}  // namespace kiosk_wm
diff --git a/services/kiosk_wm/kiosk_wm.h b/services/kiosk_wm/kiosk_wm.h
new file mode 100644
index 0000000..1583bd7
--- /dev/null
+++ b/services/kiosk_wm/kiosk_wm.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef SERVICES_KIOSK_WM_KIOSK_WM_H_
+#define SERVICES_KIOSK_WM_KIOSK_WM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "services/window_manager/window_manager_app.h"
+#include "services/window_manager/window_manager_delegate.h"
+
+namespace kiosk_wm {
+
+class KioskWMController;
+
+class KioskWM : public mojo::ApplicationDelegate,
+                public window_manager::WindowManagerControllerFactory {
+ public:
+  KioskWM();
+  ~KioskWM() override;
+
+  // Overridden from window_manager::WindowManagerControllerFactory
+  scoped_ptr<window_manager::WindowManagerController>
+  CreateWindowManagerController(
+      mojo::ApplicationConnection* connection,
+      window_manager::WindowManagerRoot* wm_root) override;
+
+ private:
+  // Overridden from mojo::ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  scoped_ptr<window_manager::WindowManagerApp> window_manager_app_;
+
+  DISALLOW_COPY_AND_ASSIGN(KioskWM);
+};
+
+}  // namespace kiosk_wm
+
+#endif  // SERVICES_KIOSK_WM_KIOSK_WM_H_
diff --git a/services/kiosk_wm/kiosk_wm_controller.cc b/services/kiosk_wm/kiosk_wm_controller.cc
new file mode 100644
index 0000000..b1434ed
--- /dev/null
+++ b/services/kiosk_wm/kiosk_wm_controller.cc
@@ -0,0 +1,109 @@
+// 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.
+
+#include "services/kiosk_wm/kiosk_wm_controller.h"
+
+#include "services/kiosk_wm/merged_service_provider.h"
+#include "services/window_manager/basic_focus_rules.h"
+#include "services/window_manager/window_manager_root.h"
+
+namespace kiosk_wm {
+
+KioskWMController::KioskWMController(window_manager::WindowManagerRoot* wm_root)
+    : window_manager_root_(wm_root),
+      root_(nullptr),
+      content_(nullptr),
+      navigator_host_(this),
+      weak_factory_(this) {
+  exposed_services_impl_.AddService(this);
+}
+
+KioskWMController::~KioskWMController() {}
+
+base::WeakPtr<KioskWMController> KioskWMController::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+void KioskWMController::OnEmbed(
+    mojo::View* root,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  // KioskWMController does not support being embedded more than once.
+  CHECK(!root_);
+
+  root_ = root;
+  root_->AddObserver(this);
+
+  // Resize to match the Nexus 5 aspect ratio:
+  window_manager_root_->SetViewportSize(gfx::Size(320, 640));
+
+  content_ = root->view_manager()->CreateView();
+  content_->SetBounds(root_->bounds());
+  root_->AddChild(content_);
+  content_->SetVisible(true);
+
+  window_manager_root_->InitFocus(
+      make_scoped_ptr(new window_manager::BasicFocusRules(root_)));
+  window_manager_root_->accelerator_manager()->Register(
+      ui::Accelerator(ui::VKEY_BROWSER_BACK, 0),
+      ui::AcceleratorManager::kNormalPriority, this);
+}
+
+void KioskWMController::Embed(
+    const mojo::String& url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  // KioskWMController is embedded in a WindowManagerRoot. WindowManagerRoot
+  // queues pending embed requests while we connect to the ViewManager. This
+  // method should only be called once ::OnEmbed has been called.
+  CHECK(content_);
+
+  merged_service_provider_.reset(
+      new MergedServiceProvider(exposed_services.Pass(), this));
+  content_->Embed(url, services.Pass(),
+                  merged_service_provider_->GetServiceProviderPtr().Pass());
+
+  navigator_host_.RecordNavigation(url);
+}
+
+void KioskWMController::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::NavigatorHost> request) {
+  navigator_host_.Bind(request.Pass());
+}
+
+void KioskWMController::OnViewManagerDisconnected(
+    mojo::ViewManager* view_manager) {
+  root_ = nullptr;
+  delete this;
+}
+
+void KioskWMController::OnViewDestroyed(mojo::View* view) {
+  view->RemoveObserver(this);
+}
+
+void KioskWMController::OnViewBoundsChanged(mojo::View* view,
+                                            const mojo::Rect& old_bounds,
+                                            const mojo::Rect& new_bounds) {
+  content_->SetBounds(new_bounds);
+}
+
+// Convenience method:
+void KioskWMController::ReplaceContentWithURL(const mojo::String& url) {
+  Embed(url, nullptr, nullptr);
+}
+
+bool KioskWMController::AcceleratorPressed(const ui::Accelerator& accelerator,
+                                           mojo::View* target) {
+  if (accelerator.key_code() != ui::VKEY_BROWSER_BACK)
+    return false;
+  navigator_host_.RequestNavigateHistory(-1);
+  return true;
+}
+
+bool KioskWMController::CanHandleAccelerators() const {
+  return true;
+}
+
+}  // namespace kiosk_wm
diff --git a/services/kiosk_wm/kiosk_wm_controller.h b/services/kiosk_wm/kiosk_wm_controller.h
new file mode 100644
index 0000000..25b7477
--- /dev/null
+++ b/services/kiosk_wm/kiosk_wm_controller.h
@@ -0,0 +1,84 @@
+// 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.
+
+#ifndef SERVICES_KIOSK_WM_KIOSK_WM_CONTROLLER_H_
+#define SERVICES_KIOSK_WM_KIOSK_WM_CONTROLLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/services/input_events/interfaces/input_events.mojom.h"
+#include "mojo/services/navigation/interfaces/navigation.mojom.h"
+#include "mojo/services/view_manager/cpp/view_manager.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "services/kiosk_wm/navigator_host_impl.h"
+#include "services/window_manager/window_manager_app.h"
+#include "services/window_manager/window_manager_delegate.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace kiosk_wm {
+
+class MergedServiceProvider;
+
+class KioskWMController : public mojo::ViewObserver,
+                          public window_manager::WindowManagerController,
+                          public mojo::InterfaceFactory<mojo::NavigatorHost>,
+                          public ui::AcceleratorTarget {
+ public:
+  KioskWMController(window_manager::WindowManagerRoot* wm_root);
+  ~KioskWMController() override;
+
+  base::WeakPtr<KioskWMController> GetWeakPtr();
+
+  void ReplaceContentWithURL(const mojo::String& url);
+
+ private:
+  // Overridden from mojo::ViewManagerDelegate:
+  void OnEmbed(mojo::View* root,
+               mojo::InterfaceRequest<mojo::ServiceProvider> services,
+               mojo::ServiceProviderPtr exposed_services) override;
+  void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override;
+
+  // Overriden from mojo::ViewObserver:
+  void OnViewDestroyed(mojo::View* view) override;
+  void OnViewBoundsChanged(mojo::View* view,
+                           const mojo::Rect& old_bounds,
+                           const mojo::Rect& new_bounds) override;
+
+  // Overridden from WindowManagerDelegate:
+  void Embed(const mojo::String& url,
+             mojo::InterfaceRequest<mojo::ServiceProvider> services,
+             mojo::ServiceProviderPtr exposed_services) override;
+
+  // Overridden from mojo::InterfaceFactory<mojo::NavigatorHost>:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::NavigatorHost> request) override;
+
+  // Overriden from ui::AcceleratorTarget:
+  bool AcceleratorPressed(const ui::Accelerator& accelerator,
+                          mojo::View* target) override;
+  bool CanHandleAccelerators() const override;
+
+  window_manager::WindowManagerRoot* window_manager_root_;
+
+  // Only support being embedded once, so both application-level
+  // and embedding-level state are shared on the same object.
+  mojo::View* root_;
+  mojo::View* content_;
+
+  mojo::ServiceProviderImpl exposed_services_impl_;
+  scoped_ptr<MergedServiceProvider> merged_service_provider_;
+
+  NavigatorHostImpl navigator_host_;
+
+  base::WeakPtrFactory<KioskWMController> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(KioskWMController);
+};
+
+}  // namespace kiosk_wm
+
+#endif  // SERVICES_KIOSK_WM_KIOSK_WM_H_
diff --git a/services/kiosk_wm/main.cc b/services/kiosk_wm/main.cc
new file mode 100644
index 0000000..6e250e5
--- /dev/null
+++ b/services/kiosk_wm/main.cc
@@ -0,0 +1,13 @@
+// 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.
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+
+#include "services/kiosk_wm/kiosk_wm.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new kiosk_wm::KioskWM);
+  return runner.Run(application_request);
+}
diff --git a/services/kiosk_wm/merged_service_provider.cc b/services/kiosk_wm/merged_service_provider.cc
new file mode 100644
index 0000000..589e40c
--- /dev/null
+++ b/services/kiosk_wm/merged_service_provider.cc
@@ -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.
+
+#include "services/kiosk_wm/merged_service_provider.h"
+
+namespace kiosk_wm {
+
+MergedServiceProvider::MergedServiceProvider(
+    mojo::ServiceProviderPtr exposed_services,
+    mojo::InterfaceFactory<mojo::NavigatorHost>* factory)
+    : exposed_services_(exposed_services.Pass()), factory_(factory) {
+}
+
+MergedServiceProvider::~MergedServiceProvider() {
+}
+
+mojo::ServiceProviderPtr MergedServiceProvider::GetServiceProviderPtr() {
+  mojo::ServiceProviderPtr sp;
+  binding_.reset(new mojo::Binding<mojo::ServiceProvider>(this, GetProxy(&sp)));
+  return sp;
+}
+
+void MergedServiceProvider::ConnectToService(
+    const mojo::String& interface_name,
+    mojo::ScopedMessagePipeHandle pipe) {
+  if (interface_name == mojo::NavigatorHost::Name_) {
+    factory_->Create(nullptr,
+                     mojo::MakeRequest<mojo::NavigatorHost>(pipe.Pass()));
+  } else if (exposed_services_.get()) {
+    exposed_services_->ConnectToService(interface_name, pipe.Pass());
+  }
+}
+
+}  // namespace kiosk_wm
diff --git a/services/kiosk_wm/merged_service_provider.h b/services/kiosk_wm/merged_service_provider.h
new file mode 100644
index 0000000..4a84e43
--- /dev/null
+++ b/services/kiosk_wm/merged_service_provider.h
@@ -0,0 +1,41 @@
+// 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_KIOSK_WM_MERGED_SERVICE_PROVIDER_H_
+#define SERVICES_KIOSK_WM_MERGED_SERVICE_PROVIDER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/navigation/interfaces/navigation.mojom.h"
+
+namespace kiosk_wm {
+
+// Used to wrap the second incoming WindowManager Embed() "exposed_services"
+// parameter with a new ServiceProvider that adds the KioskWM's
+// NavigatorHost service.
+class MergedServiceProvider : public mojo::ServiceProvider {
+ public:
+  MergedServiceProvider(mojo::ServiceProviderPtr exposed_services,
+                        mojo::InterfaceFactory<mojo::NavigatorHost>* factory);
+  ~MergedServiceProvider() override;
+
+  mojo::ServiceProviderPtr GetServiceProviderPtr();
+
+ private:
+  // mojo::ServiceProvider:
+  void ConnectToService(const mojo::String& interface_name,
+                        mojo::ScopedMessagePipeHandle pipe) override;
+
+  mojo::ServiceProviderPtr exposed_services_;
+  mojo::InterfaceFactory<mojo::NavigatorHost>* factory_;
+  scoped_ptr<mojo::Binding<ServiceProvider>> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(MergedServiceProvider);
+};
+
+}  // namespace kiosk_wm
+
+#endif  // SERVICES_KIOSK_WM_MERGED_SERVICE_PROVIDER_H_
diff --git a/services/kiosk_wm/navigator_host_impl.cc b/services/kiosk_wm/navigator_host_impl.cc
new file mode 100644
index 0000000..c9499be
--- /dev/null
+++ b/services/kiosk_wm/navigator_host_impl.cc
@@ -0,0 +1,54 @@
+// 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.
+
+#include "services/kiosk_wm/navigator_host_impl.h"
+
+#include "services/kiosk_wm/kiosk_wm_controller.h"
+
+namespace kiosk_wm {
+
+NavigatorHostImpl::NavigatorHostImpl(KioskWMController* window_manager)
+    : current_index_(-1), kiosk_wm_(window_manager) {
+}
+
+NavigatorHostImpl::~NavigatorHostImpl() {
+}
+
+void NavigatorHostImpl::Bind(
+    mojo::InterfaceRequest<mojo::NavigatorHost> request) {
+  bindings_.AddBinding(this, request.Pass());
+}
+
+void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) {
+  RecordNavigation(url);
+  // TODO(abarth): Do something interesting.
+}
+
+void NavigatorHostImpl::RequestNavigate(mojo::Target target,
+                                        mojo::URLRequestPtr request) {
+  // kiosk_wm sets up default services including navigation.
+  kiosk_wm_->ReplaceContentWithURL(request->url);
+}
+
+void NavigatorHostImpl::RequestNavigateHistory(int32_t delta) {
+  if (history_.empty())
+    return;
+  current_index_ =
+      std::max(0, std::min(current_index_ + delta,
+                           static_cast<int32_t>(history_.size()) - 1));
+  kiosk_wm_->ReplaceContentWithURL(history_[current_index_]);
+}
+
+void NavigatorHostImpl::RecordNavigation(const std::string& url) {
+  if (current_index_ >= 0 && history_[current_index_] == url) {
+    // This is a navigation to the current entry, ignore.
+    return;
+  }
+
+  history_.erase(history_.begin() + (current_index_ + 1), history_.end());
+  history_.push_back(url);
+  ++current_index_;
+}
+
+}  // namespace kiosk_wm
diff --git a/services/kiosk_wm/navigator_host_impl.h b/services/kiosk_wm/navigator_host_impl.h
new file mode 100644
index 0000000..5c494da
--- /dev/null
+++ b/services/kiosk_wm/navigator_host_impl.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef SERVICES_KIOSK_WM_NAVIGATOR_HOST_IMPL_H_
+#define SERVICES_KIOSK_WM_NAVIGATOR_HOST_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/navigation/interfaces/navigation.mojom.h"
+
+namespace kiosk_wm {
+class KioskWMController;
+
+class NavigatorHostImpl : public mojo::NavigatorHost {
+ public:
+  NavigatorHostImpl(KioskWMController* kiosk_wm);
+  ~NavigatorHostImpl() override;
+
+  void Bind(mojo::InterfaceRequest<mojo::NavigatorHost> request);
+
+  void RecordNavigation(const std::string& url);
+
+  // mojo::NavigatorHost implementation:
+  void DidNavigateLocally(const mojo::String& url) override;
+  void RequestNavigate(mojo::Target target,
+                       mojo::URLRequestPtr request) override;
+  void RequestNavigateHistory(int32_t delta) override;
+
+ private:
+  std::vector<std::string> history_;
+  int32_t current_index_;
+
+  KioskWMController* kiosk_wm_;
+  mojo::BindingSet<NavigatorHost> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl);
+};
+
+}  // namespace kiosk_wm
+
+#endif  // SERVICES_KIOSK_WM_NAVIGATOR_HOST_IMPL_H_
diff --git a/services/view_manager/BUILD.gn b/services/view_manager/BUILD.gn
new file mode 100644
index 0000000..777fa97
--- /dev/null
+++ b/services/view_manager/BUILD.gn
@@ -0,0 +1,196 @@
+# 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.
+
+import("//build/config/ui.gni")
+import("//mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("view_manager") {
+  sources = [
+    "main.cc",
+    "view_manager_app.cc",
+    "view_manager_app.h",
+    "view_manager_root_connection.cc",
+    "view_manager_root_connection.h",
+  ]
+
+  deps = [
+    ":view_manager_lib",
+    "//base",
+    "//mojo/application",
+    "//mojo/common:tracing_impl",
+    "//mojo/environment:chromium",
+    "//mojo/converters/geometry",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/view_manager/interfaces",
+    "//mojo/services/window_manager/interfaces",
+  ]
+}
+
+source_set("view_manager_lib") {
+  sources = [
+    "access_policy.h",
+    "access_policy_delegate.h",
+    "animation_runner.cc",
+    "animation_runner.h",
+    "animation_runner_observer.h",
+    "client_connection.cc",
+    "client_connection.h",
+    "connection_manager.cc",
+    "connection_manager.h",
+    "connection_manager_delegate.h",
+    "default_access_policy.cc",
+    "default_access_policy.h",
+    "display_manager.cc",
+    "display_manager.h",
+    "focus_controller.cc",
+    "focus_controller.h",
+    "focus_controller_delegate.h",
+    "gesture_manager.cc",
+    "gesture_manager.h",
+    "gesture_manager_delegate.h",
+    "scheduled_animation_group.cc",
+    "scheduled_animation_group.h",
+    "server_view.cc",
+    "server_view.h",
+    "server_view_delegate.h",
+    "server_view_drawn_tracker.cc",
+    "server_view_drawn_tracker.h",
+    "server_view_drawn_tracker_observer.h",
+    "server_view_observer.h",
+    "view_coordinate_conversions.cc",
+    "view_coordinate_conversions.h",
+    "view_locator.cc",
+    "view_locator.h",
+    "view_manager_service_impl.cc",
+    "view_manager_service_impl.h",
+    "window_manager_access_policy.cc",
+    "window_manager_access_policy.h",
+  ]
+
+  public_deps = [
+    "//mojo/services/view_manager/cpp",
+  ]
+
+  deps = [
+    "//base",
+    "//cc/surfaces",
+    "//cc/surfaces:surface_id",
+    "//mojo/application",
+    "//mojo/converters/geometry",
+    "//mojo/converters/input_events",
+    "//mojo/converters/surfaces",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/bindings:callback",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/input_events/interfaces",
+    "//mojo/services/native_viewport/interfaces",
+    "//mojo/services/surfaces/cpp",
+    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/view_manager/interfaces",
+    "//mojo/services/view_manager/cpp:common",
+    "//mojo/services/window_manager/interfaces",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+}
+
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "test_change_tracker.cc",
+    "test_change_tracker.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/converters/array_string",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/view_manager/cpp:common",
+    "//mojo/services/view_manager/interfaces",
+  ]
+}
+
+test("view_manager_service_unittests") {
+  sources = [
+    "animation_runner_unittest.cc",
+    "focus_controller_unittest.cc",
+    "gesture_manager_unittest.cc",
+    "scheduled_animation_group_unittest.cc",
+    "server_view_drawn_tracker_unittest.cc",
+    "test_server_view_delegate.cc",
+    "test_server_view_delegate.h",
+    "view_coordinate_conversions_unittest.cc",
+    "view_manager_service_unittest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    ":view_manager_lib",
+    "//base",
+    "//base/test:test_config",
+    "//mojo/converters/geometry",
+    "//mojo/converters/input_events",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/input_events/interfaces",
+    "//mojo/services/native_viewport/cpp:args",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/view_manager/interfaces",
+    "//mojo/services/window_manager/interfaces",
+    "//testing/gtest",
+    "//ui/gfx",
+    "//ui/gfx:test_support",
+    "//ui/gfx/geometry",
+  ]
+
+  if (!is_android) {  # TODO(GYP) Enable on Android when osmesa links.
+    deps += [ "//third_party/mesa:osmesa" ]
+  }
+}
+
+mojo_native_application("mojo_view_manager_client_apptests") {
+  testonly = true
+
+  sources = [
+    "view_manager_client_apptest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_config",
+    "//mojo/application",
+    "//mojo/application:test_support",
+    "//mojo/services/geometry/cpp",
+    "//mojo/services/view_manager/cpp",
+  ]
+}
+
+mojo_native_application("view_manager_service_apptests") {
+  testonly = true
+
+  sources = [
+    "view_manager_service_apptest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    "//base",
+    "//mojo/application",
+    "//mojo/application:test_support",
+    "//mojo/common",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/view_manager/interfaces",
+    "//mojo/services/window_manager/interfaces",
+  ]
+}
diff --git a/services/view_manager/access_policy.h b/services/view_manager/access_policy.h
new file mode 100644
index 0000000..91560bc
--- /dev/null
+++ b/services/view_manager/access_policy.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
+#define SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
+
+#include "mojo/services/view_manager/interfaces/view_manager_constants.mojom.h"
+#include "services/view_manager/ids.h"
+
+namespace view_manager {
+
+class ServerView;
+
+// AccessPolicy is used by ViewManagerServiceImpl to determine what a connection
+// is allowed to do.
+class AccessPolicy {
+ public:
+  virtual ~AccessPolicy() {}
+
+  // Unless otherwise mentioned all arguments have been validated. That is the
+  // |view| arguments are non-null unless otherwise stated (eg CanSetView() is
+  // allowed to take a NULL view).
+  virtual bool CanRemoveViewFromParent(const ServerView* view) const = 0;
+  virtual bool CanAddView(const ServerView* parent,
+                          const ServerView* child) const = 0;
+  virtual bool CanReorderView(const ServerView* view,
+                              const ServerView* relative_view,
+                              mojo::OrderDirection direction) const = 0;
+  virtual bool CanDeleteView(const ServerView* view) const = 0;
+  virtual bool CanGetViewTree(const ServerView* view) const = 0;
+  // Used when building a view tree (GetViewTree()) to decide if we should
+  // descend into |view|.
+  virtual bool CanDescendIntoViewForViewTree(const ServerView* view) const = 0;
+  virtual bool CanEmbed(const ServerView* view) const = 0;
+  virtual bool CanChangeViewVisibility(const ServerView* view) const = 0;
+  virtual bool CanSetViewSurfaceId(const ServerView* view) const = 0;
+  virtual bool CanSetViewBounds(const ServerView* view) const = 0;
+  virtual bool CanSetViewProperties(const ServerView* view) const = 0;
+
+  // Returns whether the connection should notify on a hierarchy change.
+  // |new_parent| and |old_parent| are initially set to the new and old parents
+  // but may be altered so that the client only sees a certain set of views.
+  virtual bool ShouldNotifyOnHierarchyChange(
+      const ServerView* view,
+      const ServerView** new_parent,
+      const ServerView** old_parent) const = 0;
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
diff --git a/services/view_manager/access_policy_delegate.h b/services/view_manager/access_policy_delegate.h
new file mode 100644
index 0000000..afcd7a8
--- /dev/null
+++ b/services/view_manager/access_policy_delegate.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "services/view_manager/ids.h"
+
+namespace view_manager {
+
+class ServerView;
+
+// Delegate used by the AccessPolicy implementations to get state.
+class AccessPolicyDelegate {
+ public:
+  // Returns true if |id| is the root of the connection.
+  virtual bool IsRootForAccessPolicy(const ViewId& id) const = 0;
+
+  // Returns true if |view| has been exposed to the client.
+  virtual bool IsViewKnownForAccessPolicy(const ServerView* view) const = 0;
+
+  // Returns true if Embed(view) has been invoked on |view|.
+  virtual bool IsViewRootOfAnotherConnectionForAccessPolicy(
+      const ServerView* view) const = 0;
+
+ protected:
+  virtual ~AccessPolicyDelegate() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
diff --git a/services/view_manager/animation_runner.cc b/services/view_manager/animation_runner.cc
new file mode 100644
index 0000000..cd1eeb4
--- /dev/null
+++ b/services/view_manager/animation_runner.cc
@@ -0,0 +1,156 @@
+// 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.
+
+#include "services/view_manager/animation_runner.h"
+
+#include "base/memory/scoped_vector.h"
+#include "services/view_manager/animation_runner_observer.h"
+#include "services/view_manager/scheduled_animation_group.h"
+#include "services/view_manager/server_view.h"
+
+namespace view_manager {
+namespace {
+
+bool ConvertViewAndAnimationPairToScheduledAnimationGroup(
+    const std::vector<AnimationRunner::ViewAndAnimationPair>& views,
+    AnimationRunner::AnimationId id,
+    base::TimeTicks now,
+    ScopedVector<ScheduledAnimationGroup>* groups) {
+  for (const auto& view_animation_pair : views) {
+    DCHECK(view_animation_pair.second);
+    scoped_ptr<ScheduledAnimationGroup> group(ScheduledAnimationGroup::Create(
+        view_animation_pair.first, now, id, *(view_animation_pair.second)));
+    if (!group.get())
+      return false;
+    groups->push_back(group.release());
+  }
+  return true;
+}
+
+}  // namespace
+
+AnimationRunner::AnimationRunner(base::TimeTicks now)
+    : next_id_(1), last_tick_time_(now) {
+}
+
+AnimationRunner::~AnimationRunner() {
+}
+
+void AnimationRunner::AddObserver(AnimationRunnerObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void AnimationRunner::RemoveObserver(AnimationRunnerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+AnimationRunner::AnimationId AnimationRunner::Schedule(
+    const std::vector<ViewAndAnimationPair>& views,
+    base::TimeTicks now) {
+  DCHECK_GE(now, last_tick_time_);
+
+  const AnimationId animation_id = next_id_++;
+  ScopedVector<ScheduledAnimationGroup> groups;
+  if (!ConvertViewAndAnimationPairToScheduledAnimationGroup(views, animation_id,
+                                                            now, &groups)) {
+    return 0;
+  }
+
+  // Cancel any animations for the views.
+  for (auto* group : groups) {
+    ScheduledAnimationGroup* current_group =
+        view_to_animation_map_.get(group->view());
+    if (current_group)
+      current_group->SetValuesToTargetValuesForPropertiesNotIn(*group);
+
+    CancelAnimationForViewImpl(group->view(), CANCEL_SOURCE_SCHEDULE);
+  }
+
+  for (auto* group : groups) {
+    group->ObtainStartValues();
+    view_to_animation_map_.set(group->view(), make_scoped_ptr(group));
+    DCHECK(!id_to_views_map_[animation_id].count(group->view()));
+    id_to_views_map_[animation_id].insert(group->view());
+  }
+  // |view_to_animation_map_| owns the groups.
+  groups.weak_clear();
+
+  FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+                    OnAnimationScheduled(animation_id));
+  return animation_id;
+}
+
+void AnimationRunner::CancelAnimation(AnimationId id) {
+  if (id_to_views_map_.count(id) == 0)
+    return;
+
+  std::set<ServerView*> views(id_to_views_map_[id]);
+  for (ServerView* view : views)
+    CancelAnimationForView(view);
+}
+
+void AnimationRunner::CancelAnimationForView(ServerView* view) {
+  CancelAnimationForViewImpl(view, CANCEL_SOURCE_CANCEL);
+}
+
+void AnimationRunner::Tick(base::TimeTicks time) {
+  DCHECK(time >= last_tick_time_);
+  last_tick_time_ = time;
+  if (view_to_animation_map_.empty())
+    return;
+
+  // The animation ids of any views whose animation completes are added here. We
+  // notify after processing all views so that if an observer mutates us in some
+  // way we're aren't left in a weird state.
+  std::set<AnimationId> animations_completed;
+  for (ViewToAnimationMap::iterator i = view_to_animation_map_.begin();
+       i != view_to_animation_map_.end();) {
+    if (i->second->Tick(time)) {
+      const AnimationId animation_id = i->second->id();
+      ServerView* view = i->first;
+      ++i;
+      if (RemoveViewFromMaps(view))
+        animations_completed.insert(animation_id);
+    } else {
+      ++i;
+    }
+  }
+  for (const AnimationId& id : animations_completed)
+    FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_, OnAnimationDone(id));
+}
+
+void AnimationRunner::CancelAnimationForViewImpl(ServerView* view,
+                                                 CancelSource source) {
+  if (!view_to_animation_map_.contains(view))
+    return;
+
+  const AnimationId animation_id = view_to_animation_map_.get(view)->id();
+  if (RemoveViewFromMaps(view)) {
+    // This was the last view in the group.
+    if (source == CANCEL_SOURCE_CANCEL) {
+      FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+                        OnAnimationCanceled(animation_id));
+    } else {
+      FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+                        OnAnimationInterrupted(animation_id));
+    }
+  }
+}
+
+bool AnimationRunner::RemoveViewFromMaps(ServerView* view) {
+  DCHECK(view_to_animation_map_.contains(view));
+
+  const AnimationId animation_id = view_to_animation_map_.get(view)->id();
+  view_to_animation_map_.erase(view);
+
+  DCHECK(id_to_views_map_.count(animation_id));
+  id_to_views_map_[animation_id].erase(view);
+  if (!id_to_views_map_[animation_id].empty())
+    return false;
+
+  id_to_views_map_.erase(animation_id);
+  return true;
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/animation_runner.h b/services/view_manager/animation_runner.h
new file mode 100644
index 0000000..f68acec
--- /dev/null
+++ b/services/view_manager/animation_runner.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_H_
+#define SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_H_
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+
+namespace mojo {
+class AnimationGroup;
+}
+
+namespace view_manager {
+
+class AnimationRunnerObserver;
+class ScheduledAnimationGroup;
+class ServerView;
+
+// AnimationRunner is responsible for maintaing and running a set of animations.
+// The animations are represented as a set of AnimationGroups. New animations
+// are scheduled by way of Schedule(). A |view| may only have one animation
+// running at a time. Schedule()ing a new animation for a view already animating
+// implicitly cancels the current animation for the view. Animations progress
+// by way of the Tick() function.
+class AnimationRunner {
+ public:
+  using AnimationId = uint32_t;
+  using ViewAndAnimationPair =
+      std::pair<ServerView*, const mojo::AnimationGroup*>;
+
+  explicit AnimationRunner(base::TimeTicks now);
+  ~AnimationRunner();
+
+  void AddObserver(AnimationRunnerObserver* observer);
+  void RemoveObserver(AnimationRunnerObserver* observer);
+
+  // Schedules animations. If any of the groups are not valid no animations are
+  // scheuled and 0 is returned. If there is an existing animation in progress
+  // for any of the views it is canceled and any properties that were animating
+  // but are no longer animating are set to their target value.
+  AnimationId Schedule(const std::vector<ViewAndAnimationPair>& views,
+                       base::TimeTicks now);
+
+  // Cancels an animation scheduled by an id that was previously returned from
+  // Schedule().
+  void CancelAnimation(AnimationId id);
+
+  // Cancels the animation scheduled for |view|. Does nothing if there is no
+  // animation scheduled for |view|. This does not change |view|. That is, any
+  // in progress animations are stopped.
+  void CancelAnimationForView(ServerView* view);
+
+  // Advance the animations updating values appropriately.
+  void Tick(base::TimeTicks time);
+
+  // Returns true if there are animations currently scheduled.
+  bool HasAnimations() const { return !view_to_animation_map_.empty(); }
+
+  // Returns true if the animation identified by |id| is valid and animating.
+  bool IsAnimating(AnimationId id) const {
+    return id_to_views_map_.count(id) > 0;
+  }
+
+  // Returns the views that are currently animating for |id|. Returns an empty
+  // set if |id| does not identify a valid animation.
+  std::set<ServerView*> GetViewsAnimating(AnimationId id) {
+    return IsAnimating(id) ? id_to_views_map_.find(id)->second
+                           : std::set<ServerView*>();
+  }
+
+ private:
+  enum CancelSource {
+    // Cancel is the result of scheduling another animation for the view.
+    CANCEL_SOURCE_SCHEDULE,
+
+    // Cancel originates from an explicit call to cancel.
+    CANCEL_SOURCE_CANCEL,
+  };
+
+  using ViewToAnimationMap =
+      base::ScopedPtrHashMap<ServerView*, scoped_ptr<ScheduledAnimationGroup>>;
+  using IdToViewsMap = std::map<AnimationId, std::set<ServerView*>>;
+
+  void CancelAnimationForViewImpl(ServerView* view, CancelSource source);
+
+  // Removes |view| from both |view_to_animation_map_| and |id_to_views_map_|.
+  // Returns true if there are no more views animating with the animation id
+  // the view is associated with.
+  bool RemoveViewFromMaps(ServerView* view);
+
+  AnimationId next_id_;
+
+  base::TimeTicks last_tick_time_;
+
+  base::ObserverList<AnimationRunnerObserver> observers_;
+
+  ViewToAnimationMap view_to_animation_map_;
+
+  IdToViewsMap id_to_views_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnimationRunner);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_H_
diff --git a/services/view_manager/animation_runner_observer.h b/services/view_manager/animation_runner_observer.h
new file mode 100644
index 0000000..9b79983
--- /dev/null
+++ b/services/view_manager/animation_runner_observer.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_OBSERVER_H_
+#define SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_OBSERVER_H_
+
+namespace view_manager {
+
+class AnimationRunnerObserver {
+ public:
+  virtual void OnAnimationScheduled(uint32_t id) = 0;
+  virtual void OnAnimationDone(uint32_t id) = 0;
+  virtual void OnAnimationInterrupted(uint32_t id) = 0;
+  virtual void OnAnimationCanceled(uint32_t id) = 0;
+
+ protected:
+  virtual ~AnimationRunnerObserver() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_ANIMATION_RUNNER_OBSERVER_H_
diff --git a/services/view_manager/animation_runner_unittest.cc b/services/view_manager/animation_runner_unittest.cc
new file mode 100644
index 0000000..d93d3b8
--- /dev/null
+++ b/services/view_manager/animation_runner_unittest.cc
@@ -0,0 +1,636 @@
+// 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.
+
+#include "services/view_manager/animation_runner.h"
+
+#include "base/strings/stringprintf.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/view_manager/interfaces/view_manager_constants.mojom.h"
+#include "services/view_manager/animation_runner_observer.h"
+#include "services/view_manager/scheduled_animation_group.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+using mojo::AnimationElement;
+using mojo::AnimationGroup;
+using mojo::AnimationProperty;
+using mojo::AnimationSequence;
+using mojo::AnimationTweenType;
+using mojo::AnimationValue;
+using mojo::AnimationValuePtr;
+using mojo::Transform;
+
+namespace view_manager {
+namespace {
+
+class TestAnimationRunnerObserver : public AnimationRunnerObserver {
+ public:
+  TestAnimationRunnerObserver() {}
+  ~TestAnimationRunnerObserver() override {}
+
+  std::vector<std::string>* changes() { return &changes_; }
+  std::vector<uint32_t>* change_ids() { return &change_ids_; }
+
+  void clear_changes() {
+    changes_.clear();
+    change_ids_.clear();
+  }
+
+  // AnimationRunnerDelgate:
+  void OnAnimationScheduled(uint32_t id) override {
+    change_ids_.push_back(id);
+    changes_.push_back("scheduled");
+  }
+  void OnAnimationDone(uint32_t id) override {
+    change_ids_.push_back(id);
+    changes_.push_back("done");
+  }
+  void OnAnimationInterrupted(uint32_t id) override {
+    change_ids_.push_back(id);
+    changes_.push_back("interrupted");
+  }
+  void OnAnimationCanceled(uint32_t id) override {
+    change_ids_.push_back(id);
+    changes_.push_back("canceled");
+  }
+
+ private:
+  std::vector<uint32_t> change_ids_;
+  std::vector<std::string> changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAnimationRunnerObserver);
+};
+
+// Creates an AnimationValuePtr from the specified float value.
+AnimationValuePtr FloatAnimationValue(float float_value) {
+  AnimationValuePtr value(AnimationValue::New());
+  value->float_value = float_value;
+  return value;
+}
+
+// Creates an AnimationValuePtr from the specified transform.
+AnimationValuePtr TransformAnimationValue(const gfx::Transform& transform) {
+  AnimationValuePtr value(AnimationValue::New());
+  value->transform = Transform::From(transform);
+  return value;
+}
+
+// Adds an AnimationElement to |group|s last sequence with the specified value.
+void AddElement(AnimationGroup* group,
+                TimeDelta time,
+                AnimationValuePtr start_value,
+                AnimationValuePtr target_value,
+                AnimationProperty property,
+                AnimationTweenType tween_type) {
+  AnimationSequence& sequence =
+      *(group->sequences[group->sequences.size() - 1]);
+  sequence.elements.push_back(AnimationElement::New());
+  AnimationElement& element =
+      *(sequence.elements[sequence.elements.size() - 1]);
+  element.property = property;
+  element.duration = time.InMicroseconds();
+  element.tween_type = tween_type;
+  element.start_value = start_value.Pass();
+  element.target_value = target_value.Pass();
+}
+
+void AddOpacityElement(AnimationGroup* group,
+                       TimeDelta time,
+                       AnimationValuePtr start_value,
+                       AnimationValuePtr target_value) {
+  AddElement(group, time, start_value.Pass(), target_value.Pass(),
+             AnimationProperty::OPACITY, AnimationTweenType::LINEAR);
+}
+
+void AddTransformElement(AnimationGroup* group,
+                         TimeDelta time,
+                         AnimationValuePtr start_value,
+                         AnimationValuePtr target_value) {
+  AddElement(group, time, start_value.Pass(), target_value.Pass(),
+             AnimationProperty::TRANSFORM, AnimationTweenType::LINEAR);
+}
+
+void AddPauseElement(AnimationGroup* group, TimeDelta time) {
+  AddElement(group, time, AnimationValuePtr(), AnimationValuePtr(),
+             AnimationProperty::NONE, AnimationTweenType::LINEAR);
+}
+
+void InitGroupForView(AnimationGroup* group,
+                      const ViewId& id,
+                      int cycle_count) {
+  group->view_id = ViewIdToTransportId(id);
+  group->sequences.push_back(AnimationSequence::New());
+  group->sequences[group->sequences.size() - 1]->cycle_count = cycle_count;
+}
+
+}  // namespace
+
+class AnimationRunnerTest : public testing::Test {
+ public:
+  AnimationRunnerTest()
+      : initial_time_(base::TimeTicks::Now()), runner_(initial_time_) {
+    runner_.AddObserver(&runner_observer_);
+  }
+  ~AnimationRunnerTest() override { runner_.RemoveObserver(&runner_observer_); }
+
+ protected:
+  // Convenience to schedule an animation for a single view/group pair.
+  AnimationRunner::AnimationId ScheduleForSingleView(
+      ServerView* view,
+      const AnimationGroup* group,
+      base::TimeTicks now) {
+    std::vector<AnimationRunner::ViewAndAnimationPair> pairs;
+    pairs.push_back(std::make_pair(view, group));
+    return runner_.Schedule(pairs, now);
+  }
+
+  // If |id| is valid and there is only one view schedule against the animation
+  // it is returned; otherwise returns null.
+  ServerView* GetSingleViewAnimating(AnimationRunner::AnimationId id) {
+    std::set<ServerView*> views(runner_.GetViewsAnimating(id));
+    return views.size() == 1 ? *views.begin() : nullptr;
+  }
+
+  const base::TimeTicks initial_time_;
+  TestAnimationRunnerObserver runner_observer_;
+  AnimationRunner runner_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AnimationRunnerTest);
+};
+
+// Opacity from 1 to .5 over 1000.
+TEST_F(AnimationRunnerTest, SingleProperty) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  const uint32_t animation_id =
+      ScheduleForSingleView(&view, &group, initial_time_);
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+  runner_observer_.clear_changes();
+
+  EXPECT_TRUE(runner_.HasAnimations());
+
+  // Opacity should still be 1 (the initial value).
+  EXPECT_EQ(1.f, view.opacity());
+
+  // Animate half way.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  // Run well past the end. Value should progress to end and delegate should
+  // be notified.
+  runner_.Tick(initial_time_ + TimeDelta::FromSeconds(10));
+  EXPECT_EQ(.5f, view.opacity());
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+
+  EXPECT_FALSE(runner_.HasAnimations());
+}
+
+// Opacity from 1 to .5, followed by transform from identity to 2x,3x.
+TEST_F(AnimationRunnerTest, TwoPropertiesInSequence) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5f));
+
+  gfx::Transform done_transform;
+  done_transform.Scale(2, 4);
+  AddTransformElement(&group, TimeDelta::FromMicroseconds(2000),
+                      AnimationValuePtr(),
+                      TransformAnimationValue(done_transform));
+
+  const uint32_t animation_id =
+      ScheduleForSingleView(&view, &group, initial_time_);
+  runner_observer_.clear_changes();
+
+  // Nothing in the view should have changed yet.
+  EXPECT_EQ(1.f, view.opacity());
+  EXPECT_TRUE(view.transform().IsIdentity());
+
+  // Animate half way from through opacity animation.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_TRUE(view.transform().IsIdentity());
+
+  // Finish first element (opacity).
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+  EXPECT_EQ(.5f, view.opacity());
+  EXPECT_TRUE(view.transform().IsIdentity());
+
+  // Half way through second (transform).
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+  EXPECT_EQ(.5f, view.opacity());
+  gfx::Transform half_way_transform;
+  half_way_transform.Scale(1.5, 2.5);
+  EXPECT_EQ(half_way_transform, view.transform());
+
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  // To end.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
+  EXPECT_EQ(.5f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Opacity from .5 to 1 over 1000, transform to 2x,4x over 500.
+TEST_F(AnimationRunnerTest, TwoPropertiesInParallel) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId(1, 1));
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    FloatAnimationValue(.5f), FloatAnimationValue(1));
+
+  group.sequences.push_back(AnimationSequence::New());
+  group.sequences[1]->cycle_count = 1;
+  gfx::Transform done_transform;
+  done_transform.Scale(2, 4);
+  AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
+                      AnimationValuePtr(),
+                      TransformAnimationValue(done_transform));
+
+  const uint32_t animation_id =
+      ScheduleForSingleView(&view, &group, initial_time_);
+
+  runner_observer_.clear_changes();
+
+  // Nothing in the view should have changed yet.
+  EXPECT_EQ(1.f, view.opacity());
+  EXPECT_TRUE(view.transform().IsIdentity());
+
+  // Animate to 250, which is 1/4 way through opacity and half way through
+  // transform.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
+
+  EXPECT_EQ(.625f, view.opacity());
+  gfx::Transform half_way_transform;
+  half_way_transform.Scale(1.5, 2.5);
+  EXPECT_EQ(half_way_transform, view.transform());
+
+  // Animate to 500, which is 1/2 way through opacity and transform done.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+
+  // Animate to 750, which is 3/4 way through opacity and transform done.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
+  EXPECT_EQ(.875f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  // To end.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
+  EXPECT_EQ(1.f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Opacity from .5 to 1 over 1000, pause for 500, 1 to .5 over 500, with a cycle
+// count of 3.
+TEST_F(AnimationRunnerTest, Cycles) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId(1, 2));
+
+  view.SetOpacity(.5f);
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 3);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(1));
+  AddPauseElement(&group, TimeDelta::FromMicroseconds(500));
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  ScheduleForSingleView(&view, &group, initial_time_);
+  runner_observer_.clear_changes();
+
+  // Nothing in the view should have changed yet.
+  EXPECT_EQ(.5f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+  EXPECT_EQ(.75f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1250));
+  EXPECT_EQ(1.f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
+  EXPECT_EQ(.75f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2500));
+  EXPECT_EQ(.75f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3250));
+  EXPECT_EQ(1.f, view.opacity());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3750));
+  EXPECT_EQ(.75f, view.opacity());
+
+  // Animate to the end.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(6500));
+  EXPECT_EQ(.5f, view.opacity());
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+}
+
+// Verifies scheduling the same view twice sends an interrupt.
+TEST_F(AnimationRunnerTest, ScheduleTwice) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId(1, 2));
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  const uint32_t animation_id =
+      ScheduleForSingleView(&view, &group, initial_time_);
+  runner_observer_.clear_changes();
+
+  // Animate half way.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  // Schedule again. We should get an interrupt, but opacity shouldn't change.
+  const uint32_t animation2_id = ScheduleForSingleView(
+      &view, &group, initial_time_ + TimeDelta::FromMicroseconds(500));
+
+  // Id should have changed.
+  EXPECT_NE(animation_id, animation2_id);
+
+  EXPECT_FALSE(runner_.IsAnimating(animation_id));
+  EXPECT_EQ(&view, GetSingleViewAnimating(animation2_id));
+
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_EQ(2u, runner_observer_.changes()->size());
+  EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+  EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
+  EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(1));
+  runner_observer_.clear_changes();
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+  EXPECT_EQ(.625f, view.opacity());
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+  EXPECT_EQ(.5f, view.opacity());
+  EXPECT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(0));
+}
+
+// Verifies Remove() works.
+TEST_F(AnimationRunnerTest, CancelAnimationForView) {
+  // Create an animation and advance it part way.
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  const uint32_t animation_id =
+      ScheduleForSingleView(&view, &group, initial_time_);
+  runner_observer_.clear_changes();
+  EXPECT_EQ(&view, GetSingleViewAnimating(animation_id));
+
+  EXPECT_TRUE(runner_.HasAnimations());
+
+  // Animate half way.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_TRUE(runner_observer_.changes()->empty());
+
+  // Cancel the animation.
+  runner_.CancelAnimationForView(&view);
+
+  EXPECT_FALSE(runner_.HasAnimations());
+  EXPECT_EQ(nullptr, GetSingleViewAnimating(animation_id));
+
+  EXPECT_EQ(.75f, view.opacity());
+
+  EXPECT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("canceled", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Verifies a tick with a very large delta and a sequence that repeats forever
+// doesn't take a long time.
+TEST_F(AnimationRunnerTest, InfiniteRepeatWithHugeGap) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId(1, 2));
+
+  view.SetOpacity(.5f);
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 0);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+                    AnimationValuePtr(), FloatAnimationValue(1));
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  ScheduleForSingleView(&view, &group, initial_time_);
+  runner_observer_.clear_changes();
+
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000000000750));
+
+  EXPECT_EQ(.75f, view.opacity());
+
+  ASSERT_EQ(0u, runner_observer_.changes()->size());
+}
+
+// Verifies a second schedule sets any properties that are no longer animating
+// to their final value.
+TEST_F(AnimationRunnerTest, RescheduleSetsPropertiesToFinalValue) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  gfx::Transform done_transform;
+  done_transform.Scale(2, 4);
+  AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
+                      AnimationValuePtr(),
+                      TransformAnimationValue(done_transform));
+
+  ScheduleForSingleView(&view, &group, initial_time_);
+
+  // Schedule() again, this time without animating opacity.
+  group.sequences[0]->elements[0]->property = AnimationProperty::NONE;
+  ScheduleForSingleView(&view, &group, initial_time_);
+
+  // Opacity should go to final value.
+  EXPECT_EQ(.5f, view.opacity());
+  // Transform shouldn't have changed since newly scheduled animation also has
+  // transform in it.
+  EXPECT_TRUE(view.transform().IsIdentity());
+}
+
+// Opacity from 1 to .5 over 1000 of v1 and v2 transform to 2x,4x over 500.
+TEST_F(AnimationRunnerTest, TwoViews) {
+  TestServerViewDelegate view_delegate;
+  ServerView view1(&view_delegate, ViewId());
+  ServerView view2(&view_delegate, ViewId(1, 2));
+
+  AnimationGroup group1;
+  InitGroupForView(&group1, view1.id(), 1);
+  AddOpacityElement(&group1, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(.5));
+
+  AnimationGroup group2;
+  InitGroupForView(&group2, view2.id(), 1);
+  gfx::Transform done_transform;
+  done_transform.Scale(2, 4);
+  AddTransformElement(&group2, TimeDelta::FromMicroseconds(500),
+                      AnimationValuePtr(),
+                      TransformAnimationValue(done_transform));
+
+  std::vector<AnimationRunner::ViewAndAnimationPair> pairs;
+  pairs.push_back(std::make_pair(&view1, &group1));
+  pairs.push_back(std::make_pair(&view2, &group2));
+
+  const uint32_t animation_id = runner_.Schedule(pairs, initial_time_);
+
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+  runner_observer_.clear_changes();
+
+  EXPECT_TRUE(runner_.HasAnimations());
+  EXPECT_TRUE(runner_.IsAnimating(animation_id));
+
+  // Properties should be at the initial value.
+  EXPECT_EQ(1.f, view1.opacity());
+  EXPECT_TRUE(view2.transform().IsIdentity());
+
+  // Animate 250ms in, which is quarter way for opacity and half way for
+  // transform.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
+  EXPECT_EQ(.875f, view1.opacity());
+  gfx::Transform half_way_transform;
+  half_way_transform.Scale(1.5, 2.5);
+  EXPECT_EQ(half_way_transform, view2.transform());
+  std::set<ServerView*> views_animating(
+      runner_.GetViewsAnimating(animation_id));
+  EXPECT_EQ(2u, views_animating.size());
+  EXPECT_EQ(1u, views_animating.count(&view1));
+  EXPECT_EQ(1u, views_animating.count(&view2));
+
+  // Animate 750ms in, view1 should be done 3/4 done, and view2 done.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
+  EXPECT_EQ(.625, view1.opacity());
+  EXPECT_EQ(done_transform, view2.transform());
+  views_animating = runner_.GetViewsAnimating(animation_id);
+  EXPECT_EQ(1u, views_animating.size());
+  EXPECT_EQ(1u, views_animating.count(&view1));
+  EXPECT_TRUE(runner_.HasAnimations());
+  EXPECT_TRUE(runner_.IsAnimating(animation_id));
+
+  // Animate to end.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
+  EXPECT_EQ(.5, view1.opacity());
+  EXPECT_EQ(done_transform, view2.transform());
+  views_animating = runner_.GetViewsAnimating(animation_id);
+  EXPECT_TRUE(views_animating.empty());
+  EXPECT_FALSE(runner_.HasAnimations());
+  EXPECT_FALSE(runner_.IsAnimating(animation_id));
+}
+
+TEST_F(AnimationRunnerTest, Reschedule) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+
+  // Animation from 1-0 over 1ms and in parallel transform to 2x,4x over 1ms.
+  AnimationGroup group;
+  InitGroupForView(&group, view.id(), 1);
+  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(0));
+  group.sequences.push_back(AnimationSequence::New());
+  group.sequences[1]->cycle_count = 1;
+  gfx::Transform done_transform;
+  done_transform.Scale(2, 4);
+  AddTransformElement(&group, TimeDelta::FromMicroseconds(1000),
+                      AnimationValuePtr(),
+                      TransformAnimationValue(done_transform));
+  const uint32_t animation_id1 =
+      ScheduleForSingleView(&view, &group, initial_time_);
+
+  // Animate half way in.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+  EXPECT_EQ(.5f, view.opacity());
+  gfx::Transform half_way_transform;
+  half_way_transform.Scale(1.5, 2.5);
+  EXPECT_EQ(half_way_transform, view.transform());
+
+  runner_observer_.clear_changes();
+
+  // Schedule the same view animating opacity to 1.
+  AnimationGroup group2;
+  InitGroupForView(&group2, view.id(), 1);
+  AddOpacityElement(&group2, TimeDelta::FromMicroseconds(1000),
+                    AnimationValuePtr(), FloatAnimationValue(1));
+  const uint32_t animation_id2 = ScheduleForSingleView(
+      &view, &group2, initial_time_ + TimeDelta::FromMicroseconds(500));
+
+  // Opacity should remain at .5, but transform should go to end state.
+  EXPECT_EQ(.5f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+
+  ASSERT_EQ(2u, runner_observer_.changes()->size());
+  EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id1, runner_observer_.change_ids()->at(0));
+  EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
+  EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(1));
+  runner_observer_.clear_changes();
+
+  // Animate half way through new sequence. Opacity should be the only thing
+  // changing.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+  EXPECT_EQ(.75f, view.opacity());
+  EXPECT_EQ(done_transform, view.transform());
+  ASSERT_EQ(0u, runner_observer_.changes()->size());
+
+  // Animate to end.
+  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+  ASSERT_EQ(1u, runner_observer_.changes()->size());
+  EXPECT_EQ("done", runner_observer_.changes()->at(0));
+  EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(0));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/client_connection.cc b/services/view_manager/client_connection.cc
new file mode 100644
index 0000000..7b4d07b
--- /dev/null
+++ b/services/view_manager/client_connection.cc
@@ -0,0 +1,36 @@
+// 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.
+
+#include "services/view_manager/client_connection.h"
+
+#include "services/view_manager/connection_manager.h"
+#include "services/view_manager/view_manager_service_impl.h"
+
+namespace view_manager {
+
+ClientConnection::ClientConnection(scoped_ptr<ViewManagerServiceImpl> service,
+                                   mojo::ViewManagerClient* client)
+    : service_(service.Pass()), client_(client) {
+}
+
+ClientConnection::~ClientConnection() {
+}
+
+DefaultClientConnection::DefaultClientConnection(
+    scoped_ptr<ViewManagerServiceImpl> service_impl,
+    ConnectionManager* connection_manager,
+    mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+    mojo::ViewManagerClientPtr client)
+    : ClientConnection(service_impl.Pass(), client.get()),
+      connection_manager_(connection_manager),
+      binding_(service(), service_request.Pass()),
+      client_(client.Pass()) {
+  binding_.set_connection_error_handler(
+      [this]() { connection_manager_->OnConnectionError(this); });
+}
+
+DefaultClientConnection::~DefaultClientConnection() {
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/client_connection.h b/services/view_manager/client_connection.h
new file mode 100644
index 0000000..84f5b1a
--- /dev/null
+++ b/services/view_manager/client_connection.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_CLIENT_CONNECTION_H_
+#define SERVICES_VIEW_MANAGER_CLIENT_CONNECTION_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+
+namespace view_manager {
+
+class ConnectionManager;
+class ViewManagerServiceImpl;
+
+// ClientConnection encapsulates the state needed for a single client connected
+// to the view manager.
+class ClientConnection {
+ public:
+  ClientConnection(scoped_ptr<ViewManagerServiceImpl> service,
+                   mojo::ViewManagerClient* client);
+  virtual ~ClientConnection();
+
+  ViewManagerServiceImpl* service() { return service_.get(); }
+  const ViewManagerServiceImpl* service() const { return service_.get(); }
+
+  mojo::ViewManagerClient* client() { return client_; }
+
+ private:
+  scoped_ptr<ViewManagerServiceImpl> service_;
+  mojo::ViewManagerClient* client_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientConnection);
+};
+
+// Bindings implementation of ClientConnection.
+class DefaultClientConnection : public ClientConnection {
+ public:
+  DefaultClientConnection(
+      scoped_ptr<ViewManagerServiceImpl> service_impl,
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ViewManagerClientPtr client);
+  ~DefaultClientConnection() override;
+
+ private:
+  ConnectionManager* connection_manager_;
+  mojo::Binding<mojo::ViewManagerService> binding_;
+  mojo::ViewManagerClientPtr client_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultClientConnection);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_CLIENT_CONNECTION_H_
diff --git a/services/view_manager/connection_manager.cc b/services/view_manager/connection_manager.cc
new file mode 100644
index 0000000..dd13cf4
--- /dev/null
+++ b/services/view_manager/connection_manager.cc
@@ -0,0 +1,507 @@
+// 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.
+
+#include "services/view_manager/connection_manager.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "services/view_manager/client_connection.h"
+#include "services/view_manager/connection_manager_delegate.h"
+#include "services/view_manager/display_manager.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/view_coordinate_conversions.h"
+#include "services/view_manager/view_manager_service_impl.h"
+
+using mojo::ConnectionSpecificId;
+
+namespace view_manager {
+namespace {
+
+// Creates a copy of |view|. The copied view has |delegate| as its delegate.
+// This does not recurse.
+ServerView* CloneView(const ServerView* view, ServerViewDelegate* delegate) {
+  ServerView* clone = new ServerView(delegate, ClonedViewId());
+  clone->SetBounds(view->bounds());
+  clone->SetSurfaceId(view->surface_id());
+  clone->SetOpacity(view->opacity());
+  return clone;
+}
+
+// Creates copies of all the visible children of |parent|. Newly cloned views
+// are added to |cloned_parent| and have |delegate| as their delegate. The
+// stacking order of the cloned views is preseved.
+void CloneViewTree(const ServerView* parent,
+                   ServerView* cloned_parent,
+                   ServerViewDelegate* delegate) {
+  DCHECK(parent->visible());
+  for (const ServerView* to_clone : parent->GetChildren()) {
+    if (to_clone->visible()) {
+      ServerView* cloned = CloneView(to_clone, delegate);
+      cloned_parent->Add(cloned);
+      CloneViewTree(to_clone, cloned, delegate);
+    }
+  }
+}
+
+// Recurses through all the children of |view| moving any cloned views to
+// |new_parent| stacked above |stack_above|. |stack_above| is updated as views
+// are moved.
+void ReparentClonedViews(ServerView* new_parent,
+                         ServerView** stack_above,
+                         ServerView* view) {
+  if (view->id() == ClonedViewId()) {
+    const gfx::Rect new_bounds(ConvertRectBetweenViews(
+        view, new_parent, gfx::Rect(view->bounds().size())));
+    new_parent->Add(view);
+    new_parent->Reorder(view, *stack_above, mojo::OrderDirection::ABOVE);
+    view->SetBounds(new_bounds);
+    *stack_above = view;
+    return;
+  }
+
+  for (ServerView* child : view->GetChildren())
+    ReparentClonedViews(new_parent, stack_above, child);
+}
+
+// Deletes |view| and all its descendants.
+void DeleteViewTree(ServerView* view) {
+  for (ServerView* child : view->GetChildren())
+    DeleteViewTree(child);
+
+  delete view;
+}
+
+// TODO(sky): nuke, proof of concept.
+bool DecrementAnimatingViewsOpacity(ServerView* view) {
+  if (view->id() == ClonedViewId()) {
+    const float new_opacity = view->opacity() - .05f;
+    if (new_opacity <= 0)
+      DeleteViewTree(view);
+    else
+      view->SetOpacity(new_opacity);
+    return true;
+  }
+  bool ret_value = false;
+  for (ServerView* child : view->GetChildren()) {
+    if (DecrementAnimatingViewsOpacity(child))
+      ret_value = true;
+  }
+  return ret_value;
+}
+
+}  // namespace
+
+ConnectionManager::ScopedChange::ScopedChange(
+    ViewManagerServiceImpl* connection,
+    ConnectionManager* connection_manager,
+    bool is_delete_view)
+    : connection_manager_(connection_manager),
+      connection_id_(connection->id()),
+      is_delete_view_(is_delete_view) {
+  connection_manager_->PrepareForChange(this);
+}
+
+ConnectionManager::ScopedChange::~ScopedChange() {
+  connection_manager_->FinishChange();
+}
+
+ConnectionManager::ConnectionManager(ConnectionManagerDelegate* delegate,
+                                     scoped_ptr<DisplayManager> display_manager,
+                                     mojo::WindowManagerInternal* wm_internal)
+    : delegate_(delegate),
+      window_manager_client_connection_(nullptr),
+      next_connection_id_(1),
+      display_manager_(display_manager.Pass()),
+      root_(CreateServerView(RootViewId())),
+      wm_internal_(wm_internal),
+      current_change_(nullptr),
+      in_destructor_(false),
+      animation_runner_(base::TimeTicks::Now()) {
+  root_->SetBounds(gfx::Rect(800, 600));
+  root_->SetVisible(true);
+  display_manager_->Init(this);
+}
+
+ConnectionManager::~ConnectionManager() {
+  in_destructor_ = true;
+
+  STLDeleteValues(&connection_map_);
+  // All the connections should have been destroyed.
+  DCHECK(connection_map_.empty());
+  root_.reset();
+}
+
+ServerView* ConnectionManager::CreateServerView(const ViewId& id) {
+  ServerView* view = new ServerView(this, id);
+  view->AddObserver(this);
+  return view;
+}
+
+ConnectionSpecificId ConnectionManager::GetAndAdvanceNextConnectionId() {
+  const ConnectionSpecificId id = next_connection_id_++;
+  DCHECK_LT(id, next_connection_id_);
+  return id;
+}
+
+void ConnectionManager::OnConnectionError(ClientConnection* connection) {
+  if (connection == window_manager_client_connection_) {
+    window_manager_client_connection_ = nullptr;
+    delegate_->OnLostConnectionToWindowManager();
+    // Assume we've been destroyed.
+    return;
+  }
+
+  scoped_ptr<ClientConnection> connection_owner(connection);
+
+  connection_map_.erase(connection->service()->id());
+
+  // Notify remaining connections so that they can cleanup.
+  for (auto& pair : connection_map_) {
+    pair.second->service()->OnWillDestroyViewManagerServiceImpl(
+        connection->service());
+  }
+}
+
+void ConnectionManager::EmbedAtView(
+    ConnectionSpecificId creator_id,
+    const std::string& url,
+    const ViewId& view_id,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  std::string creator_url;
+  ConnectionMap::const_iterator it = connection_map_.find(creator_id);
+  if (it != connection_map_.end())
+    creator_url = it->second->service()->url();
+
+  mojo::ViewManagerServicePtr service_ptr;
+  ClientConnection* client_connection =
+      delegate_->CreateClientConnectionForEmbedAtView(
+          this, GetProxy(&service_ptr), creator_id, creator_url, url, view_id);
+  AddConnection(client_connection);
+  client_connection->service()->Init(client_connection->client(),
+                                     service_ptr.Pass(), services.Pass(),
+                                     exposed_services.Pass());
+  OnConnectionMessagedClient(client_connection->service()->id());
+}
+
+void ConnectionManager::EmbedAtView(mojo::ConnectionSpecificId creator_id,
+                                    const ViewId& view_id,
+                                    mojo::ViewManagerClientPtr client) {
+  std::string creator_url;
+  ConnectionMap::const_iterator it = connection_map_.find(creator_id);
+  if (it != connection_map_.end())
+    creator_url = it->second->service()->url();
+
+  mojo::ViewManagerServicePtr service_ptr;
+  ClientConnection* client_connection =
+      delegate_->CreateClientConnectionForEmbedAtView(
+          this, GetProxy(&service_ptr), creator_id, creator_url, view_id,
+          client.Pass());
+  AddConnection(client_connection);
+  client_connection->service()->Init(client_connection->client(),
+                                     service_ptr.Pass(), nullptr, nullptr);
+  OnConnectionMessagedClient(client_connection->service()->id());
+}
+
+ViewManagerServiceImpl* ConnectionManager::GetConnection(
+    ConnectionSpecificId connection_id) {
+  ConnectionMap::iterator i = connection_map_.find(connection_id);
+  return i == connection_map_.end() ? nullptr : i->second->service();
+}
+
+ServerView* ConnectionManager::GetView(const ViewId& id) {
+  if (id == root_->id())
+    return root_.get();
+  ViewManagerServiceImpl* service = GetConnection(id.connection_id);
+  return service ? service->GetView(id) : nullptr;
+}
+
+void ConnectionManager::OnConnectionMessagedClient(ConnectionSpecificId id) {
+  if (current_change_)
+    current_change_->MarkConnectionAsMessaged(id);
+}
+
+bool ConnectionManager::DidConnectionMessageClient(
+    ConnectionSpecificId id) const {
+  return current_change_ && current_change_->DidMessageConnection(id);
+}
+
+const ViewManagerServiceImpl* ConnectionManager::GetConnectionWithRoot(
+    const ViewId& id) const {
+  for (auto& pair : connection_map_) {
+    if (pair.second->service()->IsRoot(id))
+      return pair.second->service();
+  }
+  return nullptr;
+}
+
+void ConnectionManager::SetWindowManagerClientConnection(
+    scoped_ptr<ClientConnection> connection) {
+  CHECK(!window_manager_client_connection_);
+  window_manager_client_connection_ = connection.release();
+  AddConnection(window_manager_client_connection_);
+  window_manager_client_connection_->service()->Init(
+      window_manager_client_connection_->client(), nullptr, nullptr, nullptr);
+}
+
+mojo::ViewManagerClient*
+ConnectionManager::GetWindowManagerViewManagerClient() {
+  CHECK(window_manager_client_connection_);
+  return window_manager_client_connection_->client();
+}
+
+bool ConnectionManager::CloneAndAnimate(const ViewId& view_id) {
+  ServerView* view = GetView(view_id);
+  if (!view || !view->IsDrawn(root_.get()) || view == root_.get())
+    return false;
+  if (!animation_timer_.IsRunning()) {
+    animation_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(100),
+                           this, &ConnectionManager::DoAnimation);
+  }
+  ServerView* clone = CloneView(view, this);
+  CloneViewTree(view, clone, this);
+  view->parent()->Add(clone);
+  view->parent()->Reorder(clone, view, mojo::OrderDirection::ABOVE);
+  return true;
+}
+
+void ConnectionManager::ProcessViewBoundsChanged(const ServerView* view,
+                                                 const gfx::Rect& old_bounds,
+                                                 const gfx::Rect& new_bounds) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewBoundsChanged(
+        view, old_bounds, new_bounds, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::ProcessViewportMetricsChanged(
+    const mojo::ViewportMetrics& old_metrics,
+    const mojo::ViewportMetrics& new_metrics) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewportMetricsChanged(
+        old_metrics, new_metrics, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::ProcessWillChangeViewHierarchy(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessWillChangeViewHierarchy(
+        view, new_parent, old_parent, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::ProcessViewHierarchyChanged(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewHierarchyChanged(
+        view, new_parent, old_parent, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::ProcessViewReorder(
+    const ServerView* view,
+    const ServerView* relative_view,
+    const mojo::OrderDirection direction) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewReorder(view, relative_view, direction,
+                                               IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::ProcessViewDeleted(const ViewId& view) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewDeleted(view,
+                                               IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::PrepareForChange(ScopedChange* change) {
+  // Should only ever have one change in flight.
+  CHECK(!current_change_);
+  current_change_ = change;
+}
+
+void ConnectionManager::FinishChange() {
+  // PrepareForChange/FinishChange should be balanced.
+  CHECK(current_change_);
+  current_change_ = NULL;
+}
+
+void ConnectionManager::DoAnimation() {
+  if (!DecrementAnimatingViewsOpacity(root()))
+    animation_timer_.Stop();
+}
+
+void ConnectionManager::AddConnection(ClientConnection* connection) {
+  DCHECK_EQ(0u, connection_map_.count(connection->service()->id()));
+  connection_map_[connection->service()->id()] = connection;
+}
+
+void ConnectionManager::PrepareToDestroyView(ServerView* view) {
+  if (!in_destructor_ && root_->Contains(view) && view != root_.get() &&
+      view->id() != ClonedViewId()) {
+    // We're about to destroy a view. Any cloned views need to be reparented
+    // else the animation would no longer be visible. By moving to a visible
+    // view, view->parent(), we ensure the animation is still visible.
+    ServerView* parent_above = view;
+    ReparentClonedViews(view->parent(), &parent_above, view);
+  }
+
+  animation_runner_.CancelAnimationForView(view);
+}
+
+void ConnectionManager::PrepareToChangeViewHierarchy(ServerView* view,
+                                                     ServerView* new_parent,
+                                                     ServerView* old_parent) {
+  if (view->id() == ClonedViewId() || in_destructor_)
+    return;
+
+  if (root_->Contains(view) && view != root_.get()) {
+    // We're about to reparent a view. Any cloned views need to be reparented
+    // else the animation may be effected in unusual ways. For example, the view
+    // could move to a new location such that the animation is entirely clipped.
+    // By moving to view->parent() we ensure the animation is still visible.
+    ServerView* parent_above = view;
+    ReparentClonedViews(view->parent(), &parent_above, view);
+  }
+
+  animation_runner_.CancelAnimationForView(view);
+}
+
+void ConnectionManager::PrepareToChangeViewVisibility(ServerView* view) {
+  if (in_destructor_)
+    return;
+
+  if (view != root_.get() && view->id() != ClonedViewId() &&
+      root_->Contains(view) && view->IsDrawn(root_.get())) {
+    // We're about to hide |view|, this would implicitly make any cloned views
+    // hide too. Reparent so that animations are still visible.
+    ServerView* parent_above = view;
+    ReparentClonedViews(view->parent(), &parent_above, view);
+  }
+
+  const bool is_parent_drawn =
+      view->parent() && view->parent()->IsDrawn(root_.get());
+  if (!is_parent_drawn || !view->visible())
+    animation_runner_.CancelAnimationForView(view);
+}
+
+void ConnectionManager::OnScheduleViewPaint(const ServerView* view) {
+  if (!in_destructor_)
+    display_manager_->SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnViewDestroyed(ServerView* view) {
+  if (!in_destructor_)
+    ProcessViewDeleted(view->id());
+}
+
+void ConnectionManager::OnWillChangeViewHierarchy(ServerView* view,
+                                                  ServerView* new_parent,
+                                                  ServerView* old_parent) {
+  if (view->id() == ClonedViewId() || in_destructor_)
+    return;
+
+  ProcessWillChangeViewHierarchy(view, new_parent, old_parent);
+}
+
+void ConnectionManager::OnViewHierarchyChanged(ServerView* view,
+                                               ServerView* new_parent,
+                                               ServerView* old_parent) {
+  if (in_destructor_)
+    return;
+
+  ProcessViewHierarchyChanged(view, new_parent, old_parent);
+
+  // TODO(beng): optimize.
+  if (old_parent) {
+    display_manager_->SchedulePaint(old_parent,
+                                    gfx::Rect(old_parent->bounds().size()));
+  }
+  if (new_parent) {
+    display_manager_->SchedulePaint(new_parent,
+                                    gfx::Rect(new_parent->bounds().size()));
+  }
+}
+
+void ConnectionManager::OnViewBoundsChanged(ServerView* view,
+                                            const gfx::Rect& old_bounds,
+                                            const gfx::Rect& new_bounds) {
+  if (in_destructor_)
+    return;
+
+  ProcessViewBoundsChanged(view, old_bounds, new_bounds);
+  if (!view->parent())
+    return;
+
+  // TODO(sky): optimize this.
+  display_manager_->SchedulePaint(view->parent(), old_bounds);
+  display_manager_->SchedulePaint(view->parent(), new_bounds);
+}
+
+void ConnectionManager::OnViewReordered(ServerView* view,
+                                        ServerView* relative,
+                                        mojo::OrderDirection direction) {
+  if (!in_destructor_)
+    display_manager_->SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnWillChangeViewVisibility(ServerView* view) {
+  if (in_destructor_)
+    return;
+
+  // Need to repaint if the view was drawn (which means it's in the process of
+  // hiding) or the view is transitioning to drawn.
+  if (view->IsDrawn(root_.get()) || (!view->visible() && view->parent() &&
+                                     view->parent()->IsDrawn(root_.get()))) {
+    display_manager_->SchedulePaint(view->parent(), view->bounds());
+  }
+
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessWillChangeViewVisibility(
+        view, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::OnViewSharedPropertyChanged(
+    ServerView* view,
+    const std::string& name,
+    const std::vector<uint8_t>* new_data) {
+  for (auto& pair : connection_map_) {
+    pair.second->service()->ProcessViewPropertyChanged(
+        view, name, new_data, IsChangeSource(pair.first));
+  }
+}
+
+void ConnectionManager::DispatchInputEventToView(mojo::Id transport_view_id,
+                                                 mojo::EventPtr event) {
+  const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+
+  ViewManagerServiceImpl* connection = GetConnectionWithRoot(view_id);
+  if (!connection)
+    connection = GetConnection(view_id.connection_id);
+  if (connection) {
+    connection->client()->OnViewInputEvent(
+        transport_view_id, event.Pass(), base::Bind(&base::DoNothing));
+  }
+}
+
+void ConnectionManager::SetViewportSize(mojo::SizePtr size) {
+  gfx::Size new_size = size.To<gfx::Size>();
+  display_manager_->SetViewportSize(new_size);
+}
+
+void ConnectionManager::CloneAndAnimate(mojo::Id transport_view_id) {
+  CloneAndAnimate(ViewIdFromTransportId(transport_view_id));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/connection_manager.h b/services/view_manager/connection_manager.h
new file mode 100644
index 0000000..108f456
--- /dev/null
+++ b/services/view_manager/connection_manager.h
@@ -0,0 +1,252 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
+#define SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer/timer.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/view_manager/animation_runner.h"
+#include "services/view_manager/ids.h"
+#include "services/view_manager/server_view_delegate.h"
+#include "services/view_manager/server_view_observer.h"
+
+namespace view_manager {
+
+class ClientConnection;
+class ConnectionManagerDelegate;
+class DisplayManager;
+class ServerView;
+class ViewManagerServiceImpl;
+
+// ConnectionManager manages the set of connections to the ViewManager (all the
+// ViewManagerServiceImpls) as well as providing the root of the hierarchy.
+class ConnectionManager : public ServerViewDelegate,
+                          public ServerViewObserver,
+                          public mojo::WindowManagerInternalClient {
+ public:
+  // Create when a ViewManagerServiceImpl is about to make a change. Ensures
+  // clients are notified correctly.
+  class ScopedChange {
+   public:
+    ScopedChange(ViewManagerServiceImpl* connection,
+                 ConnectionManager* connection_manager,
+                 bool is_delete_view);
+    ~ScopedChange();
+
+    mojo::ConnectionSpecificId connection_id() const { return connection_id_; }
+    bool is_delete_view() const { return is_delete_view_; }
+
+    // Marks the connection with the specified id as having seen a message.
+    void MarkConnectionAsMessaged(mojo::ConnectionSpecificId connection_id) {
+      message_ids_.insert(connection_id);
+    }
+
+    // Returns true if MarkConnectionAsMessaged(connection_id) was invoked.
+    bool DidMessageConnection(mojo::ConnectionSpecificId connection_id) const {
+      return message_ids_.count(connection_id) > 0;
+    }
+
+   private:
+    ConnectionManager* connection_manager_;
+    const mojo::ConnectionSpecificId connection_id_;
+    const bool is_delete_view_;
+
+    // See description of MarkConnectionAsMessaged/DidMessageConnection.
+    std::set<mojo::ConnectionSpecificId> message_ids_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedChange);
+  };
+
+  ConnectionManager(ConnectionManagerDelegate* delegate,
+                    scoped_ptr<DisplayManager> display_manager,
+                    mojo::WindowManagerInternal* wm_internal);
+  ~ConnectionManager() override;
+
+  // Creates a new ServerView. The return value is owned by the caller, but must
+  // be destroyed before ConnectionManager.
+  ServerView* CreateServerView(const ViewId& id);
+
+  // Returns the id for the next ViewManagerServiceImpl.
+  mojo::ConnectionSpecificId GetAndAdvanceNextConnectionId();
+
+  // Invoked when a ViewManagerServiceImpl's connection encounters an error.
+  void OnConnectionError(ClientConnection* connection);
+
+  // See description of ViewManagerService::Embed() for details. This assumes
+  // |transport_view_id| is valid.
+  void EmbedAtView(mojo::ConnectionSpecificId creator_id,
+                   const std::string& url,
+                   const ViewId& view_id,
+                   mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                   mojo::ServiceProviderPtr exposed_services);
+  void EmbedAtView(mojo::ConnectionSpecificId creator_id,
+                   const ViewId& view_id,
+                   mojo::ViewManagerClientPtr client);
+
+  // Returns the connection by id.
+  ViewManagerServiceImpl* GetConnection(
+      mojo::ConnectionSpecificId connection_id);
+
+  // Returns the View identified by |id|.
+  ServerView* GetView(const ViewId& id);
+
+  ServerView* root() { return root_.get(); }
+  DisplayManager* display_manager() { return display_manager_.get(); }
+
+  bool IsProcessingChange() const { return current_change_ != NULL; }
+
+  bool is_processing_delete_view() const {
+    return current_change_ && current_change_->is_delete_view();
+  }
+
+  // Invoked when a connection messages a client about the change. This is used
+  // to avoid sending ServerChangeIdAdvanced() unnecessarily.
+  void OnConnectionMessagedClient(mojo::ConnectionSpecificId id);
+
+  // Returns true if OnConnectionMessagedClient() was invoked for id.
+  bool DidConnectionMessageClient(mojo::ConnectionSpecificId id) const;
+
+  // Returns the ViewManagerServiceImpl that has |id| as a root.
+  ViewManagerServiceImpl* GetConnectionWithRoot(const ViewId& id) {
+    return const_cast<ViewManagerServiceImpl*>(
+        const_cast<const ConnectionManager*>(this)->GetConnectionWithRoot(id));
+  }
+  const ViewManagerServiceImpl* GetConnectionWithRoot(const ViewId& id) const;
+
+  mojo::WindowManagerInternal* wm_internal() { return wm_internal_; }
+
+  void SetWindowManagerClientConnection(
+      scoped_ptr<ClientConnection> connection);
+  bool has_window_manager_client_connection() const {
+    return window_manager_client_connection_ != nullptr;
+  }
+
+  mojo::ViewManagerClient* GetWindowManagerViewManagerClient();
+
+  // WindowManagerInternalClient implementation helper; see mojom for details.
+  bool CloneAndAnimate(const ViewId& view_id);
+
+  // These functions trivially delegate to all ViewManagerServiceImpls, which in
+  // term notify their clients.
+  void ProcessViewDestroyed(ServerView* view);
+  void ProcessViewBoundsChanged(const ServerView* view,
+                                const gfx::Rect& old_bounds,
+                                const gfx::Rect& new_bounds);
+  void ProcessViewportMetricsChanged(const mojo::ViewportMetrics& old_metrics,
+                                     const mojo::ViewportMetrics& new_metrics);
+  void ProcessWillChangeViewHierarchy(const ServerView* view,
+                                      const ServerView* new_parent,
+                                      const ServerView* old_parent);
+  void ProcessViewHierarchyChanged(const ServerView* view,
+                                   const ServerView* new_parent,
+                                   const ServerView* old_parent);
+  void ProcessViewReorder(const ServerView* view,
+                          const ServerView* relative_view,
+                          const mojo::OrderDirection direction);
+  void ProcessViewDeleted(const ViewId& view);
+
+ private:
+  typedef std::map<mojo::ConnectionSpecificId, ClientConnection*> ConnectionMap;
+
+  // Invoked when a connection is about to make a change.  Subsequently followed
+  // by FinishChange() once the change is done.
+  //
+  // Changes should never nest, meaning each PrepareForChange() must be
+  // balanced with a call to FinishChange() with no PrepareForChange()
+  // in between.
+  void PrepareForChange(ScopedChange* change);
+
+  // Balances a call to PrepareForChange().
+  void FinishChange();
+
+  // Returns true if the specified connection originated the current change.
+  bool IsChangeSource(mojo::ConnectionSpecificId connection_id) const {
+    return current_change_ && current_change_->connection_id() == connection_id;
+  }
+
+  // Adds |connection| to internal maps.
+  void AddConnection(ClientConnection* connection);
+
+  // Callback from animation timer.
+  // TODO(sky): make this real (move to a different class).
+  void DoAnimation();
+
+  // Overridden from ServerViewDelegate:
+  void PrepareToDestroyView(ServerView* view) override;
+  void PrepareToChangeViewHierarchy(ServerView* view,
+                                    ServerView* new_parent,
+                                    ServerView* old_parent) override;
+  void PrepareToChangeViewVisibility(ServerView* view) override;
+  void OnScheduleViewPaint(const ServerView* view) override;
+
+  // Overridden from ServerViewObserver:
+  void OnViewDestroyed(ServerView* view) override;
+  void OnWillChangeViewHierarchy(ServerView* view,
+                                 ServerView* new_parent,
+                                 ServerView* old_parent) override;
+  void OnViewHierarchyChanged(ServerView* view,
+                              ServerView* new_parent,
+                              ServerView* old_parent) override;
+  void OnViewBoundsChanged(ServerView* view,
+                           const gfx::Rect& old_bounds,
+                           const gfx::Rect& new_bounds) override;
+  void OnViewReordered(ServerView* view,
+                       ServerView* relative,
+                       mojo::OrderDirection direction) override;
+  void OnWillChangeViewVisibility(ServerView* view) override;
+  void OnViewSharedPropertyChanged(
+      ServerView* view,
+      const std::string& name,
+      const std::vector<uint8_t>* new_data) override;
+
+  // WindowManagerInternalClient:
+  void DispatchInputEventToView(mojo::Id transport_view_id,
+                                mojo::EventPtr event) override;
+  void SetViewportSize(mojo::SizePtr size) override;
+  void CloneAndAnimate(mojo::Id transport_view_id) override;
+
+  ConnectionManagerDelegate* delegate_;
+
+  // The ClientConnection containing the ViewManagerService implementation
+  // provided to the initial connection (the WindowManager).
+  // NOTE: |window_manager_client_connection_| is also in |connection_map_|.
+  ClientConnection* window_manager_client_connection_;
+
+  // ID to use for next ViewManagerServiceImpl.
+  mojo::ConnectionSpecificId next_connection_id_;
+
+  // Set of ViewManagerServiceImpls.
+  ConnectionMap connection_map_;
+
+  scoped_ptr<DisplayManager> display_manager_;
+
+  scoped_ptr<ServerView> root_;
+
+  mojo::WindowManagerInternal* wm_internal_;
+
+  // If non-null we're processing a change. The ScopedChange is not owned by us
+  // (it's created on the stack by ViewManagerServiceImpl).
+  ScopedChange* current_change_;
+
+  bool in_destructor_;
+
+  // TODO(sky): nuke! Just a proof of concept until get real animation api.
+  base::RepeatingTimer<ConnectionManager> animation_timer_;
+
+  AnimationRunner animation_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
diff --git a/services/view_manager/connection_manager_delegate.h b/services/view_manager/connection_manager_delegate.h
new file mode 100644
index 0000000..fea9afe
--- /dev/null
+++ b/services/view_manager/connection_manager_delegate.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_DELEGATE_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/view_manager/cpp/types.h"
+
+namespace mojo {
+class ViewManagerService;
+}
+
+namespace view_manager {
+
+class ClientConnection;
+class ConnectionManager;
+struct ViewId;
+
+class ConnectionManagerDelegate {
+ public:
+  virtual void OnLostConnectionToWindowManager() = 0;
+
+  // Creates a ClientConnection in response to Embed() calls on the
+  // ConnectionManager.
+  virtual ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const std::string& url,
+      const ViewId& root_id) = 0;
+  virtual ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr view_manager_client) = 0;
+
+ protected:
+  virtual ~ConnectionManagerDelegate() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_DELEGATE_H_
diff --git a/services/view_manager/default_access_policy.cc b/services/view_manager/default_access_policy.cc
new file mode 100644
index 0000000..336d1cc
--- /dev/null
+++ b/services/view_manager/default_access_policy.cc
@@ -0,0 +1,112 @@
+// 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.
+
+#include "services/view_manager/default_access_policy.h"
+
+#include "services/view_manager/access_policy_delegate.h"
+#include "services/view_manager/server_view.h"
+
+namespace view_manager {
+
+DefaultAccessPolicy::DefaultAccessPolicy(
+    mojo::ConnectionSpecificId connection_id,
+    AccessPolicyDelegate* delegate)
+    : connection_id_(connection_id), delegate_(delegate) {
+}
+
+DefaultAccessPolicy::~DefaultAccessPolicy() {
+}
+
+bool DefaultAccessPolicy::CanRemoveViewFromParent(
+    const ServerView* view) const {
+  if (!WasCreatedByThisConnection(view))
+    return false;  // Can only unparent views we created.
+
+  return delegate_->IsRootForAccessPolicy(view->parent()->id()) ||
+         WasCreatedByThisConnection(view->parent());
+}
+
+bool DefaultAccessPolicy::CanAddView(const ServerView* parent,
+                                     const ServerView* child) const {
+  return WasCreatedByThisConnection(child) &&
+         (delegate_->IsRootForAccessPolicy(parent->id()) ||
+          (WasCreatedByThisConnection(parent) &&
+           !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(parent)));
+}
+
+bool DefaultAccessPolicy::CanReorderView(const ServerView* view,
+                                         const ServerView* relative_view,
+                                         mojo::OrderDirection direction) const {
+  return WasCreatedByThisConnection(view) &&
+         WasCreatedByThisConnection(relative_view);
+}
+
+bool DefaultAccessPolicy::CanDeleteView(const ServerView* view) const {
+  return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanGetViewTree(const ServerView* view) const {
+  return WasCreatedByThisConnection(view) ||
+         delegate_->IsRootForAccessPolicy(view->id());
+}
+
+bool DefaultAccessPolicy::CanDescendIntoViewForViewTree(
+    const ServerView* view) const {
+  return (WasCreatedByThisConnection(view) &&
+          !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view)) ||
+      delegate_->IsRootForAccessPolicy(view->id());
+}
+
+bool DefaultAccessPolicy::CanEmbed(const ServerView* view) const {
+  return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanChangeViewVisibility(
+    const ServerView* view) const {
+  return WasCreatedByThisConnection(view) ||
+         delegate_->IsRootForAccessPolicy(view->id());
+}
+
+bool DefaultAccessPolicy::CanSetViewSurfaceId(const ServerView* view) const {
+  // Once a view embeds another app, the embedder app is no longer able to
+  // call SetViewSurfaceId() - this ability is transferred to the embedded app.
+  if (delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view))
+    return false;
+  return WasCreatedByThisConnection(view) ||
+         delegate_->IsRootForAccessPolicy(view->id());
+}
+
+bool DefaultAccessPolicy::CanSetViewBounds(const ServerView* view) const {
+  return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanSetViewProperties(const ServerView* view) const {
+  return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange(
+    const ServerView* view,
+    const ServerView** new_parent,
+    const ServerView** old_parent) const {
+  if (!WasCreatedByThisConnection(view))
+    return false;
+
+  if (*new_parent && !WasCreatedByThisConnection(*new_parent) &&
+      !delegate_->IsRootForAccessPolicy((*new_parent)->id())) {
+    *new_parent = NULL;
+  }
+
+  if (*old_parent && !WasCreatedByThisConnection(*old_parent) &&
+      !delegate_->IsRootForAccessPolicy((*old_parent)->id())) {
+    *old_parent = NULL;
+  }
+  return true;
+}
+
+bool DefaultAccessPolicy::WasCreatedByThisConnection(
+    const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/default_access_policy.h b/services/view_manager/default_access_policy.h
new file mode 100644
index 0000000..6187355
--- /dev/null
+++ b/services/view_manager/default_access_policy.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
+#define SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "services/view_manager/access_policy.h"
+
+namespace view_manager {
+
+class AccessPolicyDelegate;
+
+// AccessPolicy for all connections, except the window manager.
+class DefaultAccessPolicy : public AccessPolicy {
+ public:
+  DefaultAccessPolicy(mojo::ConnectionSpecificId connection_id,
+                      AccessPolicyDelegate* delegate);
+  ~DefaultAccessPolicy() override;
+
+  // AccessPolicy:
+  bool CanRemoveViewFromParent(const ServerView* view) const override;
+  bool CanAddView(const ServerView* parent,
+                  const ServerView* child) const override;
+  bool CanReorderView(const ServerView* view,
+                      const ServerView* relative_view,
+                      mojo::OrderDirection direction) const override;
+  bool CanDeleteView(const ServerView* view) const override;
+  bool CanGetViewTree(const ServerView* view) const override;
+  bool CanDescendIntoViewForViewTree(const ServerView* view) const override;
+  bool CanEmbed(const ServerView* view) const override;
+  bool CanChangeViewVisibility(const ServerView* view) const override;
+  bool CanSetViewSurfaceId(const ServerView* view) const override;
+  bool CanSetViewBounds(const ServerView* view) const override;
+  bool CanSetViewProperties(const ServerView* view) const override;
+  bool ShouldNotifyOnHierarchyChange(
+      const ServerView* view,
+      const ServerView** new_parent,
+      const ServerView** old_parent) const override;
+
+ private:
+  bool WasCreatedByThisConnection(const ServerView* view) const;
+
+  const mojo::ConnectionSpecificId connection_id_;
+  AccessPolicyDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultAccessPolicy);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
diff --git a/services/view_manager/display_manager.cc b/services/view_manager/display_manager.cc
new file mode 100644
index 0000000..b6ec195
--- /dev/null
+++ b/services/view_manager/display_manager.cc
@@ -0,0 +1,181 @@
+// 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.
+
+#include "services/view_manager/display_manager.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/surfaces/surfaces_type_converters.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/gpu/interfaces/gpu.mojom.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/view_manager/connection_manager.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/view_coordinate_conversions.h"
+
+namespace view_manager {
+namespace {
+
+void DrawViewTree(mojo::Pass* pass,
+                  const ServerView* view,
+                  const gfx::Vector2d& parent_to_root_origin_offset,
+                  float opacity) {
+  if (!view->visible())
+    return;
+
+  const gfx::Rect absolute_bounds =
+      view->bounds() + parent_to_root_origin_offset;
+  std::vector<const ServerView*> children(view->GetChildren());
+  const float combined_opacity = opacity * view->opacity();
+  for (std::vector<const ServerView*>::reverse_iterator it = children.rbegin();
+       it != children.rend();
+       ++it) {
+    DrawViewTree(pass, *it, absolute_bounds.OffsetFromOrigin(),
+                 combined_opacity);
+  }
+
+  cc::SurfaceId node_id = view->surface_id();
+
+  auto surface_quad_state = mojo::SurfaceQuadState::New();
+  surface_quad_state->surface = mojo::SurfaceId::From(node_id);
+
+  gfx::Transform node_transform;
+  node_transform.Translate(absolute_bounds.x(), absolute_bounds.y());
+
+  const gfx::Rect bounds_at_origin(view->bounds().size());
+  auto surface_quad = mojo::Quad::New();
+  surface_quad->material = mojo::Material::SURFACE_CONTENT;
+  surface_quad->rect = mojo::Rect::From(bounds_at_origin);
+  surface_quad->opaque_rect = mojo::Rect::From(bounds_at_origin);
+  surface_quad->visible_rect = mojo::Rect::From(bounds_at_origin);
+  surface_quad->needs_blending = true;
+  surface_quad->shared_quad_state_index =
+      base::saturated_cast<int32_t>(pass->shared_quad_states.size());
+  surface_quad->surface_quad_state = surface_quad_state.Pass();
+
+  auto sqs = CreateDefaultSQS(*mojo::Size::From(view->bounds().size()));
+  sqs->blend_mode = mojo::SkXfermode::kSrcOver_Mode;
+  sqs->opacity = combined_opacity;
+  sqs->content_to_target_transform = mojo::Transform::From(node_transform);
+
+  pass->quads.push_back(surface_quad.Pass());
+  pass->shared_quad_states.push_back(sqs.Pass());
+}
+
+}  // namespace
+
+DefaultDisplayManager::DefaultDisplayManager(
+    mojo::ApplicationImpl* app_impl,
+    mojo::ApplicationConnection* app_connection,
+    const mojo::Closure& native_viewport_closed_callback)
+    : app_impl_(app_impl),
+      app_connection_(app_connection),
+      connection_manager_(nullptr),
+      draw_timer_(false, false),
+      frame_pending_(false),
+      native_viewport_closed_callback_(native_viewport_closed_callback),
+      weak_factory_(this) {
+  metrics_.size = mojo::Size::New();
+  metrics_.size->width = 800;
+  metrics_.size->height = 600;
+}
+
+void DefaultDisplayManager::Init(ConnectionManager* connection_manager) {
+  connection_manager_ = connection_manager;
+  app_impl_->ConnectToService("mojo:native_viewport_service",
+                              &native_viewport_);
+  // The connection error handler will be called if native_viewport_ is torn
+  // down before this object is destroyed.
+  native_viewport_.set_connection_error_handler(
+      [this]() { native_viewport_closed_callback_.Run(); });
+  native_viewport_->Create(metrics_.size->Clone(),
+                           mojo::SurfaceConfiguration::New(),
+                           base::Bind(&DefaultDisplayManager::OnMetricsChanged,
+                                      weak_factory_.GetWeakPtr()));
+  native_viewport_->Show();
+
+  mojo::ContextProviderPtr context_provider;
+  native_viewport_->GetContextProvider(GetProxy(&context_provider));
+  mojo::DisplayFactoryPtr display_factory;
+  app_impl_->ConnectToService("mojo:surfaces_service", &display_factory);
+  display_factory->Create(context_provider.Pass(),
+                          nullptr,  // returner - we never submit resources.
+                          GetProxy(&display_));
+
+  mojo::NativeViewportEventDispatcherPtr event_dispatcher;
+  app_connection_->ConnectToService(&event_dispatcher);
+  native_viewport_->SetEventDispatcher(event_dispatcher.Pass());
+}
+
+DefaultDisplayManager::~DefaultDisplayManager() {
+}
+
+void DefaultDisplayManager::SchedulePaint(const ServerView* view,
+                                          const gfx::Rect& bounds) {
+  if (!view->IsDrawn(connection_manager_->root()))
+    return;
+  const gfx::Rect root_relative_rect =
+      ConvertRectBetweenViews(view, connection_manager_->root(), bounds);
+  if (root_relative_rect.IsEmpty())
+    return;
+  dirty_rect_.Union(root_relative_rect);
+  WantToDraw();
+}
+
+void DefaultDisplayManager::SetViewportSize(const gfx::Size& size) {
+  native_viewport_->SetSize(mojo::Size::From(size));
+}
+
+const mojo::ViewportMetrics& DefaultDisplayManager::GetViewportMetrics() {
+  return metrics_;
+}
+
+void DefaultDisplayManager::Draw() {
+  mojo::Rect rect;
+  rect.width = metrics_.size->width;
+  rect.height = metrics_.size->height;
+  auto pass = CreateDefaultPass(1, rect);
+  pass->damage_rect = mojo::Rect::From(dirty_rect_);
+
+  DrawViewTree(pass.get(), connection_manager_->root(), gfx::Vector2d(), 1.0f);
+
+  auto frame = mojo::Frame::New();
+  frame->passes.push_back(pass.Pass());
+  frame->resources.resize(0u);
+  frame_pending_ = true;
+  display_->SubmitFrame(
+      frame.Pass(),
+      base::Bind(&DefaultDisplayManager::DidDraw, base::Unretained(this)));
+  dirty_rect_ = gfx::Rect();
+}
+
+void DefaultDisplayManager::DidDraw() {
+  frame_pending_ = false;
+  if (!dirty_rect_.IsEmpty())
+    WantToDraw();
+}
+
+void DefaultDisplayManager::WantToDraw() {
+  if (draw_timer_.IsRunning() || frame_pending_)
+    return;
+
+  draw_timer_.Start(
+      FROM_HERE, base::TimeDelta(),
+      base::Bind(&DefaultDisplayManager::Draw, base::Unretained(this)));
+}
+
+void DefaultDisplayManager::OnMetricsChanged(mojo::ViewportMetricsPtr metrics) {
+  metrics_.size = metrics->size.Clone();
+  metrics_.device_pixel_ratio = metrics->device_pixel_ratio;
+  gfx::Rect bounds(metrics_.size.To<gfx::Size>());
+  connection_manager_->root()->SetBounds(bounds);
+  connection_manager_->ProcessViewportMetricsChanged(metrics_, *metrics);
+  native_viewport_->RequestMetrics(base::Bind(
+      &DefaultDisplayManager::OnMetricsChanged, weak_factory_.GetWeakPtr()));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/display_manager.h b/services/view_manager/display_manager.h
new file mode 100644
index 0000000..99b6a46
--- /dev/null
+++ b/services/view_manager/display_manager.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
+#define SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+class ApplicationConnection;
+class ApplicationImpl;
+}
+
+namespace view_manager {
+
+class ConnectionManager;
+class ServerView;
+
+// DisplayManager is used to connect the root ServerView to a display.
+class DisplayManager {
+ public:
+  virtual ~DisplayManager() {}
+
+  virtual void Init(ConnectionManager* connection_manager) = 0;
+
+  // Schedules a paint for the specified region in the coordinates of |view|.
+  virtual void SchedulePaint(const ServerView* view,
+                             const gfx::Rect& bounds) = 0;
+
+  virtual void SetViewportSize(const gfx::Size& size) = 0;
+
+  virtual const mojo::ViewportMetrics& GetViewportMetrics() = 0;
+};
+
+// DisplayManager implementation that connects to the services necessary to
+// actually display.
+class DefaultDisplayManager : public DisplayManager {
+ public:
+  DefaultDisplayManager(mojo::ApplicationImpl* app_impl,
+                        mojo::ApplicationConnection* app_connection,
+                        const mojo::Closure& native_viewport_closed_callback);
+  ~DefaultDisplayManager() override;
+
+  // DisplayManager:
+  void Init(ConnectionManager* connection_manager) override;
+  void SchedulePaint(const ServerView* view, const gfx::Rect& bounds) override;
+  void SetViewportSize(const gfx::Size& size) override;
+  const mojo::ViewportMetrics& GetViewportMetrics() override;
+
+ private:
+  void WantToDraw();
+  void Draw();
+  void DidDraw();
+
+  void OnMetricsChanged(mojo::ViewportMetricsPtr metrics);
+
+  mojo::ApplicationImpl* app_impl_;
+  mojo::ApplicationConnection* app_connection_;
+  ConnectionManager* connection_manager_;
+
+  mojo::ViewportMetrics metrics_;
+  gfx::Rect dirty_rect_;
+  base::Timer draw_timer_;
+  bool frame_pending_;
+
+  mojo::DisplayPtr display_;
+  mojo::NativeViewportPtr native_viewport_;
+  mojo::Closure native_viewport_closed_callback_;
+  base::WeakPtrFactory<DefaultDisplayManager> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultDisplayManager);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
diff --git a/services/view_manager/focus_controller.cc b/services/view_manager/focus_controller.cc
new file mode 100644
index 0000000..65ee0fe
--- /dev/null
+++ b/services/view_manager/focus_controller.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 "services/view_manager/focus_controller.h"
+
+#include "services/view_manager/focus_controller_delegate.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/server_view_drawn_tracker.h"
+
+namespace view_manager {
+
+FocusController::FocusController(FocusControllerDelegate* delegate,
+                                 ServerView* root)
+    : delegate_(delegate), root_(root) {
+}
+
+FocusController::~FocusController() {
+}
+
+void FocusController::SetFocusedView(ServerView* view) {
+  if (GetFocusedView() == view)
+    return;
+
+  SetFocusedViewImpl(view, CHANGE_SOURCE_EXPLICIT);
+}
+
+ServerView* FocusController::GetFocusedView() {
+  return drawn_tracker_ ? drawn_tracker_->view() : nullptr;
+}
+
+void FocusController::SetFocusedViewImpl(ServerView* view,
+                                         ChangeSource change_source) {
+  ServerView* old = GetFocusedView();
+
+  DCHECK(!view || view->IsDrawn(root_));
+
+  if (view)
+    drawn_tracker_.reset(new ServerViewDrawnTracker(root_, view, this));
+  else
+    drawn_tracker_.reset();
+
+  if (change_source == CHANGE_SOURCE_DRAWN_STATE_CHANGED)
+    delegate_->OnFocusChanged(old, view);
+}
+
+void FocusController::OnDrawnStateChanged(ServerView* ancestor,
+                                          ServerView* view,
+                                          bool is_drawn) {
+  DCHECK(!is_drawn);  // We only observe when drawn.
+  SetFocusedViewImpl(ancestor, CHANGE_SOURCE_DRAWN_STATE_CHANGED);
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/focus_controller.h b/services/view_manager/focus_controller.h
new file mode 100644
index 0000000..e3b10a8
--- /dev/null
+++ b/services/view_manager/focus_controller.h
@@ -0,0 +1,53 @@
+// 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_VIEW_MANAGER_FOCUS_CONTROLLER_H_
+#define SERVICES_VIEW_MANAGER_FOCUS_CONTROLLER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "services/view_manager/server_view_drawn_tracker_observer.h"
+
+namespace view_manager {
+
+class FocusControllerDelegate;
+class ServerView;
+class ServerViewDrawnTracker;
+
+// Tracks a focused view. Focus is moved to another view when the drawn state
+// of the focused view changes and the delegate is notified.
+class FocusController : public ServerViewDrawnTrackerObserver {
+ public:
+  FocusController(FocusControllerDelegate* delegate, ServerView* root);
+  ~FocusController() override;
+
+  // Sets the focused view. Does nothing if |view| is currently focused. This
+  // does not notify the delegate.
+  void SetFocusedView(ServerView* view);
+  ServerView* GetFocusedView();
+
+ private:
+  // Describes the source of the change.
+  enum ChangeSource {
+    CHANGE_SOURCE_EXPLICIT,
+    CHANGE_SOURCE_DRAWN_STATE_CHANGED,
+  };
+
+  // Implementation of SetFocusedView().
+  void SetFocusedViewImpl(ServerView* view, ChangeSource change_source);
+
+  // ServerViewDrawnTrackerObserver:
+  void OnDrawnStateChanged(ServerView* ancestor,
+                           ServerView* view,
+                           bool is_drawn) override;
+
+  FocusControllerDelegate* delegate_;
+  ServerView* root_;
+  scoped_ptr<ServerViewDrawnTracker> drawn_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusController);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_FOCUS_CONTROLLER_H_
diff --git a/services/view_manager/focus_controller_delegate.h b/services/view_manager/focus_controller_delegate.h
new file mode 100644
index 0000000..6a91a67
--- /dev/null
+++ b/services/view_manager/focus_controller_delegate.h
@@ -0,0 +1,23 @@
+// 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_VIEW_MANAGER_FOCUS_CONTROLLER_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_FOCUS_CONTROLLER_DELEGATE_H_
+
+namespace view_manager {
+
+class ServerView;
+
+class FocusControllerDelegate {
+ public:
+  virtual void OnFocusChanged(ServerView* old_focused_view,
+                              ServerView* new_focused_view) = 0;
+
+ protected:
+  ~FocusControllerDelegate() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_FOCUS_CONTROLLER_DELEGATE_H_
diff --git a/services/view_manager/focus_controller_unittest.cc b/services/view_manager/focus_controller_unittest.cc
new file mode 100644
index 0000000..583f175
--- /dev/null
+++ b/services/view_manager/focus_controller_unittest.cc
@@ -0,0 +1,106 @@
+// 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/view_manager/focus_controller.h"
+
+#include "services/view_manager/focus_controller_delegate.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace view_manager {
+namespace {
+
+class TestFocusControllerDelegate : public FocusControllerDelegate {
+ public:
+  TestFocusControllerDelegate()
+      : change_count_(0u),
+        old_focused_view_(nullptr),
+        new_focused_view_(nullptr) {}
+
+  void ClearAll() {
+    change_count_ = 0u;
+    old_focused_view_ = nullptr;
+    new_focused_view_ = nullptr;
+  }
+  size_t change_count() const { return change_count_; }
+  ServerView* old_focused_view() { return old_focused_view_; }
+  ServerView* new_focused_view() { return new_focused_view_; }
+
+ private:
+  // FocusControllerDelegate:
+  void OnFocusChanged(ServerView* old_focused_view,
+                      ServerView* new_focused_view) override {
+    change_count_++;
+    old_focused_view_ = old_focused_view;
+    new_focused_view_ = new_focused_view;
+  }
+
+  size_t change_count_;
+  ServerView* old_focused_view_;
+  ServerView* new_focused_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestFocusControllerDelegate);
+};
+
+}  // namespace
+
+TEST(FocusControllerTest, Basic) {
+  TestServerViewDelegate server_view_delegate;
+  ServerView root(&server_view_delegate, ViewId());
+  root.SetVisible(true);
+  ServerView child(&server_view_delegate, ViewId());
+  child.SetVisible(true);
+  root.Add(&child);
+  ServerView child_child(&server_view_delegate, ViewId());
+  child_child.SetVisible(true);
+  child.Add(&child_child);
+
+  TestFocusControllerDelegate focus_delegate;
+  FocusController focus_controller(&focus_delegate, &root);
+
+  focus_controller.SetFocusedView(&child_child);
+  EXPECT_EQ(0u, focus_delegate.change_count());
+
+  // Remove the ancestor of the focused view, focus should go to the |root|.
+  root.Remove(&child);
+  EXPECT_EQ(1u, focus_delegate.change_count());
+  EXPECT_EQ(&root, focus_delegate.new_focused_view());
+  EXPECT_EQ(&child_child, focus_delegate.old_focused_view());
+  focus_delegate.ClearAll();
+
+  // Make the focused view invisible. Focus is lost in this case (as no one
+  // to give focus to).
+  root.SetVisible(false);
+  EXPECT_EQ(1u, focus_delegate.change_count());
+  EXPECT_EQ(nullptr, focus_delegate.new_focused_view());
+  EXPECT_EQ(&root, focus_delegate.old_focused_view());
+  focus_delegate.ClearAll();
+
+  // Go back to initial state and focus |child_child|.
+  root.SetVisible(true);
+  root.Add(&child);
+  focus_controller.SetFocusedView(&child_child);
+  EXPECT_EQ(0u, focus_delegate.change_count());
+
+  // Hide the focused view, focus should go to parent.
+  child_child.SetVisible(false);
+  EXPECT_EQ(1u, focus_delegate.change_count());
+  EXPECT_EQ(&child, focus_delegate.new_focused_view());
+  EXPECT_EQ(&child_child, focus_delegate.old_focused_view());
+  focus_delegate.ClearAll();
+
+  child_child.SetVisible(true);
+  focus_controller.SetFocusedView(&child_child);
+  EXPECT_EQ(0u, focus_delegate.change_count());
+
+  // Hide the parent of the focused view.
+  child.SetVisible(false);
+  EXPECT_EQ(1u, focus_delegate.change_count());
+  EXPECT_EQ(&root, focus_delegate.new_focused_view());
+  EXPECT_EQ(&child_child, focus_delegate.old_focused_view());
+  focus_delegate.ClearAll();
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/gesture_manager.cc b/services/view_manager/gesture_manager.cc
new file mode 100644
index 0000000..47ae5fc
--- /dev/null
+++ b/services/view_manager/gesture_manager.cc
@@ -0,0 +1,701 @@
+// 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/view_manager/gesture_manager.h"
+
+#include <algorithm>
+
+#include "mojo/services/input_events/interfaces/input_events.mojom.h"
+#include "mojo/services/view_manager/cpp/keys.h"
+#include "services/view_manager/gesture_manager_delegate.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/view_coordinate_conversions.h"
+#include "services/view_manager/view_locator.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace view_manager {
+
+using Views = std::vector<const ServerView*>;
+
+namespace {
+
+GestureManager::GestureAndConnectionId MakeGestureAndConnectionId(
+    const ServerView* view,
+    uint32_t gesture_id) {
+  return (static_cast<GestureManager::GestureAndConnectionId>(
+              view->id().connection_id)
+          << 32) |
+         gesture_id;
+}
+
+// Returns the views (deepest first) that should receive touch events. This only
+// returns one view per connection. If multiple views from the same connection
+// are interested in touch events the shallowest view is returned.
+Views GetTouchTargets(const ServerView* deepest) {
+  Views result;
+  const ServerView* view = deepest;
+  while (view) {
+    if (view->properties().count(mojo::kViewManagerKeyWantsTouchEvents)) {
+      if (!result.empty() &&
+          result.back()->id().connection_id == view->id().connection_id) {
+        result.pop_back();
+      }
+      result.push_back(view);
+    }
+    view = view->parent();
+  }
+  // TODO(sky): I'm doing this until things are converted. Seems as though we
+  // shouldn't do this long term.
+  if (result.empty())
+    result.push_back(deepest);
+  return result;
+}
+
+mojo::EventPtr CloneEventForView(const mojo::Event& event,
+                                 const ServerView* view) {
+  mojo::EventPtr result(event.Clone());
+  const gfx::PointF location(event.pointer_data->x, event.pointer_data->y);
+  const gfx::PointF target_location(
+      ConvertPointFBetweenViews(view->GetRoot(), view, location));
+  result->pointer_data->x = target_location.x();
+  result->pointer_data->y = target_location.y();
+  return result;
+}
+
+}  // namespace
+
+// GestureStateChange ----------------------------------------------------------
+
+GestureStateChange::GestureStateChange()
+    : chosen_gesture(GestureManager::kInvalidGestureId) {
+}
+
+GestureStateChange::~GestureStateChange() {
+}
+
+// ViewIterator ----------------------------------------------------------------
+
+// Used to iterate over a set of views.
+class ViewIterator {
+ public:
+  explicit ViewIterator(const Views& views)
+      : views_(views), current_(views_.begin()) {}
+
+  // Advances to the next view. Returns true if there are no more views (at
+  // the end).
+  bool advance() { return ++current_ != views_.end(); }
+
+  bool at_end() const { return current_ == views_.end(); }
+
+  bool empty() const { return views_.empty(); }
+
+  const ServerView* current() const { return *current_; }
+
+  void reset_to_beginning() { current_ = views_.begin(); }
+
+  void remove(const ServerView* view) {
+    Views::iterator iter = std::find(views_.begin(), views_.end(), view);
+    DCHECK(iter != views_.end());
+    if (iter == current_) {
+      current_ = views_.erase(current_);
+    } else if (!at_end()) {
+      size_t index = current_ - views_.begin();
+      if (current_ > iter)
+        index--;
+      views_.erase(iter);
+      current_ = views_.begin() + index;
+    } else {
+      views_.erase(iter);
+      current_ = views_.end();
+    }
+  }
+
+  bool contains(const ServerView* view) const {
+    return std::find(views_.begin(), views_.end(), view) != views_.end();
+  }
+
+ private:
+  Views views_;
+  Views::iterator current_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewIterator);
+};
+
+// PointerAndView --------------------------------------------------------------
+
+struct GestureManager::PointerAndView {
+  PointerAndView();
+  PointerAndView(Pointer* pointer, const ServerView* view);
+
+  // Compares two PointerAndView instances based on pointer id, then view id.
+  // This is really only interesting for unit tests so that they get a known
+  // order of events.
+  bool operator<(const PointerAndView& other) const;
+
+  Pointer* pointer;
+  const ServerView* view;
+};
+
+// Gesture ---------------------------------------------------------------------
+
+// Gesture maintains the set of pointers and views it is attached to.
+class GestureManager::Gesture {
+ public:
+  enum State { STATE_INITIAL, STATE_CANCELED, STATE_CHOSEN };
+
+  explicit Gesture(uint32_t id);
+  ~Gesture();
+
+  uint32_t id() const { return id_; }
+
+  void Attach(Pointer* pointer, const ServerView* view);
+  void Detach(Pointer* pointer, const ServerView* view);
+
+  void set_state(State state) { state_ = state; }
+  State state() const { return state_; }
+
+  const std::set<PointerAndView>& pointers_and_views() const {
+    return pointers_and_views_;
+  }
+
+ private:
+  const uint32_t id_;
+  State state_;
+  std::set<PointerAndView> pointers_and_views_;
+
+  DISALLOW_COPY_AND_ASSIGN(Gesture);
+};
+
+GestureManager::Gesture::Gesture(uint32_t id) : id_(id), state_(STATE_INITIAL) {
+}
+
+GestureManager::Gesture::~Gesture() {
+}
+
+void GestureManager::Gesture::Attach(Pointer* pointer, const ServerView* view) {
+  pointers_and_views_.insert(PointerAndView(pointer, view));
+}
+
+void GestureManager::Gesture::Detach(Pointer* pointer, const ServerView* view) {
+  pointers_and_views_.erase(PointerAndView(pointer, view));
+}
+
+// Pointer ---------------------------------------------------------------------
+
+// Pointer manages the state associated with a particular pointer from the time
+// of the POINTER_DOWN to the time of the POINTER_UP (or POINTER_CANCEL). This
+// state includes a mapping from view to the set of gestures the view is
+// interested in. It also manages choosing gestures at the appropriate point as
+// well as which view to dispatch to and the events to dispatch.
+// See description in GestureManager for more.
+class GestureManager::Pointer {
+ public:
+  Pointer(GestureManager* gesture_manager,
+          int32_t pointer_id,
+          const mojo::Event& event,
+          const Views& views);
+  ~Pointer();
+
+  int32_t pointer_id() const { return pointer_id_; }
+  bool was_chosen_or_canceled() const { return was_chosen_or_canceled_; }
+
+  // Sets the set of gestures for this pointer.
+  void SetGestures(const ServerView* view,
+                   uint32_t chosen_gesture_id,
+                   const std::set<uint32_t>& possible_gesture_ids,
+                   const std::set<uint32_t>& canceled_ids);
+
+  // Called when a Gesture we contain has been canceled.
+  void GestureCanceled(Gesture* gesture);
+
+  // Called when a Gesture we contain has been chosen.
+  void GestureChosen(Gesture* gesture, const ServerView* view);
+
+  // Process a move or up event. This may delay processing if we're waiting for
+  // previous results.
+  void ProcessEvent(const mojo::Event& event);
+
+ private:
+  // Corresponds to the type of event we're dispatching.
+  enum Phase {
+    // We're dispatching the initial down.
+    PHASE_DOWN,
+
+    // We're dispatching a move.
+    PHASE_MOVE,
+  };
+
+  // Sends the event for the current phase to the delegate.
+  void ForwardCurrentEvent();
+
+  // Moves |pending_event_| to |current_event_| and notifies the delegate.
+  void MovePendingToCurrentAndProcess();
+
+  // If |was_chosen_or_canceled_| is false and there is only one possible
+  // gesture and it is in the initial state, choose it. Otherwise do nothing.
+  void ChooseGestureIfPossible();
+
+  bool ScheduleDeleteIfNecessary();
+
+  GestureManager* gesture_manager_;
+  const int32_t pointer_id_;
+  Phase phase_;
+
+  // Used to iterate over the set of views that potentially have gestures.
+  ViewIterator view_iter_;
+
+  // Maps from the view to the set of possible gestures for the view.
+  std::map<const ServerView*, std::set<Gesture*>> view_to_gestures_;
+
+  Gesture* chosen_gesture_;
+
+  bool was_chosen_or_canceled_;
+
+  // The event we're processing. When initially created this is the supplied
+  // down event. When in PHASE_MOVE this is a move event.
+  mojo::EventPtr current_event_;
+
+  // Incoming events (move or up) are added here while while waiting for
+  // responses.
+  mojo::EventPtr pending_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(Pointer);
+};
+
+GestureManager::Pointer::Pointer(GestureManager* gesture_manager,
+                                 int32_t pointer_id,
+                                 const mojo::Event& event,
+                                 const Views& views)
+    : gesture_manager_(gesture_manager),
+      pointer_id_(pointer_id),
+      phase_(PHASE_DOWN),
+      view_iter_(views),
+      chosen_gesture_(nullptr),
+      was_chosen_or_canceled_(false),
+      current_event_(event.Clone()) {
+  ForwardCurrentEvent();
+}
+
+GestureManager::Pointer::~Pointer() {
+  for (auto& pair : view_to_gestures_) {
+    for (Gesture* gesture : pair.second)
+      gesture_manager_->DetachGesture(gesture, this, pair.first);
+  }
+}
+
+void GestureManager::Pointer::SetGestures(
+    const ServerView* view,
+    uint32_t chosen_gesture_id,
+    const std::set<uint32_t>& possible_gesture_ids,
+    const std::set<uint32_t>& canceled_gesture_ids) {
+  if (!view_iter_.contains(view)) {
+    // We don't know about this view.
+    return;
+  }
+
+  // True if this is the view we're waiting for a response from.
+  const bool was_waiting_on =
+      (!was_chosen_or_canceled_ &&
+       (!view_iter_.at_end() && view_iter_.current() == view));
+
+  if (possible_gesture_ids.empty()) {
+    // The view no longer wants to be notified.
+    for (Gesture* gesture : view_to_gestures_[view])
+      gesture_manager_->DetachGesture(gesture, this, view);
+    view_to_gestures_.erase(view);
+    view_iter_.remove(view);
+    if (view_iter_.empty()) {
+      gesture_manager_->PointerHasNoGestures(this);
+      // WARNING: we've been deleted.
+      return;
+    }
+  } else {
+    if (was_waiting_on)
+      view_iter_.advance();
+
+    Gesture* to_choose = nullptr;
+    std::set<Gesture*> gestures;
+    for (auto gesture_id : possible_gesture_ids) {
+      Gesture* gesture = gesture_manager_->GetGesture(view, gesture_id);
+      gesture_manager_->AttachGesture(gesture, this, view);
+      gestures.insert(gesture);
+      if (gesture->state() == Gesture::STATE_CHOSEN &&
+          !was_chosen_or_canceled_) {
+        to_choose = gesture;
+      }
+    }
+
+    // Give preference to the supplied |chosen_gesture_id|.
+    if (!was_chosen_or_canceled_ && chosen_gesture_id != kInvalidGestureId) {
+      Gesture* gesture = gesture_manager_->GetGesture(view, chosen_gesture_id);
+      if (gesture->state() != Gesture::STATE_CANCELED)
+        to_choose = gesture;
+
+      DCHECK(possible_gesture_ids.count(gesture->id()));
+      gesture_manager_->AttachGesture(gesture, this, view);
+    }
+
+    // Tell GestureManager of any Gestures we're no longer associated with.
+    std::set<Gesture*> removed_gestures;
+    std::set_difference(
+        view_to_gestures_[view].begin(), view_to_gestures_[view].end(),
+        gestures.begin(), gestures.end(),
+        std::inserter(removed_gestures, removed_gestures.begin()));
+    view_to_gestures_[view].swap(gestures);
+    for (Gesture* gesture : removed_gestures)
+      gesture_manager_->DetachGesture(gesture, this, view);
+
+    if (chosen_gesture_ && removed_gestures.count(chosen_gesture_))
+      chosen_gesture_ = nullptr;
+
+    if (to_choose) {
+      gesture_manager_->ChooseGesture(to_choose, this, view);
+    } else {
+      // Choosing a gesture implicitly cancels all other gestures. If we didn't
+      // choose a gesture we need to update the state of any newly added
+      // gestures.
+      for (Gesture* gesture : gestures) {
+        if (gesture != chosen_gesture_ &&
+            (was_chosen_or_canceled_ ||
+             canceled_gesture_ids.count(gesture->id()))) {
+          gesture_manager_->CancelGesture(gesture, this, view);
+        }
+      }
+    }
+  }
+
+  if (was_waiting_on && !was_chosen_or_canceled_) {
+    if (view_iter_.at_end()) {
+      if (ScheduleDeleteIfNecessary())
+        return;
+      // If we're got all the responses, check if there is only one valid
+      // gesture.
+      ChooseGestureIfPossible();
+      if (!was_chosen_or_canceled_) {
+        // There is more than one valid gesture and none chosen. Continue
+        // synchronous dispatch of move events.
+        phase_ = PHASE_MOVE;
+        MovePendingToCurrentAndProcess();
+      }
+    } else {
+      ForwardCurrentEvent();
+    }
+  } else if (!was_chosen_or_canceled_ && phase_ != PHASE_DOWN) {
+    // We weren't waiting on this view but we're in the move phase. The set of
+    // gestures may have changed such that we only have one valid gesture. Check
+    // for that.
+    ChooseGestureIfPossible();
+  }
+}
+
+void GestureManager::Pointer::GestureCanceled(Gesture* gesture) {
+  if (was_chosen_or_canceled_ && gesture == chosen_gesture_) {
+    chosen_gesture_ = nullptr;
+    // No need to cancel other gestures as they are already canceled by virtue
+    // of us having been chosen.
+  } else if (!was_chosen_or_canceled_ && phase_ == PHASE_MOVE) {
+    ChooseGestureIfPossible();
+  }
+}
+
+void GestureManager::Pointer::GestureChosen(Gesture* gesture,
+                                            const ServerView* view) {
+  DCHECK(!was_chosen_or_canceled_);
+  was_chosen_or_canceled_ = true;
+  chosen_gesture_ = gesture;
+  for (auto& pair : view_to_gestures_) {
+    for (Gesture* g : pair.second) {
+      if (g != gesture)
+        gesture_manager_->CancelGesture(g, this, pair.first);
+    }
+  }
+
+  while (!view_iter_.at_end()) {
+    ForwardCurrentEvent();
+    view_iter_.advance();
+  }
+  if (ScheduleDeleteIfNecessary())
+    return;
+  phase_ = PHASE_MOVE;
+  MovePendingToCurrentAndProcess();
+}
+
+void GestureManager::Pointer::ProcessEvent(const mojo::Event& event) {
+  // |event| is either a move or up. In either case it has the new coordinates
+  // and is safe to replace the existing one with.
+  pending_event_ = event.Clone();
+  if (was_chosen_or_canceled_) {
+    MovePendingToCurrentAndProcess();
+  } else if (view_iter_.at_end()) {
+    view_iter_.reset_to_beginning();
+    MovePendingToCurrentAndProcess();
+  }
+  // The else case is we are waiting on a response from a view before we
+  // continue dispatching. When we get the response for the last view in the
+  // stack we'll move pending to current and start dispatching it.
+}
+
+void GestureManager::Pointer::ForwardCurrentEvent() {
+  DCHECK(!view_iter_.at_end());
+  const ServerView* view = view_iter_.current();
+  gesture_manager_->delegate_->ProcessEvent(
+      view, CloneEventForView(*current_event_, view), was_chosen_or_canceled_);
+}
+
+void GestureManager::Pointer::MovePendingToCurrentAndProcess() {
+  if (!pending_event_.get()) {
+    current_event_ = nullptr;
+    return;
+  }
+  current_event_ = pending_event_.Pass();
+  view_iter_.reset_to_beginning();
+  ForwardCurrentEvent();
+  if (was_chosen_or_canceled_) {
+    while (view_iter_.advance())
+      ForwardCurrentEvent();
+    if (ScheduleDeleteIfNecessary())
+      return;
+    current_event_ = nullptr;
+  }
+}
+
+void GestureManager::Pointer::ChooseGestureIfPossible() {
+  if (was_chosen_or_canceled_)
+    return;
+
+  Gesture* gesture_to_choose = nullptr;
+  const ServerView* view = nullptr;
+  for (auto& pair : view_to_gestures_) {
+    for (Gesture* gesture : pair.second) {
+      if (gesture->state() == Gesture::STATE_INITIAL) {
+        if (gesture_to_choose)
+          return;
+        view = pair.first;
+        gesture_to_choose = gesture;
+      }
+    }
+  }
+  if (view)
+    gesture_manager_->ChooseGesture(gesture_to_choose, this, view);
+}
+
+bool GestureManager::Pointer::ScheduleDeleteIfNecessary() {
+  if (current_event_ &&
+      (current_event_->action == mojo::EventType::POINTER_UP ||
+       current_event_->action == mojo::EventType::POINTER_CANCEL)) {
+    gesture_manager_->ScheduleDelete(this);
+    return true;
+  }
+  return false;
+}
+
+// ScheduledDeleteProcessor ---------------------------------------------------
+
+class GestureManager::ScheduledDeleteProcessor {
+ public:
+  explicit ScheduledDeleteProcessor(GestureManager* manager)
+      : manager_(manager) {}
+
+  ~ScheduledDeleteProcessor() { manager_->pointers_to_delete_.clear(); }
+
+ private:
+  GestureManager* manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScheduledDeleteProcessor);
+};
+
+// PointerAndView --------------------------------------------------------------
+
+GestureManager::PointerAndView::PointerAndView()
+    : pointer(nullptr), view(nullptr) {
+}
+
+GestureManager::PointerAndView::PointerAndView(Pointer* pointer,
+                                               const ServerView* view)
+    : pointer(pointer), view(view) {
+}
+
+bool GestureManager::PointerAndView::operator<(
+    const PointerAndView& other) const {
+  if (other.pointer->pointer_id() == pointer->pointer_id())
+    return view->id().connection_id < other.view->id().connection_id;
+  return pointer->pointer_id() < other.pointer->pointer_id();
+}
+
+// GestureManager --------------------------------------------------------------
+
+// static
+const uint32_t GestureManager::kInvalidGestureId = 0u;
+
+GestureManager::GestureManager(GestureManagerDelegate* delegate,
+                               const ServerView* root)
+    : delegate_(delegate), root_view_(root) {
+}
+
+GestureManager::~GestureManager() {
+  // Explicitly delete the pointers first as this may result in calling back to
+  // us to cleanup and delete gestures.
+  active_pointers_.clear();
+}
+
+bool GestureManager::ProcessEvent(const mojo::Event& event) {
+  if (!event.pointer_data)
+    return false;
+
+  ScheduledDeleteProcessor delete_processor(this);
+  const gfx::Point location(static_cast<int>(event.pointer_data->x),
+                            static_cast<int>(event.pointer_data->y));
+  switch (event.action) {
+    case mojo::EventType::POINTER_DOWN: {
+      if (GetPointerById(event.pointer_data->pointer_id)) {
+        DVLOG(1) << "received more than one down for a pointer without a "
+                 << "corresponding up, id=" << event.pointer_data->pointer_id;
+        NOTREACHED();
+        return true;
+      }
+
+      const ServerView* deepest = FindDeepestVisibleView(root_view_, location);
+      Views targets(GetTouchTargets(deepest));
+      if (targets.empty())
+        return true;
+
+      scoped_ptr<Pointer> pointer(
+          new Pointer(this, event.pointer_data->pointer_id, event, targets));
+      active_pointers_.push_back(pointer.Pass());
+      return true;
+    }
+
+    case mojo::EventType::POINTER_CANCEL:
+    case mojo::EventType::POINTER_MOVE:
+    case mojo::EventType::POINTER_UP: {
+      Pointer* pointer = GetPointerById(event.pointer_data->pointer_id);
+      // We delete a pointer when it has no gestures, so it's possible to get
+      // here with no gestures. Additionally there is no need to explicitly
+      // delete |pointer| as it'll tell us when it's ready to be deleted.
+      if (pointer)
+        pointer->ProcessEvent(event);
+      return true;
+    }
+
+    default:
+      break;
+  }
+  return false;
+}
+
+scoped_ptr<ChangeMap> GestureManager::SetGestures(
+    const ServerView* view,
+    int32_t pointer_id,
+    uint32_t chosen_gesture_id,
+    const std::set<uint32_t>& possible_gesture_ids,
+    const std::set<uint32_t>& canceled_gesture_ids) {
+  // TODO(sky): caller should validate ids and make sure possible contains
+  // canceled and chosen.
+  DCHECK(!canceled_gesture_ids.count(kInvalidGestureId));
+  DCHECK(!possible_gesture_ids.count(kInvalidGestureId));
+  DCHECK(chosen_gesture_id == kInvalidGestureId ||
+         possible_gesture_ids.count(chosen_gesture_id));
+  DCHECK(chosen_gesture_id == kInvalidGestureId ||
+         !canceled_gesture_ids.count(chosen_gesture_id));
+  ScheduledDeleteProcessor delete_processor(this);
+  Pointer* pointer = GetPointerById(pointer_id);
+  current_change_.reset(new ChangeMap);
+  if (pointer) {
+    pointer->SetGestures(view, chosen_gesture_id, possible_gesture_ids,
+                         canceled_gesture_ids);
+  }
+  return current_change_.Pass();
+}
+
+GestureManager::Pointer* GestureManager::GetPointerById(int32_t pointer_id) {
+  for (Pointer* pointer : active_pointers_) {
+    if (pointer->pointer_id() == pointer_id)
+      return pointer;
+  }
+  return nullptr;
+}
+
+void GestureManager::PointerHasNoGestures(Pointer* pointer) {
+  auto iter =
+      std::find(active_pointers_.begin(), active_pointers_.end(), pointer);
+  CHECK(iter != active_pointers_.end());
+  active_pointers_.erase(iter);
+}
+
+GestureManager::Gesture* GestureManager::GetGesture(const ServerView* view,
+                                                    uint32_t gesture_id) {
+  GestureAndConnectionId gesture_and_connection_id =
+      MakeGestureAndConnectionId(view, gesture_id);
+  Gesture* gesture = gesture_map_[gesture_and_connection_id];
+  if (!gesture) {
+    gesture = new Gesture(gesture_id);
+    gesture_map_[gesture_and_connection_id] = gesture;
+  }
+  return gesture;
+}
+
+void GestureManager::AttachGesture(Gesture* gesture,
+                                   Pointer* pointer,
+                                   const ServerView* view) {
+  gesture->Attach(pointer, view);
+}
+
+void GestureManager::DetachGesture(Gesture* gesture,
+                                   Pointer* pointer,
+                                   const ServerView* view) {
+  gesture->Detach(pointer, view);
+  if (gesture->pointers_and_views().empty()) {
+    gesture_map_.erase(MakeGestureAndConnectionId(view, gesture->id()));
+    delete gesture;
+  }
+}
+
+void GestureManager::CancelGesture(Gesture* gesture,
+                                   Pointer* pointer,
+                                   const ServerView* view) {
+  if (gesture->state() == Gesture::STATE_CANCELED)
+    return;
+
+  gesture->set_state(Gesture::STATE_CANCELED);
+  for (auto& pointer_and_view : gesture->pointers_and_views()) {
+    (*current_change_)[pointer_and_view.view].canceled_gestures.insert(
+        gesture->id());
+    if (pointer_and_view.pointer != pointer)
+      pointer_and_view.pointer->GestureCanceled(gesture);
+  }
+}
+
+void GestureManager::ChooseGesture(Gesture* gesture,
+                                   Pointer* pointer,
+                                   const ServerView* view) {
+  if (gesture->state() == Gesture::STATE_CHOSEN) {
+    // This happens when |pointer| is supplied a gesture that is already
+    // chosen.
+    DCHECK((*current_change_)[view].chosen_gesture == kInvalidGestureId ||
+           (*current_change_)[view].chosen_gesture == gesture->id());
+    (*current_change_)[view].chosen_gesture = gesture->id();
+    pointer->GestureChosen(gesture, view);
+  } else {
+    gesture->set_state(Gesture::STATE_CHOSEN);
+    for (auto& pointer_and_view : gesture->pointers_and_views()) {
+      DCHECK((*current_change_)[pointer_and_view.view].chosen_gesture ==
+                 kInvalidGestureId ||
+             (*current_change_)[pointer_and_view.view].chosen_gesture ==
+                 gesture->id());
+      (*current_change_)[pointer_and_view.view].chosen_gesture = gesture->id();
+      pointer_and_view.pointer->GestureChosen(gesture, view);
+    }
+  }
+}
+
+void GestureManager::ScheduleDelete(Pointer* pointer) {
+  auto iter =
+      std::find(active_pointers_.begin(), active_pointers_.end(), pointer);
+  if (iter != active_pointers_.end()) {
+    active_pointers_.weak_erase(iter);
+    pointers_to_delete_.push_back(pointer);
+  }
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/gesture_manager.h b/services/view_manager/gesture_manager.h
new file mode 100644
index 0000000..666dfec
--- /dev/null
+++ b/services/view_manager/gesture_manager.h
@@ -0,0 +1,188 @@
+// 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_VIEW_MANAGER_GESTURE_MANAGER_H_
+#define SERVICES_VIEW_MANAGER_GESTURE_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+
+namespace mojo {
+class Event;
+}
+
+namespace view_manager {
+
+class GestureManagerDelegate;
+class GestureManagerTest;
+class ServerView;
+
+struct GestureStateChange {
+  GestureStateChange();
+  ~GestureStateChange();
+
+  uint32_t chosen_gesture;
+  std::set<uint32_t> canceled_gestures;
+};
+
+using ChangeMap = std::map<const ServerView*, GestureStateChange>;
+
+// GestureManager handles incoming pointer events. It determines the set of
+// views (at most one per connection) that are interested in the event and
+// informs the delegate at the appropriate time.
+//
+// Each pointer may have any number of views associated with it, and each view
+// may have any number of gestures associated with it. Gesture are identified
+// the by the pair of the connection id of the view and the client supplied
+// gesture.  The same gesture may be used in multiple pointers (see example
+// below).
+//
+// Gestures have the following states:
+// . initial: Initial state for new gestures. From this state the gesture may
+//   become chosen or canceled. Once a gesture moves out of this state it can
+//   never go back.
+// . chosen: the gesture has been chosen. From this state the gesture may be
+//   canceled.
+// . canceled: the gesture has been canceled.
+// Gestures are active as long as they are included in the set of
+// |possible_gesture_ids|. Gestures can be removed at any time by removing the
+// gesture from |possible_gesture_ids|.
+//
+// A particular pointer has two distinct states:
+// . initial: none of the gestures associated with the pointer have been
+//   chosen.
+// . chosen: when a gesture associated with the pointer has been chosen.
+// Pointers are removed when a POINTER_UP or POINTER_CANCEL event is received.
+//
+// When a pointer is chosen all other gestures associated with the pointer are
+// implicitly canceled. If the chosen gesture is canceled the pointer remains
+// in the chosen state and no gestures can be chosen.
+//
+// Event propagation (ProcessEvent()) is handled in two distinct ways:
+// . Until a gesture has been chosen for the pointer, views are notified in
+//   order (deepest first). The next view (ancestor) is notified once
+//   SetGestures() has been invoked for the previous (descendant) view.
+// . Once a gesture has been chosen, then incoming events are immediately
+//   dispatched.
+//
+// The following example highlights various states and transitions:
+// . A POINTER_DOWN event is received for the pointer p1. The views that
+//   contain the location of the event (starting with the deepest) are v1, v2,
+//   v3 and v4. Both v1 and v2 have the property kViewManagerKeyWantsTouchEvents
+//   set, so only v1 and v2 are considered. v1 is the deepest view, so the
+//   touch event is set to it and only it first.
+// . v1 responds with possible gestures g1 and g2. v1 does not specify either
+//   of the gestures as chosen.
+// . As the response from v1 has been received and there is no chosen gesture
+//   the POINTER_DOWN event is sent to v2.
+// . v2 responds with gestures g3 and g4, neither of which are chosen.
+// . A POINTER_MOVE for p1 is received. As no gestures have been chosen event
+//   of the POINTER_MOVE continues with v1 first.
+// . v1 returns g1 and g2 as possible gestures and does not choose one.
+// . The POINTER_MOVE is sent to v2.
+// . v2 returns g3 and g4 as possible gestures and does not choose one.
+// At this point p1 has the possible gestures g1, g2, g3, g4. Gestures g1 and
+// g2 are associated with v1. Gestures g3 and g4 are associated with v2.
+// . A POINTER_DOWN event is received for the pointer p2. v1 and v2 again
+//   contain the location of the pointer. v1 is handed the event first.
+// . A POINTER_MOVE event is received for the pointer p2. As the response from
+//   v1 has not been received yet, the event is not sent yet (it is queued up).
+// . v1 responds to the POINTER_DOWN for p2 with g1 and g2 and chooses g1.
+// At this point g2, g3 and g4 are all canceled with g1 chosen. p2 is in the
+// chosen state, as is p1 (p1 enters the chosen state as it contains the chosen
+// gesture as well).
+// . The POINTER_DOWN event for p2 is sent to v2. As p2 is in the chosen state
+//   the POINTER_MOVE event that was queued up is sent to both v1 and v2 at the
+//   same time (no waiting for response).
+//
+// TODO(sky): add some sort of timeout to deal with hung processes.
+class GestureManager {
+ public:
+  using GestureAndConnectionId = uint64_t;
+
+  static const uint32_t kInvalidGestureId;
+
+  GestureManager(GestureManagerDelegate* delegate, const ServerView* root);
+  ~GestureManager();
+
+  // Processes the current event. See GestureManager description for details.
+  bool ProcessEvent(const mojo::Event& event);
+
+  // Sets the gestures for the specified view and pointer.
+  scoped_ptr<ChangeMap> SetGestures(
+      const ServerView* view,
+      int32_t pointer_id,
+      uint32_t chosen_gesture_id,
+      const std::set<uint32_t>& possible_gesture_ids,
+      const std::set<uint32_t>& canceled_gesture_ids);
+
+ private:
+  friend class GestureManagerTest;
+
+  class Gesture;
+  class Pointer;
+  struct PointerAndView;
+  class ScheduledDeleteProcessor;
+
+  // Returns the Pointer for |pointer_id|, or null if one doesn't exist.
+  Pointer* GetPointerById(int32_t pointer_id);
+
+  // Notification that |pointer| has no gestures. This deletes |pointer|.
+  void PointerHasNoGestures(Pointer* pointer);
+
+  // Returns the Gesture for the specified arguments, creating if necessary.
+  Gesture* GetGesture(const ServerView* view, uint32_t gesture_id);
+
+  // Called from Pointer when a gesture is associated with a pointer.
+  void AttachGesture(Gesture* gesture,
+                     Pointer* pointer,
+                     const ServerView* view);
+
+  // Called from Pointer when a gesture is no longer associated with a
+  // pointer.
+  void DetachGesture(Gesture* gesture,
+                     Pointer* pointer,
+                     const ServerView* view);
+
+  // Cancels the supplied gesture (if it isn't canceled already). Notifies all
+  // pointers containing |gesture| that |gesture| has been canceled.
+  void CancelGesture(Gesture* gesture,
+                     Pointer* pointer,
+                     const ServerView* view);
+
+  // Chooses the supplied gesture. Notifies all pointers containing |gesture|
+  // that |gesture| has been chosen.
+  void ChooseGesture(Gesture* gesture,
+                     Pointer* pointer,
+                     const ServerView* view);
+
+  // Deletes |pointer| after processing the current event. We delay deletion
+  // until after the event as immediate deletion can cause problems for Pointer
+  // (this is because the same Pointer may be on multiple frames of the stack).
+  void ScheduleDelete(Pointer* pointer);
+
+  GestureManagerDelegate* delegate_;
+  const ServerView* root_view_;
+
+  // Map for looking up gestures. Gestures are identified by the pair of
+  // connection id and supplied gesture id.
+  std::map<GestureAndConnectionId, Gesture*> gesture_map_;
+
+  ScopedVector<Pointer> active_pointers_;
+
+  // See comment in ScheduleDelete() for details.
+  ScopedVector<Pointer> pointers_to_delete_;
+
+  // Accumulates changes as the result of SetGestures().
+  scoped_ptr<ChangeMap> current_change_;
+
+  DISALLOW_COPY_AND_ASSIGN(GestureManager);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_GESTURE_MANAGER_H_
diff --git a/services/view_manager/gesture_manager_delegate.h b/services/view_manager/gesture_manager_delegate.h
new file mode 100644
index 0000000..44f2e60
--- /dev/null
+++ b/services/view_manager/gesture_manager_delegate.h
@@ -0,0 +1,31 @@
+// 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_VIEW_MANAGER_GESTURE_MANAGER_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_GESTURE_MANAGER_DELEGATE_H_
+
+#include <set>
+
+#include "mojo/services/input_events/interfaces/input_events.mojom.h"
+#include "mojo/services/view_manager/cpp/types.h"
+
+namespace view_manager {
+
+class GestureManagerDelegate {
+ public:
+  // Informs the delegate of a pointer event. The delegate should asynchronously
+  // respond with the set of gestures appropriate for the view
+  // (GestureManager::SetGestures()).
+  // |has_chosen_gesture| is true if a gesture has been chosen.
+  virtual void ProcessEvent(const ServerView* view,
+                            mojo::EventPtr event,
+                            bool has_chosen_gesture) = 0;
+
+ protected:
+  virtual ~GestureManagerDelegate() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_GESTURE_MANAGER_DELEGATE_H_
diff --git a/services/view_manager/gesture_manager_unittest.cc b/services/view_manager/gesture_manager_unittest.cc
new file mode 100644
index 0000000..ca08026
--- /dev/null
+++ b/services/view_manager/gesture_manager_unittest.cc
@@ -0,0 +1,467 @@
+// 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/view_manager/gesture_manager.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "mojo/services/input_events/interfaces/input_events.mojom.h"
+#include "mojo/services/view_manager/cpp/keys.h"
+#include "services/view_manager/gesture_manager_delegate.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace view_manager {
+namespace {
+
+const uint32_t kInvalidGestureId = GestureManager::kInvalidGestureId;
+
+void MarkAsRespondsToTouch(ServerView* view) {
+  std::vector<uint8_t> empty_vector;
+  view->SetProperty(mojo::kViewManagerKeyWantsTouchEvents, &empty_vector);
+}
+
+std::set<uint32_t> SetWith(uint32_t v1) {
+  std::set<uint32_t> result;
+  result.insert(v1);
+  return result;
+}
+
+std::set<uint32_t> SetWith(uint32_t v1, uint32_t v2) {
+  std::set<uint32_t> result;
+  result.insert(v1);
+  result.insert(v2);
+  return result;
+}
+
+std::set<uint32_t> SetWith(uint32_t v1, uint32_t v2, uint32_t v3) {
+  std::set<uint32_t> result;
+  result.insert(v1);
+  result.insert(v2);
+  result.insert(v3);
+  return result;
+}
+
+std::string EventTypeToString(mojo::EventType event_type) {
+  switch (event_type) {
+    case mojo::EventType::POINTER_CANCEL:
+      return "cancel";
+    case mojo::EventType::POINTER_DOWN:
+      return "down";
+    case mojo::EventType::POINTER_MOVE:
+      return "move";
+    case mojo::EventType::POINTER_UP:
+      return "up";
+    default:
+      break;
+  }
+  return std::string("unexpected event");
+}
+
+mojo::EventPtr CreateEvent(mojo::EventType type,
+                           int32_t pointer_id,
+                           int x,
+                           int y) {
+  mojo::EventPtr event(mojo::Event::New());
+  event->action = type;
+  event->pointer_data = mojo::PointerData::New();
+  event->pointer_data->pointer_id = pointer_id;
+  event->pointer_data->x = x;
+  event->pointer_data->y = y;
+  return event;
+}
+
+struct CompareViewByConnectionId {
+  bool operator()(const ServerView* a, const ServerView* b) {
+    return a->id().connection_id < b->id().connection_id;
+  }
+};
+
+std::string IDsToString(const std::set<uint32_t>& ids) {
+  std::string result;
+  for (uint32_t id : ids) {
+    if (!result.empty())
+      result += ",";
+    result += base::UintToString(id);
+  }
+  return result;
+}
+
+std::string GestureStateChangeToString(const ServerView* view,
+                                       const GestureStateChange& change) {
+  std::string result =
+      "connection=" + base::IntToString(view->id().connection_id);
+  if (change.chosen_gesture != GestureManager::kInvalidGestureId)
+    result += " chosen=" + base::UintToString(change.chosen_gesture);
+  if (!change.canceled_gestures.empty())
+    result += " canceled=" + IDsToString(change.canceled_gestures);
+  return result;
+}
+
+}  // namespace
+
+class TestGestureManagerDelegate : public GestureManagerDelegate {
+ public:
+  TestGestureManagerDelegate() {}
+  ~TestGestureManagerDelegate() override {}
+
+  std::string GetAndClearDescriptions() {
+    const std::string result(JoinString(descriptions_, '\n'));
+    descriptions_.clear();
+    return result;
+  }
+
+  std::vector<std::string>& descriptions() { return descriptions_; }
+
+  void AppendDescriptionsFromResults(const ChangeMap& change_map) {
+    std::set<const ServerView*, CompareViewByConnectionId> views_by_id;
+    for (const auto& pair : change_map)
+      views_by_id.insert(pair.first);
+
+    for (auto* view : views_by_id) {
+      descriptions_.push_back(
+          GestureStateChangeToString(view, change_map.find(view)->second));
+    }
+  }
+
+  // GestureManagerDelegate:
+  void ProcessEvent(const ServerView* view,
+                    mojo::EventPtr event,
+                    bool has_chosen_gesture) override {
+    descriptions_.push_back(
+        EventTypeToString(event->action) + " pointer=" +
+        base::IntToString(event->pointer_data->pointer_id) + " connection=" +
+        base::UintToString(view->id().connection_id) + " chosen=" +
+        (has_chosen_gesture ? "true" : "false"));
+  }
+
+ private:
+  std::vector<std::string> descriptions_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestGestureManagerDelegate);
+};
+
+class GestureManagerTest : public testing::Test {
+ public:
+  GestureManagerTest()
+      : root_(&view_delegate_, ViewId(1, 1)),
+        child_(&view_delegate_, ViewId(2, 2)),
+        gesture_manager_(&gesture_delegate_, &root_) {
+    root_.SetVisible(true);
+    MarkAsRespondsToTouch(&root_);
+    root_.SetBounds(gfx::Rect(0, 0, 100, 100));
+  }
+  ~GestureManagerTest() override {}
+
+  void SetGestures(const ServerView* view,
+                   int32_t pointer_id,
+                   uint32_t chosen_gesture_id,
+                   const std::set<uint32_t>& possible_gesture_ids,
+                   const std::set<uint32_t>& canceled_ids) {
+    scoped_ptr<ChangeMap> result(
+        gesture_manager_.SetGestures(view, pointer_id, chosen_gesture_id,
+                                     possible_gesture_ids, canceled_ids));
+    gesture_delegate_.AppendDescriptionsFromResults(*result);
+  }
+
+  void AddChildView() {
+    MarkAsRespondsToTouch(&child_);
+    child_.SetVisible(true);
+    root_.Add(&child_);
+    child_.SetBounds(gfx::Rect(0, 0, 100, 100));
+  }
+
+ protected:
+  TestServerViewDelegate view_delegate_;
+  ServerView root_;
+  ServerView child_;
+  TestGestureManagerDelegate gesture_delegate_;
+  GestureManager gesture_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GestureManagerTest);
+};
+
+TEST_F(GestureManagerTest, SingleViewAndSingleGesture) {
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Choose this pointer.
+  SetGestures(&root_, pointer_id, 10u, SetWith(10u), std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=10",
+            gesture_delegate_.GetAndClearDescriptions());
+}
+
+TEST_F(GestureManagerTest, SingleViewAndTwoGestures) {
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+
+  // Delegate should have got nothing.
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Cancel 10, 5 should become active.
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              SetWith(10u));
+
+  EXPECT_EQ("connection=1 chosen=5 canceled=10",
+            gesture_delegate_.GetAndClearDescriptions());
+}
+
+TEST_F(GestureManagerTest, TwoViewsSingleGesture) {
+  AddChildView();
+
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  // Deepest child should be queried first.
+  EXPECT_EQ("down pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond from the first view, which triggers the second to be queried.
+  SetGestures(&child_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond with 5,10 for the second view. Should get nothing.
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Cancel 10 in the child.
+  SetGestures(&child_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              SetWith(10u));
+  EXPECT_EQ("connection=2 canceled=10",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Choose 5 in the root. This should choose 5 in the root and cancel 5 in
+  // the child.
+  SetGestures(&root_, pointer_id, 5u, SetWith(5u, 10u), SetWith(10u));
+  ASSERT_EQ(2u, gesture_delegate_.descriptions().size());
+  EXPECT_EQ("connection=1 chosen=5 canceled=10",
+            gesture_delegate_.descriptions()[0]);
+  EXPECT_EQ("connection=2 canceled=5", gesture_delegate_.descriptions()[1]);
+}
+
+TEST_F(GestureManagerTest, TwoViewsWaitForMoveToChoose) {
+  AddChildView();
+
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  // Deepest child should be queried first.
+  EXPECT_EQ("down pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Send a move. The move should not be processed as GestureManager is
+  // still waiting for responses.
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_MOVE, pointer_id, 6, 6));
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond from the first view, which triggers the second to be queried.
+  SetGestures(&child_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond with 1,2 for the second view.
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(1u, 2u),
+              std::set<uint32_t>());
+  // Now that we've responded to the down requests we should get a move for the
+  // child.
+  EXPECT_EQ("move pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond for the child, root should now get move.
+  SetGestures(&child_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+  EXPECT_EQ("move pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond with nothing chosen for the root. Nothing should come in as no
+  // pending moves.
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(1u, 2u),
+              std::set<uint32_t>());
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Send another move event and respond with a chosen id.
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_MOVE, pointer_id, 7, 7));
+  EXPECT_EQ("move pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+  SetGestures(&child_, pointer_id, 5u, SetWith(5u, 10u), std::set<uint32_t>());
+  ASSERT_EQ(3u, gesture_delegate_.descriptions().size());
+  // Now that a gesture is chosen the move event is generated.
+  EXPECT_EQ("move pointer=1 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[0]);
+  EXPECT_EQ("connection=1 canceled=1,2", gesture_delegate_.descriptions()[1]);
+  EXPECT_EQ("connection=2 chosen=5 canceled=10",
+            gesture_delegate_.descriptions()[2]);
+}
+
+TEST_F(GestureManagerTest, SingleViewNewPointerAfterChoose) {
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Choose 5.
+  SetGestures(&root_, pointer_id, 5u, SetWith(5u, 10u), std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=5 canceled=10",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Start another down event with a different pointer.
+  const int32_t pointer_id2 = 2;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id2, 5, 5));
+  EXPECT_EQ("down pointer=2 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // For the new pointer supply the id of a gesture that has been chosen.
+  // Even though we didn't explicitly supply 5 as chosen, 5 is chosen because
+  // it's already in the chosen state for pointer 1.
+  SetGestures(&root_, pointer_id2, kInvalidGestureId, SetWith(5u, 11u),
+              std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=5 canceled=11",
+            gesture_delegate_.GetAndClearDescriptions());
+}
+
+TEST_F(GestureManagerTest, SingleViewChoosingConflictingGestures) {
+  // For pointer1 choose 1 with 1,2,3 as possibilities.
+  const int32_t pointer1 = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer1, 5, 5));
+  gesture_delegate_.GetAndClearDescriptions();
+  SetGestures(&root_, pointer1, 1u, SetWith(1u, 2u, 3u), std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=1 canceled=2,3",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // For pointer2 choose 11 with 11,12 as possibilities.
+  const int32_t pointer2 = 2;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer2, 5, 5));
+  gesture_delegate_.GetAndClearDescriptions();
+  SetGestures(&root_, pointer2, 11u, SetWith(11u, 12u), std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=11 canceled=12",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // For pointer3 choose 21 with 1,11,21 as possibilties.
+  const int32_t pointer3 = 3;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer3, 5, 5));
+  gesture_delegate_.GetAndClearDescriptions();
+  SetGestures(&root_, pointer3, 21u, SetWith(1u, 11u, 21u),
+              std::set<uint32_t>());
+  EXPECT_EQ("connection=1 chosen=21 canceled=1,11",
+            gesture_delegate_.GetAndClearDescriptions());
+}
+
+TEST_F(GestureManagerTest,
+       TwoViewsRespondingWithChosenGestureSendsRemainingEvents) {
+  AddChildView();
+
+  // Start two pointer downs, don't respond to either.
+  const int32_t pointer1 = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer1, 5, 5));
+  EXPECT_EQ("down pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  const int32_t pointer2 = 2;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer2, 5, 5));
+  EXPECT_EQ("down pointer=2 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Queue up a move event for pointer1. The event should not be forwarded
+  // as we're still waiting.
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_MOVE, pointer1, 5, 5));
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond with 1,2 for pointer1 (nothing chosen yet).
+  SetGestures(&child_, pointer1, kInvalidGestureId, SetWith(1u, 2u),
+              std::set<uint32_t>());
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond with 1,2 and choose 1 for pointer2. This results in the following:
+  // down for pointer 1 (because we chose a gesture in common with pointer1),
+  // move for pointer 1 in both connections (because a gesture was chosen queued
+  // up events are sent), down for pointer2 for the root and finally
+  // notification of what was chosen.
+  SetGestures(&child_, pointer2, 1u, SetWith(1u, 2u), std::set<uint32_t>());
+  ASSERT_EQ(5u, gesture_delegate_.descriptions().size());
+  EXPECT_EQ("down pointer=1 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[0]);
+  EXPECT_EQ("move pointer=1 connection=2 chosen=true",
+            gesture_delegate_.descriptions()[1]);
+  EXPECT_EQ("move pointer=1 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[2]);
+  EXPECT_EQ("down pointer=2 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[3]);
+  EXPECT_EQ("connection=2 chosen=1 canceled=2",
+            gesture_delegate_.descriptions()[4]);
+}
+
+TEST_F(GestureManagerTest, TwoViewsSingleGestureUp) {
+  AddChildView();
+
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  // Deepest child should be queried first.
+  EXPECT_EQ("down pointer=1 connection=2 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Send an up, shouldn't result in anything.
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_UP, pointer_id, 5, 5));
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond from the first view, with a chosen gesture.
+  SetGestures(&child_, pointer_id, 5u, SetWith(5u, 10u), std::set<uint32_t>());
+  ASSERT_EQ(4u, gesture_delegate_.descriptions().size());
+  EXPECT_EQ("down pointer=1 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[0]);
+  EXPECT_EQ("up pointer=1 connection=2 chosen=true",
+            gesture_delegate_.descriptions()[1]);
+  EXPECT_EQ("up pointer=1 connection=1 chosen=true",
+            gesture_delegate_.descriptions()[2]);
+  EXPECT_EQ("connection=2 chosen=5 canceled=10",
+            gesture_delegate_.descriptions()[3]);
+}
+
+TEST_F(GestureManagerTest, SingleViewSingleGestureCancel) {
+  const int32_t pointer_id = 1;
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_DOWN, pointer_id, 5, 5));
+  EXPECT_EQ("down pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+
+  // Send a cancel, shouldn't result in anything.
+  gesture_manager_.ProcessEvent(
+      *CreateEvent(mojo::EventType::POINTER_CANCEL, pointer_id, 5, 5));
+  EXPECT_EQ(std::string(), gesture_delegate_.GetAndClearDescriptions());
+
+  // Respond from the first view, with no gesture, should unblock cancel.
+  SetGestures(&root_, pointer_id, kInvalidGestureId, SetWith(5u, 10u),
+              std::set<uint32_t>());
+  EXPECT_EQ("cancel pointer=1 connection=1 chosen=false",
+            gesture_delegate_.GetAndClearDescriptions());
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/ids.h b/services/view_manager/ids.h
new file mode 100644
index 0000000..b305854
--- /dev/null
+++ b/services/view_manager/ids.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_IDS_H_
+#define SERVICES_VIEW_MANAGER_IDS_H_
+
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/view_manager/cpp/util.h"
+
+namespace view_manager {
+
+// Connection id is used to indicate no connection. That is, no
+// ViewManagerServiceImpl ever gets this id.
+const mojo::ConnectionSpecificId kInvalidConnectionId = 0;
+
+// Adds a bit of type safety to view ids.
+struct ViewId {
+  ViewId(mojo::ConnectionSpecificId connection_id,
+         mojo::ConnectionSpecificId view_id)
+      : connection_id(connection_id), view_id(view_id) {}
+  ViewId() : connection_id(0), view_id(0) {}
+
+  bool operator==(const ViewId& other) const {
+    return other.connection_id == connection_id &&
+        other.view_id == view_id;
+  }
+
+  bool operator!=(const ViewId& other) const {
+    return !(*this == other);
+  }
+
+  mojo::ConnectionSpecificId connection_id;
+  mojo::ConnectionSpecificId view_id;
+};
+
+inline ViewId ViewIdFromTransportId(mojo::Id id) {
+  return ViewId(mojo::HiWord(id), mojo::LoWord(id));
+}
+
+inline mojo::Id ViewIdToTransportId(const ViewId& id) {
+  return (id.connection_id << 16) | id.view_id;
+}
+
+inline ViewId RootViewId() {
+  return ViewId(kInvalidConnectionId, 1);
+}
+
+// Returns a ViewId that is reserved to indicate no view. That is, no view will
+// ever be created with this id.
+inline ViewId InvalidViewId() {
+  return ViewId(kInvalidConnectionId, 0);
+}
+
+// All cloned views use this id.
+inline ViewId ClonedViewId() {
+  return ViewId(kInvalidConnectionId, 2);
+}
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_IDS_H_
diff --git a/services/view_manager/main.cc b/services/view_manager/main.cc
new file mode 100644
index 0000000..da74b8b
--- /dev/null
+++ b/services/view_manager/main.cc
@@ -0,0 +1,12 @@
+// 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.
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "services/view_manager/view_manager_app.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new view_manager::ViewManagerApp);
+  return runner.Run(application_request);
+}
diff --git a/services/view_manager/scheduled_animation_group.cc b/services/view_manager/scheduled_animation_group.cc
new file mode 100644
index 0000000..4cd301a
--- /dev/null
+++ b/services/view_manager/scheduled_animation_group.cc
@@ -0,0 +1,348 @@
+// 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.
+
+#include "services/view_manager/scheduled_animation_group.h"
+
+#include <set>
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "services/view_manager/server_view.h"
+
+using mojo::AnimationProperty;
+
+namespace view_manager {
+namespace {
+
+using Sequences = std::vector<ScheduledAnimationSequence>;
+
+// Gets the value of |property| from |view| into |value|.
+void GetValueFromView(const ServerView* view,
+                      AnimationProperty property,
+                      ScheduledAnimationValue* value) {
+  switch (property) {
+    case AnimationProperty::NONE:
+      NOTREACHED();
+      break;
+    case AnimationProperty::OPACITY:
+      value->float_value = view->opacity();
+      break;
+    case AnimationProperty::TRANSFORM:
+      value->transform = view->transform();
+      break;
+  }
+}
+
+// Sets the value of |property| from |value| into |view|.
+void SetViewPropertyFromValue(ServerView* view,
+                              AnimationProperty property,
+                              const ScheduledAnimationValue& value) {
+  switch (property) {
+    case AnimationProperty::NONE:
+      break;
+    case AnimationProperty::OPACITY:
+      view->SetOpacity(value.float_value);
+      break;
+    case AnimationProperty::TRANSFORM:
+      view->SetTransform(value.transform);
+      break;
+  }
+}
+
+// Sets the value of |property| into |view| between two points.
+void SetViewPropertyFromValueBetween(ServerView* view,
+                                     AnimationProperty property,
+                                     double value,
+                                     gfx::Tween::Type tween_type,
+                                     const ScheduledAnimationValue& start,
+                                     const ScheduledAnimationValue& target) {
+  const double tween_value = gfx::Tween::CalculateValue(tween_type, value);
+  switch (property) {
+    case AnimationProperty::NONE:
+      break;
+    case AnimationProperty::OPACITY:
+      view->SetOpacity(gfx::Tween::FloatValueBetween(
+          tween_value, start.float_value, target.float_value));
+      break;
+    case AnimationProperty::TRANSFORM:
+      view->SetTransform(gfx::Tween::TransformValueBetween(
+          tween_value, start.transform, target.transform));
+      break;
+  }
+}
+
+gfx::Tween::Type AnimationTypeToTweenType(mojo::AnimationTweenType type) {
+  switch (type) {
+    case mojo::AnimationTweenType::LINEAR:
+      return gfx::Tween::LINEAR;
+    case mojo::AnimationTweenType::EASE_IN:
+      return gfx::Tween::EASE_IN;
+    case mojo::AnimationTweenType::EASE_OUT:
+      return gfx::Tween::EASE_OUT;
+    case mojo::AnimationTweenType::EASE_IN_OUT:
+      return gfx::Tween::EASE_IN_OUT;
+  }
+  return gfx::Tween::LINEAR;
+}
+
+void ConvertToScheduledValue(const mojo::AnimationValue& transport_value,
+                             ScheduledAnimationValue* value) {
+  value->float_value = transport_value.float_value;
+  value->transform = transport_value.transform.To<gfx::Transform>();
+}
+
+void ConvertToScheduledElement(const mojo::AnimationElement& transport_element,
+                               ScheduledAnimationElement* element) {
+  element->property = transport_element.property;
+  element->duration =
+      base::TimeDelta::FromMicroseconds(transport_element.duration);
+  element->tween_type = AnimationTypeToTweenType(transport_element.tween_type);
+  if (transport_element.property != AnimationProperty::NONE) {
+    if (transport_element.start_value.get()) {
+      element->is_start_valid = true;
+      ConvertToScheduledValue(*transport_element.start_value,
+                              &(element->start_value));
+    } else {
+      element->is_start_valid = false;
+    }
+    ConvertToScheduledValue(*transport_element.target_value,
+                            &(element->target_value));
+  }
+}
+
+bool IsAnimationValueValid(AnimationProperty property,
+                           const mojo::AnimationValue& value) {
+  switch (property) {
+    case AnimationProperty::NONE:
+      NOTREACHED();
+      return false;
+    case AnimationProperty::OPACITY:
+      return value.float_value >= 0.f && value.float_value <= 1.f;
+    case AnimationProperty::TRANSFORM:
+      return value.transform.get() && value.transform->matrix.size() == 16u;
+  }
+  return false;
+}
+
+bool IsAnimationElementValid(const mojo::AnimationElement& element) {
+  if (element.property == AnimationProperty::NONE)
+    return true;  // None is a pause and doesn't need any values.
+  if (element.start_value.get() &&
+      !IsAnimationValueValid(element.property, *element.start_value))
+    return false;
+  // For all other properties we require a target.
+  return element.target_value.get() &&
+         IsAnimationValueValid(element.property, *element.target_value);
+}
+
+bool IsAnimationSequenceValid(const mojo::AnimationSequence& sequence) {
+  if (sequence.elements.size() == 0u)
+    return false;
+
+  for (size_t i = 0; i < sequence.elements.size(); ++i) {
+    if (!IsAnimationElementValid(*sequence.elements[i]))
+      return false;
+  }
+  return true;
+}
+
+bool IsAnimationGroupValid(const mojo::AnimationGroup& transport_group) {
+  if (transport_group.sequences.size() == 0u)
+    return false;
+  for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
+    if (!IsAnimationSequenceValid(*transport_group.sequences[i]))
+      return false;
+  }
+  return true;
+}
+
+// If the start value for |element| isn't valid, the value for the property
+// is obtained from |view| and placed into |element|.
+void GetStartValueFromViewIfNecessary(const ServerView* view,
+                                      ScheduledAnimationElement* element) {
+  if (element->property != AnimationProperty::NONE &&
+      !element->is_start_valid) {
+    GetValueFromView(view, element->property, &(element->start_value));
+  }
+}
+
+void GetScheduledAnimationProperties(const Sequences& sequences,
+                                     std::set<AnimationProperty>* properties) {
+  for (const ScheduledAnimationSequence& sequence : sequences) {
+    for (const ScheduledAnimationElement& element : sequence.elements)
+      properties->insert(element.property);
+  }
+}
+
+void SetPropertyToTargetProperty(ServerView* view,
+                                 mojo::AnimationProperty property,
+                                 const Sequences& sequences) {
+  // NOTE: this doesn't deal with |cycle_count| quite right, but I'm honestly
+  // not sure we really want to support the same property in multiple sequences
+  // animating at once so I'm not dealing.
+  base::TimeDelta max_end_duration;
+  scoped_ptr<ScheduledAnimationValue> value;
+  for (const ScheduledAnimationSequence& sequence : sequences) {
+    base::TimeDelta duration;
+    for (const ScheduledAnimationElement& element : sequence.elements) {
+      if (element.property != property)
+        continue;
+
+      duration += element.duration;
+      if (duration > max_end_duration) {
+        max_end_duration = duration;
+        value.reset(new ScheduledAnimationValue(element.target_value));
+      }
+    }
+  }
+  if (value.get())
+    SetViewPropertyFromValue(view, property, *value);
+}
+
+void ConvertSequenceToScheduled(
+    const mojo::AnimationSequence& transport_sequence,
+    base::TimeTicks now,
+    ScheduledAnimationSequence* sequence) {
+  sequence->run_until_stopped = transport_sequence.cycle_count == 0u;
+  sequence->cycle_count = transport_sequence.cycle_count;
+  DCHECK_NE(0u, transport_sequence.elements.size());
+  sequence->elements.resize(transport_sequence.elements.size());
+
+  base::TimeTicks element_start_time = now;
+  for (size_t i = 0; i < transport_sequence.elements.size(); ++i) {
+    ConvertToScheduledElement(*(transport_sequence.elements[i].get()),
+                              &(sequence->elements[i]));
+    sequence->elements[i].start_time = element_start_time;
+    sequence->duration += sequence->elements[i].duration;
+    element_start_time += sequence->elements[i].duration;
+  }
+}
+
+bool AdvanceSequence(ServerView* view,
+                     ScheduledAnimationSequence* sequence,
+                     base::TimeTicks now) {
+  ScheduledAnimationElement* element =
+      &(sequence->elements[sequence->current_index]);
+  while (element->start_time + element->duration < now) {
+    SetViewPropertyFromValue(view, element->property, element->target_value);
+    if (++sequence->current_index == sequence->elements.size()) {
+      if (!sequence->run_until_stopped && --sequence->cycle_count == 0) {
+        SetViewPropertyFromValue(view, element->property,
+                                 element->target_value);
+        return false;
+      }
+
+      sequence->current_index = 0;
+    }
+    sequence->elements[sequence->current_index].start_time =
+        element->start_time + element->duration;
+    element = &(sequence->elements[sequence->current_index]);
+    GetStartValueFromViewIfNecessary(view, element);
+
+    // It's possible for the delta between now and |last_tick_time_| to be very
+    // big (could happen if machine sleeps and is woken up much later). Normally
+    // the repeat count is smallish, so we don't bother optimizing it. OTOH if
+    // a sequence repeats forever we optimize it lest we get stuck in this loop
+    // for a very long time.
+    if (sequence->run_until_stopped && sequence->current_index == 0) {
+      element->start_time =
+          now - base::TimeDelta::FromMicroseconds(
+                    (now - element->start_time).InMicroseconds() %
+                    sequence->duration.InMicroseconds());
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+ScheduledAnimationValue::ScheduledAnimationValue() {
+}
+ScheduledAnimationValue::~ScheduledAnimationValue() {
+}
+
+ScheduledAnimationElement::ScheduledAnimationElement()
+    : property(AnimationProperty::OPACITY),
+      tween_type(gfx::Tween::EASE_IN),
+      is_start_valid(false) {
+}
+ScheduledAnimationElement::~ScheduledAnimationElement() {
+}
+
+ScheduledAnimationSequence::ScheduledAnimationSequence()
+    : run_until_stopped(false), cycle_count(0), current_index(0u) {
+}
+ScheduledAnimationSequence::~ScheduledAnimationSequence() {
+}
+
+ScheduledAnimationGroup::~ScheduledAnimationGroup() {
+}
+
+// static
+scoped_ptr<ScheduledAnimationGroup> ScheduledAnimationGroup::Create(
+    ServerView* view,
+    base::TimeTicks now,
+    uint32_t id,
+    const mojo::AnimationGroup& transport_group) {
+  if (!IsAnimationGroupValid(transport_group))
+    return nullptr;
+
+  scoped_ptr<ScheduledAnimationGroup> group(
+      new ScheduledAnimationGroup(view, id, now));
+  group->sequences_.resize(transport_group.sequences.size());
+  for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
+    const mojo::AnimationSequence& transport_sequence(
+        *(transport_group.sequences[i]));
+    DCHECK_NE(0u, transport_sequence.elements.size());
+    ConvertSequenceToScheduled(transport_sequence, now, &group->sequences_[i]);
+  }
+  return group;
+}
+
+void ScheduledAnimationGroup::ObtainStartValues() {
+  for (ScheduledAnimationSequence& sequence : sequences_)
+    GetStartValueFromViewIfNecessary(view_, &(sequence.elements[0]));
+}
+
+void ScheduledAnimationGroup::SetValuesToTargetValuesForPropertiesNotIn(
+    const ScheduledAnimationGroup& other) {
+  std::set<AnimationProperty> our_properties;
+  GetScheduledAnimationProperties(sequences_, &our_properties);
+
+  std::set<AnimationProperty> other_properties;
+  GetScheduledAnimationProperties(other.sequences_, &other_properties);
+
+  for (AnimationProperty property : our_properties) {
+    if (other_properties.count(property) == 0 &&
+        property != AnimationProperty::NONE) {
+      SetPropertyToTargetProperty(view_, property, sequences_);
+    }
+  }
+}
+
+bool ScheduledAnimationGroup::Tick(base::TimeTicks time) {
+  for (Sequences::iterator i = sequences_.begin(); i != sequences_.end();) {
+    if (!AdvanceSequence(view_, &(*i), time)) {
+      i = sequences_.erase(i);
+      continue;
+    }
+    const ScheduledAnimationElement& active_element(
+        i->elements[i->current_index]);
+    const double percent =
+        (time - active_element.start_time).InMillisecondsF() /
+        active_element.duration.InMillisecondsF();
+    SetViewPropertyFromValueBetween(
+        view_, active_element.property, percent, active_element.tween_type,
+        active_element.start_value, active_element.target_value);
+    ++i;
+  }
+  return sequences_.empty();
+}
+
+ScheduledAnimationGroup::ScheduledAnimationGroup(ServerView* view,
+                                                 uint32_t id,
+                                                 base::TimeTicks time_scheduled)
+    : view_(view), id_(id), time_scheduled_(time_scheduled) {
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/scheduled_animation_group.h b/services/view_manager/scheduled_animation_group.h
new file mode 100644
index 0000000..ca5c241
--- /dev/null
+++ b/services/view_manager/scheduled_animation_group.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_SCHEDULED_ANIMATION_GROUP_H_
+#define SERVICES_VIEW_MANAGER_SCHEDULED_ANIMATION_GROUP_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "mojo/services/view_manager/interfaces/animations.mojom.h"
+#include "ui/gfx/animation/tween.h"
+#include "ui/gfx/transform.h"
+
+namespace view_manager {
+
+class ServerView;
+
+struct ScheduledAnimationValue {
+  ScheduledAnimationValue();
+  ~ScheduledAnimationValue();
+
+  float float_value;
+  gfx::Transform transform;
+};
+
+struct ScheduledAnimationElement {
+  ScheduledAnimationElement();
+  ~ScheduledAnimationElement();
+
+  mojo::AnimationProperty property;
+  base::TimeDelta duration;
+  gfx::Tween::Type tween_type;
+  bool is_start_valid;
+  ScheduledAnimationValue start_value;
+  ScheduledAnimationValue target_value;
+  // Start time is based on scheduled time and relative to any other elements
+  // in the sequence.
+  base::TimeTicks start_time;
+};
+
+struct ScheduledAnimationSequence {
+  ScheduledAnimationSequence();
+  ~ScheduledAnimationSequence();
+
+  bool run_until_stopped;
+  std::vector<ScheduledAnimationElement> elements;
+
+  // Sum of the duration of all elements. This does not take into account
+  // |cycle_count|.
+  base::TimeDelta duration;
+
+  // The following values are updated as the animation progresses.
+
+  // Number of cycles remaining. This is only used if |run_until_stopped| is
+  // false.
+  uint32_t cycle_count;
+
+  // Index into |elements| of the element currently animating.
+  size_t current_index;
+};
+
+// Corresponds to a mojo::AnimationGroup and is responsible for running the
+// actual animation.
+class ScheduledAnimationGroup {
+ public:
+  ~ScheduledAnimationGroup();
+
+  // Returns a new ScheduledAnimationGroup from the supplied parameters, or
+  // null if |transport_group| isn't valid.
+  static scoped_ptr<ScheduledAnimationGroup> Create(
+      ServerView* view,
+      base::TimeTicks now,
+      uint32_t id,
+      const mojo::AnimationGroup& transport_group);
+
+  uint32_t id() const { return id_; }
+
+  // Gets the start value for any elements that don't have an explicit start.
+  // value.
+  void ObtainStartValues();
+
+  // Sets the values of any properties that are not in |other| to their final
+  // value.
+  void SetValuesToTargetValuesForPropertiesNotIn(
+      const ScheduledAnimationGroup& other);
+
+  // Advances the group. |time| is the current time. Returns true if the group
+  // is done (nothing left to animate).
+  bool Tick(base::TimeTicks time);
+
+  ServerView* view() { return view_; }
+
+ private:
+  ScheduledAnimationGroup(ServerView* view,
+                          uint32_t id,
+                          base::TimeTicks time_scheduled);
+
+  ServerView* view_;
+  const uint32_t id_;
+  base::TimeTicks time_scheduled_;
+  std::vector<ScheduledAnimationSequence> sequences_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScheduledAnimationGroup);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SCHEDULED_ANIMATION_GROUP_H_
diff --git a/services/view_manager/scheduled_animation_group_unittest.cc b/services/view_manager/scheduled_animation_group_unittest.cc
new file mode 100644
index 0000000..cc55024
--- /dev/null
+++ b/services/view_manager/scheduled_animation_group_unittest.cc
@@ -0,0 +1,94 @@
+// 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.
+
+#include "services/view_manager/scheduled_animation_group.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/view_manager/interfaces/animations.mojom.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mojo::AnimationProperty;
+using mojo::AnimationTweenType;
+using mojo::AnimationGroup;
+using mojo::AnimationSequence;
+using mojo::AnimationElement;
+using mojo::AnimationValue;
+
+namespace view_manager {
+namespace {
+
+bool IsAnimationGroupValid(const AnimationGroup& transport_group) {
+  TestServerViewDelegate view_delegate;
+  ServerView view(&view_delegate, ViewId());
+  scoped_ptr<ScheduledAnimationGroup> group(ScheduledAnimationGroup::Create(
+      &view, base::TimeTicks::Now(), 1, transport_group));
+  return group.get() != nullptr;
+}
+
+}  // namespace
+
+TEST(ScheduledAnimationGroupTest, IsAnimationGroupValid) {
+  AnimationGroup group;
+
+  // AnimationGroup with no sequences is not valid.
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  group.sequences.push_back(AnimationSequence::New());
+
+  // Sequence with no elements is not valid.
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  AnimationSequence& sequence = *(group.sequences[0]);
+  sequence.elements.push_back(AnimationElement::New());
+  AnimationElement& element = *(sequence.elements[0]);
+  element.property = AnimationProperty::OPACITY;
+  element.tween_type = AnimationTweenType::LINEAR;
+
+  // Element with no target_value is not valid.
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  // Opacity must be between 0 and 1.
+  element.target_value = AnimationValue::New();
+  element.target_value->float_value = 2.5f;
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  element.target_value->float_value = .5f;
+  EXPECT_TRUE(IsAnimationGroupValid(group));
+
+  // Bogus start value.
+  element.start_value = AnimationValue::New();
+  element.start_value->float_value = 2.5f;
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  element.start_value->float_value = .5f;
+  EXPECT_TRUE(IsAnimationGroupValid(group));
+
+  // Bogus transform.
+  element.property = AnimationProperty::TRANSFORM;
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+  element.start_value->transform = mojo::Transform::From(gfx::Transform());
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+  element.target_value->transform = mojo::Transform::From(gfx::Transform());
+  EXPECT_TRUE(IsAnimationGroupValid(group));
+
+  // Add another empty sequence, should be invalid again.
+  group.sequences.push_back(AnimationSequence::New());
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  AnimationSequence& sequence2 = *(group.sequences[1]);
+  sequence2.elements.push_back(AnimationElement::New());
+  AnimationElement& element2 = *(sequence2.elements[0]);
+  element2.property = AnimationProperty::OPACITY;
+  element2.tween_type = AnimationTweenType::LINEAR;
+
+  // Element with no target_value is not valid.
+  EXPECT_FALSE(IsAnimationGroupValid(group));
+
+  element2.property = AnimationProperty::NONE;
+  EXPECT_TRUE(IsAnimationGroupValid(group));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/server_view.cc b/services/view_manager/server_view.cc
new file mode 100644
index 0000000..6da2ea2
--- /dev/null
+++ b/services/view_manager/server_view.cc
@@ -0,0 +1,234 @@
+// 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.
+
+#include "services/view_manager/server_view.h"
+
+#include <inttypes.h>
+
+#include "base/strings/stringprintf.h"
+#include "services/view_manager/server_view_delegate.h"
+#include "services/view_manager/server_view_observer.h"
+
+namespace view_manager {
+
+ServerView::ServerView(ServerViewDelegate* delegate, const ViewId& id)
+    : delegate_(delegate),
+      id_(id),
+      parent_(nullptr),
+      visible_(false),
+      opacity_(1),
+      // Don't notify newly added observers during notification. This causes
+      // problems for code that adds an observer as part of an observer
+      // notification (such as ServerViewDrawTracker).
+      observers_(base::ObserverList<ServerViewObserver>::NOTIFY_EXISTING_ONLY) {
+  DCHECK(delegate);  // Must provide a delegate.
+}
+
+ServerView::~ServerView() {
+  delegate_->PrepareToDestroyView(this);
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_, OnWillDestroyView(this));
+
+  while (!children_.empty())
+    children_.front()->parent()->Remove(children_.front());
+
+  if (parent_)
+    parent_->Remove(this);
+
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_, OnViewDestroyed(this));
+}
+
+void ServerView::AddObserver(ServerViewObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ServerView::RemoveObserver(ServerViewObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void ServerView::Add(ServerView* child) {
+  // We assume validation checks happened already.
+  DCHECK(child);
+  DCHECK(child != this);
+  DCHECK(!child->Contains(this));
+  if (child->parent() == this) {
+    if (children_.size() == 1)
+      return;  // Already in the right position.
+    Reorder(child, children_.back(), mojo::OrderDirection::ABOVE);
+    return;
+  }
+
+  ServerView* old_parent = child->parent();
+  child->delegate_->PrepareToChangeViewHierarchy(child, this, old_parent);
+  FOR_EACH_OBSERVER(ServerViewObserver, child->observers_,
+                    OnWillChangeViewHierarchy(child, this, old_parent));
+
+  if (child->parent())
+    child->parent()->RemoveImpl(child);
+
+  child->parent_ = this;
+  children_.push_back(child);
+  FOR_EACH_OBSERVER(ServerViewObserver, child->observers_,
+                    OnViewHierarchyChanged(child, this, old_parent));
+}
+
+void ServerView::Remove(ServerView* child) {
+  // We assume validation checks happened else where.
+  DCHECK(child);
+  DCHECK(child != this);
+  DCHECK(child->parent() == this);
+
+  child->delegate_->PrepareToChangeViewHierarchy(child, NULL, this);
+  FOR_EACH_OBSERVER(ServerViewObserver, child->observers_,
+                    OnWillChangeViewHierarchy(child, nullptr, this));
+  RemoveImpl(child);
+  FOR_EACH_OBSERVER(ServerViewObserver, child->observers_,
+                    OnViewHierarchyChanged(child, nullptr, this));
+}
+
+void ServerView::Reorder(ServerView* child,
+                         ServerView* relative,
+                         mojo::OrderDirection direction) {
+  // We assume validation checks happened else where.
+  DCHECK(child);
+  DCHECK(child->parent() == this);
+  DCHECK_GT(children_.size(), 1u);
+  children_.erase(std::find(children_.begin(), children_.end(), child));
+  Views::iterator i = std::find(children_.begin(), children_.end(), relative);
+  if (direction == mojo::OrderDirection::ABOVE) {
+    DCHECK(i != children_.end());
+    children_.insert(++i, child);
+  } else if (direction == mojo::OrderDirection::BELOW) {
+    DCHECK(i != children_.end());
+    children_.insert(i, child);
+  }
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_,
+                    OnViewReordered(this, relative, direction));
+}
+
+void ServerView::SetBounds(const gfx::Rect& bounds) {
+  if (bounds_ == bounds)
+    return;
+
+  const gfx::Rect old_bounds = bounds_;
+  bounds_ = bounds;
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_,
+                    OnViewBoundsChanged(this, old_bounds, bounds));
+}
+
+const ServerView* ServerView::GetRoot() const {
+  const ServerView* view = this;
+  while (view && view->parent())
+    view = view->parent();
+  return view;
+}
+
+std::vector<const ServerView*> ServerView::GetChildren() const {
+  std::vector<const ServerView*> children;
+  children.reserve(children_.size());
+  for (size_t i = 0; i < children_.size(); ++i)
+    children.push_back(children_[i]);
+  return children;
+}
+
+std::vector<ServerView*> ServerView::GetChildren() {
+  // TODO(sky): rename to children() and fix return type.
+  return children_;
+}
+
+bool ServerView::Contains(const ServerView* view) const {
+  for (const ServerView* parent = view; parent; parent = parent->parent_) {
+    if (parent == this)
+      return true;
+  }
+  return false;
+}
+
+void ServerView::SetVisible(bool value) {
+  if (visible_ == value)
+    return;
+
+  delegate_->PrepareToChangeViewVisibility(this);
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_,
+                    OnWillChangeViewVisibility(this));
+  visible_ = value;
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_,
+                    OnViewVisibilityChanged(this));
+}
+
+void ServerView::SetOpacity(float value) {
+  if (value == opacity_)
+    return;
+  opacity_ = value;
+  delegate_->OnScheduleViewPaint(this);
+}
+
+void ServerView::SetTransform(const gfx::Transform& transform) {
+  if (transform_ == transform)
+    return;
+
+  transform_ = transform;
+  delegate_->OnScheduleViewPaint(this);
+}
+
+void ServerView::SetProperty(const std::string& name,
+                             const std::vector<uint8_t>* value) {
+  auto it = properties_.find(name);
+  if (it != properties_.end()) {
+    if (value && it->second == *value)
+      return;
+  } else if (!value) {
+    // This property isn't set in |properties_| and |value| is NULL, so there's
+    // no change.
+    return;
+  }
+
+  if (value) {
+    properties_[name] = *value;
+  } else if (it != properties_.end()) {
+    properties_.erase(it);
+  }
+
+  FOR_EACH_OBSERVER(ServerViewObserver, observers_,
+                    OnViewSharedPropertyChanged(this, name, value));
+}
+
+bool ServerView::IsDrawn(const ServerView* root) const {
+  if (!root->visible_)
+    return false;
+  const ServerView* view = this;
+  while (view && view != root && view->visible_)
+    view = view->parent_;
+  return view == root;
+}
+
+void ServerView::SetSurfaceId(cc::SurfaceId surface_id) {
+  surface_id_ = surface_id;
+  delegate_->OnScheduleViewPaint(this);
+}
+
+#if !defined(NDEBUG)
+std::string ServerView::GetDebugWindowHierarchy() const {
+  std::string result;
+  BuildDebugInfo(std::string(), &result);
+  return result;
+}
+
+void ServerView::BuildDebugInfo(const std::string& depth,
+                                std::string* result) const {
+  *result += base::StringPrintf(
+      "%sid=%d,%d visible=%s bounds=%d,%d %dx%d surface_id=%" PRIu64 "\n",
+      depth.c_str(), static_cast<int>(id_.connection_id),
+      static_cast<int>(id_.view_id), visible_ ? "true" : "false", bounds_.x(),
+      bounds_.y(), bounds_.width(), bounds_.height(), surface_id_.id);
+  for (const ServerView* child : children_)
+    child->BuildDebugInfo(depth + "  ", result);
+}
+#endif
+
+void ServerView::RemoveImpl(ServerView* view) {
+  view->parent_ = NULL;
+  children_.erase(std::find(children_.begin(), children_.end(), view));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/server_view.h b/services/view_manager/server_view.h
new file mode 100644
index 0000000..83883f4
--- /dev/null
+++ b/services/view_manager/server_view.h
@@ -0,0 +1,120 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
+#define SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/observer_list.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "services/view_manager/ids.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace view_manager {
+
+class ServerViewDelegate;
+class ServerViewObserver;
+
+// Server side representation of a view. Delegate is informed of interesting
+// events.
+//
+// It is assumed that all functions that mutate the tree have validated the
+// mutation is possible before hand. For example, Reorder() assumes the supplied
+// view is a child and not already in position.
+//
+// ServerViews do not own their children. If you delete a view that has children
+// the children are implicitly removed. Similarly if a view has a parent and the
+// view is deleted the deleted view is implicitly removed from the parent.
+class ServerView {
+ public:
+  ServerView(ServerViewDelegate* delegate, const ViewId& id);
+  virtual ~ServerView();
+
+  void AddObserver(ServerViewObserver* observer);
+  void RemoveObserver(ServerViewObserver* observer);
+
+  const ViewId& id() const { return id_; }
+
+  void Add(ServerView* child);
+  void Remove(ServerView* child);
+  void Reorder(ServerView* child,
+               ServerView* relative,
+               mojo::OrderDirection direction);
+
+  const gfx::Rect& bounds() const { return bounds_; }
+  void SetBounds(const gfx::Rect& bounds);
+
+  const ServerView* parent() const { return parent_; }
+  ServerView* parent() { return parent_; }
+
+  const ServerView* GetRoot() const;
+  ServerView* GetRoot() {
+    return const_cast<ServerView*>(
+        const_cast<const ServerView*>(this)->GetRoot());
+  }
+
+  std::vector<const ServerView*> GetChildren() const;
+  std::vector<ServerView*> GetChildren();
+
+  // Returns true if this contains |view| or is |view|.
+  bool Contains(const ServerView* view) const;
+
+  // Returns true if the window is visible. This does not consider visibility
+  // of any ancestors.
+  bool visible() const { return visible_; }
+  void SetVisible(bool value);
+
+  float opacity() const { return opacity_; }
+  void SetOpacity(float value);
+
+  const gfx::Transform& transform() const { return transform_; }
+  void SetTransform(const gfx::Transform& transform);
+
+  const std::map<std::string, std::vector<uint8_t>>& properties() const {
+    return properties_;
+  }
+  void SetProperty(const std::string& name, const std::vector<uint8_t>* value);
+
+  // Returns true if this view is attached to |root| and all ancestors are
+  // visible.
+  bool IsDrawn(const ServerView* root) const;
+
+  void SetSurfaceId(cc::SurfaceId surface_id);
+  const cc::SurfaceId& surface_id() const { return surface_id_; }
+
+#if !defined(NDEBUG)
+  std::string GetDebugWindowHierarchy() const;
+  void BuildDebugInfo(const std::string& depth, std::string* result) const;
+#endif
+
+ private:
+  typedef std::vector<ServerView*> Views;
+
+  // Implementation of removing a view. Doesn't send any notification.
+  void RemoveImpl(ServerView* view);
+
+  ServerViewDelegate* delegate_;
+  const ViewId id_;
+  ServerView* parent_;
+  Views children_;
+  bool visible_;
+  gfx::Rect bounds_;
+  cc::SurfaceId surface_id_;
+  float opacity_;
+  gfx::Transform transform_;
+
+  std::map<std::string, std::vector<uint8_t>> properties_;
+
+  base::ObserverList<ServerViewObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerView);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
diff --git a/services/view_manager/server_view_delegate.h b/services/view_manager/server_view_delegate.h
new file mode 100644
index 0000000..0a3e3ee
--- /dev/null
+++ b/services/view_manager/server_view_delegate.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
+
+#include "mojo/services/view_manager/interfaces/view_manager_constants.mojom.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+class ViewportMetrics;
+}
+
+namespace view_manager {
+
+class ServerView;
+
+// ServerViewDelegate is notified at key points in the lifetime of a
+// ServerView. Some of the functions are similar to that of
+// ServerViewObserver. For example, ServerViewDelegate::PrepareToDestroyView()
+// and ServerViewObserver::OnWillDestroyView(). The key difference between
+// the two are the ServerViewDelegate ones are always notified first, and
+// ServerViewDelegate gets non-const arguments.
+class ServerViewDelegate {
+ public:
+  // Invoked when a view is about to be destroyed; before any of the children
+  // have been removed and before the view has been removed from its parent.
+  virtual void PrepareToDestroyView(ServerView* view) = 0;
+
+  virtual void PrepareToChangeViewHierarchy(ServerView* view,
+                                            ServerView* new_parent,
+                                            ServerView* old_parent) = 0;
+
+  virtual void PrepareToChangeViewVisibility(ServerView* view) = 0;
+
+  virtual void OnScheduleViewPaint(const ServerView* view) = 0;
+
+ protected:
+  virtual ~ServerViewDelegate() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
diff --git a/services/view_manager/server_view_drawn_tracker.cc b/services/view_manager/server_view_drawn_tracker.cc
new file mode 100644
index 0000000..aee06ae
--- /dev/null
+++ b/services/view_manager/server_view_drawn_tracker.cc
@@ -0,0 +1,76 @@
+// 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/view_manager/server_view_drawn_tracker.h"
+
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/server_view_drawn_tracker_observer.h"
+
+namespace view_manager {
+
+ServerViewDrawnTracker::ServerViewDrawnTracker(
+    ServerView* root,
+    ServerView* view,
+    ServerViewDrawnTrackerObserver* observer)
+    : root_(root),
+      view_(view),
+      observer_(observer),
+      drawn_(view->IsDrawn(root)) {
+  AddObservers();
+}
+
+ServerViewDrawnTracker::~ServerViewDrawnTracker() {
+  RemoveObservers();
+}
+
+void ServerViewDrawnTracker::SetDrawn(ServerView* ancestor, bool drawn) {
+  if (drawn == drawn_)
+    return;
+
+  drawn_ = drawn;
+  observer_->OnDrawnStateChanged(ancestor, view_, drawn);
+}
+
+void ServerViewDrawnTracker::AddObservers() {
+  if (!view_)
+    return;
+
+  for (ServerView* v = view_; v; v = v->parent()) {
+    v->AddObserver(this);
+    views_.insert(v);
+  }
+}
+
+void ServerViewDrawnTracker::RemoveObservers() {
+  for (ServerView* view : views_)
+    view->RemoveObserver(this);
+
+  views_.clear();
+}
+
+void ServerViewDrawnTracker::OnViewDestroyed(ServerView* view) {
+  // As views are removed before being destroyed, resulting in
+  // OnViewHierarchyChanged() and us removing ourself as an observer, the only
+  // view we should ever get notified of destruction on is |view_|.
+  DCHECK_EQ(view, view_);
+  RemoveObservers();
+  view_ = nullptr;
+  SetDrawn(nullptr, false);
+}
+
+void ServerViewDrawnTracker::OnViewHierarchyChanged(ServerView* view,
+                                                    ServerView* new_parent,
+                                                    ServerView* old_parent) {
+  RemoveObservers();
+  AddObservers();
+  const bool is_drawn = view_->IsDrawn(root_);
+  SetDrawn(is_drawn ? nullptr : old_parent, is_drawn);
+}
+
+void ServerViewDrawnTracker::OnViewVisibilityChanged(ServerView* view) {
+  const bool is_drawn = view_->IsDrawn(root_);
+  SetDrawn(is_drawn ? nullptr : view->parent(), is_drawn);
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/server_view_drawn_tracker.h b/services/view_manager/server_view_drawn_tracker.h
new file mode 100644
index 0000000..4c90390
--- /dev/null
+++ b/services/view_manager/server_view_drawn_tracker.h
@@ -0,0 +1,58 @@
+// 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_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_H_
+#define SERVICES_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "services/view_manager/server_view_observer.h"
+
+namespace view_manager {
+
+class ServerViewDrawnTrackerObserver;
+
+// ServerViewDrawnTracker notifies its observer any time the drawn state of
+// the supplied view changes.
+//
+// NOTE: you must ensure this class is destroyed before the root.
+class ServerViewDrawnTracker : public ServerViewObserver {
+ public:
+  ServerViewDrawnTracker(ServerView* root,
+                         ServerView* view,
+                         ServerViewDrawnTrackerObserver* observer);
+  ~ServerViewDrawnTracker() override;
+
+  ServerView* view() { return view_; }
+
+ private:
+  void SetDrawn(ServerView* ancestor, bool drawn);
+
+  // Adds |this| as an observer to |view_| and its ancestors.
+  void AddObservers();
+
+  // Stops observerving any views we added as an observer in AddObservers().
+  void RemoveObservers();
+
+  // ServerViewObserver:
+  void OnViewDestroyed(ServerView* view) override;
+  void OnViewHierarchyChanged(ServerView* view,
+                              ServerView* new_parent,
+                              ServerView* old_parent) override;
+  void OnViewVisibilityChanged(ServerView* view) override;
+
+  ServerView* root_;
+  ServerView* view_;
+  ServerViewDrawnTrackerObserver* observer_;
+  bool drawn_;
+  // Set of views we're observing. This is |view_| and all its ancestors.
+  std::set<ServerView*> views_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerViewDrawnTracker);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_H_
diff --git a/services/view_manager/server_view_drawn_tracker_observer.h b/services/view_manager/server_view_drawn_tracker_observer.h
new file mode 100644
index 0000000..1aacd65
--- /dev/null
+++ b/services/view_manager/server_view_drawn_tracker_observer.h
@@ -0,0 +1,28 @@
+// 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_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_OBSERVER_H_
+#define SERVICES_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_OBSERVER_H_
+
+namespace view_manager {
+
+class ServerView;
+
+class ServerViewDrawnTrackerObserver {
+ public:
+  // Invoked when the drawn state changes. If |is_drawn| is false |ancestor|
+  // identifies where the change occurred. In the case of a remove |ancestor| is
+  // the parent of the view that was removed. In the case of a visibility change
+  // |ancestor| is the parent of the view whose visibility changed.
+  virtual void OnDrawnStateChanged(ServerView* ancestor,
+                                   ServerView* view,
+                                   bool is_drawn) {}
+
+ protected:
+  virtual ~ServerViewDrawnTrackerObserver() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SERVER_VIEW_DRAWN_TRACKER_OBSERVER_H_
diff --git a/services/view_manager/server_view_drawn_tracker_unittest.cc b/services/view_manager/server_view_drawn_tracker_unittest.cc
new file mode 100644
index 0000000..ecece9d
--- /dev/null
+++ b/services/view_manager/server_view_drawn_tracker_unittest.cc
@@ -0,0 +1,136 @@
+// 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/view_manager/server_view_drawn_tracker.h"
+
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/server_view_drawn_tracker_observer.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace view_manager {
+namespace {
+
+class TestServerViewDrawnTrackerObserver
+    : public ServerViewDrawnTrackerObserver {
+ public:
+  TestServerViewDrawnTrackerObserver()
+      : change_count_(0u),
+        ancestor_(nullptr),
+        view_(nullptr),
+        is_drawn_(false) {}
+
+  void clear_change_count() { change_count_ = 0u; }
+  size_t change_count() const { return change_count_; }
+  const ServerView* ancestor() const { return ancestor_; }
+  const ServerView* view() const { return view_; }
+  bool is_drawn() const { return is_drawn_; }
+
+ private:
+  // ServerViewDrawnTrackerObserver:
+  void OnDrawnStateChanged(ServerView* ancestor,
+                           ServerView* view,
+                           bool is_drawn) override {
+    change_count_++;
+    ancestor_ = ancestor;
+    view_ = view;
+    is_drawn_ = is_drawn;
+  }
+
+  size_t change_count_;
+  const ServerView* ancestor_;
+  const ServerView* view_;
+  bool is_drawn_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestServerViewDrawnTrackerObserver);
+};
+
+}  // namespace
+
+TEST(ServerViewDrawnTrackerTest, ChangeBecauseOfDeletionAndVisibility) {
+  TestServerViewDelegate server_view_delegate;
+  scoped_ptr<ServerView> view(new ServerView(&server_view_delegate, ViewId()));
+  TestServerViewDrawnTrackerObserver drawn_observer;
+  ServerViewDrawnTracker tracker(view.get(), view.get(), &drawn_observer);
+  view->SetVisible(true);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(view.get(), drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_TRUE(drawn_observer.is_drawn());
+  drawn_observer.clear_change_count();
+
+  view->SetVisible(false);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(view.get(), drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_FALSE(drawn_observer.is_drawn());
+  drawn_observer.clear_change_count();
+
+  view->SetVisible(true);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(view.get(), drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_TRUE(drawn_observer.is_drawn());
+  drawn_observer.clear_change_count();
+
+  view.reset();
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(view.get(), drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_FALSE(drawn_observer.is_drawn());
+}
+
+TEST(ServerViewDrawnTrackerTest, ChangeBecauseOfRemovingFromRoot) {
+  TestServerViewDelegate server_view_delegate;
+  ServerView root(&server_view_delegate, ViewId());
+  root.SetVisible(true);
+  ServerView child(&server_view_delegate, ViewId());
+  child.SetVisible(true);
+  root.Add(&child);
+
+  TestServerViewDrawnTrackerObserver drawn_observer;
+  ServerViewDrawnTracker tracker(&root, &child, &drawn_observer);
+  root.Remove(&child);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(&child, drawn_observer.view());
+  EXPECT_EQ(&root, drawn_observer.ancestor());
+  EXPECT_FALSE(drawn_observer.is_drawn());
+  drawn_observer.clear_change_count();
+
+  root.Add(&child);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(&child, drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_TRUE(drawn_observer.is_drawn());
+}
+
+TEST(ServerViewDrawnTrackerTest, ChangeBecauseOfRemovingAncestorFromRoot) {
+  TestServerViewDelegate server_view_delegate;
+  ServerView root(&server_view_delegate, ViewId());
+  root.SetVisible(true);
+  ServerView child(&server_view_delegate, ViewId());
+  child.SetVisible(true);
+  root.Add(&child);
+
+  ServerView child_child(&server_view_delegate, ViewId());
+  child_child.SetVisible(true);
+  child.Add(&child_child);
+
+  TestServerViewDrawnTrackerObserver drawn_observer;
+  ServerViewDrawnTracker tracker(&root, &child_child, &drawn_observer);
+  root.Remove(&child);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(&child_child, drawn_observer.view());
+  EXPECT_EQ(&root, drawn_observer.ancestor());
+  EXPECT_FALSE(drawn_observer.is_drawn());
+  drawn_observer.clear_change_count();
+
+  root.Add(&child_child);
+  EXPECT_EQ(1u, drawn_observer.change_count());
+  EXPECT_EQ(&child_child, drawn_observer.view());
+  EXPECT_EQ(nullptr, drawn_observer.ancestor());
+  EXPECT_TRUE(drawn_observer.is_drawn());
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/server_view_observer.h b/services/view_manager/server_view_observer.h
new file mode 100644
index 0000000..dab9f22
--- /dev/null
+++ b/services/view_manager/server_view_observer.h
@@ -0,0 +1,63 @@
+// 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_VIEW_MANAGER_SERVER_VIEW_OBSERVER_H_
+#define SERVICES_VIEW_MANAGER_SERVER_VIEW_OBSERVER_H_
+
+#include "mojo/services/view_manager/interfaces/view_manager_constants.mojom.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+class ViewportMetrics;
+}
+
+namespace view_manager {
+
+class ServerView;
+
+// TODO(sky): rename to OnDid and OnWill everywhere.
+class ServerViewObserver {
+ public:
+  // Invoked when a view is about to be destroyed; before any of the children
+  // have been removed and before the view has been removed from its parent.
+  virtual void OnWillDestroyView(ServerView* view) {}
+
+  // Invoked at the end of the View's destructor (after it has been removed from
+  // the hierarchy).
+  virtual void OnViewDestroyed(ServerView* view) {}
+
+  virtual void OnWillChangeViewHierarchy(ServerView* view,
+                                         ServerView* new_parent,
+                                         ServerView* old_parent) {}
+
+  virtual void OnViewHierarchyChanged(ServerView* view,
+                                      ServerView* new_parent,
+                                      ServerView* old_parent) {}
+
+  virtual void OnViewBoundsChanged(ServerView* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) {}
+
+  virtual void OnViewReordered(ServerView* view,
+                               ServerView* relative,
+                               mojo::OrderDirection direction) {}
+
+  virtual void OnWillChangeViewVisibility(ServerView* view) {}
+  virtual void OnViewVisibilityChanged(ServerView* view) {}
+
+  virtual void OnViewSharedPropertyChanged(
+      ServerView* view,
+      const std::string& name,
+      const std::vector<uint8_t>* new_data) {}
+
+ protected:
+  virtual ~ServerViewObserver() {}
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_SERVER_VIEW_OBSERVER_H_
diff --git a/services/view_manager/test_change_tracker.cc b/services/view_manager/test_change_tracker.cc
new file mode 100644
index 0000000..f7fd069
--- /dev/null
+++ b/services/view_manager/test_change_tracker.cc
@@ -0,0 +1,314 @@
+// 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.
+
+#include "services/view_manager/test_change_tracker.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/converters/array_string/array_string_type_converters.h"
+#include "mojo/services/view_manager/cpp/util.h"
+
+using mojo::Array;
+using mojo::Id;
+using mojo::ViewDataPtr;
+using mojo::String;
+
+namespace view_manager {
+
+std::string ViewIdToString(Id id) {
+  return (id == 0) ? "null" : base::StringPrintf("%d,%d", mojo::HiWord(id),
+                                                 mojo::LoWord(id));
+}
+
+namespace {
+
+std::string RectToString(const mojo::Rect& rect) {
+  return base::StringPrintf("%d,%d %dx%d", rect.x, rect.y, rect.width,
+                            rect.height);
+}
+
+std::string DirectionToString(mojo::OrderDirection direction) {
+  return direction == mojo::OrderDirection::ABOVE ? "above" : "below";
+}
+
+std::string ChangeToDescription1(const Change& change) {
+  switch (change.type) {
+    case CHANGE_TYPE_EMBED:
+      return base::StringPrintf("OnEmbed creator=%s",
+                                change.creator_url.data());
+
+    case CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED:
+      return base::StringPrintf("OnEmbeddedAppDisconnected view=%s",
+                                ViewIdToString(change.view_id).c_str());
+
+    case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
+      return base::StringPrintf(
+          "BoundsChanged view=%s old_bounds=%s new_bounds=%s",
+          ViewIdToString(change.view_id).c_str(),
+          RectToString(change.bounds).c_str(),
+          RectToString(change.bounds2).c_str());
+
+    case CHANGE_TYPE_NODE_VIEWPORT_METRICS_CHANGED:
+      // TODO(sky): Not implemented.
+      return "ViewportMetricsChanged";
+
+    case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
+      return base::StringPrintf(
+          "HierarchyChanged view=%s new_parent=%s old_parent=%s",
+          ViewIdToString(change.view_id).c_str(),
+          ViewIdToString(change.view_id2).c_str(),
+          ViewIdToString(change.view_id3).c_str());
+
+    case CHANGE_TYPE_NODE_REORDERED:
+      return base::StringPrintf("Reordered view=%s relative=%s direction=%s",
+                                ViewIdToString(change.view_id).c_str(),
+                                ViewIdToString(change.view_id2).c_str(),
+                                DirectionToString(change.direction).c_str());
+
+    case CHANGE_TYPE_NODE_DELETED:
+      return base::StringPrintf("ViewDeleted view=%s",
+                                ViewIdToString(change.view_id).c_str());
+
+    case CHANGE_TYPE_NODE_VISIBILITY_CHANGED:
+      return base::StringPrintf("VisibilityChanged view=%s visible=%s",
+                                ViewIdToString(change.view_id).c_str(),
+                                change.bool_value ? "true" : "false");
+
+    case CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED:
+      return base::StringPrintf("DrawnStateChanged view=%s drawn=%s",
+                                ViewIdToString(change.view_id).c_str(),
+                                change.bool_value ? "true" : "false");
+
+    case CHANGE_TYPE_INPUT_EVENT:
+      return base::StringPrintf("InputEvent view=%s event_action=%d",
+                                ViewIdToString(change.view_id).c_str(),
+                                static_cast<int32_t>(change.event_action));
+
+    case CHANGE_TYPE_PROPERTY_CHANGED:
+      return base::StringPrintf("PropertyChanged view=%s key=%s value=%s",
+                                ViewIdToString(change.view_id).c_str(),
+                                change.property_key.c_str(),
+                                change.property_value.c_str());
+
+    case CHANGE_TYPE_DELEGATE_EMBED:
+      return base::StringPrintf("DelegateEmbed url=%s",
+                                change.embed_url.data());
+  }
+  return std::string();
+}
+
+}  // namespace
+
+std::vector<std::string> ChangesToDescription1(
+    const std::vector<Change>& changes) {
+  std::vector<std::string> strings(changes.size());
+  for (size_t i = 0; i < changes.size(); ++i)
+    strings[i] = ChangeToDescription1(changes[i]);
+  return strings;
+}
+
+std::string SingleChangeToDescription(const std::vector<Change>& changes) {
+  if (changes.size() != 1u)
+    return std::string();
+  return ChangeToDescription1(changes[0]);
+}
+
+std::string SingleViewDescription(const std::vector<TestView>& views) {
+  if (views.size() != 1u)
+    return std::string();
+  return views[0].ToString();
+}
+
+std::string ChangeViewDescription(const std::vector<Change>& changes) {
+  if (changes.size() != 1)
+    return std::string();
+  std::vector<std::string> view_strings(changes[0].views.size());
+  for (size_t i = 0; i < changes[0].views.size(); ++i)
+    view_strings[i] = "[" + changes[0].views[i].ToString() + "]";
+  return JoinString(view_strings, ',');
+}
+
+TestView ViewDataToTestView(const ViewDataPtr& data) {
+  TestView view;
+  view.parent_id = data->parent_id;
+  view.view_id = data->view_id;
+  view.visible = data->visible;
+  view.drawn = data->drawn;
+  view.properties =
+      data->properties.To<std::map<std::string, std::vector<uint8_t>>>();
+  return view;
+}
+
+void ViewDatasToTestViews(const Array<ViewDataPtr>& data,
+                          std::vector<TestView>* test_views) {
+  for (size_t i = 0; i < data.size(); ++i)
+    test_views->push_back(ViewDataToTestView(data[i]));
+}
+
+Change::Change()
+    : type(CHANGE_TYPE_EMBED),
+      connection_id(0),
+      view_id(0),
+      view_id2(0),
+      view_id3(0),
+      event_action(mojo::EventType::UNKNOWN),
+      direction(mojo::OrderDirection::ABOVE),
+      bool_value(false) {
+}
+
+Change::~Change() {
+}
+
+TestChangeTracker::TestChangeTracker()
+    : delegate_(NULL) {
+}
+
+TestChangeTracker::~TestChangeTracker() {
+}
+
+void TestChangeTracker::OnEmbed(mojo::ConnectionSpecificId connection_id,
+                                const String& creator_url,
+                                ViewDataPtr root) {
+  Change change;
+  change.type = CHANGE_TYPE_EMBED;
+  change.connection_id = connection_id;
+  change.creator_url = creator_url;
+  change.views.push_back(ViewDataToTestView(root));
+  AddChange(change);
+}
+
+void TestChangeTracker::OnEmbeddedAppDisconnected(Id view_id) {
+  Change change;
+  change.type = CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED;
+  change.view_id = view_id;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewBoundsChanged(Id view_id,
+                                            mojo::RectPtr old_bounds,
+                                            mojo::RectPtr new_bounds) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+  change.view_id = view_id;
+  change.bounds.x = old_bounds->x;
+  change.bounds.y = old_bounds->y;
+  change.bounds.width = old_bounds->width;
+  change.bounds.height = old_bounds->height;
+  change.bounds2.x = new_bounds->x;
+  change.bounds2.y = new_bounds->y;
+  change.bounds2.width = new_bounds->width;
+  change.bounds2.height = new_bounds->height;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewViewportMetricsChanged(
+    mojo::ViewportMetricsPtr old_metrics,
+    mojo::ViewportMetricsPtr new_metrics) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_VIEWPORT_METRICS_CHANGED;
+  // NOT IMPLEMENTED
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewHierarchyChanged(Id view_id,
+                                               Id new_parent_id,
+                                               Id old_parent_id,
+                                               Array<ViewDataPtr> views) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
+  change.view_id = view_id;
+  change.view_id2 = new_parent_id;
+  change.view_id3 = old_parent_id;
+  ViewDatasToTestViews(views, &change.views);
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewReordered(Id view_id,
+                                        Id relative_view_id,
+                                        mojo::OrderDirection direction) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_REORDERED;
+  change.view_id = view_id;
+  change.view_id2 = relative_view_id;
+  change.direction = direction;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewDeleted(Id view_id) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_DELETED;
+  change.view_id = view_id;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewVisibilityChanged(Id view_id, bool visible) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_VISIBILITY_CHANGED;
+  change.view_id = view_id;
+  change.bool_value = visible;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewDrawnStateChanged(Id view_id, bool drawn) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED;
+  change.view_id = view_id;
+  change.bool_value = drawn;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewInputEvent(Id view_id, mojo::EventPtr event) {
+  Change change;
+  change.type = CHANGE_TYPE_INPUT_EVENT;
+  change.view_id = view_id;
+  change.event_action = event->action;
+  AddChange(change);
+}
+
+void TestChangeTracker::OnViewSharedPropertyChanged(Id view_id,
+                                                    String name,
+                                                    Array<uint8_t> data) {
+  Change change;
+  change.type = CHANGE_TYPE_PROPERTY_CHANGED;
+  change.view_id = view_id;
+  change.property_key = name;
+  if (data.is_null())
+    change.property_value = "NULL";
+  else
+    change.property_value = data.To<std::string>();
+  AddChange(change);
+}
+
+void TestChangeTracker::DelegateEmbed(const String& url) {
+  Change change;
+  change.type = CHANGE_TYPE_DELEGATE_EMBED;
+  change.embed_url = url;
+  AddChange(change);
+}
+
+void TestChangeTracker::AddChange(const Change& change) {
+  changes_.push_back(change);
+  if (delegate_)
+    delegate_->OnChangeAdded();
+}
+
+TestView::TestView() {}
+
+TestView::~TestView() {}
+
+std::string TestView::ToString() const {
+  return base::StringPrintf("view=%s parent=%s",
+                            ViewIdToString(view_id).c_str(),
+                            ViewIdToString(parent_id).c_str());
+}
+
+std::string TestView::ToString2() const {
+  return base::StringPrintf("view=%s parent=%s visible=%s drawn=%s",
+                            ViewIdToString(view_id).c_str(),
+                            ViewIdToString(parent_id).c_str(),
+                            visible ? "true" : "false",
+                            drawn ? "true" : "false");
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/test_change_tracker.h b/services/view_manager/test_change_tracker.h
new file mode 100644
index 0000000..da5d08c
--- /dev/null
+++ b/services/view_manager/test_change_tracker.h
@@ -0,0 +1,157 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+#define SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+
+namespace view_manager {
+
+enum ChangeType {
+  CHANGE_TYPE_EMBED,
+  CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED,
+  // TODO(sky): NODE->VIEW.
+  CHANGE_TYPE_NODE_BOUNDS_CHANGED,
+  CHANGE_TYPE_NODE_VIEWPORT_METRICS_CHANGED,
+  CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
+  CHANGE_TYPE_NODE_REORDERED,
+  CHANGE_TYPE_NODE_VISIBILITY_CHANGED,
+  CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED,
+  CHANGE_TYPE_NODE_DELETED,
+  CHANGE_TYPE_INPUT_EVENT,
+  CHANGE_TYPE_PROPERTY_CHANGED,
+  CHANGE_TYPE_DELEGATE_EMBED,
+};
+
+// TODO(sky): consider nuking and converting directly to ViewData.
+struct TestView {
+  TestView();
+  ~TestView();
+
+  // Returns a string description of this.
+  std::string ToString() const;
+
+  // Returns a string description that includes visible and drawn.
+  std::string ToString2() const;
+
+  mojo::Id parent_id;
+  mojo::Id view_id;
+  bool visible;
+  bool drawn;
+  std::map<std::string, std::vector<uint8_t>> properties;
+};
+
+// Tracks a call to ViewManagerClient. See the individual functions for the
+// fields that are used.
+struct Change {
+  Change();
+  ~Change();
+
+  ChangeType type;
+  mojo::ConnectionSpecificId connection_id;
+  std::vector<TestView> views;
+  mojo::Id view_id;
+  mojo::Id view_id2;
+  mojo::Id view_id3;
+  mojo::Rect bounds;
+  mojo::Rect bounds2;
+  mojo::EventType event_action;
+  mojo::String creator_url;
+  mojo::String embed_url;
+  mojo::OrderDirection direction;
+  bool bool_value;
+  std::string property_key;
+  std::string property_value;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+    const std::vector<Change>& changes);
+
+// Convenience for returning the description of the first item in |changes|.
+// Returns an empty string if |changes| has something other than one entry.
+std::string SingleChangeToDescription(const std::vector<Change>& changes);
+
+// Convenience for returning the description of the first item in |views|.
+// Returns an empty string if |views| has something other than one entry.
+std::string SingleViewDescription(const std::vector<TestView>& views);
+
+// Returns a string description of |changes[0].views|. Returns an empty string
+// if change.size() != 1.
+std::string ChangeViewDescription(const std::vector<Change>& changes);
+
+// Converts ViewDatas to TestViews.
+void ViewDatasToTestViews(const mojo::Array<mojo::ViewDataPtr>& data,
+                          std::vector<TestView>* test_views);
+
+// TestChangeTracker is used to record ViewManagerClient functions. It notifies
+// a delegate any time a change is added.
+class TestChangeTracker {
+ public:
+  // Used to notify the delegate when a change is added. A change corresponds to
+  // a single ViewManagerClient function.
+  class Delegate {
+   public:
+    virtual void OnChangeAdded() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  TestChangeTracker();
+  ~TestChangeTracker();
+
+  void set_delegate(Delegate* delegate) {
+    delegate_ = delegate;
+  }
+
+  std::vector<Change>* changes() { return &changes_; }
+
+  // Each of these functions generate a Change. There is one per
+  // ViewManagerClient function.
+  void OnEmbed(mojo::ConnectionSpecificId connection_id,
+               const mojo::String& creator_url,
+               mojo::ViewDataPtr root);
+  void OnEmbeddedAppDisconnected(mojo::Id view_id);
+  void OnViewBoundsChanged(mojo::Id view_id,
+                           mojo::RectPtr old_bounds,
+                           mojo::RectPtr new_bounds);
+  void OnViewViewportMetricsChanged(mojo::ViewportMetricsPtr old_bounds,
+                                    mojo::ViewportMetricsPtr new_bounds);
+  void OnViewHierarchyChanged(mojo::Id view_id,
+                              mojo::Id new_parent_id,
+                              mojo::Id old_parent_id,
+                              mojo::Array<mojo::ViewDataPtr> views);
+  void OnViewReordered(mojo::Id view_id,
+                       mojo::Id relative_view_id,
+                       mojo::OrderDirection direction);
+  void OnViewDeleted(mojo::Id view_id);
+  void OnViewVisibilityChanged(mojo::Id view_id, bool visible);
+  void OnViewDrawnStateChanged(mojo::Id view_id, bool drawn);
+  void OnViewInputEvent(mojo::Id view_id, mojo::EventPtr event);
+  void OnViewSharedPropertyChanged(mojo::Id view_id,
+                                   mojo::String name,
+                                   mojo::Array<uint8_t> data);
+  void DelegateEmbed(const mojo::String& url);
+
+ private:
+  void AddChange(const Change& change);
+
+  Delegate* delegate_;
+  std::vector<Change> changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
diff --git a/services/view_manager/test_server_view_delegate.cc b/services/view_manager/test_server_view_delegate.cc
new file mode 100644
index 0000000..9945480
--- /dev/null
+++ b/services/view_manager/test_server_view_delegate.cc
@@ -0,0 +1,30 @@
+// 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.
+
+#include "services/view_manager/test_server_view_delegate.h"
+
+namespace view_manager {
+
+TestServerViewDelegate::TestServerViewDelegate() {
+}
+
+TestServerViewDelegate::~TestServerViewDelegate() {
+}
+
+void TestServerViewDelegate::PrepareToDestroyView(ServerView* view) {
+}
+
+void TestServerViewDelegate::PrepareToChangeViewHierarchy(
+    ServerView* view,
+    ServerView* new_parent,
+    ServerView* old_parent) {
+}
+
+void TestServerViewDelegate::PrepareToChangeViewVisibility(ServerView* view) {
+}
+
+void TestServerViewDelegate::OnScheduleViewPaint(const ServerView* view) {
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/test_server_view_delegate.h b/services/view_manager/test_server_view_delegate.h
new file mode 100644
index 0000000..6f53497
--- /dev/null
+++ b/services/view_manager/test_server_view_delegate.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_TEST_SERVER_VIEW_DELEGATE_H_
+#define SERVICES_VIEW_MANAGER_TEST_SERVER_VIEW_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "services/view_manager/server_view_delegate.h"
+
+namespace view_manager {
+
+class TestServerViewDelegate : public ServerViewDelegate {
+ public:
+  TestServerViewDelegate();
+  ~TestServerViewDelegate() override;
+
+ private:
+  // ServerViewDelegate:
+  void PrepareToDestroyView(ServerView* view) override;
+  void PrepareToChangeViewHierarchy(ServerView* view,
+                                    ServerView* new_parent,
+                                    ServerView* old_parent) override;
+  void PrepareToChangeViewVisibility(ServerView* view) override;
+  void OnScheduleViewPaint(const ServerView* view) override;
+
+  DISALLOW_COPY_AND_ASSIGN(TestServerViewDelegate);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_TEST_SERVER_VIEW_DELEGATE_H_
diff --git a/services/view_manager/view_coordinate_conversions.cc b/services/view_manager/view_coordinate_conversions.cc
new file mode 100644
index 0000000..7ad19b5
--- /dev/null
+++ b/services/view_manager/view_coordinate_conversions.cc
@@ -0,0 +1,69 @@
+// 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.
+
+#include "services/view_manager/view_coordinate_conversions.h"
+
+#include "services/view_manager/server_view.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace view_manager {
+
+namespace {
+
+gfx::Vector2dF CalculateOffsetToAncestor(const ServerView* view,
+                                         const ServerView* ancestor) {
+  DCHECK(ancestor->Contains(view));
+  gfx::Vector2d result;
+  for (const ServerView* v = view; v != ancestor; v = v->parent())
+    result += v->bounds().OffsetFromOrigin();
+  return gfx::Vector2dF(result.x(), result.y());
+}
+
+}  // namespace
+
+gfx::Point ConvertPointBetweenViews(const ServerView* from,
+                                    const ServerView* to,
+                                    const gfx::Point& point) {
+  return gfx::ToFlooredPoint(
+      ConvertPointFBetweenViews(from, to, gfx::PointF(point.x(), point.y())));
+}
+
+gfx::PointF ConvertPointFBetweenViews(const ServerView* from,
+                                      const ServerView* to,
+                                      const gfx::PointF& point) {
+  DCHECK(from);
+  DCHECK(to);
+  if (from == to)
+    return point;
+
+  if (from->Contains(to)) {
+    const gfx::Vector2dF offset(CalculateOffsetToAncestor(to, from));
+    return point - offset;
+  }
+  DCHECK(to->Contains(from));
+  const gfx::Vector2dF offset(CalculateOffsetToAncestor(from, to));
+  return point + offset;
+}
+
+gfx::Rect ConvertRectBetweenViews(const ServerView* from,
+                                  const ServerView* to,
+                                  const gfx::Rect& rect) {
+  DCHECK(from);
+  DCHECK(to);
+  if (from == to)
+    return rect;
+
+  const gfx::Point top_left(ConvertPointBetweenViews(from, to, rect.origin()));
+  const gfx::Point bottom_right(gfx::ToCeiledPoint(ConvertPointFBetweenViews(
+      from, to, gfx::PointF(rect.right(), rect.bottom()))));
+  return gfx::Rect(top_left.x(), top_left.y(), bottom_right.x() - top_left.x(),
+                   bottom_right.y() - top_left.y());
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_coordinate_conversions.h b/services/view_manager/view_coordinate_conversions.h
new file mode 100644
index 0000000..9f62eec
--- /dev/null
+++ b/services/view_manager/view_coordinate_conversions.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_VIEW_COORDINATE_CONVERSIONS_H_
+#define SERVICES_VIEW_MANAGER_VIEW_COORDINATE_CONVERSIONS_H_
+
+namespace gfx {
+class Point;
+class PointF;
+class Rect;
+}
+
+namespace view_manager {
+
+class ServerView;
+
+// Converts |point| from the coordinates of |from| to the coordinates of |to|.
+// |from| and |to| must be an ancestors or descendants of each other.
+gfx::Point ConvertPointBetweenViews(const ServerView* from,
+                                    const ServerView* to,
+                                    const gfx::Point& point);
+gfx::PointF ConvertPointFBetweenViews(const ServerView* from,
+                                      const ServerView* to,
+                                      const gfx::PointF& point);
+
+// Converts |rect| from the coordinates of |from| to the coordinates of |to|.
+// |from| and |to| must be an ancestors or descendants of each other.
+gfx::Rect ConvertRectBetweenViews(const ServerView* from,
+                                  const ServerView* to,
+                                  const gfx::Rect& rect);
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_VIEW_COORDINATE_CONVERSIONS_H_
diff --git a/services/view_manager/view_coordinate_conversions_unittest.cc b/services/view_manager/view_coordinate_conversions_unittest.cc
new file mode 100644
index 0000000..a75df1b
--- /dev/null
+++ b/services/view_manager/view_coordinate_conversions_unittest.cc
@@ -0,0 +1,58 @@
+// 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.
+
+#include "services/view_manager/view_coordinate_conversions.h"
+
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/server_view_delegate.h"
+#include "services/view_manager/test_server_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace view_manager {
+
+using ViewCoordinateConversionsTest = testing::Test;
+
+TEST_F(ViewCoordinateConversionsTest, ConvertRectBetweenViews) {
+  TestServerViewDelegate d1, d2, d3;
+  ServerView v1(&d1, ViewId()), v2(&d2, ViewId()), v3(&d3, ViewId());
+  v1.SetBounds(gfx::Rect(1, 2, 100, 100));
+  v2.SetBounds(gfx::Rect(3, 4, 100, 100));
+  v3.SetBounds(gfx::Rect(5, 6, 100, 100));
+  v1.Add(&v2);
+  v2.Add(&v3);
+
+  EXPECT_EQ(gfx::Rect(2, 1, 8, 9),
+            ConvertRectBetweenViews(&v1, &v3, gfx::Rect(10, 11, 8, 9)));
+
+  EXPECT_EQ(gfx::Rect(18, 21, 8, 9),
+            ConvertRectBetweenViews(&v3, &v1, gfx::Rect(10, 11, 8, 9)));
+}
+
+TEST_F(ViewCoordinateConversionsTest, ConvertPointFBetweenViews) {
+  TestServerViewDelegate d1, d2, d3;
+  ServerView v1(&d1, ViewId()), v2(&d2, ViewId()), v3(&d3, ViewId());
+  v1.SetBounds(gfx::Rect(1, 2, 100, 100));
+  v2.SetBounds(gfx::Rect(3, 4, 100, 100));
+  v3.SetBounds(gfx::Rect(5, 6, 100, 100));
+  v1.Add(&v2);
+  v2.Add(&v3);
+
+  {
+    const gfx::PointF result(
+        ConvertPointFBetweenViews(&v1, &v3, gfx::PointF(10.5f, 11.9f)));
+    EXPECT_FLOAT_EQ(2.5f, result.x());
+    EXPECT_FLOAT_EQ(1.9f, result.y());
+  }
+
+  {
+    const gfx::PointF result(
+        ConvertPointFBetweenViews(&v3, &v1, gfx::PointF(10.2f, 11.4f)));
+    EXPECT_FLOAT_EQ(18.2f, result.x());
+    EXPECT_FLOAT_EQ(21.4f, result.y());
+  }
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_locator.cc b/services/view_manager/view_locator.cc
new file mode 100644
index 0000000..46fc29b
--- /dev/null
+++ b/services/view_manager/view_locator.cc
@@ -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.
+
+#include "services/view_manager/view_locator.h"
+
+#include "services/view_manager/server_view.h"
+
+namespace view_manager {
+
+const ServerView* FindDeepestVisibleView(const ServerView* view,
+                                         const gfx::Point& location) {
+  for (const ServerView* child : view->GetChildren()) {
+    if (!child->visible())
+      continue;
+
+    // TODO(sky): support transform.
+    const gfx::Point child_location(location.x() - child->bounds().x(),
+                                    location.y() - child->bounds().y());
+    if (child_location.x() >= 0 && child_location.y() >= 0 &&
+        child_location.x() < child->bounds().width() &&
+        child_location.y() < child->bounds().height()) {
+      return FindDeepestVisibleView(child, child_location);
+    }
+  }
+  return view;
+}
+
+ServerView* FindDeepestVisibleView(ServerView* view,
+                                   const gfx::Point& location) {
+  return const_cast<ServerView*>(
+      FindDeepestVisibleView(const_cast<const ServerView*>(view), location));
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_locator.h b/services/view_manager/view_locator.h
new file mode 100644
index 0000000..8937b75
--- /dev/null
+++ b/services/view_manager/view_locator.h
@@ -0,0 +1,24 @@
+// 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_VIEW_MANAGER_VIEW_LOCATOR_H_
+#define SERVICES_VIEW_MANAGER_VIEW_LOCATOR_H_
+
+namespace gfx {
+class Point;
+}
+
+namespace view_manager {
+
+class ServerView;
+
+// Finds the deepest visible view that contains the specified location.
+const ServerView* FindDeepestVisibleView(const ServerView* view,
+                                         const gfx::Point& location);
+ServerView* FindDeepestVisibleView(ServerView* view,
+                                   const gfx::Point& location);
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_VIEW_LOCATOR_H_
diff --git a/services/view_manager/view_manager_app.cc b/services/view_manager/view_manager_app.cc
new file mode 100644
index 0000000..d4965aa
--- /dev/null
+++ b/services/view_manager/view_manager_app.cc
@@ -0,0 +1,58 @@
+// 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.
+
+#include "services/view_manager/view_manager_app.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/view_manager/view_manager_root_connection.h"
+
+using mojo::ApplicationConnection;
+using mojo::ApplicationImpl;
+using mojo::InterfaceRequest;
+using mojo::ViewManagerService;
+using mojo::WindowManagerInternalClient;
+
+namespace view_manager {
+
+ViewManagerApp::ViewManagerApp() : app_impl_(nullptr) {}
+
+ViewManagerApp::~ViewManagerApp() {}
+
+void ViewManagerApp::Initialize(ApplicationImpl* app) {
+  app_impl_ = app;
+  tracing_.Initialize(app);
+  TRACE_EVENT0("view_manager", __func__);
+}
+
+bool ViewManagerApp::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  TRACE_EVENT0("view_manager", __func__);
+  // We keep a raw pointer in active_root_connections_ in order to be able to
+  // close the ViewManagerApp when all outstanding ViewManagerRootConnections
+  // are closed. ViewManagerRootConnection manages its own lifetime.
+  ViewManagerRootConnection* root_connection =
+      new ViewManagerRootConnection(app_impl_, this);
+  if (root_connection->Init(connection)) {
+    active_root_connections_.insert(root_connection);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+void ViewManagerApp::OnCloseViewManagerRootConnection(
+    ViewManagerRootConnection* view_manager_root_connection) {
+  active_root_connections_.erase(view_manager_root_connection);
+
+  if (active_root_connections_.size() == 0) {
+    ApplicationImpl::Terminate();
+  }
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_manager_app.h b/services/view_manager/view_manager_app.h
new file mode 100644
index 0000000..73e45e8
--- /dev/null
+++ b/services/view_manager/view_manager_app.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_VIEW_MANAGER_APP_H_
+#define SERVICES_VIEW_MANAGER_VIEW_MANAGER_APP_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "services/view_manager/view_manager_root_connection.h"
+
+namespace view_manager {
+
+class ViewManagerApp
+    : public mojo::ApplicationDelegate,
+      public ViewManagerRootConnection::ViewManagerRootConnectionObserver {
+ public:
+  ViewManagerApp();
+  ~ViewManagerApp() override;
+
+ private:
+  // ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // ViewManagerRootConnectionObserver:
+  void OnCloseViewManagerRootConnection(
+      ViewManagerRootConnection* view_manager_root_connection) override;
+
+  std::set<ViewManagerRootConnection*> active_root_connections_;
+  mojo::ApplicationImpl* app_impl_;
+  mojo::TracingImpl tracing_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerApp);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_VIEW_MANAGER_APP_H_
diff --git a/services/view_manager/view_manager_client_apptest.cc b/services/view_manager/view_manager_client_apptest.cc
new file mode 100644
index 0000000..0aaccf4
--- /dev/null
+++ b/services/view_manager/view_manager_client_apptest.cc
@@ -0,0 +1,653 @@
+// 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.
+
+#include "mojo/services/view_manager/cpp/view_manager.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_timeouts.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/services/geometry/cpp/geometry_util.h"
+#include "mojo/services/view_manager/cpp/lib/view_manager_client_impl.h"
+#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
+#include "mojo/services/view_manager/cpp/view_manager_context.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+
+namespace mojo {
+
+namespace {
+
+base::RunLoop* current_run_loop = nullptr;
+
+void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
+  CHECK(current_run_loop);
+  *timeout = true;
+  timeout_task.Run();
+}
+
+bool DoRunLoopWithTimeout() {
+  if (current_run_loop != nullptr)
+    return false;
+
+  bool timeout = false;
+  base::RunLoop run_loop;
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
+      TestTimeouts::action_timeout());
+
+  current_run_loop = &run_loop;
+  current_run_loop->Run();
+  current_run_loop = nullptr;
+  return !timeout;
+}
+
+void QuitRunLoop() {
+  current_run_loop->Quit();
+  current_run_loop = nullptr;
+}
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+  explicit BoundsChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~BoundsChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewBoundsChanged(View* view,
+                           const Rect& old_bounds,
+                           const Rect& new_bounds) override {
+    DCHECK_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+// Wait until the bounds of the supplied view change; returns false on timeout.
+bool WaitForBoundsToChange(View* view) {
+  BoundsChangeObserver observer(view);
+  return DoRunLoopWithTimeout();
+}
+
+// Spins a run loop until the tree beginning at |root| has |tree_size| views
+// (including |root|).
+class TreeSizeMatchesObserver : public ViewObserver {
+ public:
+  TreeSizeMatchesObserver(View* tree, size_t tree_size)
+      : tree_(tree), tree_size_(tree_size) {
+    tree_->AddObserver(this);
+  }
+  ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
+
+  bool IsTreeCorrectSize() { return CountViews(tree_) == tree_size_; }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnTreeChanged(const TreeChangeParams& params) override {
+    if (IsTreeCorrectSize())
+      QuitRunLoop();
+  }
+
+  size_t CountViews(const View* view) const {
+    size_t count = 1;
+    View::Children::const_iterator it = view->children().begin();
+    for (; it != view->children().end(); ++it)
+      count += CountViews(*it);
+    return count;
+  }
+
+  View* tree_;
+  size_t tree_size_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
+};
+
+// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
+bool WaitForTreeSizeToMatch(View* view, size_t tree_size) {
+  TreeSizeMatchesObserver observer(view, tree_size);
+  return observer.IsTreeCorrectSize() || DoRunLoopWithTimeout();
+}
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+  OrderChangeObserver(View* view) : view_(view) { view_->AddObserver(this); }
+  ~OrderChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewReordered(View* view,
+                       View* relative_view,
+                       OrderDirection direction) override {
+    DCHECK_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
+bool WaitForOrderChange(ViewManager* view_manager, View* view) {
+  OrderChangeObserver observer(view);
+  return DoRunLoopWithTimeout();
+}
+
+// Tracks a view's destruction. Query is_valid() for current state.
+class ViewTracker : public ViewObserver {
+ public:
+  explicit ViewTracker(View* view) : view_(view) { view_->AddObserver(this); }
+  ~ViewTracker() override {
+    if (view_)
+      view_->RemoveObserver(this);
+  }
+
+  bool is_valid() const { return !!view_; }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(view, view_);
+    view_ = nullptr;
+  }
+
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker);
+};
+
+}  // namespace
+
+// ViewManager -----------------------------------------------------------------
+
+// These tests model synchronization of two peer connections to the view manager
+// service, that are given access to some root view.
+
+class ViewManagerTest : public test::ApplicationTestBase,
+                        public ApplicationDelegate,
+                        public ViewManagerDelegate {
+ public:
+  ViewManagerTest()
+      : most_recent_view_manager_(nullptr), window_manager_(nullptr) {}
+
+  // Overridden from ApplicationDelegate:
+  void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  // ApplicationDelegate implementation.
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  ViewManager* window_manager() { return window_manager_; }
+
+  // Embeds another version of the test app @ view; returns nullptr on timeout.
+  ViewManager* Embed(ViewManager* view_manager, View* view) {
+    DCHECK_EQ(view_manager, view->view_manager());
+    most_recent_view_manager_ = nullptr;
+    view->Embed(application_impl()->url());
+    if (!DoRunLoopWithTimeout())
+      return nullptr;
+    ViewManager* vm = nullptr;
+    std::swap(vm, most_recent_view_manager_);
+    return vm;
+  }
+
+  ApplicationDelegate* GetApplicationDelegate() override { return this; }
+
+  // Overridden from ViewManagerDelegate:
+  void OnEmbed(View* root,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services) override {
+    most_recent_view_manager_ = root->view_manager();
+    QuitRunLoop();
+  }
+  void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ private:
+  // Overridden from testing::Test:
+  void SetUp() override {
+    ApplicationTestBase::SetUp();
+
+    view_manager_context_.reset(new ViewManagerContext(application_impl()));
+    view_manager_context_->Embed(application_impl()->url());
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+    std::swap(window_manager_, most_recent_view_manager_);
+  }
+
+  // Overridden from testing::Test:
+  void TearDown() override { ApplicationTestBase::TearDown(); }
+
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  scoped_ptr<ViewManagerContext> view_manager_context_;
+
+  // Used to receive the most recent view manager loaded by an embed action.
+  ViewManager* most_recent_view_manager_;
+  // The View Manager connection held by the window manager (app running at the
+  // root view).
+  ViewManager* window_manager_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+TEST_F(ViewManagerTest, RootView) {
+  ASSERT_NE(nullptr, window_manager());
+  EXPECT_NE(nullptr, window_manager()->GetRoot());
+  EXPECT_EQ("mojo:window_manager", window_manager()->GetEmbedderURL());
+}
+
+TEST_F(ViewManagerTest, Embed) {
+  View* view = window_manager()->CreateView();
+  ASSERT_NE(nullptr, view);
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+
+  View* view_in_embedded = embedded->GetRoot();
+  ASSERT_NE(nullptr, view_in_embedded);
+  EXPECT_EQ(view->id(), view_in_embedded->id());
+  EXPECT_EQ(nullptr, view_in_embedded->parent());
+  EXPECT_TRUE(view_in_embedded->children().empty());
+}
+
+// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
+// N11.
+TEST_F(ViewManagerTest, EmbeddedDoesntSeeChild) {
+  View* view = window_manager()->CreateView();
+  ASSERT_NE(nullptr, view);
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  View* nested = window_manager()->CreateView();
+  ASSERT_NE(nullptr, nested);
+  nested->SetVisible(true);
+  view->AddChild(nested);
+
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+  View* view_in_embedded = embedded->GetRoot();
+  EXPECT_EQ(view->id(), view_in_embedded->id());
+  EXPECT_EQ(nullptr, view_in_embedded->parent());
+  EXPECT_TRUE(view_in_embedded->children().empty());
+}
+
+// TODO(beng): write a replacement test for the one that once existed here:
+// This test validates the following scenario:
+// -  a view originating from one connection
+// -  a view originating from a second connection
+// +  the connection originating the view is destroyed
+// -> the view should still exist (since the second connection is live) but
+//    should be disconnected from any views.
+// http://crbug.com/396300
+//
+// TODO(beng): The new test should validate the scenario as described above
+//             except that the second connection still has a valid tree.
+
+// Verifies that bounds changes applied to a view hierarchy in one connection
+// are reflected to another.
+TEST_F(ViewManagerTest, SetBounds) {
+  View* view = window_manager()->CreateView();
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+
+  View* view_in_embedded = embedded->GetViewById(view->id());
+  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+
+  Rect rect;
+  rect.width = rect.height = 100;
+  view->SetBounds(rect);
+  ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
+  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that bounds changes applied to a view owned by a different
+// connection are refused.
+TEST_F(ViewManagerTest, SetBoundsSecurity) {
+  View* view = window_manager()->CreateView();
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+
+  View* view_in_embedded = embedded->GetViewById(view->id());
+  Rect rect;
+  rect.width = 800;
+  rect.height = 600;
+  view->SetBounds(rect);
+  ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
+
+  rect.width = 1024;
+  rect.height = 768;
+  view_in_embedded->SetBounds(rect);
+  // Bounds change should have been rejected.
+  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that a view can only be destroyed by the connection that created it.
+TEST_F(ViewManagerTest, DestroySecurity) {
+  View* view = window_manager()->CreateView();
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+
+  View* view_in_embedded = embedded->GetViewById(view->id());
+
+  ViewTracker tracker2(view_in_embedded);
+  view_in_embedded->Destroy();
+  // View should not have been destroyed.
+  EXPECT_TRUE(tracker2.is_valid());
+
+  ViewTracker tracker1(view);
+  view->Destroy();
+  EXPECT_FALSE(tracker1.is_valid());
+}
+
+TEST_F(ViewManagerTest, MultiRoots) {
+  View* view1 = window_manager()->CreateView();
+  view1->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view1);
+  View* view2 = window_manager()->CreateView();
+  view2->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view2);
+  ViewManager* embedded1 = Embed(window_manager(), view1);
+  ASSERT_NE(nullptr, embedded1);
+  ViewManager* embedded2 = Embed(window_manager(), view2);
+  ASSERT_NE(nullptr, embedded2);
+  EXPECT_NE(embedded1, embedded2);
+}
+
+TEST_F(ViewManagerTest, EmbeddingIdentity) {
+  View* view = window_manager()->CreateView();
+  view->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  ASSERT_NE(nullptr, embedded);
+  EXPECT_EQ(application_impl()->url(), embedded->GetEmbedderURL());
+}
+
+// TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
+// Debug and re-enable this.
+TEST_F(ViewManagerTest, DISABLED_Reorder) {
+  View* view1 = window_manager()->CreateView();
+  view1->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view1);
+
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_NE(nullptr, embedded);
+
+  View* view11 = embedded->CreateView();
+  view11->SetVisible(true);
+  embedded->GetRoot()->AddChild(view11);
+  View* view12 = embedded->CreateView();
+  view12->SetVisible(true);
+  embedded->GetRoot()->AddChild(view12);
+
+  View* root_in_embedded = embedded->GetRoot();
+
+  {
+    ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded, 3u));
+    view11->MoveToFront();
+    ASSERT_TRUE(WaitForOrderChange(embedded, root_in_embedded));
+
+    EXPECT_EQ(root_in_embedded->children().front(),
+              embedded->GetViewById(view12->id()));
+    EXPECT_EQ(root_in_embedded->children().back(),
+              embedded->GetViewById(view11->id()));
+  }
+
+  {
+    view11->MoveToBack();
+    ASSERT_TRUE(WaitForOrderChange(embedded,
+                                   embedded->GetViewById(view11->id())));
+
+    EXPECT_EQ(root_in_embedded->children().front(),
+              embedded->GetViewById(view11->id()));
+    EXPECT_EQ(root_in_embedded->children().back(),
+              embedded->GetViewById(view12->id()));
+  }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+  explicit VisibilityChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewVisibilityChanged(View* view) override {
+    EXPECT_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewManagerTest, Visible) {
+  View* view1 = window_manager()->CreateView();
+  view1->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view1);
+
+  // Embed another app and verify initial state.
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_NE(nullptr, embedded);
+  ASSERT_NE(nullptr, embedded->GetRoot());
+  View* embedded_root = embedded->GetRoot();
+  EXPECT_TRUE(embedded_root->visible());
+  EXPECT_TRUE(embedded_root->IsDrawn());
+
+  // Change the visible state from the first connection and verify its mirrored
+  // correctly to the embedded app.
+  {
+    VisibilityChangeObserver observer(embedded_root);
+    view1->SetVisible(false);
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+  }
+
+  EXPECT_FALSE(view1->visible());
+  EXPECT_FALSE(view1->IsDrawn());
+
+  EXPECT_FALSE(embedded_root->visible());
+  EXPECT_FALSE(embedded_root->IsDrawn());
+
+  // Make the node visible again.
+  {
+    VisibilityChangeObserver observer(embedded_root);
+    view1->SetVisible(true);
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+  }
+
+  EXPECT_TRUE(view1->visible());
+  EXPECT_TRUE(view1->IsDrawn());
+
+  EXPECT_TRUE(embedded_root->visible());
+  EXPECT_TRUE(embedded_root->IsDrawn());
+}
+
+namespace {
+
+class DrawnChangeObserver : public ViewObserver {
+ public:
+  explicit DrawnChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  ~DrawnChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnViewDrawnChanged(View* view) override {
+    EXPECT_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewManagerTest, Drawn) {
+  View* view1 = window_manager()->CreateView();
+  view1->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view1);
+
+  // Embed another app and verify initial state.
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_NE(nullptr, embedded);
+  ASSERT_NE(nullptr, embedded->GetRoot());
+  View* embedded_root = embedded->GetRoot();
+  EXPECT_TRUE(embedded_root->visible());
+  EXPECT_TRUE(embedded_root->IsDrawn());
+
+  // Change the visibility of the root, this should propagate a drawn state
+  // change to |embedded|.
+  {
+    DrawnChangeObserver observer(embedded_root);
+    window_manager()->GetRoot()->SetVisible(false);
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+  }
+
+  EXPECT_TRUE(view1->visible());
+  EXPECT_FALSE(view1->IsDrawn());
+
+  EXPECT_TRUE(embedded_root->visible());
+  EXPECT_FALSE(embedded_root->IsDrawn());
+}
+
+// TODO(beng): tests for view event dispatcher.
+// - verify that we see events for all views.
+
+namespace {
+
+class FocusChangeObserver : public ViewObserver {
+ public:
+  explicit FocusChangeObserver(View* view)
+      : view_(view), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
+    view_->AddObserver(this);
+  }
+  ~FocusChangeObserver() override { view_->RemoveObserver(this); }
+
+  View* last_gained_focus() { return last_gained_focus_; }
+
+  View* last_lost_focus() { return last_lost_focus_; }
+
+ private:
+  // Overridden from ViewObserver.
+  void OnViewFocusChanged(View* gained_focus, View* lost_focus) override {
+    last_gained_focus_ = gained_focus;
+    last_lost_focus_ = lost_focus;
+    QuitRunLoop();
+  }
+
+  View* view_;
+  View* last_gained_focus_;
+  View* last_lost_focus_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewManagerTest, Focus) {
+  View* view1 = window_manager()->CreateView();
+  view1->SetVisible(true);
+  window_manager()->GetRoot()->AddChild(view1);
+
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_NE(nullptr, embedded);
+  View* view11 = embedded->CreateView();
+  view11->SetVisible(true);
+  embedded->GetRoot()->AddChild(view11);
+
+  // TODO(alhaad): Figure out why switching focus between views from different
+  // connections is causing the tests to crash and add tests for that.
+  {
+    View* embedded_root = embedded->GetRoot();
+    FocusChangeObserver observer(embedded_root);
+    embedded_root->SetFocus();
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+    ASSERT_NE(nullptr, observer.last_gained_focus());
+    EXPECT_EQ(embedded_root->id(), observer.last_gained_focus()->id());
+  }
+  {
+    FocusChangeObserver observer(view11);
+    view11->SetFocus();
+    ASSERT_TRUE(DoRunLoopWithTimeout());
+    ASSERT_NE(nullptr, observer.last_gained_focus());
+    ASSERT_NE(nullptr, observer.last_lost_focus());
+    EXPECT_EQ(view11->id(), observer.last_gained_focus()->id());
+    EXPECT_EQ(embedded->GetRoot()->id(), observer.last_lost_focus()->id());
+  }
+}
+
+class ViewRemovedFromParentObserver : public ViewObserver {
+ public:
+  explicit ViewRemovedFromParentObserver(View* view)
+      : view_(view), was_removed_(false) {
+    view_->AddObserver(this);
+  }
+  ~ViewRemovedFromParentObserver() override { view_->RemoveObserver(this); }
+
+  bool was_removed() const { return was_removed_; }
+
+ private:
+  // Overridden from ViewObserver:
+  void OnTreeChanged(const TreeChangeParams& params) override {
+    if (params.target == view_ && !params.new_parent)
+      was_removed_ = true;
+  }
+
+  View* view_;
+  bool was_removed_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver);
+};
+
+TEST_F(ViewManagerTest, EmbedRemovesChildren) {
+  View* view1 = window_manager()->CreateView();
+  View* view2 = window_manager()->CreateView();
+  window_manager()->GetRoot()->AddChild(view1);
+  view1->AddChild(view2);
+
+  ViewRemovedFromParentObserver observer(view2);
+  view1->Embed(application_impl()->url());
+  EXPECT_TRUE(observer.was_removed());
+  EXPECT_EQ(nullptr, view2->parent());
+  EXPECT_TRUE(view1->children().empty());
+
+  // Run the message loop so the Embed() call above completes. Without this
+  // we may end up reconnecting to the test and rerunning the test, which is
+  // problematic since the other services don't shut down.
+  ASSERT_TRUE(DoRunLoopWithTimeout());
+}
+
+}  // namespace mojo
diff --git a/services/view_manager/view_manager_root_connection.cc b/services/view_manager/view_manager_root_connection.cc
new file mode 100644
index 0000000..3f19eaf
--- /dev/null
+++ b/services/view_manager/view_manager_root_connection.cc
@@ -0,0 +1,139 @@
+// 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/view_manager/view_manager_root_connection.h"
+
+#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "services/view_manager/client_connection.h"
+#include "services/view_manager/connection_manager.h"
+#include "services/view_manager/display_manager.h"
+#include "services/view_manager/view_manager_service_impl.h"
+
+using mojo::ApplicationConnection;
+using mojo::InterfaceRequest;
+using mojo::ViewManagerService;
+using mojo::WindowManagerInternalClient;
+
+namespace view_manager {
+
+ViewManagerRootConnection::ViewManagerRootConnection(
+    mojo::ApplicationImpl* application_impl,
+    ViewManagerRootConnectionObserver* observer)
+    : app_impl_(application_impl), observer_(observer) {}
+
+ViewManagerRootConnection::~ViewManagerRootConnection() {
+  observer_->OnCloseViewManagerRootConnection(this);
+}
+
+bool ViewManagerRootConnection::Init(mojo::ApplicationConnection* connection) {
+  TRACE_EVENT0("view_manager", __func__);
+  if (connection_manager_.get()) {
+    NOTREACHED() << "Only one incoming connection is allowed.";
+  }
+  // |connection| originates from the WindowManager. Let it connect directly
+  // to the ViewManager and WindowManagerInternalClient.
+  connection->AddService<ViewManagerService>(this);
+  connection->AddService<WindowManagerInternalClient>(this);
+  connection->ConnectToService(&wm_internal_);
+  // If no ServiceProvider has been sent, refuse the connection.
+  if (!wm_internal_) {
+    delete this;
+    return false;
+  }
+  wm_internal_.set_connection_error_handler(
+      base::Bind(&ViewManagerRootConnection::OnLostConnectionToWindowManager,
+                 base::Unretained(this)));
+
+  scoped_ptr<DefaultDisplayManager> display_manager(new DefaultDisplayManager(
+      app_impl_, connection,
+      base::Bind(&ViewManagerRootConnection::OnLostConnectionToWindowManager,
+                 base::Unretained(this))));
+  connection_manager_.reset(
+      new ConnectionManager(this, display_manager.Pass(), wm_internal_.get()));
+  return true;
+}
+
+void ViewManagerRootConnection::OnLostConnectionToWindowManager() {
+  delete this;
+}
+
+ClientConnection*
+ViewManagerRootConnection::CreateClientConnectionForEmbedAtView(
+    ConnectionManager* connection_manager,
+    mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+    mojo::ConnectionSpecificId creator_id,
+    const std::string& creator_url,
+    const std::string& url,
+    const ViewId& root_id) {
+  TRACE_EVENT0("view_manager", __func__);
+  mojo::ViewManagerClientPtr client;
+  app_impl_->ConnectToService(url, &client);
+
+  scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+      connection_manager, creator_id, creator_url, url, root_id));
+  return new DefaultClientConnection(service.Pass(), connection_manager,
+                                     service_request.Pass(), client.Pass());
+}
+
+ClientConnection*
+ViewManagerRootConnection::CreateClientConnectionForEmbedAtView(
+    ConnectionManager* connection_manager,
+    mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+    mojo::ConnectionSpecificId creator_id,
+    const std::string& creator_url,
+    const ViewId& root_id,
+    mojo::ViewManagerClientPtr view_manager_client) {
+  TRACE_EVENT0("view_manager", __func__);
+  scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+      connection_manager, creator_id, creator_url, std::string(), root_id));
+  return new DefaultClientConnection(service.Pass(), connection_manager,
+                                     service_request.Pass(),
+                                     view_manager_client.Pass());
+}
+
+void ViewManagerRootConnection::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<ViewManagerService> request) {
+  TRACE_EVENT0("view_manager", __func__);
+  if (connection_manager_->has_window_manager_client_connection()) {
+    VLOG(1) << "ViewManager interface requested more than once.";
+    return;
+  }
+
+  scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+      connection_manager_.get(), kInvalidConnectionId, std::string(),
+      std::string("mojo:window_manager"), RootViewId()));
+  mojo::ViewManagerClientPtr client;
+  wm_internal_client_request_ = GetProxy(&client);
+  scoped_ptr<ClientConnection> client_connection(
+      new DefaultClientConnection(service.Pass(), connection_manager_.get(),
+                                  request.Pass(), client.Pass()));
+  connection_manager_->SetWindowManagerClientConnection(
+      client_connection.Pass());
+}
+
+void ViewManagerRootConnection::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<WindowManagerInternalClient> request) {
+  TRACE_EVENT0("view_manager", __func__);
+  if (wm_internal_client_binding_.get()) {
+    VLOG(1) << "WindowManagerInternalClient requested more than once.";
+    return;
+  }
+
+  // ConfigureIncomingConnection() must have been called before getting here.
+  DCHECK(connection_manager_.get());
+  wm_internal_client_binding_.reset(
+      new mojo::Binding<WindowManagerInternalClient>(connection_manager_.get(),
+                                                     request.Pass()));
+  wm_internal_client_binding_->set_connection_error_handler(
+      base::Bind(&ViewManagerRootConnection::OnLostConnectionToWindowManager,
+                 base::Unretained(this)));
+  wm_internal_->SetViewManagerClient(
+      wm_internal_client_request_.PassMessagePipe());
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_manager_root_connection.h b/services/view_manager/view_manager_root_connection.h
new file mode 100644
index 0000000..2d8348e
--- /dev/null
+++ b/services/view_manager/view_manager_root_connection.h
@@ -0,0 +1,91 @@
+// 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_VIEW_MANAGER_VIEW_MANAGER_ROOT_CONNECTION_H_
+#define SERVICES_VIEW_MANAGER_VIEW_MANAGER_ROOT_CONNECTION_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/view_manager/connection_manager.h"
+#include "services/view_manager/connection_manager_delegate.h"
+
+namespace mojo {
+class ApplicationImpl;
+}
+
+namespace view_manager {
+
+// An instance of ViewManagerRootConnection represents one inbound connection
+// from a window manager to the view manager app. It handles the creation of
+// services for this connection, as well as any outbound connection necessary.
+class ViewManagerRootConnection
+    : public ConnectionManagerDelegate,
+      public mojo::InterfaceFactory<mojo::ViewManagerService>,
+      public mojo::InterfaceFactory<mojo::WindowManagerInternalClient> {
+ public:
+  class ViewManagerRootConnectionObserver {
+   public:
+    // Called when a ViewManagerRootConnection is closing.
+    virtual void OnCloseViewManagerRootConnection(
+        ViewManagerRootConnection* view_manager_root_connection) = 0;
+
+   protected:
+    virtual ~ViewManagerRootConnectionObserver() {}
+  };
+
+  ViewManagerRootConnection(mojo::ApplicationImpl* application_impl,
+                            ViewManagerRootConnectionObserver* observer);
+  ~ViewManagerRootConnection() override;
+
+  // Returns true if the view manager root connection is established, false
+  // otherwise. In that case, the object should probably be destroyed.
+  bool Init(mojo::ApplicationConnection* connection);
+
+ private:
+  // ConnectionManagerDelegate:
+  void OnLostConnectionToWindowManager() override;
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const std::string& url,
+      const ViewId& root_id) override;
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr view_manager_client) override;
+
+  // mojo::InterfaceFactory<mojo::ViewManagerService>:
+  void Create(
+      mojo::ApplicationConnection* connection,
+      mojo::InterfaceRequest<mojo::ViewManagerService> request) override;
+
+  // mojo::InterfaceFactory<mojo::WindowManagerInternalClient>:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::WindowManagerInternalClient> request)
+      override;
+
+  mojo::ApplicationImpl* app_impl_;
+  ViewManagerRootConnectionObserver* observer_;
+  scoped_ptr<mojo::Binding<mojo::WindowManagerInternalClient>>
+      wm_internal_client_binding_;
+  mojo::InterfaceRequest<mojo::ViewManagerClient> wm_internal_client_request_;
+  mojo::WindowManagerInternalPtr wm_internal_;
+  scoped_ptr<ConnectionManager> connection_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerRootConnection);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_VIEW_MANAGER_ROOT_CONNECTION_H_
diff --git a/services/view_manager/view_manager_service_apptest.cc b/services/view_manager/view_manager_service_apptest.cc
new file mode 100644
index 0000000..7ba07ad
--- /dev/null
+++ b/services/view_manager/view_manager_service_apptest.cc
@@ -0,0 +1,1496 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/view_manager/ids.h"
+#include "services/view_manager/test_change_tracker.h"
+
+using mojo::ApplicationConnection;
+using mojo::ApplicationDelegate;
+using mojo::Array;
+using mojo::Callback;
+using mojo::ConnectionSpecificId;
+using mojo::ErrorCode;
+using mojo::EventPtr;
+using mojo::Id;
+using mojo::InterfaceRequest;
+using mojo::OrderDirection;
+using mojo::RectPtr;
+using mojo::ServiceProvider;
+using mojo::ServiceProviderPtr;
+using mojo::String;
+using mojo::ViewDataPtr;
+using mojo::ViewManagerClient;
+using mojo::ViewManagerService;
+using mojo::ViewportMetricsPtr;
+
+namespace view_manager {
+
+// Creates an id used for transport from the specified parameters.
+Id BuildViewId(ConnectionSpecificId connection_id,
+               ConnectionSpecificId view_id) {
+  return (connection_id << 16) | view_id;
+}
+
+// Callback function from ViewManagerService functions. ------------------------
+
+void BoolResultCallback(base::RunLoop* run_loop,
+                        bool* result_cache,
+                        bool result) {
+  *result_cache = result;
+  run_loop->Quit();
+}
+
+void ErrorCodeResultCallback(base::RunLoop* run_loop,
+                             ErrorCode* result_cache,
+                             ErrorCode result) {
+  *result_cache = result;
+  run_loop->Quit();
+}
+
+void ViewTreeResultCallback(base::RunLoop* run_loop,
+                            std::vector<TestView>* views,
+                            Array<ViewDataPtr> results) {
+  ViewDatasToTestViews(results, views);
+  run_loop->Quit();
+}
+
+// -----------------------------------------------------------------------------
+
+// The following functions call through to the supplied ViewManagerService. They
+// block until call completes and return the result.
+bool CreateView(ViewManagerService* vm, Id view_id) {
+  ErrorCode result = ErrorCode::NONE;
+  base::RunLoop run_loop;
+  vm->CreateView(view_id,
+                 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result == ErrorCode::NONE;
+}
+
+bool EmbedUrl(ViewManagerService* vm, const String& url, Id root_id) {
+  bool result = false;
+  base::RunLoop run_loop;
+  {
+    vm->EmbedUrl(url, root_id, nullptr, nullptr,
+                 base::Bind(&BoolResultCallback, &run_loop, &result));
+  }
+  run_loop.Run();
+  return result;
+}
+
+bool Embed(ViewManagerService* vm,
+           Id root_id,
+           mojo::ViewManagerClientPtr client) {
+  bool result = false;
+  base::RunLoop run_loop;
+  {
+    vm->Embed(root_id, client.Pass(),
+              base::Bind(&BoolResultCallback, &run_loop, &result));
+  }
+  run_loop.Run();
+  return result;
+}
+
+ErrorCode CreateViewWithErrorCode(ViewManagerService* vm, Id view_id) {
+  ErrorCode result = ErrorCode::NONE;
+  base::RunLoop run_loop;
+  vm->CreateView(view_id,
+                 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool AddView(ViewManagerService* vm, Id parent, Id child) {
+  bool result = false;
+  base::RunLoop run_loop;
+  vm->AddView(parent, child,
+              base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool RemoveViewFromParent(ViewManagerService* vm, Id view_id) {
+  bool result = false;
+  base::RunLoop run_loop;
+  vm->RemoveViewFromParent(view_id,
+                           base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool ReorderView(ViewManagerService* vm,
+                 Id view_id,
+                 Id relative_view_id,
+                 OrderDirection direction) {
+  bool result = false;
+  base::RunLoop run_loop;
+  vm->ReorderView(view_id, relative_view_id, direction,
+                  base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+void GetViewTree(ViewManagerService* vm,
+                 Id view_id,
+                 std::vector<TestView>* views) {
+  base::RunLoop run_loop;
+  vm->GetViewTree(view_id,
+                  base::Bind(&ViewTreeResultCallback, &run_loop, views));
+  run_loop.Run();
+}
+
+bool DeleteView(ViewManagerService* vm, Id view_id) {
+  base::RunLoop run_loop;
+  bool result = false;
+  vm->DeleteView(view_id, base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool SetViewBounds(ViewManagerService* vm,
+                   Id view_id,
+                   int x,
+                   int y,
+                   int w,
+                   int h) {
+  base::RunLoop run_loop;
+  bool result = false;
+  RectPtr rect(mojo::Rect::New());
+  rect->x = x;
+  rect->y = y;
+  rect->width = w;
+  rect->height = h;
+  vm->SetViewBounds(view_id, rect.Pass(),
+                    base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool SetViewVisibility(ViewManagerService* vm, Id view_id, bool visible) {
+  base::RunLoop run_loop;
+  bool result = false;
+  vm->SetViewVisibility(view_id, visible,
+                        base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+bool SetViewProperty(ViewManagerService* vm,
+                     Id view_id,
+                     const std::string& name,
+                     const std::vector<uint8_t>* data) {
+  base::RunLoop run_loop;
+  bool result = false;
+  Array<uint8_t> mojo_data;
+  if (data)
+    mojo_data = Array<uint8_t>::From(*data);
+  vm->SetViewProperty(view_id, name, mojo_data.Pass(),
+                      base::Bind(&BoolResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
+// Utility functions -----------------------------------------------------------
+
+// Waits for all messages to be received by |vm|. This is done by attempting to
+// create a bogus view. When we get the response we know all messages have been
+// processed.
+bool WaitForAllMessages(ViewManagerService* vm) {
+  ErrorCode result = ErrorCode::NONE;
+  base::RunLoop run_loop;
+  vm->CreateView(ViewIdToTransportId(InvalidViewId()),
+                 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
+  run_loop.Run();
+  return result != ErrorCode::NONE;
+}
+
+bool HasClonedView(const std::vector<TestView>& views) {
+  for (size_t i = 0; i < views.size(); ++i)
+    if (views[i].view_id == ViewIdToTransportId(ClonedViewId()))
+      return true;
+  return false;
+}
+
+// -----------------------------------------------------------------------------
+
+// A ViewManagerClient implementation that logs all changes to a tracker.
+class ViewManagerClientImpl : public mojo::ViewManagerClient,
+                              public TestChangeTracker::Delegate {
+ public:
+  ViewManagerClientImpl() : binding_(this) { tracker_.set_delegate(this); }
+
+  void Bind(mojo::InterfaceRequest<mojo::ViewManagerClient> request) {
+    binding_.Bind(request.Pass());
+  }
+
+  mojo::ViewManagerService* service() { return service_.get(); }
+  TestChangeTracker* tracker() { return &tracker_; }
+
+  // Runs a nested MessageLoop until |count| changes (calls to
+  // ViewManagerClient functions) have been received.
+  void WaitForChangeCount(size_t count) {
+    if (count == tracker_.changes()->size())
+      return;
+
+    ASSERT_TRUE(wait_state_.get() == nullptr);
+    wait_state_.reset(new WaitState);
+    wait_state_->change_count = count;
+    wait_state_->run_loop.Run();
+    wait_state_.reset();
+  }
+
+  // Runs a nested MessageLoop until OnEmbed() has been encountered.
+  void WaitForOnEmbed() {
+    if (service_)
+      return;
+    embed_run_loop_.reset(new base::RunLoop);
+    embed_run_loop_->Run();
+    embed_run_loop_.reset();
+  }
+
+  bool WaitForIncomingMethodCall() {
+    return binding_.WaitForIncomingMethodCall();
+  }
+
+ private:
+  // Used when running a nested MessageLoop.
+  struct WaitState {
+    WaitState() : change_count(0) {}
+
+    // Number of changes waiting for.
+    size_t change_count;
+    base::RunLoop run_loop;
+  };
+
+  // TestChangeTracker::Delegate:
+  void OnChangeAdded() override {
+    if (wait_state_.get() &&
+        wait_state_->change_count == tracker_.changes()->size()) {
+      wait_state_->run_loop.Quit();
+    }
+  }
+
+  // ViewManagerClient:
+  void OnEmbed(ConnectionSpecificId connection_id,
+               const String& creator_url,
+               ViewDataPtr root,
+               mojo::ViewManagerServicePtr view_manager_service,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services,
+               mojo::ScopedMessagePipeHandle window_manager_pipe) override {
+    service_ = view_manager_service.Pass();
+    tracker()->OnEmbed(connection_id, creator_url, root.Pass());
+    if (embed_run_loop_)
+      embed_run_loop_->Quit();
+  }
+  void OnEmbeddedAppDisconnected(Id view_id) override {
+    tracker()->OnEmbeddedAppDisconnected(view_id);
+  }
+  void OnViewBoundsChanged(Id view_id,
+                           RectPtr old_bounds,
+                           RectPtr new_bounds) override {
+    tracker()->OnViewBoundsChanged(view_id, old_bounds.Pass(),
+                                   new_bounds.Pass());
+  }
+  void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
+                                    ViewportMetricsPtr new_metrics) override {
+    tracker()->OnViewViewportMetricsChanged(old_metrics.Pass(),
+                                            new_metrics.Pass());
+  }
+  void OnViewHierarchyChanged(Id view,
+                              Id new_parent,
+                              Id old_parent,
+                              Array<ViewDataPtr> views) override {
+    tracker()->OnViewHierarchyChanged(view, new_parent, old_parent,
+                                      views.Pass());
+  }
+  void OnViewReordered(Id view_id,
+                       Id relative_view_id,
+                       OrderDirection direction) override {
+    tracker()->OnViewReordered(view_id, relative_view_id, direction);
+  }
+  void OnViewDeleted(Id view) override { tracker()->OnViewDeleted(view); }
+  void OnViewVisibilityChanged(uint32_t view, bool visible) override {
+    tracker()->OnViewVisibilityChanged(view, visible);
+  }
+  void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
+    tracker()->OnViewDrawnStateChanged(view, drawn);
+  }
+  void OnViewInputEvent(Id view_id,
+                        EventPtr event,
+                        const Callback<void()>& callback) override {
+    tracker()->OnViewInputEvent(view_id, event.Pass());
+    callback.Run();
+  }
+  void OnViewSharedPropertyChanged(uint32_t view,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override {
+    tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
+  }
+  void OnPerformAction(uint32_t view,
+                       const String& name,
+                       const Callback<void(bool)>& callback) override {}
+
+  TestChangeTracker tracker_;
+
+  mojo::ViewManagerServicePtr service_;
+
+  // If non-null we're waiting for OnEmbed() using this RunLoop.
+  scoped_ptr<base::RunLoop> embed_run_loop_;
+
+  // If non-null we're waiting for a certain number of change notifications to
+  // be encountered.
+  scoped_ptr<WaitState> wait_state_;
+
+  mojo::Binding<ViewManagerClient> binding_;
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+// -----------------------------------------------------------------------------
+
+// InterfaceFactory for vending ViewManagerClientImpls.
+class ViewManagerClientFactory
+    : public mojo::InterfaceFactory<ViewManagerClient> {
+ public:
+  ViewManagerClientFactory() {}
+  ~ViewManagerClientFactory() override {}
+
+  // Runs a nested MessageLoop until a new instance has been created.
+  scoped_ptr<ViewManagerClientImpl> WaitForInstance() {
+    if (!client_impl_.get()) {
+      DCHECK(!run_loop_.get());
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+      run_loop_.reset();
+    }
+    return client_impl_.Pass();
+  }
+
+ private:
+  // InterfaceFactory<ViewManagerClient>:
+  void Create(ApplicationConnection* connection,
+              InterfaceRequest<ViewManagerClient> request) override {
+    client_impl_.reset(new ViewManagerClientImpl);
+    client_impl_->Bind(request.Pass());
+    if (run_loop_.get())
+      run_loop_->Quit();
+  }
+
+  scoped_ptr<ViewManagerClientImpl> client_impl_;
+  scoped_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerClientFactory);
+};
+
+class ViewManagerServiceAppTest
+    : public mojo::test::ApplicationTestBase,
+      public ApplicationDelegate,
+      public mojo::InterfaceFactory<mojo::WindowManagerInternal>,
+      public mojo::WindowManagerInternal {
+ public:
+  ViewManagerServiceAppTest() : wm_internal_binding_(this) {}
+  ~ViewManagerServiceAppTest() override {}
+
+ protected:
+  // Returns the changes from the various connections.
+  std::vector<Change>* changes1() { return vm_client1_.tracker()->changes(); }
+  std::vector<Change>* changes2() { return vm_client2_->tracker()->changes(); }
+  std::vector<Change>* changes3() { return vm_client3_->tracker()->changes(); }
+
+  // Various connections. |vm1()|, being the first connection, has special
+  // permissions (it's treated as the window manager).
+  ViewManagerService* vm1() { return vm1_.get(); }
+  ViewManagerService* vm2() { return vm_client2_->service(); }
+  ViewManagerService* vm3() { return vm_client3_->service(); }
+
+  void EstablishSecondConnectionWithRoot(Id root_id) {
+    ASSERT_TRUE(vm_client2_.get() == nullptr);
+    vm_client2_ = EstablishConnectionViaEmbed(vm1(), root_id);
+    ASSERT_TRUE(vm_client2_.get() != nullptr);
+  }
+
+  void EstablishSecondConnection(bool create_initial_view) {
+    if (create_initial_view)
+      ASSERT_TRUE(CreateView(vm1_.get(), BuildViewId(1, 1)));
+    ASSERT_NO_FATAL_FAILURE(
+        EstablishSecondConnectionWithRoot(BuildViewId(1, 1)));
+
+    if (create_initial_view)
+      EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(*changes2()));
+  }
+
+  void EstablishThirdConnection(ViewManagerService* owner, Id root_id) {
+    ASSERT_TRUE(vm_client3_.get() == nullptr);
+    vm_client3_ = EstablishConnectionViaEmbed(owner, root_id);
+    ASSERT_TRUE(vm_client3_.get() != nullptr);
+  }
+
+  // Establishes a new connection by way of Embed() on the specified
+  // ViewManagerService.
+  scoped_ptr<ViewManagerClientImpl> EstablishConnectionViaEmbed(
+      ViewManagerService* owner,
+      Id root_id) {
+    if (!EmbedUrl(owner, application_impl()->url(), root_id)) {
+      ADD_FAILURE() << "Embed() failed";
+      return nullptr;
+    }
+    scoped_ptr<ViewManagerClientImpl> client =
+        client_factory_.WaitForInstance();
+    if (!client.get()) {
+      ADD_FAILURE() << "WaitForInstance failed";
+      return nullptr;
+    }
+    client->WaitForOnEmbed();
+
+    const std::string expected_creator =
+        owner == vm1() ? "mojo:window_manager" : application_impl()->url();
+    EXPECT_EQ("OnEmbed creator=" + expected_creator,
+              SingleChangeToDescription(*client->tracker()->changes()));
+    return client;
+  }
+
+  // ApplicationTestBase:
+  ApplicationDelegate* GetApplicationDelegate() override { return this; }
+  void SetUp() override {
+    ApplicationTestBase::SetUp();
+    ApplicationConnection* vm_connection =
+        application_impl()->ConnectToApplication("mojo:view_manager");
+    vm_connection->AddService(this);
+    vm_connection->ConnectToService(&vm1_);
+    vm_connection->ConnectToService(&wm_internal_client_);
+    // Spin a run loop until the view manager service sends us the
+    // ViewManagerClient pipe to use for the "window manager" connection.
+    view_manager_setup_run_loop_.reset(new base::RunLoop);
+    view_manager_setup_run_loop_->Run();
+    view_manager_setup_run_loop_ = nullptr;
+    // Next we should get an embed call on the "window manager" client.
+    vm_client1_.WaitForIncomingMethodCall();
+    ASSERT_EQ(1u, changes1()->size());
+    EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
+    // All these tests assume 1 for the client id. The only real assertion here
+    // is the client id is not zero, but adding this as rest of code here
+    // assumes 1.
+    ASSERT_EQ(1, (*changes1())[0].connection_id);
+    changes1()->clear();
+  }
+
+  // ApplicationDelegate implementation.
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService(&client_factory_);
+    return true;
+  }
+
+  // mojo::InterfaceFactory<mojo::WindowManagerInternal> implementation.
+  void Create(
+      ApplicationConnection* connection,
+      mojo::InterfaceRequest<mojo::WindowManagerInternal> request) override {
+    DCHECK(!wm_internal_binding_.is_bound());
+    wm_internal_binding_.Bind(request.Pass());
+  }
+
+  // mojo::WindowManagerInternal implementation.
+  void CreateWindowManagerForViewManagerClient(
+      uint16_t connection_id,
+      mojo::ScopedMessagePipeHandle window_manager_pipe) override {}
+  void SetViewManagerClient(
+      mojo::ScopedMessagePipeHandle view_manager_client_request) override {
+    auto typed_request = mojo::MakeRequest<mojo::ViewManagerClient>(
+        view_manager_client_request.Pass());
+    vm_client1_.Bind(typed_request.Pass());
+    view_manager_setup_run_loop_->Quit();
+  }
+
+  mojo::Binding<mojo::WindowManagerInternal> wm_internal_binding_;
+  mojo::WindowManagerInternalClientPtr wm_internal_client_;
+  ViewManagerClientImpl vm_client1_;
+  scoped_ptr<ViewManagerClientImpl> vm_client2_;
+  scoped_ptr<ViewManagerClientImpl> vm_client3_;
+
+ private:
+  mojo::ViewManagerServicePtr vm1_;
+  ViewManagerClientFactory client_factory_;
+  scoped_ptr<base::RunLoop> view_manager_setup_run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceAppTest);
+};
+
+// Verifies two clients/connections get different ids.
+TEST_F(ViewManagerServiceAppTest, TwoClientsGetDifferentConnectionIds) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // It isn't strictly necessary that the second connection gets 2, but these
+  // tests are written assuming that is the case. The key thing is the
+  // connection ids of |connection_| and |connection2_| differ.
+  ASSERT_EQ(1u, changes2()->size());
+  ASSERT_EQ(2, (*changes2())[0].connection_id);
+}
+
+// Verifies when Embed() is invoked any child views are removed.
+TEST_F(ViewManagerServiceAppTest, ViewsRemovedWhenEmbedding) {
+  // Two views 1 and 2. 2 is parented to 1.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+  EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(*changes2()));
+
+  // Embed() removed view 2.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 2), &views);
+    EXPECT_EQ("view=1,2 parent=null", SingleViewDescription(views));
+  }
+
+  // vm2 should not see view 2.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(1, 1), &views);
+    EXPECT_EQ("view=1,1 parent=null", SingleViewDescription(views));
+  }
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(1, 2), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Views 3 and 4 in connection 2.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 4)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(2, 3), BuildViewId(2, 4)));
+
+  // Connection 3 rooted at 2.
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), BuildViewId(2, 3)));
+
+  // View 4 should no longer have a parent.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(2, 3), &views);
+    EXPECT_EQ("view=2,3 parent=null", SingleViewDescription(views));
+
+    views.clear();
+    GetViewTree(vm2(), BuildViewId(2, 4), &views);
+    EXPECT_EQ("view=2,4 parent=null", SingleViewDescription(views));
+  }
+
+  // And view 4 should not be visible to connection 3.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm3(), BuildViewId(2, 3), &views);
+    EXPECT_EQ("view=2,3 parent=null", SingleViewDescription(views));
+  }
+}
+
+// Verifies once Embed() has been invoked the parent connection can't see any
+// children.
+TEST_F(ViewManagerServiceAppTest, CantAccessChildrenOfEmbeddedView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), BuildViewId(2, 2)));
+
+  ASSERT_TRUE(CreateView(vm3(), BuildViewId(3, 3)));
+  ASSERT_TRUE(AddView(vm3(), BuildViewId(2, 2), BuildViewId(3, 3)));
+
+  // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
+  // different connection.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(2, 2), &views);
+    EXPECT_EQ("view=2,2 parent=1,1", SingleViewDescription(views));
+  }
+
+  // Connection 2 shouldn't be able to get view 3 at all.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(3, 3), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Connection 1 should be able to see it all (its the root).
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(3u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+    EXPECT_EQ("view=2,2 parent=1,1", views[1].ToString());
+    EXPECT_EQ("view=3,3 parent=2,2", views[2].ToString());
+  }
+}
+
+// Verifies once Embed() has been invoked the parent can't mutate the children.
+TEST_F(ViewManagerServiceAppTest, CantModifyChildrenOfEmbeddedView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), BuildViewId(2, 2)));
+
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  // Connection 2 shouldn't be able to add anything to the view anymore.
+  ASSERT_FALSE(AddView(vm2(), BuildViewId(2, 2), BuildViewId(2, 3)));
+
+  // Create view 3 in connection 3 and add it to view 3.
+  ASSERT_TRUE(CreateView(vm3(), BuildViewId(3, 3)));
+  ASSERT_TRUE(AddView(vm3(), BuildViewId(2, 2), BuildViewId(3, 3)));
+
+  // Connection 2 shouldn't be able to remove view 3.
+  ASSERT_FALSE(RemoveViewFromParent(vm2(), BuildViewId(3, 3)));
+}
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerServiceAppTest, CreateView) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  EXPECT_TRUE(changes1()->empty());
+
+  // Can't create a view with the same id.
+  ASSERT_EQ(mojo::ErrorCode::VALUE_IN_USE,
+            CreateViewWithErrorCode(vm1(), BuildViewId(1, 1)));
+  EXPECT_TRUE(changes1()->empty());
+
+  // Can't create a view with a bogus connection id.
+  EXPECT_EQ(mojo::ErrorCode::ILLEGAL_ARGUMENT,
+            CreateViewWithErrorCode(vm1(), BuildViewId(2, 1)));
+  EXPECT_TRUE(changes1()->empty());
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerServiceAppTest, AddViewWithNoChange) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Make 3 a child of 2.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 2), BuildViewId(1, 3)));
+
+  // Try again, this should fail.
+  EXPECT_FALSE(AddView(vm1(), BuildViewId(1, 2), BuildViewId(1, 3)));
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerServiceAppTest, AddAncestorFails) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Make 3 a child of 2.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 2), BuildViewId(1, 3)));
+
+  // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
+  EXPECT_FALSE(AddView(vm1(), BuildViewId(1, 3), BuildViewId(1, 2)));
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(ViewManagerServiceAppTest, AddToRoot) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 21)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  changes2()->clear();
+
+  // Make 3 a child of 21.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 21), BuildViewId(1, 3)));
+
+  // Make 21 a child of 1.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 21)));
+
+  // Connection 2 should not be told anything (because the view is from a
+  // different connection). Create a view to ensure we got a response from
+  // the server.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 100)));
+  EXPECT_TRUE(changes2()->empty());
+}
+
+// Verifies HierarchyChanged is correctly sent for various adds/removes.
+TEST_F(ViewManagerServiceAppTest, ViewHierarchyChangedViews) {
+  // 1,2->1,11.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 2), true));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 11)));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 11), true));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 2), BuildViewId(1, 11)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), true));
+
+  ASSERT_TRUE(WaitForAllMessages(vm2()));
+  changes2()->clear();
+
+  // 1,1->1,2->1,11
+  {
+    // Client 2 should not get anything (1,2 is from another connection).
+    ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 2)));
+    ASSERT_TRUE(WaitForAllMessages(vm2()));
+    EXPECT_TRUE(changes2()->empty());
+  }
+
+  // 0,1->1,1->1,2->1,11.
+  {
+    // Client 2 is now connected to the root, so it should have gotten a drawn
+    // notification.
+    ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+    vm_client2_->WaitForChangeCount(1u);
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  // 1,1->1,2->1,11.
+  {
+    // Client 2 is no longer connected to the root, should get drawn state
+    // changed.
+    changes2()->clear();
+    ASSERT_TRUE(RemoveViewFromParent(vm1(), BuildViewId(1, 1)));
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=false",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  // 1,1->1,2->1,11->1,111.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 111)));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 111), true));
+  {
+    changes2()->clear();
+    ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 11), BuildViewId(1, 111)));
+    ASSERT_TRUE(WaitForAllMessages(vm2()));
+    EXPECT_TRUE(changes2()->empty());
+  }
+
+  // 0,1->1,1->1,2->1,11->1,111
+  {
+    changes2()->clear();
+    ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+              SingleChangeToDescription(*changes2()));
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, ViewHierarchyChangedAddingKnownToUnknown) {
+  // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
+  // parent).
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 11)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 21)));
+
+  // Set up the hierarchy.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 11)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(2, 2), BuildViewId(2, 21)));
+
+  // Remove 11, should result in a hierarchy change for the root.
+  {
+    changes1()->clear();
+    ASSERT_TRUE(RemoveViewFromParent(vm2(), BuildViewId(2, 11)));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=2,11 new_parent=null old_parent=1,1",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  // Add 2 to 1.
+  {
+    changes1()->clear();
+    ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              SingleChangeToDescription(*changes1()));
+    EXPECT_EQ(
+        "[view=2,2 parent=1,1],"
+        "[view=2,21 parent=2,2]",
+        ChangeViewDescription(*changes1()));
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, ReorderView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  Id view1_id = BuildViewId(2, 1);
+  Id view2_id = BuildViewId(2, 2);
+  Id view3_id = BuildViewId(2, 3);
+  Id view4_id = BuildViewId(1, 4);  // Peer to 1,1
+  Id view5_id = BuildViewId(1, 5);  // Peer to 1,1
+  Id view6_id = BuildViewId(2, 6);  // Child of 1,2.
+  Id view7_id = BuildViewId(2, 7);  // Unparented.
+  Id view8_id = BuildViewId(2, 8);  // Unparented.
+  ASSERT_TRUE(CreateView(vm2(), view1_id));
+  ASSERT_TRUE(CreateView(vm2(), view2_id));
+  ASSERT_TRUE(CreateView(vm2(), view3_id));
+  ASSERT_TRUE(CreateView(vm1(), view4_id));
+  ASSERT_TRUE(CreateView(vm1(), view5_id));
+  ASSERT_TRUE(CreateView(vm2(), view6_id));
+  ASSERT_TRUE(CreateView(vm2(), view7_id));
+  ASSERT_TRUE(CreateView(vm2(), view8_id));
+  ASSERT_TRUE(AddView(vm2(), view1_id, view2_id));
+  ASSERT_TRUE(AddView(vm2(), view2_id, view6_id));
+  ASSERT_TRUE(AddView(vm2(), view1_id, view3_id));
+  ASSERT_TRUE(AddView(vm1(), ViewIdToTransportId(RootViewId()), view4_id));
+  ASSERT_TRUE(AddView(vm1(), ViewIdToTransportId(RootViewId()), view5_id));
+  ASSERT_TRUE(AddView(vm1(), ViewIdToTransportId(RootViewId()), view1_id));
+
+  {
+    changes1()->clear();
+    ASSERT_TRUE(ReorderView(vm2(), view2_id, view3_id, OrderDirection::ABOVE));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=above",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  {
+    changes1()->clear();
+    ASSERT_TRUE(ReorderView(vm2(), view2_id, view3_id, OrderDirection::BELOW));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=below",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  // view2 is already below view3.
+  EXPECT_FALSE(ReorderView(vm2(), view2_id, view3_id, OrderDirection::BELOW));
+
+  // view4 & 5 are unknown to connection2_.
+  EXPECT_FALSE(ReorderView(vm2(), view4_id, view5_id, OrderDirection::ABOVE));
+
+  // view6 & view3 have different parents.
+  EXPECT_FALSE(ReorderView(vm1(), view3_id, view6_id, OrderDirection::ABOVE));
+
+  // Non-existent view-ids
+  EXPECT_FALSE(ReorderView(vm1(), BuildViewId(1, 27), BuildViewId(1, 28),
+                           OrderDirection::ABOVE));
+
+  // view7 & view8 are un-parented.
+  EXPECT_FALSE(ReorderView(vm1(), view7_id, view8_id, OrderDirection::ABOVE));
+}
+
+// Verifies DeleteView works.
+TEST_F(ViewManagerServiceAppTest, DeleteView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+
+  // Make 2 a child of 1.
+  {
+    changes1()->clear();
+    ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  // Delete 2.
+  {
+    changes1()->clear();
+    changes2()->clear();
+    ASSERT_TRUE(DeleteView(vm2(), BuildViewId(2, 2)));
+    EXPECT_TRUE(changes2()->empty());
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("ViewDeleted view=2,2", SingleChangeToDescription(*changes1()));
+  }
+}
+
+// Verifies DeleteView isn't allowed from a separate connection.
+TEST_F(ViewManagerServiceAppTest, DeleteViewFromAnotherConnectionDisallowed) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  EXPECT_FALSE(DeleteView(vm2(), BuildViewId(1, 1)));
+}
+
+// Verifies if a view was deleted and then reused that other clients are
+// properly notified.
+TEST_F(ViewManagerServiceAppTest, ReuseDeletedViewId) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+
+  // Add 2 to 1.
+  {
+    changes1()->clear();
+    ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              SingleChangeToDescription(*changes1()));
+    EXPECT_EQ("[view=2,2 parent=1,1]", ChangeViewDescription(*changes1()));
+  }
+
+  // Delete 2.
+  {
+    changes1()->clear();
+    ASSERT_TRUE(DeleteView(vm2(), BuildViewId(2, 2)));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("ViewDeleted view=2,2", SingleChangeToDescription(*changes1()));
+  }
+
+  // Create 2 again, and add it back to 1. Should get the same notification.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  {
+    changes1()->clear();
+    ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              SingleChangeToDescription(*changes1()));
+    EXPECT_EQ("[view=2,2 parent=1,1]", ChangeViewDescription(*changes1()));
+  }
+}
+
+// Assertions for GetViewTree.
+TEST_F(ViewManagerServiceAppTest, GetViewTree) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Create 11 in first connection and make it a child of 1.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 11)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 11)));
+
+  // Create two views in second connection, 2 and 3, both children of 1.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 3)));
+
+  // Verifies GetViewTree() on the root. The root connection sees all.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(5u, views.size());
+    EXPECT_EQ("view=0,1 parent=null", views[0].ToString());
+    EXPECT_EQ("view=1,1 parent=0,1", views[1].ToString());
+    EXPECT_EQ("view=1,11 parent=1,1", views[2].ToString());
+    EXPECT_EQ("view=2,2 parent=1,1", views[3].ToString());
+    EXPECT_EQ("view=2,3 parent=1,1", views[4].ToString());
+  }
+
+  // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
+  // is vm2()'s root and vm2() sees all the views it created.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(3u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+    EXPECT_EQ("view=2,2 parent=1,1", views[1].ToString());
+    EXPECT_EQ("view=2,3 parent=1,1", views[2].ToString());
+  }
+
+  // Connection 2 shouldn't be able to get the root tree.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(0u, views.size());
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, SetViewBounds) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  changes2()->clear();
+  ASSERT_TRUE(SetViewBounds(vm1(), BuildViewId(1, 1), 0, 0, 100, 100));
+
+  vm_client2_->WaitForChangeCount(1);
+  EXPECT_EQ("BoundsChanged view=1,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100",
+            SingleChangeToDescription(*changes2()));
+
+  // Should not be possible to change the bounds of a view created by another
+  // connection.
+  ASSERT_FALSE(SetViewBounds(vm2(), BuildViewId(1, 1), 0, 0, 0, 0));
+}
+
+// Verify AddView fails when trying to manipulate views in other roots.
+TEST_F(ViewManagerServiceAppTest, CantMoveViewsFromOtherRoot) {
+  // Create 1 and 2 in the first connection.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
+  // should not be able to access 1.
+  ASSERT_FALSE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(1, 2)));
+
+  // Try to reparent 1 to the root. A connection is not allowed to reparent its
+  // roots.
+  ASSERT_FALSE(AddView(vm2(), BuildViewId(0, 1), BuildViewId(1, 1)));
+}
+
+// Verify RemoveViewFromParent fails for views that are descendants of the
+// roots.
+TEST_F(ViewManagerServiceAppTest, CantRemoveViewsInOtherRoots) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 2)));
+
+  // Establish the second connection and give it the root 1.
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  // Connection 2 should not be able to remove view 2 or 1 from its parent.
+  ASSERT_FALSE(RemoveViewFromParent(vm2(), BuildViewId(1, 2)));
+  ASSERT_FALSE(RemoveViewFromParent(vm2(), BuildViewId(1, 1)));
+
+  // Create views 10 and 11 in 2.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 10)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 11)));
+
+  // Parent 11 to 10.
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(2, 10), BuildViewId(2, 11)));
+  // Remove 11 from 10.
+  ASSERT_TRUE(RemoveViewFromParent(vm2(), BuildViewId(2, 11)));
+
+  // Verify nothing was actually removed.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(3u, views.size());
+    EXPECT_EQ("view=0,1 parent=null", views[0].ToString());
+    EXPECT_EQ("view=1,1 parent=0,1", views[1].ToString());
+    EXPECT_EQ("view=1,2 parent=0,1", views[2].ToString());
+  }
+}
+
+// Verify GetViewTree fails for views that are not descendants of the roots.
+TEST_F(ViewManagerServiceAppTest, CantGetViewTreeOfOtherRoots) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  std::vector<TestView> views;
+
+  // Should get nothing for the root.
+  GetViewTree(vm2(), BuildViewId(0, 1), &views);
+  ASSERT_TRUE(views.empty());
+
+  // Should get nothing for view 2.
+  GetViewTree(vm2(), BuildViewId(1, 2), &views);
+  ASSERT_TRUE(views.empty());
+
+  // Should get view 1 if asked for.
+  GetViewTree(vm2(), BuildViewId(1, 1), &views);
+  ASSERT_EQ(1u, views.size());
+  EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+}
+
+TEST_F(ViewManagerServiceAppTest, OnViewInputEvent) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  changes2()->clear();
+
+  // Dispatch an event to the view and verify it's received.
+  {
+    EventPtr event(mojo::Event::New());
+    event->action = static_cast<mojo::EventType>(1);
+    wm_internal_client_->DispatchInputEventToView(BuildViewId(1, 1),
+                                                  event.Pass());
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("InputEvent view=1,1 event_action=1",
+              SingleChangeToDescription(*changes2()));
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, EmbedWithSameViewId) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  changes2()->clear();
+
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), BuildViewId(1, 1)));
+
+  // Connection2 should have been told the view was deleted.
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("ViewDeleted view=1,1", SingleChangeToDescription(*changes2()));
+  }
+
+  // Connection2 has no root. Verify it can't see view 1,1 anymore.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm2(), BuildViewId(1, 1), &views);
+    EXPECT_TRUE(views.empty());
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, EmbedWithSameViewId2) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  changes2()->clear();
+
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), BuildViewId(1, 1)));
+
+  // Connection2 should have been told the view was deleted.
+  vm_client2_->WaitForChangeCount(1);
+  changes2()->clear();
+
+  // Create a view in the third connection and parent it to the root.
+  ASSERT_TRUE(CreateView(vm3(), BuildViewId(3, 1)));
+  ASSERT_TRUE(AddView(vm3(), BuildViewId(1, 1), BuildViewId(3, 1)));
+
+  // Connection 1 should have been told about the add (it owns the view).
+  {
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("HierarchyChanged view=3,1 new_parent=1,1 old_parent=null",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  // Embed 1,1 again.
+  {
+    changes3()->clear();
+
+    // We should get a new connection for the new embedding.
+    scoped_ptr<ViewManagerClientImpl> connection4(
+        EstablishConnectionViaEmbed(vm1(), BuildViewId(1, 1)));
+    ASSERT_TRUE(connection4.get());
+    EXPECT_EQ("[view=1,1 parent=null]",
+              ChangeViewDescription(*connection4->tracker()->changes()));
+
+    // And 3 should get a delete.
+    vm_client3_->WaitForChangeCount(1);
+    EXPECT_EQ("ViewDeleted view=1,1", SingleChangeToDescription(*changes3()));
+  }
+
+  // vm3() has no root. Verify it can't see view 1,1 anymore.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm3(), BuildViewId(1, 1), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
+  // vm3() can no longer see 1,1.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+  }
+
+  // Verify vm3() can still see the view it created 3,1.
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm3(), BuildViewId(3, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=3,1 parent=null", views[0].ToString());
+  }
+}
+
+// Assertions for SetViewVisibility.
+TEST_F(ViewManagerServiceAppTest, SetViewVisibility) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(2u, views.size());
+    EXPECT_EQ("view=0,1 parent=null visible=true drawn=true",
+              views[0].ToString2());
+    EXPECT_EQ("view=1,1 parent=0,1 visible=false drawn=false",
+              views[1].ToString2());
+  }
+
+  // Show all the views.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), true));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 2), true));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(2u, views.size());
+    EXPECT_EQ("view=0,1 parent=null visible=true drawn=true",
+              views[0].ToString2());
+    EXPECT_EQ("view=1,1 parent=0,1 visible=true drawn=true",
+              views[1].ToString2());
+  }
+
+  // Hide 1.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), false));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,1 parent=0,1 visible=false drawn=false",
+              views[0].ToString2());
+  }
+
+  // Attach 2 to 1.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 2)));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(2u, views.size());
+    EXPECT_EQ("view=1,1 parent=0,1 visible=false drawn=false",
+              views[0].ToString2());
+    EXPECT_EQ("view=1,2 parent=1,1 visible=true drawn=false",
+              views[1].ToString2());
+  }
+
+  // Show 1.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), true));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(2u, views.size());
+    EXPECT_EQ("view=1,1 parent=0,1 visible=true drawn=true",
+              views[0].ToString2());
+    EXPECT_EQ("view=1,2 parent=1,1 visible=true drawn=true",
+              views[1].ToString2());
+  }
+}
+
+// Assertions for SetViewVisibility sending notifications.
+TEST_F(ViewManagerServiceAppTest, SetViewVisibilityNotifications) {
+  // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), true));
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 2)));
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 2), true));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(1, 1), BuildViewId(1, 2)));
+
+  // Establish the second connection at 1,2.
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(BuildViewId(1, 2)));
+
+  // Add 2,3 as a child of 1,2.
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  ASSERT_TRUE(SetViewVisibility(vm2(), BuildViewId(2, 3), true));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 2), BuildViewId(2, 3)));
+  WaitForAllMessages(vm1());
+
+  changes2()->clear();
+  // Hide 1,2 from connection 1. Connection 2 should see this.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 2), false));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("VisibilityChanged view=1,2 visible=false",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  changes1()->clear();
+  // Show 1,2 from connection 2, connection 1 should be notified.
+  ASSERT_TRUE(SetViewVisibility(vm2(), BuildViewId(1, 2), true));
+  {
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("VisibilityChanged view=1,2 visible=true",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  changes2()->clear();
+  // Hide 1,1, connection 2 should be told the draw state changed.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), false));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  changes2()->clear();
+  // Show 1,1 from connection 1. Connection 2 should see this.
+  ASSERT_TRUE(SetViewVisibility(vm1(), BuildViewId(1, 1), true));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  // Change visibility of 2,3, connection 1 should see this.
+  changes1()->clear();
+  ASSERT_TRUE(SetViewVisibility(vm2(), BuildViewId(2, 3), false));
+  {
+    vm_client1_.WaitForChangeCount(1);
+    EXPECT_EQ("VisibilityChanged view=2,3 visible=false",
+              SingleChangeToDescription(*changes1()));
+  }
+
+  changes2()->clear();
+  // Remove 1,1 from the root, connection 2 should see drawn state changed.
+  ASSERT_TRUE(RemoveViewFromParent(vm1(), BuildViewId(1, 1)));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  changes2()->clear();
+  // Add 1,1 back to the root, connection 2 should see drawn state changed.
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+              SingleChangeToDescription(*changes2()));
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, SetViewProperty) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+  changes2()->clear();
+
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(0, 1), &views);
+    ASSERT_EQ(2u, views.size());
+    EXPECT_EQ(BuildViewId(0, 1), views[0].view_id);
+    EXPECT_EQ(BuildViewId(1, 1), views[1].view_id);
+    ASSERT_EQ(0u, views[1].properties.size());
+  }
+
+  // Set properties on 1.
+  changes2()->clear();
+  std::vector<uint8_t> one(1, '1');
+  ASSERT_TRUE(SetViewProperty(vm1(), BuildViewId(1, 1), "one", &one));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("PropertyChanged view=1,1 key=one value=1",
+              SingleChangeToDescription(*changes2()));
+  }
+
+  // Test that our properties exist in the view tree
+  {
+    std::vector<TestView> views;
+    GetViewTree(vm1(), BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    ASSERT_EQ(1u, views[0].properties.size());
+    EXPECT_EQ(one, views[0].properties["one"]);
+  }
+
+  changes2()->clear();
+  // Set back to null.
+  ASSERT_TRUE(SetViewProperty(vm1(), BuildViewId(1, 1), "one", NULL));
+  {
+    vm_client2_->WaitForChangeCount(1);
+    EXPECT_EQ("PropertyChanged view=1,1 key=one value=NULL",
+              SingleChangeToDescription(*changes2()));
+  }
+}
+
+TEST_F(ViewManagerServiceAppTest, OnEmbeddedAppDisconnected) {
+  // Create connection 2 and 3.
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+  changes2()->clear();
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), BuildViewId(2, 2)));
+
+  // Close connection 3. Connection 2 (which had previously embedded 3) should
+  // be notified of this.
+  vm_client3_.reset();
+  vm_client2_->WaitForChangeCount(1);
+  EXPECT_EQ("OnEmbeddedAppDisconnected view=2,2",
+            SingleChangeToDescription(*changes2()));
+}
+
+// Verifies when the parent of an Embed() is destroyed the embedded app gets
+// a ViewDeleted (and doesn't trigger a DCHECK).
+TEST_F(ViewManagerServiceAppTest, OnParentOfEmbedDisconnects) {
+  // Create connection 2 and 3.
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(2, 2), BuildViewId(2, 3)));
+  changes2()->clear();
+  ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), BuildViewId(2, 3)));
+  changes3()->clear();
+
+  // Close connection 2. Connection 3 should get a delete (for its root).
+  vm_client2_.reset();
+  vm_client3_->WaitForChangeCount(1);
+  EXPECT_EQ("ViewDeleted view=2,3", SingleChangeToDescription(*changes3()));
+}
+
+// Verifies ViewManagerServiceImpl doesn't incorrectly erase from its internal
+// map when a view from another connection with the same view_id is removed.
+TEST_F(ViewManagerServiceAppTest, DontCleanMapOnDestroy) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 1)));
+  changes1()->clear();
+  vm_client2_.reset();
+  vm_client1_.WaitForChangeCount(1);
+  EXPECT_EQ("OnEmbeddedAppDisconnected view=1,1",
+            SingleChangeToDescription(*changes1()));
+  std::vector<TestView> views;
+  GetViewTree(vm1(), BuildViewId(1, 1), &views);
+  EXPECT_FALSE(views.empty());
+}
+
+TEST_F(ViewManagerServiceAppTest, CloneAndAnimate) {
+  // Create connection 2 and 3.
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(AddView(vm1(), BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 2)));
+  ASSERT_TRUE(CreateView(vm2(), BuildViewId(2, 3)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(1, 1), BuildViewId(2, 2)));
+  ASSERT_TRUE(AddView(vm2(), BuildViewId(2, 2), BuildViewId(2, 3)));
+  changes2()->clear();
+
+  ASSERT_TRUE(WaitForAllMessages(vm1()));
+  changes1()->clear();
+
+  wm_internal_client_->CloneAndAnimate(BuildViewId(2, 3));
+  ASSERT_TRUE(WaitForAllMessages(vm1()));
+
+  ASSERT_TRUE(WaitForAllMessages(vm1()));
+  ASSERT_TRUE(WaitForAllMessages(vm2()));
+
+  // No messages should have been received.
+  EXPECT_TRUE(changes1()->empty());
+  EXPECT_TRUE(changes2()->empty());
+
+  // No one should be able to see the cloned tree.
+  std::vector<TestView> views;
+  GetViewTree(vm1(), BuildViewId(1, 1), &views);
+  EXPECT_FALSE(HasClonedView(views));
+  views.clear();
+
+  GetViewTree(vm2(), BuildViewId(1, 1), &views);
+  EXPECT_FALSE(HasClonedView(views));
+}
+
+// Verifies Embed() works when supplying a ViewManagerClient.
+TEST_F(ViewManagerServiceAppTest, EmbedSupplyingViewManagerClient) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+
+  ViewManagerClientImpl client2;
+  mojo::ViewManagerClientPtr client2_ptr;
+  mojo::Binding<ViewManagerClient> client2_binding(&client2, &client2_ptr);
+  ASSERT_TRUE(Embed(vm1(), BuildViewId(1, 1), client2_ptr.Pass()));
+  client2.WaitForOnEmbed();
+  EXPECT_EQ("OnEmbed creator=mojo:window_manager",
+            SingleChangeToDescription(*client2.tracker()->changes()));
+}
+
+// TODO(sky): need to better track changes to initial connection. For example,
+// that SetBounsdViews/AddView and the like don't result in messages to the
+// originating connection.
+
+// TODO(sky): make sure coverage of what was
+// ViewManagerTest.SecondEmbedRoot_InitService and
+// ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
+// tests.
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_manager_service_impl.cc b/services/view_manager/view_manager_service_impl.cc
new file mode 100644
index 0000000..de1eaf3
--- /dev/null
+++ b/services/view_manager/view_manager_service_impl.cc
@@ -0,0 +1,656 @@
+// 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.
+
+#include "services/view_manager/view_manager_service_impl.h"
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/converters/surfaces/surfaces_type_converters.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/view_manager/connection_manager.h"
+#include "services/view_manager/default_access_policy.h"
+#include "services/view_manager/display_manager.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/window_manager_access_policy.h"
+
+using mojo::Array;
+using mojo::Callback;
+using mojo::Id;
+using mojo::InterfaceRequest;
+using mojo::OrderDirection;
+using mojo::ServiceProvider;
+using mojo::ServiceProviderPtr;
+using mojo::String;
+using mojo::ViewDataPtr;
+
+namespace view_manager {
+
+ViewManagerServiceImpl::ViewManagerServiceImpl(
+    ConnectionManager* connection_manager,
+    mojo::ConnectionSpecificId creator_id,
+    const std::string& creator_url,
+    const std::string& url,
+    const ViewId& root_id)
+    : connection_manager_(connection_manager),
+      id_(connection_manager_->GetAndAdvanceNextConnectionId()),
+      url_(url),
+      creator_id_(creator_id),
+      creator_url_(creator_url),
+      client_(nullptr) {
+  CHECK(GetView(root_id));
+  root_.reset(new ViewId(root_id));
+  if (root_id == RootViewId())
+    access_policy_.reset(new WindowManagerAccessPolicy(id_, this));
+  else
+    access_policy_.reset(new DefaultAccessPolicy(id_, this));
+}
+
+ViewManagerServiceImpl::~ViewManagerServiceImpl() {
+  DestroyViews();
+}
+
+void ViewManagerServiceImpl::Init(mojo::ViewManagerClient* client,
+                                  mojo::ViewManagerServicePtr service_ptr,
+                                  InterfaceRequest<ServiceProvider> services,
+                                  ServiceProviderPtr exposed_services) {
+  TRACE_EVENT0("view_manager", __func__);
+  DCHECK(!client_);
+  client_ = client;
+  std::vector<const ServerView*> to_send;
+  if (root_.get())
+    GetUnknownViewsFrom(GetView(*root_), &to_send);
+
+  mojo::MessagePipe pipe;
+  connection_manager_->wm_internal()->CreateWindowManagerForViewManagerClient(
+      id_, pipe.handle1.Pass());
+  client->OnEmbed(id_, creator_url_, ViewToViewData(to_send.front()),
+                  service_ptr.Pass(), services.Pass(), exposed_services.Pass(),
+                  pipe.handle0.Pass());
+}
+
+const ServerView* ViewManagerServiceImpl::GetView(const ViewId& id) const {
+  TRACE_EVENT0("view_manager", __func__);
+  if (id_ == id.connection_id) {
+    ViewMap::const_iterator i = view_map_.find(id.view_id);
+    return i == view_map_.end() ? NULL : i->second;
+  }
+  return connection_manager_->GetView(id);
+}
+
+bool ViewManagerServiceImpl::IsRoot(const ViewId& id) const {
+  return root_.get() && *root_ == id;
+}
+
+void ViewManagerServiceImpl::OnWillDestroyViewManagerServiceImpl(
+    ViewManagerServiceImpl* connection) {
+  if (creator_id_ == connection->id())
+    creator_id_ = kInvalidConnectionId;
+  if (connection->root_ && connection->root_->connection_id == id_ &&
+      view_map_.count(connection->root_->view_id) > 0) {
+    client()->OnEmbeddedAppDisconnected(
+        ViewIdToTransportId(*connection->root_));
+  }
+  if (root_.get() && root_->connection_id == connection->id())
+    root_.reset();
+}
+
+mojo::ErrorCode ViewManagerServiceImpl::CreateView(const ViewId& view_id) {
+  if (view_id.connection_id != id_)
+    return mojo::ErrorCode::ILLEGAL_ARGUMENT;
+  if (view_map_.find(view_id.view_id) != view_map_.end())
+    return mojo::ErrorCode::VALUE_IN_USE;
+  view_map_[view_id.view_id] = connection_manager_->CreateServerView(view_id);
+  known_views_.insert(ViewIdToTransportId(view_id));
+  return mojo::ErrorCode::NONE;
+}
+
+bool ViewManagerServiceImpl::AddView(const ViewId& parent_id,
+                                     const ViewId& child_id) {
+  ServerView* parent = GetView(parent_id);
+  ServerView* child = GetView(child_id);
+  if (parent && child && child->parent() != parent &&
+      !child->Contains(parent) && access_policy_->CanAddView(parent, child)) {
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    parent->Add(child);
+    return true;
+  }
+  return false;
+}
+
+std::vector<const ServerView*> ViewManagerServiceImpl::GetViewTree(
+    const ViewId& view_id) const {
+  const ServerView* view = GetView(view_id);
+  std::vector<const ServerView*> views;
+  if (view)
+    GetViewTreeImpl(view, &views);
+  return views;
+}
+
+bool ViewManagerServiceImpl::SetViewVisibility(const ViewId& view_id,
+                                               bool visible) {
+  ServerView* view = GetView(view_id);
+  if (!view || view->visible() == visible ||
+      !access_policy_->CanChangeViewVisibility(view)) {
+    return false;
+  }
+  ConnectionManager::ScopedChange change(this, connection_manager_, false);
+  view->SetVisible(visible);
+  return true;
+}
+
+bool ViewManagerServiceImpl::EmbedUrl(
+    const std::string& url,
+    const ViewId& view_id,
+    InterfaceRequest<ServiceProvider> services,
+    ServiceProviderPtr exposed_services) {
+  TRACE_EVENT0("view_manager", __func__);
+  if (!PrepareForEmbed(view_id))
+    return false;
+  connection_manager_->EmbedAtView(id_, url, view_id, services.Pass(),
+                                   exposed_services.Pass());
+  return true;
+}
+
+bool ViewManagerServiceImpl::Embed(const ViewId& view_id,
+                                   mojo::ViewManagerClientPtr client) {
+  TRACE_EVENT0("view_manager", __func__);
+  if (!client.get() || !PrepareForEmbed(view_id))
+    return false;
+  connection_manager_->EmbedAtView(id_, view_id, client.Pass());
+  return true;
+}
+
+void ViewManagerServiceImpl::ProcessViewBoundsChanged(
+    const ServerView* view,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    bool originated_change) {
+  if (originated_change || !IsViewKnown(view))
+    return;
+  client()->OnViewBoundsChanged(ViewIdToTransportId(view->id()),
+                                mojo::Rect::From(old_bounds),
+                                mojo::Rect::From(new_bounds));
+}
+
+void ViewManagerServiceImpl::ProcessViewportMetricsChanged(
+    const mojo::ViewportMetrics& old_metrics,
+    const mojo::ViewportMetrics& new_metrics,
+    bool originated_change) {
+  client()->OnViewViewportMetricsChanged(old_metrics.Clone(),
+                                         new_metrics.Clone());
+}
+
+void ViewManagerServiceImpl::ProcessWillChangeViewHierarchy(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent,
+    bool originated_change) {
+  if (originated_change)
+    return;
+
+  const bool old_drawn = view->IsDrawn(connection_manager_->root());
+  const bool new_drawn = view->visible() && new_parent &&
+                         new_parent->IsDrawn(connection_manager_->root());
+  if (old_drawn == new_drawn)
+    return;
+
+  NotifyDrawnStateChanged(view, new_drawn);
+}
+
+void ViewManagerServiceImpl::ProcessViewPropertyChanged(
+    const ServerView* view,
+    const std::string& name,
+    const std::vector<uint8_t>* new_data,
+    bool originated_change) {
+  if (originated_change)
+    return;
+
+  Array<uint8_t> data;
+  if (new_data)
+    data = Array<uint8_t>::From(*new_data);
+
+  client()->OnViewSharedPropertyChanged(ViewIdToTransportId(view->id()),
+                                        String(name), data.Pass());
+}
+
+void ViewManagerServiceImpl::ProcessViewHierarchyChanged(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent,
+    bool originated_change) {
+  if (originated_change && !IsViewKnown(view) && new_parent &&
+      IsViewKnown(new_parent)) {
+    std::vector<const ServerView*> unused;
+    GetUnknownViewsFrom(view, &unused);
+  }
+  if (originated_change || connection_manager_->is_processing_delete_view() ||
+      connection_manager_->DidConnectionMessageClient(id_)) {
+    return;
+  }
+
+  if (!access_policy_->ShouldNotifyOnHierarchyChange(view, &new_parent,
+                                                     &old_parent)) {
+    return;
+  }
+  // Inform the client of any new views and update the set of views we know
+  // about.
+  std::vector<const ServerView*> to_send;
+  if (!IsViewKnown(view))
+    GetUnknownViewsFrom(view, &to_send);
+  const ViewId new_parent_id(new_parent ? new_parent->id() : ViewId());
+  const ViewId old_parent_id(old_parent ? old_parent->id() : ViewId());
+  client()->OnViewHierarchyChanged(
+      ViewIdToTransportId(view->id()), ViewIdToTransportId(new_parent_id),
+      ViewIdToTransportId(old_parent_id), ViewsToViewDatas(to_send));
+  connection_manager_->OnConnectionMessagedClient(id_);
+}
+
+void ViewManagerServiceImpl::ProcessViewReorder(const ServerView* view,
+                                                const ServerView* relative_view,
+                                                OrderDirection direction,
+                                                bool originated_change) {
+  if (originated_change || !IsViewKnown(view) || !IsViewKnown(relative_view))
+    return;
+
+  client()->OnViewReordered(ViewIdToTransportId(view->id()),
+                            ViewIdToTransportId(relative_view->id()),
+                            direction);
+}
+
+void ViewManagerServiceImpl::ProcessViewDeleted(const ViewId& view,
+                                                bool originated_change) {
+  if (view.connection_id == id_)
+    view_map_.erase(view.view_id);
+
+  const bool in_known = known_views_.erase(ViewIdToTransportId(view)) > 0;
+
+  if (IsRoot(view))
+    root_.reset();
+
+  if (originated_change)
+    return;
+
+  if (in_known) {
+    client()->OnViewDeleted(ViewIdToTransportId(view));
+    connection_manager_->OnConnectionMessagedClient(id_);
+  }
+}
+
+void ViewManagerServiceImpl::ProcessWillChangeViewVisibility(
+    const ServerView* view,
+    bool originated_change) {
+  if (originated_change)
+    return;
+
+  if (IsViewKnown(view)) {
+    client()->OnViewVisibilityChanged(ViewIdToTransportId(view->id()),
+                                      !view->visible());
+    return;
+  }
+
+  bool view_target_drawn_state;
+  if (view->visible()) {
+    // View is being hidden, won't be drawn.
+    view_target_drawn_state = false;
+  } else {
+    // View is being shown. View will be drawn if its parent is drawn.
+    view_target_drawn_state =
+        view->parent() && view->parent()->IsDrawn(connection_manager_->root());
+  }
+
+  NotifyDrawnStateChanged(view, view_target_drawn_state);
+}
+
+bool ViewManagerServiceImpl::IsViewKnown(const ServerView* view) const {
+  return known_views_.count(ViewIdToTransportId(view->id())) > 0;
+}
+
+bool ViewManagerServiceImpl::CanReorderView(const ServerView* view,
+                                            const ServerView* relative_view,
+                                            OrderDirection direction) const {
+  if (!view || !relative_view)
+    return false;
+
+  if (!view->parent() || view->parent() != relative_view->parent())
+    return false;
+
+  if (!access_policy_->CanReorderView(view, relative_view, direction))
+    return false;
+
+  std::vector<const ServerView*> children = view->parent()->GetChildren();
+  const size_t child_i =
+      std::find(children.begin(), children.end(), view) - children.begin();
+  const size_t target_i =
+      std::find(children.begin(), children.end(), relative_view) -
+      children.begin();
+  if ((direction == mojo::OrderDirection::ABOVE && child_i == target_i + 1) ||
+      (direction == mojo::OrderDirection::BELOW && child_i + 1 == target_i)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool ViewManagerServiceImpl::DeleteViewImpl(ViewManagerServiceImpl* source,
+                                            ServerView* view) {
+  DCHECK(view);
+  DCHECK_EQ(view->id().connection_id, id_);
+  ConnectionManager::ScopedChange change(source, connection_manager_, true);
+  delete view;
+  return true;
+}
+
+void ViewManagerServiceImpl::GetUnknownViewsFrom(
+    const ServerView* view,
+    std::vector<const ServerView*>* views) {
+  if (IsViewKnown(view) || !access_policy_->CanGetViewTree(view))
+    return;
+  views->push_back(view);
+  known_views_.insert(ViewIdToTransportId(view->id()));
+  if (!access_policy_->CanDescendIntoViewForViewTree(view))
+    return;
+  std::vector<const ServerView*> children(view->GetChildren());
+  for (size_t i = 0; i < children.size(); ++i)
+    GetUnknownViewsFrom(children[i], views);
+}
+
+void ViewManagerServiceImpl::RemoveFromKnown(
+    const ServerView* view,
+    std::vector<ServerView*>* local_views) {
+  if (view->id().connection_id == id_) {
+    if (local_views)
+      local_views->push_back(GetView(view->id()));
+    return;
+  }
+  known_views_.erase(ViewIdToTransportId(view->id()));
+  std::vector<const ServerView*> children = view->GetChildren();
+  for (size_t i = 0; i < children.size(); ++i)
+    RemoveFromKnown(children[i], local_views);
+}
+
+void ViewManagerServiceImpl::RemoveRoot() {
+  CHECK(root_.get());
+  const ViewId root_id(*root_);
+  root_.reset();
+  // No need to do anything if we created the view.
+  if (root_id.connection_id == id_)
+    return;
+
+  client()->OnViewDeleted(ViewIdToTransportId(root_id));
+  connection_manager_->OnConnectionMessagedClient(id_);
+
+  // This connection no longer knows about the view. Unparent any views that
+  // were parented to views in the root.
+  std::vector<ServerView*> local_views;
+  RemoveFromKnown(GetView(root_id), &local_views);
+  for (size_t i = 0; i < local_views.size(); ++i)
+    local_views[i]->parent()->Remove(local_views[i]);
+}
+
+void ViewManagerServiceImpl::RemoveChildrenAsPartOfEmbed(
+    const ViewId& view_id) {
+  ServerView* view = GetView(view_id);
+  CHECK(view);
+  CHECK(view->id().connection_id == view_id.connection_id);
+  std::vector<ServerView*> children = view->GetChildren();
+  for (size_t i = 0; i < children.size(); ++i)
+    view->Remove(children[i]);
+}
+
+Array<ViewDataPtr> ViewManagerServiceImpl::ViewsToViewDatas(
+    const std::vector<const ServerView*>& views) {
+  auto array = Array<ViewDataPtr>::New(views.size());
+  for (size_t i = 0; i < views.size(); ++i)
+    array[i] = ViewToViewData(views[i]).Pass();
+  return array;
+}
+
+ViewDataPtr ViewManagerServiceImpl::ViewToViewData(const ServerView* view) {
+  DCHECK(IsViewKnown(view));
+  const ServerView* parent = view->parent();
+  // If the parent isn't known, it means the parent is not visible to us (not
+  // in roots), and should not be sent over.
+  if (parent && !IsViewKnown(parent))
+    parent = NULL;
+  ViewDataPtr view_data(mojo::ViewData::New());
+  view_data->parent_id = ViewIdToTransportId(parent ? parent->id() : ViewId());
+  view_data->view_id = ViewIdToTransportId(view->id());
+  view_data->bounds = mojo::Rect::From(view->bounds());
+  view_data->properties =
+      mojo::Map<String, Array<uint8_t>>::From(view->properties());
+  view_data->visible = view->visible();
+  view_data->drawn = view->IsDrawn(connection_manager_->root());
+  view_data->viewport_metrics =
+      connection_manager_->display_manager()->GetViewportMetrics().Clone();
+  return view_data;
+}
+
+void ViewManagerServiceImpl::GetViewTreeImpl(
+    const ServerView* view,
+    std::vector<const ServerView*>* views) const {
+  DCHECK(view);
+
+  if (!access_policy_->CanGetViewTree(view))
+    return;
+
+  views->push_back(view);
+
+  if (!access_policy_->CanDescendIntoViewForViewTree(view))
+    return;
+
+  std::vector<const ServerView*> children(view->GetChildren());
+  for (size_t i = 0; i < children.size(); ++i)
+    GetViewTreeImpl(children[i], views);
+}
+
+void ViewManagerServiceImpl::NotifyDrawnStateChanged(const ServerView* view,
+                                                     bool new_drawn_value) {
+  // Even though we don't know about view, it may be an ancestor of our root, in
+  // which case the change may effect our roots drawn state.
+  if (!root_.get())
+    return;
+
+  const ServerView* root = GetView(*root_);
+  DCHECK(root);
+  if (view->Contains(root) &&
+      (new_drawn_value != root->IsDrawn(connection_manager_->root()))) {
+    client()->OnViewDrawnStateChanged(ViewIdToTransportId(root->id()),
+                                      new_drawn_value);
+  }
+}
+
+void ViewManagerServiceImpl::DestroyViews() {
+  if (!view_map_.empty()) {
+    ConnectionManager::ScopedChange change(this, connection_manager_, true);
+    // If we get here from the destructor we're not going to get
+    // ProcessViewDeleted(). Copy the map and delete from the copy so that we
+    // don't have to worry about whether |view_map_| changes or not.
+    ViewMap view_map_copy;
+    view_map_.swap(view_map_copy);
+    STLDeleteValues(&view_map_copy);
+  }
+}
+
+bool ViewManagerServiceImpl::PrepareForEmbed(const ViewId& view_id) {
+  const ServerView* view = GetView(view_id);
+  if (!view || !access_policy_->CanEmbed(view))
+    return false;
+
+  // Only allow a node to be the root for one connection.
+  ViewManagerServiceImpl* existing_owner =
+      connection_manager_->GetConnectionWithRoot(view_id);
+
+  ConnectionManager::ScopedChange change(this, connection_manager_, true);
+  RemoveChildrenAsPartOfEmbed(view_id);
+  if (existing_owner) {
+    // Never message the originating connection.
+    connection_manager_->OnConnectionMessagedClient(id_);
+    existing_owner->RemoveRoot();
+  }
+  return true;
+}
+
+void ViewManagerServiceImpl::CreateView(
+    Id transport_view_id,
+    const Callback<void(mojo::ErrorCode)>& callback) {
+  callback.Run(CreateView(ViewIdFromTransportId(transport_view_id)));
+}
+
+void ViewManagerServiceImpl::DeleteView(Id transport_view_id,
+                                        const Callback<void(bool)>& callback) {
+  ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+  bool success = false;
+  if (view && access_policy_->CanDeleteView(view)) {
+    ViewManagerServiceImpl* connection =
+        connection_manager_->GetConnection(view->id().connection_id);
+    success = connection && connection->DeleteViewImpl(this, view);
+  }
+  callback.Run(success);
+}
+
+void ViewManagerServiceImpl::AddView(Id parent_id,
+                                     Id child_id,
+                                     const Callback<void(bool)>& callback) {
+  callback.Run(AddView(ViewIdFromTransportId(parent_id),
+                       ViewIdFromTransportId(child_id)));
+}
+
+void ViewManagerServiceImpl::RemoveViewFromParent(
+    Id view_id,
+    const Callback<void(bool)>& callback) {
+  bool success = false;
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  if (view && view->parent() && access_policy_->CanRemoveViewFromParent(view)) {
+    success = true;
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    view->parent()->Remove(view);
+  }
+  callback.Run(success);
+}
+
+void ViewManagerServiceImpl::ReorderView(Id view_id,
+                                         Id relative_view_id,
+                                         OrderDirection direction,
+                                         const Callback<void(bool)>& callback) {
+  bool success = false;
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  ServerView* relative_view = GetView(ViewIdFromTransportId(relative_view_id));
+  if (CanReorderView(view, relative_view, direction)) {
+    success = true;
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    view->parent()->Reorder(view, relative_view, direction);
+    connection_manager_->ProcessViewReorder(view, relative_view, direction);
+  }
+  callback.Run(success);
+}
+
+void ViewManagerServiceImpl::GetViewTree(
+    Id view_id,
+    const Callback<void(Array<ViewDataPtr>)>& callback) {
+  std::vector<const ServerView*> views(
+      GetViewTree(ViewIdFromTransportId(view_id)));
+  callback.Run(ViewsToViewDatas(views));
+}
+
+void ViewManagerServiceImpl::SetViewSurfaceId(
+    Id view_id,
+    mojo::SurfaceIdPtr surface_id,
+    const Callback<void(bool)>& callback) {
+  // TODO(sky): add coverage of not being able to set for random node.
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  if (!view || !access_policy_->CanSetViewSurfaceId(view)) {
+    callback.Run(false);
+    return;
+  }
+  view->SetSurfaceId(surface_id.To<cc::SurfaceId>());
+  callback.Run(true);
+}
+
+void ViewManagerServiceImpl::SetViewBounds(
+    Id view_id,
+    mojo::RectPtr bounds,
+    const Callback<void(bool)>& callback) {
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  const bool success = view && access_policy_->CanSetViewBounds(view);
+  if (success) {
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    view->SetBounds(bounds.To<gfx::Rect>());
+  }
+  callback.Run(success);
+}
+
+void ViewManagerServiceImpl::SetViewVisibility(
+    Id transport_view_id,
+    bool visible,
+    const Callback<void(bool)>& callback) {
+  callback.Run(
+      SetViewVisibility(ViewIdFromTransportId(transport_view_id), visible));
+}
+
+void ViewManagerServiceImpl::SetViewProperty(
+    uint32_t view_id,
+    const mojo::String& name,
+    mojo::Array<uint8_t> value,
+    const mojo::Callback<void(bool)>& callback) {
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  const bool success = view && access_policy_->CanSetViewProperties(view);
+  if (success) {
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+
+    if (value.is_null()) {
+      view->SetProperty(name, nullptr);
+    } else {
+      std::vector<uint8_t> data = value.To<std::vector<uint8_t>>();
+      view->SetProperty(name, &data);
+    }
+  }
+  callback.Run(success);
+}
+
+void ViewManagerServiceImpl::EmbedUrl(
+    const String& url,
+    Id transport_view_id,
+    InterfaceRequest<ServiceProvider> services,
+    ServiceProviderPtr exposed_services,
+    const Callback<void(bool)>& callback) {
+  callback.Run(EmbedUrl(url.To<std::string>(),
+                        ViewIdFromTransportId(transport_view_id),
+                        services.Pass(), exposed_services.Pass()));
+}
+
+void ViewManagerServiceImpl::Embed(mojo::Id transport_view_id,
+                                   mojo::ViewManagerClientPtr client,
+                                   const mojo::Callback<void(bool)>& callback) {
+  callback.Run(Embed(ViewIdFromTransportId(transport_view_id), client.Pass()));
+}
+
+void ViewManagerServiceImpl::PerformAction(
+    mojo::Id transport_view_id,
+    const mojo::String& action,
+    const mojo::Callback<void(bool)>& callback) {
+  TRACE_EVENT0("view_manager", __func__);
+  connection_manager_->GetWindowManagerViewManagerClient()->OnPerformAction(
+      transport_view_id, action, callback);
+}
+
+bool ViewManagerServiceImpl::IsRootForAccessPolicy(const ViewId& id) const {
+  return IsRoot(id);
+}
+
+bool ViewManagerServiceImpl::IsViewKnownForAccessPolicy(
+    const ServerView* view) const {
+  return IsViewKnown(view);
+}
+
+bool ViewManagerServiceImpl::IsViewRootOfAnotherConnectionForAccessPolicy(
+    const ServerView* view) const {
+  ViewManagerServiceImpl* connection =
+      connection_manager_->GetConnectionWithRoot(view->id());
+  return connection && connection != this;
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/view_manager_service_impl.h b/services/view_manager/view_manager_service_impl.h
new file mode 100644
index 0000000..e0ac507
--- /dev/null
+++ b/services/view_manager/view_manager_service_impl.h
@@ -0,0 +1,260 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
+#define SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "services/view_manager/access_policy_delegate.h"
+#include "services/view_manager/ids.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace view_manager {
+
+class AccessPolicy;
+class ConnectionManager;
+class ServerView;
+
+// An instance of ViewManagerServiceImpl is created for every ViewManagerService
+// request. ViewManagerServiceImpl tracks all the state and views created by a
+// client. ViewManagerServiceImpl coordinates with ConnectionManager to update
+// the client (and internal state) as necessary.
+class ViewManagerServiceImpl : public mojo::ViewManagerService,
+                               public AccessPolicyDelegate {
+ public:
+  using ViewIdSet = base::hash_set<mojo::Id>;
+
+  ViewManagerServiceImpl(ConnectionManager* connection_manager,
+                         mojo::ConnectionSpecificId creator_id,
+                         const std::string& creator_url,
+                         const std::string& url,
+                         const ViewId& root_id);
+  ~ViewManagerServiceImpl() override;
+
+  // |services| and |exposed_services| are the ServiceProviders to pass to the
+  // client via OnEmbed().
+  void Init(mojo::ViewManagerClient* client,
+            mojo::ViewManagerServicePtr service_ptr,
+            mojo::InterfaceRequest<mojo::ServiceProvider> services,
+            mojo::ServiceProviderPtr exposed_services);
+
+  mojo::ConnectionSpecificId id() const { return id_; }
+  mojo::ConnectionSpecificId creator_id() const { return creator_id_; }
+  const std::string& url() const { return url_; }
+
+  mojo::ViewManagerClient* client() { return client_; }
+
+  // Returns the View with the specified id.
+  ServerView* GetView(const ViewId& id) {
+    return const_cast<ServerView*>(
+        const_cast<const ViewManagerServiceImpl*>(this)->GetView(id));
+  }
+  const ServerView* GetView(const ViewId& id) const;
+
+  // Returns true if this connection's root is |id|.
+  bool IsRoot(const ViewId& id) const;
+
+  // Returns the id of the root node. This is null if the root has been
+  // destroyed but the connection is still valid.
+  const ViewId* root() const { return root_.get(); }
+
+  // Invoked when a connection is about to be destroyed.
+  void OnWillDestroyViewManagerServiceImpl(ViewManagerServiceImpl* connection);
+
+  // These functions are synchronous variants of those defined in the mojom. The
+  // ViewManagerService implementations all call into these. See the mojom for
+  // details.
+  mojo::ErrorCode CreateView(const ViewId& view_id);
+  bool AddView(const ViewId& parent_id, const ViewId& child_id);
+  std::vector<const ServerView*> GetViewTree(const ViewId& view_id) const;
+  bool SetViewVisibility(const ViewId& view_id, bool visible);
+  bool EmbedUrl(const std::string& url,
+                const ViewId& view_id,
+                mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                mojo::ServiceProviderPtr exposed_services);
+  bool Embed(const ViewId& view_id, mojo::ViewManagerClientPtr client);
+
+  // The following methods are invoked after the corresponding change has been
+  // processed. They do the appropriate bookkeeping and update the client as
+  // necessary.
+  void ProcessViewBoundsChanged(const ServerView* view,
+                                const gfx::Rect& old_bounds,
+                                const gfx::Rect& new_bounds,
+                                bool originated_change);
+  void ProcessViewportMetricsChanged(const mojo::ViewportMetrics& old_metrics,
+                                     const mojo::ViewportMetrics& new_metrics,
+                                     bool originated_change);
+  void ProcessWillChangeViewHierarchy(const ServerView* view,
+                                      const ServerView* new_parent,
+                                      const ServerView* old_parent,
+                                      bool originated_change);
+  void ProcessViewPropertyChanged(const ServerView* view,
+                                  const std::string& name,
+                                  const std::vector<uint8_t>* new_data,
+                                  bool originated_change);
+  void ProcessViewHierarchyChanged(const ServerView* view,
+                                   const ServerView* new_parent,
+                                   const ServerView* old_parent,
+                                   bool originated_change);
+  void ProcessViewReorder(const ServerView* view,
+                          const ServerView* relative_view,
+                          mojo::OrderDirection direction,
+                          bool originated_change);
+  void ProcessViewDeleted(const ViewId& view, bool originated_change);
+  void ProcessWillChangeViewVisibility(const ServerView* view,
+                                       bool originated_change);
+  void ProcessViewPropertiesChanged(const ServerView* view,
+                                    bool originated_change);
+
+ private:
+  typedef std::map<mojo::ConnectionSpecificId, ServerView*> ViewMap;
+
+  bool IsViewKnown(const ServerView* view) const;
+
+  // These functions return true if the corresponding mojom function is allowed
+  // for this connection.
+  bool CanReorderView(const ServerView* view,
+                      const ServerView* relative_view,
+                      mojo::OrderDirection direction) const;
+
+  // Deletes a view owned by this connection. Returns true on success. |source|
+  // is the connection that originated the change.
+  bool DeleteViewImpl(ViewManagerServiceImpl* source, ServerView* view);
+
+  // If |view| is known (in |known_views_|) does nothing. Otherwise adds |view|
+  // to |views|, marks |view| as known and recurses.
+  void GetUnknownViewsFrom(const ServerView* view,
+                           std::vector<const ServerView*>* views);
+
+  // Removes |view| and all its descendants from |known_views_|. This does not
+  // recurse through views that were created by this connection. All views owned
+  // by this connection are added to |local_views|.
+  void RemoveFromKnown(const ServerView* view,
+                       std::vector<ServerView*>* local_views);
+
+  // Resets the root of this connection.
+  void RemoveRoot();
+
+  void RemoveChildrenAsPartOfEmbed(const ViewId& view_id);
+
+  // Converts View(s) to ViewData(s) for transport. This assumes all the views
+  // are valid for the client. The parent of views the client is not allowed to
+  // see are set to NULL (in the returned ViewData(s)).
+  mojo::Array<mojo::ViewDataPtr> ViewsToViewDatas(
+      const std::vector<const ServerView*>& views);
+  mojo::ViewDataPtr ViewToViewData(const ServerView* view);
+
+  // Implementation of GetViewTree(). Adds |view| to |views| and recurses if
+  // CanDescendIntoViewForViewTree() returns true.
+  void GetViewTreeImpl(const ServerView* view,
+                       std::vector<const ServerView*>* views) const;
+
+  // Notify the client if the drawn state of any of the roots changes.
+  // |view| is the view that is changing to the drawn state |new_drawn_value|.
+  void NotifyDrawnStateChanged(const ServerView* view, bool new_drawn_value);
+
+  // Deletes all Views we own.
+  void DestroyViews();
+
+  bool PrepareForEmbed(const ViewId& view_id);
+
+  // ViewManagerService:
+  void CreateView(
+      mojo::Id transport_view_id,
+      const mojo::Callback<void(mojo::ErrorCode)>& callback) override;
+  void DeleteView(mojo::Id transport_view_id,
+                  const mojo::Callback<void(bool)>& callback) override;
+  void AddView(mojo::Id parent_id,
+               mojo::Id child_id,
+               const mojo::Callback<void(bool)>& callback) override;
+  void RemoveViewFromParent(
+      mojo::Id view_id,
+      const mojo::Callback<void(bool)>& callback) override;
+  void ReorderView(mojo::Id view_id,
+                   mojo::Id relative_view_id,
+                   mojo::OrderDirection direction,
+                   const mojo::Callback<void(bool)>& callback) override;
+  void GetViewTree(mojo::Id view_id,
+                   const mojo::Callback<void(mojo::Array<mojo::ViewDataPtr>)>&
+                       callback) override;
+  void SetViewSurfaceId(mojo::Id view_id,
+                        mojo::SurfaceIdPtr surface_id,
+                        const mojo::Callback<void(bool)>& callback) override;
+  void SetViewBounds(mojo::Id view_id,
+                     mojo::RectPtr bounds,
+                     const mojo::Callback<void(bool)>& callback) override;
+  void SetViewVisibility(mojo::Id view_id,
+                         bool visible,
+                         const mojo::Callback<void(bool)>& callback) override;
+  void SetViewProperty(mojo::Id view_id,
+                       const mojo::String& name,
+                       mojo::Array<uint8_t> value,
+                       const mojo::Callback<void(bool)>& callback) override;
+  void EmbedUrl(const mojo::String& url,
+                mojo::Id transport_view_id,
+                mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                mojo::ServiceProviderPtr exposed_services,
+                const mojo::Callback<void(bool)>& callback) override;
+  void Embed(mojo::Id transport_view_id,
+             mojo::ViewManagerClientPtr client,
+             const mojo::Callback<void(bool)>& callback) override;
+  void PerformAction(mojo::Id transport_view_id,
+                     const mojo::String& action,
+                     const mojo::Callback<void(bool)>& callback) override;
+
+  // AccessPolicyDelegate:
+  bool IsRootForAccessPolicy(const ViewId& id) const override;
+  bool IsViewKnownForAccessPolicy(const ServerView* view) const override;
+  bool IsViewRootOfAnotherConnectionForAccessPolicy(
+      const ServerView* view) const override;
+
+  ConnectionManager* connection_manager_;
+
+  // Id of this connection as assigned by ConnectionManager.
+  const mojo::ConnectionSpecificId id_;
+
+  // URL this connection was created for.
+  const std::string url_;
+
+  // ID of the connection that created us. If 0 it indicates either we were
+  // created by the root, or the connection that created us has been destroyed.
+  mojo::ConnectionSpecificId creator_id_;
+
+  // The URL of the app that embedded the app this connection was created for.
+  // NOTE: this is empty if the connection was created by way of directly
+  // supplying the ViewManagerClient.
+  const std::string creator_url_;
+
+  mojo::ViewManagerClient* client_;
+
+  scoped_ptr<AccessPolicy> access_policy_;
+
+  // The views created by this connection. This connection owns these objects.
+  ViewMap view_map_;
+
+  // The set of views that has been communicated to the client.
+  ViewIdSet known_views_;
+
+  // The root of this connection. This is a scoped_ptr to reinforce the
+  // connection may have no root. A connection has no root if either the root
+  // is destroyed or Embed() is invoked on the root.
+  scoped_ptr<ViewId> root_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceImpl);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
diff --git a/services/view_manager/view_manager_service_unittest.cc b/services/view_manager/view_manager_service_unittest.cc
new file mode 100644
index 0000000..5e23aa2
--- /dev/null
+++ b/services/view_manager/view_manager_service_unittest.cc
@@ -0,0 +1,471 @@
+// 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.
+
+#include <string>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/view_manager/cpp/util.h"
+#include "mojo/services/view_manager/interfaces/view_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/view_manager/client_connection.h"
+#include "services/view_manager/connection_manager.h"
+#include "services/view_manager/connection_manager_delegate.h"
+#include "services/view_manager/display_manager.h"
+#include "services/view_manager/ids.h"
+#include "services/view_manager/server_view.h"
+#include "services/view_manager/test_change_tracker.h"
+#include "services/view_manager/view_manager_service_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+using mojo::Array;
+using mojo::ErrorCode;
+using mojo::InterfaceRequest;
+using mojo::ServiceProvider;
+using mojo::ServiceProviderPtr;
+using mojo::String;
+using mojo::ViewDataPtr;
+
+namespace view_manager {
+namespace {
+
+// -----------------------------------------------------------------------------
+
+// ViewManagerClient implementation that logs all calls to a TestChangeTracker.
+// TODO(sky): refactor so both this and ViewManagerServiceAppTest share code.
+class TestViewManagerClient : public mojo::ViewManagerClient {
+ public:
+  TestViewManagerClient() {}
+  ~TestViewManagerClient() override {}
+
+  TestChangeTracker* tracker() { return &tracker_; }
+
+ private:
+  // ViewManagerClient:
+  void OnEmbed(uint16_t connection_id,
+               const String& embedder_url,
+               ViewDataPtr root,
+               mojo::ViewManagerServicePtr view_manager_service,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services,
+               mojo::ScopedMessagePipeHandle window_manager_pipe) override {
+    tracker_.OnEmbed(connection_id, embedder_url, root.Pass());
+  }
+  void OnEmbeddedAppDisconnected(uint32_t view) override {
+    tracker_.OnEmbeddedAppDisconnected(view);
+  }
+  void OnViewBoundsChanged(uint32_t view,
+                           mojo::RectPtr old_bounds,
+                           mojo::RectPtr new_bounds) override {
+    tracker_.OnViewBoundsChanged(view, old_bounds.Pass(), new_bounds.Pass());
+  }
+  void OnViewViewportMetricsChanged(
+      mojo::ViewportMetricsPtr old_metrics,
+      mojo::ViewportMetricsPtr new_metrics) override {
+    tracker_.OnViewViewportMetricsChanged(old_metrics.Pass(),
+                                          new_metrics.Pass());
+  }
+  void OnViewHierarchyChanged(uint32_t view,
+                              uint32_t new_parent,
+                              uint32_t old_parent,
+                              Array<ViewDataPtr> views) override {
+    tracker_.OnViewHierarchyChanged(view, new_parent, old_parent, views.Pass());
+  }
+  void OnViewReordered(uint32_t view_id,
+                       uint32_t relative_view_id,
+                       mojo::OrderDirection direction) override {
+    tracker_.OnViewReordered(view_id, relative_view_id, direction);
+  }
+  void OnViewDeleted(uint32_t view) override { tracker_.OnViewDeleted(view); }
+  void OnViewVisibilityChanged(uint32_t view, bool visible) override {
+    tracker_.OnViewVisibilityChanged(view, visible);
+  }
+  void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
+    tracker_.OnViewDrawnStateChanged(view, drawn);
+  }
+  void OnViewSharedPropertyChanged(uint32_t view,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override {
+    tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
+  }
+  void OnViewInputEvent(uint32_t view,
+                        mojo::EventPtr event,
+                        const mojo::Callback<void()>& callback) override {
+    tracker_.OnViewInputEvent(view, event.Pass());
+  }
+  void OnPerformAction(uint32_t view_id,
+                       const String& name,
+                       const mojo::Callback<void(bool)>& callback) override {}
+
+  TestChangeTracker tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestViewManagerClient);
+};
+
+// -----------------------------------------------------------------------------
+
+// ClientConnection implementation that vends TestViewManagerClient.
+class TestClientConnection : public ClientConnection {
+ public:
+  explicit TestClientConnection(scoped_ptr<ViewManagerServiceImpl> service_impl)
+      : ClientConnection(service_impl.Pass(), &client_) {}
+  ~TestClientConnection() override {}
+
+  TestViewManagerClient* client() { return &client_; }
+
+ private:
+  TestViewManagerClient client_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestClientConnection);
+};
+
+// -----------------------------------------------------------------------------
+
+// Empty implementation of ConnectionManagerDelegate.
+class TestConnectionManagerDelegate : public ConnectionManagerDelegate {
+ public:
+  TestConnectionManagerDelegate() : last_connection_(nullptr) {}
+  ~TestConnectionManagerDelegate() override {}
+
+  TestViewManagerClient* last_client() {
+    return last_connection_ ? last_connection_->client() : nullptr;
+  }
+
+  TestClientConnection* last_connection() { return last_connection_; }
+
+ private:
+  // ConnectionManagerDelegate:
+  void OnLostConnectionToWindowManager() override {}
+
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const std::string& url,
+      const ViewId& root_id) override {
+    scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+        connection_manager, creator_id, creator_url, url, root_id));
+    last_connection_ = new TestClientConnection(service.Pass());
+    return last_connection_;
+  }
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr client) override {
+    NOTIMPLEMENTED();
+    return nullptr;
+  }
+
+  TestClientConnection* last_connection_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnectionManagerDelegate);
+};
+
+// -----------------------------------------------------------------------------
+
+// Empty implementation of DisplayManager.
+class TestDisplayManager : public DisplayManager {
+ public:
+  TestDisplayManager() {}
+  ~TestDisplayManager() override {}
+
+  // DisplayManager:
+  void Init(ConnectionManager* connection_manager) override {}
+  void SchedulePaint(const ServerView* view, const gfx::Rect& bounds) override {
+  }
+  void SetViewportSize(const gfx::Size& size) override {}
+  const mojo::ViewportMetrics& GetViewportMetrics() override {
+    return display_metrices_;
+  }
+
+ private:
+  mojo::ViewportMetrics display_metrices_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDisplayManager);
+};
+
+// -----------------------------------------------------------------------------
+
+// Empty implementation of WindowManagerInternal.
+class TestWindowManagerInternal : public mojo::WindowManagerInternal {
+ public:
+  TestWindowManagerInternal() {}
+  ~TestWindowManagerInternal() override {}
+
+  // WindowManagerInternal:
+  void CreateWindowManagerForViewManagerClient(
+      uint16_t connection_id,
+      mojo::ScopedMessagePipeHandle window_manager_pipe) override {}
+  void SetViewManagerClient(mojo::ScopedMessagePipeHandle) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestWindowManagerInternal);
+};
+
+}  // namespace
+
+// -----------------------------------------------------------------------------
+
+class ViewManagerServiceTest : public testing::Test {
+ public:
+  ViewManagerServiceTest() : wm_client_(nullptr) {}
+  ~ViewManagerServiceTest() override {}
+
+  // ViewManagerServiceImpl for the window manager.
+  ViewManagerServiceImpl* wm_connection() {
+    return connection_manager_->GetConnection(1);
+  }
+
+  TestViewManagerClient* last_view_manager_client() {
+    return delegate_.last_client();
+  }
+
+  TestClientConnection* last_client_connection() {
+    return delegate_.last_connection();
+  }
+
+  ConnectionManager* connection_manager() { return connection_manager_.get(); }
+
+  TestViewManagerClient* wm_client() { return wm_client_; }
+
+ protected:
+  // testing::Test:
+  void SetUp() override {
+    connection_manager_.reset(new ConnectionManager(
+        &delegate_, scoped_ptr<DisplayManager>(new TestDisplayManager),
+        &wm_internal_));
+    scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+        connection_manager_.get(), kInvalidConnectionId, std::string(),
+        std::string("mojo:window_manager"), RootViewId()));
+    scoped_ptr<TestClientConnection> client_connection(
+        new TestClientConnection(service.Pass()));
+    wm_client_ = client_connection->client();
+    ASSERT_TRUE(wm_client_ != nullptr);
+    connection_manager_->SetWindowManagerClientConnection(
+        client_connection.Pass());
+    ASSERT_TRUE(wm_connection() != nullptr);
+    ASSERT_TRUE(wm_connection()->root() != nullptr);
+  }
+
+ private:
+  // TestViewManagerClient that is used for the WM connection.
+  TestViewManagerClient* wm_client_;
+
+  TestWindowManagerInternal wm_internal_;
+  TestConnectionManagerDelegate delegate_;
+  scoped_ptr<ConnectionManager> connection_manager_;
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceTest);
+};
+
+namespace {
+
+const ServerView* GetFirstCloned(const ServerView* view) {
+  for (const ServerView* child : view->GetChildren()) {
+    if (child->id() == ClonedViewId())
+      return child;
+  }
+  return nullptr;
+}
+
+// Provides common setup for animation tests. Creates the following views:
+// 0,1 (the root, provided by view manager)
+//   1,1 the second connection is embedded here (view owned by wm_connection()).
+//     2,1 bounds=1,2 11x22
+//       2,2 bounds=2,3 6x7
+//         2,3 bounds=3,4 6x7
+// CloneAndAnimate() is invoked for 2,2.
+void SetUpAnimate1(ViewManagerServiceTest* test, ViewId* embed_view_id) {
+  *embed_view_id = ViewId(test->wm_connection()->id(), 1);
+  EXPECT_EQ(ErrorCode::NONE, test->wm_connection()->CreateView(*embed_view_id));
+  EXPECT_TRUE(test->wm_connection()->SetViewVisibility(*embed_view_id, true));
+  EXPECT_TRUE(test->wm_connection()->AddView(*(test->wm_connection()->root()),
+                                             *embed_view_id));
+  test->wm_connection()->EmbedUrl(std::string(), *embed_view_id, nullptr,
+                                  nullptr);
+  ViewManagerServiceImpl* connection1 =
+      test->connection_manager()->GetConnectionWithRoot(*embed_view_id);
+  ASSERT_TRUE(connection1 != nullptr);
+  ASSERT_NE(connection1, test->wm_connection());
+
+  const ViewId child1(connection1->id(), 1);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child1));
+  const ViewId child2(connection1->id(), 2);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child2));
+  const ViewId child3(connection1->id(), 3);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child3));
+
+  ServerView* v1 = connection1->GetView(child1);
+  v1->SetVisible(true);
+  v1->SetBounds(gfx::Rect(1, 2, 11, 22));
+  ServerView* v2 = connection1->GetView(child2);
+  v2->SetVisible(true);
+  v2->SetBounds(gfx::Rect(2, 3, 6, 7));
+  ServerView* v3 = connection1->GetView(child3);
+  v3->SetVisible(true);
+  v3->SetBounds(gfx::Rect(3, 4, 6, 7));
+
+  EXPECT_TRUE(connection1->AddView(*embed_view_id, child1));
+  EXPECT_TRUE(connection1->AddView(child1, child2));
+  EXPECT_TRUE(connection1->AddView(child2, child3));
+
+  TestViewManagerClient* connection1_client = test->last_view_manager_client();
+  connection1_client->tracker()->changes()->clear();
+  test->wm_client()->tracker()->changes()->clear();
+  EXPECT_TRUE(test->connection_manager()->CloneAndAnimate(child2));
+  EXPECT_TRUE(connection1_client->tracker()->changes()->empty());
+  EXPECT_TRUE(test->wm_client()->tracker()->changes()->empty());
+
+  // We cloned v2. The cloned view ends up as a sibling of it.
+  const ServerView* cloned_view = GetFirstCloned(connection1->GetView(child1));
+  ASSERT_TRUE(cloned_view);
+  // |cloned_view| should have one and only one cloned child (corresponds to
+  // |child3|).
+  ASSERT_EQ(1u, cloned_view->GetChildren().size());
+  EXPECT_TRUE(cloned_view->GetChildren()[0]->id() == ClonedViewId());
+
+  // Cloned views should match the bounds of the view they were cloned from.
+  EXPECT_EQ(v2->bounds(), cloned_view->bounds());
+  EXPECT_EQ(v3->bounds(), cloned_view->GetChildren()[0]->bounds());
+
+  // Cloned views are owned by the ConnectionManager and shouldn't be returned
+  // from ViewManagerServiceImpl::GetView.
+  EXPECT_TRUE(connection1->GetView(ClonedViewId()) == nullptr);
+  EXPECT_TRUE(test->wm_connection()->GetView(ClonedViewId()) == nullptr);
+}
+
+}  // namespace
+
+// Verifies ViewManagerService::GetViewTree() doesn't return cloned views.
+TEST_F(ViewManagerServiceTest, ConnectionsCantSeeClonedViews) {
+  ViewId embed_view_id;
+  EXPECT_NO_FATAL_FAILURE(SetUpAnimate1(this, &embed_view_id));
+
+  ViewManagerServiceImpl* connection1 =
+      connection_manager()->GetConnectionWithRoot(embed_view_id);
+
+  const ViewId child1(connection1->id(), 1);
+  const ViewId child2(connection1->id(), 2);
+  const ViewId child3(connection1->id(), 3);
+
+  // Verify the root doesn't see any cloned views.
+  std::vector<const ServerView*> views(
+      wm_connection()->GetViewTree(*wm_connection()->root()));
+  ASSERT_EQ(5u, views.size());
+  ASSERT_TRUE(views[0]->id() == *wm_connection()->root());
+  ASSERT_TRUE(views[1]->id() == embed_view_id);
+  ASSERT_TRUE(views[2]->id() == child1);
+  ASSERT_TRUE(views[3]->id() == child2);
+  ASSERT_TRUE(views[4]->id() == child3);
+
+  // Verify connection1 doesn't see any cloned views.
+  std::vector<const ServerView*> v1_views(
+      connection1->GetViewTree(embed_view_id));
+  ASSERT_EQ(4u, v1_views.size());
+  ASSERT_TRUE(v1_views[0]->id() == embed_view_id);
+  ASSERT_TRUE(v1_views[1]->id() == child1);
+  ASSERT_TRUE(v1_views[2]->id() == child2);
+  ASSERT_TRUE(v1_views[3]->id() == child3);
+}
+
+TEST_F(ViewManagerServiceTest, ClonedViewsPromotedOnConnectionClose) {
+  ViewId embed_view_id;
+  EXPECT_NO_FATAL_FAILURE(SetUpAnimate1(this, &embed_view_id));
+
+  // Destroy connection1, which should force the cloned view to become a child
+  // of where it was embedded (the embedded view still exists).
+  connection_manager()->OnConnectionError(last_client_connection());
+
+  ServerView* embed_view = wm_connection()->GetView(embed_view_id);
+  ASSERT_TRUE(embed_view != nullptr);
+  const ServerView* cloned_view = GetFirstCloned(embed_view);
+  ASSERT_TRUE(cloned_view);
+  ASSERT_EQ(1u, cloned_view->GetChildren().size());
+  EXPECT_TRUE(cloned_view->GetChildren()[0]->id() == ClonedViewId());
+
+  // Because the cloned view changed parents its bounds should have changed.
+  EXPECT_EQ(gfx::Rect(3, 5, 6, 7), cloned_view->bounds());
+  // The bounds of the cloned child should not have changed though.
+  EXPECT_EQ(gfx::Rect(3, 4, 6, 7), cloned_view->GetChildren()[0]->bounds());
+}
+
+TEST_F(ViewManagerServiceTest, ClonedViewsPromotedOnHide) {
+  ViewId embed_view_id;
+  EXPECT_NO_FATAL_FAILURE(SetUpAnimate1(this, &embed_view_id));
+
+  ViewManagerServiceImpl* connection1 =
+      connection_manager()->GetConnectionWithRoot(embed_view_id);
+
+  // Hide the parent of the cloned view, which should force the cloned view to
+  // become a sibling of the parent.
+  const ServerView* view_to_hide =
+      connection1->GetView(ViewId(connection1->id(), 1));
+  ASSERT_TRUE(connection1->SetViewVisibility(view_to_hide->id(), false));
+
+  const ServerView* cloned_view = GetFirstCloned(view_to_hide->parent());
+  ASSERT_TRUE(cloned_view);
+  ASSERT_EQ(1u, cloned_view->GetChildren().size());
+  EXPECT_TRUE(cloned_view->GetChildren()[0]->id() == ClonedViewId());
+  EXPECT_EQ(2u, cloned_view->parent()->GetChildren().size());
+  EXPECT_TRUE(cloned_view->parent()->GetChildren()[1] == cloned_view);
+}
+
+// Clone and animate on a tree with more depth. Basically that of
+// SetUpAnimate1() but cloning 2,1.
+TEST_F(ViewManagerServiceTest, CloneAndAnimateLargerDepth) {
+  const ViewId embed_view_id(wm_connection()->id(), 1);
+  EXPECT_EQ(ErrorCode::NONE, wm_connection()->CreateView(embed_view_id));
+  EXPECT_TRUE(wm_connection()->SetViewVisibility(embed_view_id, true));
+  EXPECT_TRUE(
+      wm_connection()->AddView(*(wm_connection()->root()), embed_view_id));
+  wm_connection()->EmbedUrl(std::string(), embed_view_id, nullptr, nullptr);
+  ViewManagerServiceImpl* connection1 =
+      connection_manager()->GetConnectionWithRoot(embed_view_id);
+  ASSERT_TRUE(connection1 != nullptr);
+  ASSERT_NE(connection1, wm_connection());
+
+  const ViewId child1(connection1->id(), 1);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child1));
+  const ViewId child2(connection1->id(), 2);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child2));
+  const ViewId child3(connection1->id(), 3);
+  EXPECT_EQ(ErrorCode::NONE, connection1->CreateView(child3));
+
+  ServerView* v1 = connection1->GetView(child1);
+  v1->SetVisible(true);
+  connection1->GetView(child2)->SetVisible(true);
+  connection1->GetView(child3)->SetVisible(true);
+
+  EXPECT_TRUE(connection1->AddView(embed_view_id, child1));
+  EXPECT_TRUE(connection1->AddView(child1, child2));
+  EXPECT_TRUE(connection1->AddView(child2, child3));
+
+  TestViewManagerClient* connection1_client = last_view_manager_client();
+  connection1_client->tracker()->changes()->clear();
+  wm_client()->tracker()->changes()->clear();
+  EXPECT_TRUE(connection_manager()->CloneAndAnimate(child1));
+  EXPECT_TRUE(connection1_client->tracker()->changes()->empty());
+  EXPECT_TRUE(wm_client()->tracker()->changes()->empty());
+
+  // We cloned v1. The cloned view ends up as a sibling of it.
+  const ServerView* cloned_view = GetFirstCloned(v1->parent());
+  ASSERT_TRUE(cloned_view);
+  // |cloned_view| should have a child and its child should have a child.
+  ASSERT_EQ(1u, cloned_view->GetChildren().size());
+  const ServerView* cloned_view_child = cloned_view->GetChildren()[0];
+  EXPECT_EQ(1u, cloned_view_child->GetChildren().size());
+  EXPECT_TRUE(cloned_view_child->id() == ClonedViewId());
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/window_manager_access_policy.cc b/services/view_manager/window_manager_access_policy.cc
new file mode 100644
index 0000000..03fd510
--- /dev/null
+++ b/services/view_manager/window_manager_access_policy.cc
@@ -0,0 +1,97 @@
+// 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.
+
+#include "services/view_manager/window_manager_access_policy.h"
+
+#include "services/view_manager/access_policy_delegate.h"
+#include "services/view_manager/server_view.h"
+
+namespace view_manager {
+
+// TODO(sky): document why this differs from default for each case. Maybe want
+// to subclass DefaultAccessPolicy.
+
+WindowManagerAccessPolicy::WindowManagerAccessPolicy(
+    mojo::ConnectionSpecificId connection_id,
+    AccessPolicyDelegate* delegate)
+    : connection_id_(connection_id), delegate_(delegate) {
+}
+
+WindowManagerAccessPolicy::~WindowManagerAccessPolicy() {
+}
+
+bool WindowManagerAccessPolicy::CanRemoveViewFromParent(
+    const ServerView* view) const {
+  return true;
+}
+
+bool WindowManagerAccessPolicy::CanAddView(const ServerView* parent,
+                                           const ServerView* child) const {
+  return true;
+}
+
+bool WindowManagerAccessPolicy::CanReorderView(
+    const ServerView* view,
+    const ServerView* relative_view,
+    mojo::OrderDirection direction) const {
+  return true;
+}
+
+bool WindowManagerAccessPolicy::CanDeleteView(const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanGetViewTree(const ServerView* view) const {
+  return view->id() != ClonedViewId();
+}
+
+bool WindowManagerAccessPolicy::CanDescendIntoViewForViewTree(
+    const ServerView* view) const {
+  return view->id() != ClonedViewId();
+}
+
+bool WindowManagerAccessPolicy::CanEmbed(const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanChangeViewVisibility(
+    const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanSetViewSurfaceId(
+    const ServerView* view) const {
+  if (delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view))
+    return false;
+  return view->id().connection_id == connection_id_ ||
+         (delegate_->IsRootForAccessPolicy(view->id()));
+}
+
+bool WindowManagerAccessPolicy::CanSetViewBounds(const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanSetViewProperties(
+    const ServerView* view) const {
+  return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::ShouldNotifyOnHierarchyChange(
+    const ServerView* view,
+    const ServerView** new_parent,
+    const ServerView** old_parent) const {
+  if (view->id() == ClonedViewId())
+    return false;
+
+  // Notify if we've already told the window manager about the view, or if we've
+  // already told the window manager about the parent. The later handles the
+  // case of a view that wasn't parented to the root getting added to the root.
+  return IsViewKnown(view) || (*new_parent && IsViewKnown(*new_parent));
+}
+
+bool WindowManagerAccessPolicy::IsViewKnown(const ServerView* view) const {
+  return delegate_->IsViewKnownForAccessPolicy(view);
+}
+
+}  // namespace view_manager
diff --git a/services/view_manager/window_manager_access_policy.h b/services/view_manager/window_manager_access_policy.h
new file mode 100644
index 0000000..1975aec
--- /dev/null
+++ b/services/view_manager/window_manager_access_policy.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
+#define SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "services/view_manager/access_policy.h"
+
+namespace view_manager {
+
+class AccessPolicyDelegate;
+
+class WindowManagerAccessPolicy : public AccessPolicy {
+ public:
+  WindowManagerAccessPolicy(mojo::ConnectionSpecificId connection_id,
+                            AccessPolicyDelegate* delegate);
+  ~WindowManagerAccessPolicy() override;
+
+  // AccessPolicy:
+  bool CanRemoveViewFromParent(const ServerView* view) const override;
+  bool CanAddView(const ServerView* parent,
+                  const ServerView* child) const override;
+  bool CanReorderView(const ServerView* view,
+                      const ServerView* relative_view,
+                      mojo::OrderDirection direction) const override;
+  bool CanDeleteView(const ServerView* view) const override;
+  bool CanGetViewTree(const ServerView* view) const override;
+  bool CanDescendIntoViewForViewTree(const ServerView* view) const override;
+  bool CanEmbed(const ServerView* view) const override;
+  bool CanChangeViewVisibility(const ServerView* view) const override;
+  bool CanSetViewSurfaceId(const ServerView* view) const override;
+  bool CanSetViewBounds(const ServerView* view) const override;
+  bool CanSetViewProperties(const ServerView* view) const override;
+  bool ShouldNotifyOnHierarchyChange(
+      const ServerView* view,
+      const ServerView** new_parent,
+      const ServerView** old_parent) const override;
+
+ private:
+  bool IsViewKnown(const ServerView* view) const;
+
+  const mojo::ConnectionSpecificId connection_id_;
+  AccessPolicyDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerAccessPolicy);
+};
+
+}  // namespace view_manager
+
+#endif  // SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
diff --git a/services/window_manager/BUILD.gn b/services/window_manager/BUILD.gn
new file mode 100644
index 0000000..d118586
--- /dev/null
+++ b/services/window_manager/BUILD.gn
@@ -0,0 +1,130 @@
+# 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.
+
+import("//build/config/ui.gni")
+import("//mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("window_manager") {
+  sources = [
+    "main.cc",
+  ]
+
+  public_deps = [
+    ":lib",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common:tracing_impl",
+    "//mojo/services/view_manager/cpp",
+  ]
+}
+
+source_set("lib") {
+  sources = [
+    "basic_focus_rules.cc",
+    "basic_focus_rules.h",
+    "capture_controller.cc",
+    "capture_controller.h",
+    "capture_controller_observer.h",
+    "focus_controller.cc",
+    "focus_controller.h",
+    "focus_controller_observer.h",
+    "focus_rules.h",
+    "native_viewport_event_dispatcher_impl.cc",
+    "native_viewport_event_dispatcher_impl.h",
+    "view_event_dispatcher.cc",
+    "view_event_dispatcher.h",
+    "view_target.cc",
+    "view_target.h",
+    "view_targeter.cc",
+    "view_targeter.h",
+    "window_manager_app.cc",
+    "window_manager_app.h",
+    "window_manager_delegate.h",
+    "window_manager_impl.cc",
+    "window_manager_impl.h",
+    "window_manager_root.cc",
+    "window_manager_root.h",
+    "window_manager_root_android.cc",
+    "window_manager_root_linux.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//ui/base",
+    "//ui/events",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/converters/geometry",
+    "//mojo/converters/input_events",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/native_viewport/interfaces",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/window_manager/interfaces",
+  ]
+}
+
+test("window_manager_unittests") {
+  sources = [
+    "focus_controller_unittest.cc",
+    "run_all_unittests.cc",
+    "view_target_unittest.cc",
+    "view_targeter_unittest.cc",
+    "window_manager_test_util.cc",
+    "window_manager_test_util.h",
+  ]
+
+  public_deps = [
+    ":lib",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//mojo/converters/geometry",
+    "//mojo/edk/base_edk",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/application",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/view_manager/interfaces",
+    "//mojo/services/window_manager/interfaces",
+    "//testing/gtest",
+    "//ui/events:test_support",
+    "//ui/gfx",
+    "//ui/gfx:test_support",
+    "//ui/gl",
+  ]
+
+  if (use_x11) {
+    deps += [ "//ui/gfx/x" ]
+  }
+}
+
+mojo_native_application("window_manager_apptests") {
+  testonly = true
+
+  sources = [
+    "window_manager_apptest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/application:test_support",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/system:system",
+    "//mojo/services/view_manager/cpp",
+    "//mojo/services/window_manager/interfaces",
+  ]
+
+  data_deps = [
+    ":window_manager($default_toolchain)",
+  ]
+}
diff --git a/services/window_manager/basic_focus_rules.cc b/services/window_manager/basic_focus_rules.cc
new file mode 100644
index 0000000..862f3e4
--- /dev/null
+++ b/services/window_manager/basic_focus_rules.cc
@@ -0,0 +1,171 @@
+// 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.
+
+#include "services/window_manager/basic_focus_rules.h"
+
+#include "base/macros.h"
+#include "mojo/services/view_manager/cpp/view.h"
+
+using mojo::View;
+
+namespace window_manager {
+
+BasicFocusRules::BasicFocusRules(View* window_container)
+    : window_container_(window_container) {
+}
+
+BasicFocusRules::~BasicFocusRules() {}
+
+bool BasicFocusRules::SupportsChildActivation(View* view) const {
+  return true;
+}
+
+bool BasicFocusRules::IsToplevelView(View* view) const {
+  if (!IsViewParentedToWindowContainer(view))
+    return false;
+
+  // The window must exist within a container that supports activation.
+  // The window cannot be blocked by a modal transient.
+  return SupportsChildActivation(view->parent());
+}
+
+bool BasicFocusRules::CanActivateView(View* view) const {
+  if (!view)
+    return true;
+
+  // Only toplevel windows can be activated
+  if (!IsToplevelView(view))
+    return false;
+
+  // The view must be visible.
+  if (!view->visible())
+    return false;
+
+  // TODO(erg): The aura version of this class asks the aura::Window's
+  // ActivationDelegate whether the window is activatable.
+
+  // A window must be focusable to be activatable. We don't call
+  // CanFocusWindow() from here because it will call back to us via
+  // GetActivatableWindow().
+  if (!CanFocusViewImpl(view))
+    return false;
+
+  // TODO(erg): In the aura version, we also check whether the window is
+  // blocked by a modal transient window.
+
+  return true;
+}
+
+bool BasicFocusRules::CanFocusView(View* view) const {
+  // It is possible to focus a NULL window, it is equivalent to clearing focus.
+  if (!view)
+    return true;
+
+  // The focused view is always inside the active view, so views that aren't
+  // activatable can't contain the focused view.
+  View* activatable = GetActivatableView(view);
+  if (!activatable || !activatable->Contains(view))
+    return false;
+  return CanFocusViewImpl(view);
+}
+
+View* BasicFocusRules::GetToplevelView(View* view) const {
+  View* parent = view->parent();
+  View* child = view;
+  while (parent) {
+    if (IsToplevelView(child))
+      return child;
+
+    parent = parent->parent();
+    child = child->parent();
+  }
+
+  return nullptr;
+}
+
+View* BasicFocusRules::GetActivatableView(View* view) const {
+  View* parent = view->parent();
+  View* child = view;
+  while (parent) {
+    if (CanActivateView(child))
+      return child;
+
+    // TODO(erg): In the aura version of this class, we have a whole bunch of
+    // checks to support modal transient windows, and transient parents.
+
+    parent = parent->parent();
+    child = child->parent();
+  }
+
+  return nullptr;
+}
+
+View* BasicFocusRules::GetFocusableView(View* view) const {
+  if (CanFocusView(view))
+    return view;
+
+  // |view| may be in a hierarchy that is non-activatable, in which case we
+  // need to cut over to the activatable hierarchy.
+  View* activatable = GetActivatableView(view);
+  if (!activatable) {
+    // There may not be a related activatable hierarchy to cut over to, in which
+    // case we try an unrelated one.
+    View* toplevel = GetToplevelView(view);
+    if (toplevel)
+      activatable = GetNextActivatableView(toplevel);
+    if (!activatable)
+      return nullptr;
+  }
+
+  if (!activatable->Contains(view)) {
+    // If there's already a child window focused in the activatable hierarchy,
+    // just use that (i.e. don't shift focus), otherwise we need to at least cut
+    // over to the activatable hierarchy.
+    View* focused = GetFocusableView(activatable);
+    return activatable->Contains(focused) ? focused : activatable;
+  }
+
+  while (view && !CanFocusView(view))
+    view = view->parent();
+  return view;
+}
+
+View* BasicFocusRules::GetNextActivatableView(View* activatable) const {
+  DCHECK(activatable);
+
+  // In the basic scenarios handled by BasicFocusRules, the pool of activatable
+  // windows is limited to the |ignore|'s siblings.
+  const View::Children& siblings = activatable->parent()->children();
+  DCHECK(!siblings.empty());
+
+  for (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) {
+    View* cur = *rit;
+    if (cur == activatable)
+      continue;
+    if (CanActivateView(cur))
+      return cur;
+  }
+  return nullptr;
+}
+
+// TODO(erg): aura::Window::CanFocus() exists. View::CanFocus() does
+// not. This is a hack that does everything that Window::CanFocus() currently
+// does that doesn't require a delegate or an EventClient.
+bool BasicFocusRules::CanFocusViewImpl(View* view) const {
+  // TODO(erg): In unit tests, views will never be drawn, so we can't rely on
+  // IsDrawn() here.
+  if (IsViewParentedToWindowContainer(view))
+    return view->visible();
+
+  // TODO(erg): Add the intermediary delegate and event client checks once we
+  // have those.
+
+  return CanFocusViewImpl(view->parent());
+}
+
+bool BasicFocusRules::IsViewParentedToWindowContainer(View* view) const {
+  return view->parent() == window_container_;
+}
+
+}  // namespace mojo
diff --git a/services/window_manager/basic_focus_rules.h b/services/window_manager/basic_focus_rules.h
new file mode 100644
index 0000000..34482e1
--- /dev/null
+++ b/services/window_manager/basic_focus_rules.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
+#define SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
+
+#include "services/window_manager/focus_rules.h"
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+// The focusing rules used inside a window manager.
+//
+// This is intended to be a user supplyable, subclassable component passed to
+// WindowManagerApp, allowing for the creation of other window managers.
+class BasicFocusRules : public FocusRules {
+ public:
+  BasicFocusRules(mojo::View* window_container);
+  ~BasicFocusRules() override;
+
+ protected:
+  // Overridden from mojo::FocusRules:
+  bool SupportsChildActivation(mojo::View* view) const override;
+  bool IsToplevelView(mojo::View* view) const override;
+  bool CanActivateView(mojo::View* view) const override;
+  bool CanFocusView(mojo::View* view) const override;
+  mojo::View* GetToplevelView(mojo::View* view) const override;
+  mojo::View* GetActivatableView(mojo::View* view) const override;
+  mojo::View* GetFocusableView(mojo::View* view) const override;
+  mojo::View* GetNextActivatableView(mojo::View* activatable) const override;
+
+ private:
+  bool CanFocusViewImpl(mojo::View* view) const;
+
+  // Tests to see if |view| is in |window_container_|.
+  bool IsViewParentedToWindowContainer(mojo::View* view) const;
+
+  mojo::View* window_container_;
+
+  DISALLOW_COPY_AND_ASSIGN(BasicFocusRules);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
diff --git a/services/window_manager/capture_controller.cc b/services/window_manager/capture_controller.cc
new file mode 100644
index 0000000..b705677
--- /dev/null
+++ b/services/window_manager/capture_controller.cc
@@ -0,0 +1,103 @@
+// 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.
+
+#include "services/window_manager/capture_controller.h"
+
+#include "mojo/services/view_manager/cpp/view_property.h"
+#include "mojo/services/view_manager/cpp/view_tracker.h"
+#include "services/window_manager/capture_controller_observer.h"
+
+DECLARE_VIEW_PROPERTY_TYPE(window_manager::CaptureController*);
+
+namespace window_manager {
+
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(CaptureController*,
+                         kRootViewCaptureController,
+                         nullptr);
+}  // namespace
+
+CaptureController::CaptureController()
+    : capture_view_(nullptr) {}
+
+CaptureController::~CaptureController() {}
+
+void CaptureController::AddObserver(CaptureControllerObserver* observer) {
+  capture_controller_observers_.AddObserver(observer);
+}
+
+void CaptureController::RemoveObserver(CaptureControllerObserver* observer) {
+  capture_controller_observers_.RemoveObserver(observer);
+}
+
+void CaptureController::SetCapture(mojo::View* view) {
+  if (capture_view_ == view)
+    return;
+
+  if (capture_view_)
+    capture_view_->RemoveObserver(this);
+
+  mojo::View* old_capture_view = capture_view_;
+  capture_view_ = view;
+
+  if (capture_view_)
+    capture_view_->AddObserver(this);
+
+  NotifyCaptureChange(capture_view_, old_capture_view);
+}
+
+void CaptureController::ReleaseCapture(mojo::View* view) {
+  if (capture_view_ != view)
+    return;
+  SetCapture(nullptr);
+}
+
+mojo::View* CaptureController::GetCapture() {
+  return capture_view_;
+}
+
+void CaptureController::NotifyCaptureChange(mojo::View* new_capture,
+                                            mojo::View* old_capture) {
+  mojo::ViewTracker view_tracker;
+  if (new_capture)
+    view_tracker.Add(new_capture);
+  if (old_capture)
+    view_tracker.Add(old_capture);
+
+  FOR_EACH_OBSERVER(
+      CaptureControllerObserver, capture_controller_observers_,
+      OnCaptureChanged(view_tracker.Contains(new_capture) ? new_capture
+                                                          : nullptr));
+}
+
+void CaptureController::OnViewDestroying(mojo::View* view) {
+  if (view == capture_view_) {
+    view->RemoveObserver(this);
+    NotifyCaptureChange(nullptr, view);
+    capture_view_ = nullptr;
+  }
+}
+
+void SetCaptureController(mojo::View* root_view,
+                          CaptureController* capture_controller) {
+  DCHECK_EQ(root_view->GetRoot(), root_view);
+  root_view->SetLocalProperty(kRootViewCaptureController, capture_controller);
+}
+
+CaptureController* GetCaptureController(mojo::View* root_view) {
+  if (root_view)
+    DCHECK_EQ(root_view->GetRoot(), root_view);
+  return root_view ?
+      root_view->GetLocalProperty(kRootViewCaptureController) : nullptr;
+}
+
+mojo::View* GetCaptureView(mojo::View* view) {
+  mojo::View* root = view->GetRoot();
+  if (!root)
+    return nullptr;
+  CaptureController* controller = GetCaptureController(root);
+  return controller ? controller->GetCapture() : nullptr;
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/capture_controller.h b/services/window_manager/capture_controller.h
new file mode 100644
index 0000000..3e323c6
--- /dev/null
+++ b/services/window_manager/capture_controller.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
+#define SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
+
+#include "base/observer_list.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+
+namespace window_manager {
+
+class CaptureControllerObserver;
+
+// Manages input capture. A view which has capture will take all input events.
+class CaptureController : public mojo::ViewObserver {
+ public:
+  CaptureController();
+  ~CaptureController() override;
+
+  void AddObserver(CaptureControllerObserver* observer);
+  void RemoveObserver(CaptureControllerObserver* observer);
+
+  void SetCapture(mojo::View* view);
+  void ReleaseCapture(mojo::View* view);
+  mojo::View* GetCapture();
+
+ private:
+  void NotifyCaptureChange(mojo::View* new_capture, mojo::View* old_capture);
+
+  // Overridden from ViewObserver:
+  void OnViewDestroying(mojo::View* view) override;
+
+  // The current capture view. Null if there is no capture view.
+  mojo::View* capture_view_;
+
+  base::ObserverList<CaptureControllerObserver> capture_controller_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(CaptureController);
+};
+
+void SetCaptureController(mojo::View* view,
+                          CaptureController* capture_controller);
+CaptureController* GetCaptureController(mojo::View* view);
+mojo::View* GetCaptureView(mojo::View* view);
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
diff --git a/services/window_manager/capture_controller_observer.h b/services/window_manager/capture_controller_observer.h
new file mode 100644
index 0000000..fc62d60
--- /dev/null
+++ b/services/window_manager/capture_controller_observer.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
+#define SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
+
+namespace window_manager {
+
+class CaptureControllerObserver {
+ public:
+  virtual void OnCaptureChanged(mojo::View* gained_capture) = 0;
+
+ protected:
+  virtual ~CaptureControllerObserver() {}
+};
+
+}  // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
diff --git a/services/window_manager/focus_controller.cc b/services/window_manager/focus_controller.cc
new file mode 100644
index 0000000..dd1ec6a
--- /dev/null
+++ b/services/window_manager/focus_controller.cc
@@ -0,0 +1,317 @@
+// 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.
+
+#include "services/window_manager/focus_controller.h"
+
+#include "base/auto_reset.h"
+#include "mojo/services/view_manager/cpp/view_property.h"
+#include "mojo/services/view_manager/cpp/view_tracker.h"
+#include "services/window_manager/focus_controller_observer.h"
+#include "services/window_manager/focus_rules.h"
+#include "services/window_manager/view_target.h"
+#include "services/window_manager/window_manager_app.h"
+#include "ui/events/event.h"
+
+DECLARE_VIEW_PROPERTY_TYPE(window_manager::FocusController*);
+
+using mojo::View;
+
+namespace window_manager {
+
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(FocusController*, kRootViewFocusController, nullptr);
+}  // namespace
+
+FocusController::FocusController(scoped_ptr<FocusRules> rules)
+    : active_view_(nullptr),
+      focused_view_(nullptr),
+      updating_focus_(false),
+      updating_activation_(false),
+      rules_(rules.Pass()),
+      observer_manager_(this) {
+  DCHECK(rules_);
+}
+
+FocusController::~FocusController() {}
+
+void FocusController::AddObserver(FocusControllerObserver* observer) {
+  focus_controller_observers_.AddObserver(observer);
+}
+
+void FocusController::RemoveObserver(FocusControllerObserver* observer) {
+  focus_controller_observers_.RemoveObserver(observer);
+}
+
+void FocusController::ActivateView(View* view) {
+  FocusView(view);
+}
+
+void FocusController::DeactivateView(View* view) {
+  if (view)
+    FocusView(rules_->GetNextActivatableView(view));
+}
+
+View* FocusController::GetActiveView() {
+  return active_view_;
+}
+
+View* FocusController::GetActivatableView(View* view) {
+  return rules_->GetActivatableView(view);
+}
+
+View* FocusController::GetToplevelView(View* view) {
+  return rules_->GetToplevelView(view);
+}
+
+bool FocusController::CanActivateView(View* view) const {
+  return rules_->CanActivateView(view);
+}
+
+void FocusController::FocusView(View* view) {
+  if (view &&
+      (view->Contains(focused_view_) || view->Contains(active_view_))) {
+    return;
+  }
+
+  // Focusing a window also activates its containing activatable window. Note
+  // that the rules could redirect activation activation and/or focus.
+  View* focusable = rules_->GetFocusableView(view);
+  View* activatable =
+      focusable ? rules_->GetActivatableView(focusable) : nullptr;
+
+  // We need valid focusable/activatable windows in the event we're not clearing
+  // focus. "Clearing focus" is inferred by whether or not |window| passed to
+  // this function is non-null.
+  if (view && (!focusable || !activatable))
+    return;
+  DCHECK((focusable && activatable) || !view);
+
+  // Activation change observers may change the focused window. If this happens
+  // we must not adjust the focus below since this will clobber that change.
+  View* last_focused_view = focused_view_;
+  if (!updating_activation_)
+    SetActiveView(view, activatable);
+
+  // If the window's ActivationChangeObserver shifted focus to a valid window,
+  // we don't want to focus the window we thought would be focused by default.
+  bool activation_changed_focus = last_focused_view != focused_view_;
+  if (!updating_focus_ && (!activation_changed_focus || !focused_view_)) {
+    if (active_view_ && focusable)
+      DCHECK(active_view_->Contains(focusable));
+    SetFocusedView(focusable);
+  }
+}
+
+void FocusController::ResetFocusWithinActiveView(View* view) {
+  DCHECK(view);
+  if (!active_view_)
+    return;
+  if (!active_view_->Contains(view))
+    return;
+  SetFocusedView(view);
+}
+
+View* FocusController::GetFocusedView() {
+  return focused_view_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, ui::EventHandler implementation:
+
+void FocusController::OnKeyEvent(ui::KeyEvent* event) {
+}
+
+void FocusController::OnMouseEvent(ui::MouseEvent* event) {
+  if (event->type() == ui::ET_MOUSE_PRESSED && !event->handled()) {
+    View* view = static_cast<ViewTarget*>(event->target())->view();
+    ViewFocusedFromInputEvent(view);
+  }
+}
+
+void FocusController::OnScrollEvent(ui::ScrollEvent* event) {
+}
+
+void FocusController::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->type() == ui::ET_TOUCH_PRESSED && !event->handled()) {
+    View* view = static_cast<ViewTarget*>(event->target())->view();
+    ViewFocusedFromInputEvent(view);
+  }
+}
+
+void FocusController::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() == ui::ET_GESTURE_BEGIN &&
+      event->details().touch_points() == 1 &&
+      !event->handled()) {
+    View* view = static_cast<ViewTarget*>(event->target())->view();
+    ViewFocusedFromInputEvent(view);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, mojo::ViewObserver implementation:
+
+void FocusController::OnViewVisibilityChanged(View* view) {
+  bool visible = view->visible();
+  if (!visible)
+    ViewLostFocusFromDispositionChange(view, view->parent());
+}
+
+void FocusController::OnViewDestroying(View* view) {
+  ViewLostFocusFromDispositionChange(view, view->parent());
+}
+
+void FocusController::OnTreeChanging(const TreeChangeParams& params) {
+  // TODO(erg): In the aura version, you could get into a situation where you
+  // have different focus clients, so you had to check for that. Does that
+  // happen here? Could we get away with not checking if it does?
+  if (params.receiver == active_view_ &&
+      params.target->Contains(params.receiver) &&
+      (!params.new_parent ||
+       /* different_focus_clients */ false)) {
+    ViewLostFocusFromDispositionChange(params.receiver, params.old_parent);
+  }
+}
+
+void FocusController::OnTreeChanged(const TreeChangeParams& params) {
+  // TODO(erg): Same as Changing version.
+  if (params.receiver == focused_view_ &&
+      params.target->Contains(params.receiver) &&
+      (!params.new_parent ||
+       /* different_focus_clients */ false)) {
+    ViewLostFocusFromDispositionChange(params.receiver, params.old_parent);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, private:
+
+void FocusController::SetFocusedView(View* view) {
+  if (updating_focus_ || view == focused_view_)
+    return;
+  DCHECK(rules_->CanFocusView(view));
+  if (view)
+    DCHECK_EQ(view, rules_->GetFocusableView(view));
+
+  base::AutoReset<bool> updating_focus(&updating_focus_, true);
+  View* lost_focus = focused_view_;
+
+  // TODO(erg): In the aura version, we reset the text input client here. Do
+  // that if we bring in something like the TextInputClient.
+
+  // Allow for the window losing focus to be deleted during dispatch. If it is
+  // deleted pass null to observers instead of a deleted window.
+  mojo::ViewTracker view_tracker;
+  if (lost_focus)
+    view_tracker.Add(lost_focus);
+  if (focused_view_ && observer_manager_.IsObserving(focused_view_) &&
+      focused_view_ != active_view_) {
+    observer_manager_.Remove(focused_view_);
+  }
+  focused_view_ = view;
+  if (focused_view_ && !observer_manager_.IsObserving(focused_view_))
+    observer_manager_.Add(focused_view_);
+
+  FOR_EACH_OBSERVER(FocusControllerObserver, focus_controller_observers_,
+                    OnFocused(focused_view_));
+
+  // TODO(erg): In aura, there's a concept of a single FocusChangeObserver that
+  // is attached to an aura::Window. We don't currently have this in
+  // mojo::View, but if we add it later, we should make something analogous
+  // here.
+
+  // TODO(erg): In the aura version, we reset the TextInputClient here, too.
+}
+
+void FocusController::SetActiveView(View* requested_view, View* view) {
+  if (updating_activation_)
+    return;
+
+  if (view == active_view_) {
+    if (requested_view) {
+      FOR_EACH_OBSERVER(FocusControllerObserver,
+                        focus_controller_observers_,
+                        OnAttemptToReactivateView(requested_view,
+                                                  active_view_));
+    }
+    return;
+  }
+
+  DCHECK(rules_->CanActivateView(view));
+  if (view)
+    DCHECK_EQ(view, rules_->GetActivatableView(view));
+
+  base::AutoReset<bool> updating_activation(&updating_activation_, true);
+  View* lost_activation = active_view_;
+  // Allow for the window losing activation to be deleted during dispatch. If
+  // it is deleted pass null to observers instead of a deleted window.
+  mojo::ViewTracker view_tracker;
+  if (lost_activation)
+    view_tracker.Add(lost_activation);
+  if (active_view_ && observer_manager_.IsObserving(active_view_) &&
+      focused_view_ != active_view_) {
+    observer_manager_.Remove(active_view_);
+  }
+  active_view_ = view;
+  if (active_view_ && !observer_manager_.IsObserving(active_view_))
+    observer_manager_.Add(active_view_);
+
+  if (active_view_) {
+    // TODO(erg): Reenable this when we have modal windows.
+    // StackTransientParentsBelowModalWindow(active_view_);
+
+    active_view_->MoveToFront();
+  }
+
+  // TODO(erg): Individual windows can have a single ActivationChangeObserver
+  // set on them. In the aura version of this code, it sends an
+  // OnWindowActivated message to both the window that lost activation, and the
+  // window that gained it.
+
+  FOR_EACH_OBSERVER(FocusControllerObserver, focus_controller_observers_,
+                    OnActivated(active_view_));
+}
+
+void FocusController::ViewLostFocusFromDispositionChange(
+    View* view,
+    View* next) {
+  // TODO(erg): We clear the modality state here in the aura::Window version of
+  // this class, and should probably do the same once we have modal windows.
+
+  // TODO(beng): See if this function can be replaced by a call to
+  //             FocusWindow().
+  // Activation adjustments are handled first in the event of a disposition
+  // changed. If an activation change is necessary, focus is reset as part of
+  // that process so there's no point in updating focus independently.
+  if (view == active_view_) {
+    View* next_activatable = rules_->GetNextActivatableView(view);
+    SetActiveView(nullptr, next_activatable);
+    if (!(active_view_ && active_view_->Contains(focused_view_)))
+      SetFocusedView(next_activatable);
+  } else if (view->Contains(focused_view_)) {
+    // Active window isn't changing, but focused window might be.
+    SetFocusedView(rules_->GetFocusableView(next));
+  }
+}
+
+void FocusController::ViewFocusedFromInputEvent(View* view) {
+  // Only focus |window| if it or any of its parents can be focused. Otherwise
+  // FocusWindow() will focus the topmost window, which may not be the
+  // currently focused one.
+  if (rules_->CanFocusView(GetToplevelView(view)))
+    FocusView(view);
+}
+
+void SetFocusController(View* root_view, FocusController* focus_controller) {
+  DCHECK_EQ(root_view->GetRoot(), root_view);
+  root_view->SetLocalProperty(kRootViewFocusController, focus_controller);
+}
+
+FocusController* GetFocusController(View* root_view) {
+  if (root_view)
+    DCHECK_EQ(root_view->GetRoot(), root_view);
+  return root_view ?
+      root_view->GetLocalProperty(kRootViewFocusController) : nullptr;
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/focus_controller.h b/services/window_manager/focus_controller.h
new file mode 100644
index 0000000..a06ee2d
--- /dev/null
+++ b/services/window_manager/focus_controller.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/scoped_observer.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "ui/events/event_handler.h"
+
+namespace window_manager {
+
+class FocusControllerObserver;
+class FocusRules;
+
+// FocusController handles focus and activation changes in a mojo window
+// manager. Within the window manager, there can only be one focused and one
+// active window at a time. When focus or activation changes, notifications are
+// sent using the FocusControllerObserver interface.
+class FocusController : public ui::EventHandler, public mojo::ViewObserver {
+ public:
+  // |rules| cannot be null.
+  explicit FocusController(scoped_ptr<FocusRules> rules);
+  ~FocusController() override;
+
+  void AddObserver(FocusControllerObserver* observer);
+  void RemoveObserver(FocusControllerObserver* observer);
+
+  void ActivateView(mojo::View* view);
+  void DeactivateView(mojo::View* view);
+  mojo::View* GetActiveView();
+  mojo::View* GetActivatableView(mojo::View* view);
+  mojo::View* GetToplevelView(mojo::View* view);
+  bool CanActivateView(mojo::View* view) const;
+
+  void FocusView(mojo::View* view);
+
+  void ResetFocusWithinActiveView(mojo::View* view);
+  mojo::View* GetFocusedView();
+
+  // Overridden from ui::EventHandler:
+  void OnKeyEvent(ui::KeyEvent* event) override;
+  void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnScrollEvent(ui::ScrollEvent* event) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
+  // Overridden from ViewObserver:
+  void OnTreeChanging(const TreeChangeParams& params) override;
+  void OnTreeChanged(const TreeChangeParams& params) override;
+  void OnViewVisibilityChanged(mojo::View* view) override;
+  void OnViewDestroying(mojo::View* view) override;
+
+ private:
+  // Internal implementation that sets the focused view, fires events etc.
+  // This function must be called with a valid focusable view.
+  void SetFocusedView(mojo::View* view);
+
+  // Internal implementation that sets the active window, fires events etc.
+  // This function must be called with a valid |activatable_window|.
+  // |requested window| refers to the window that was passed in to an external
+  // request (e.g. FocusWindow or ActivateWindow). It may be null, e.g. if
+  // SetActiveWindow was not called by an external request. |activatable_window|
+  // refers to the actual window to be activated, which may be different.
+  void SetActiveView(mojo::View* requested_view, mojo::View* activatable_view);
+
+  // Called when a window's disposition changed such that it and its hierarchy
+  // are no longer focusable/activatable. |next| is a valid window that is used
+  // as a starting point for finding a window to focus next based on rules.
+  void ViewLostFocusFromDispositionChange(mojo::View* view, mojo::View* next);
+
+  // Called when an attempt is made to focus or activate a window via an input
+  // event targeted at that window. Rules determine the best focusable window
+  // for the input window.
+  void ViewFocusedFromInputEvent(mojo::View* view);
+
+  mojo::View* active_view_;
+  mojo::View* focused_view_;
+
+  bool updating_focus_;
+  bool updating_activation_;
+
+  scoped_ptr<FocusRules> rules_;
+
+  base::ObserverList<FocusControllerObserver> focus_controller_observers_;
+
+  ScopedObserver<mojo::View, ViewObserver> observer_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusController);
+};
+
+// Sets/Gets the focus controller for a view.
+void SetFocusController(mojo::View* view, FocusController* focus_controller);
+FocusController* GetFocusController(mojo::View* view);
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
diff --git a/services/window_manager/focus_controller_observer.h b/services/window_manager/focus_controller_observer.h
new file mode 100644
index 0000000..c5960b0
--- /dev/null
+++ b/services/window_manager/focus_controller_observer.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+class FocusControllerObserver {
+ public:
+  // Called when |active| gains focus, or there is no active view
+  // (|active| is null in this case.).
+  virtual void OnActivated(mojo::View* gained_active) = 0;
+
+  // Called when focus moves to |gained_focus|.
+  virtual void OnFocused(mojo::View* gained_focus) = 0;
+
+  // Called when during view activation the currently active view is
+  // selected for activation. This can happen when a view requested for
+  // activation cannot be activated because a system modal view is active.
+  virtual void OnAttemptToReactivateView(mojo::View* request_active,
+                                         mojo::View* actual_active) {}
+
+ protected:
+  virtual ~FocusControllerObserver() {}
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
diff --git a/services/window_manager/focus_controller_unittest.cc b/services/window_manager/focus_controller_unittest.cc
new file mode 100644
index 0000000..684906b
--- /dev/null
+++ b/services/window_manager/focus_controller_unittest.cc
@@ -0,0 +1,1197 @@
+// Copyright (c) 2012 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/window_manager/focus_controller.h"
+
+#include "ui/events/event_utils.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "services/window_manager/basic_focus_rules.h"
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller_observer.h"
+#include "services/window_manager/view_event_dispatcher.h"
+#include "services/window_manager/view_targeter.h"
+#include "services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+using mojo::View;
+
+namespace window_manager {
+
+// Counts the number of events that occur.
+class FocusNotificationObserver : public FocusControllerObserver {
+ public:
+  FocusNotificationObserver()
+      : activation_changed_count_(0),
+        focus_changed_count_(0),
+        reactivation_count_(0),
+        reactivation_requested_view_(NULL),
+        reactivation_actual_view_(NULL) {}
+  ~FocusNotificationObserver() override {}
+
+  void ExpectCounts(int activation_changed_count, int focus_changed_count) {
+    EXPECT_EQ(activation_changed_count, activation_changed_count_);
+    EXPECT_EQ(focus_changed_count, focus_changed_count_);
+  }
+  int reactivation_count() const { return reactivation_count_; }
+  View* reactivation_requested_view() const {
+    return reactivation_requested_view_;
+  }
+  View* reactivation_actual_view() const {
+    return reactivation_actual_view_;
+  }
+
+ protected:
+  // Overridden from FocusControllerObserver:
+  void OnActivated(View* gained_active) override {
+    ++activation_changed_count_;
+  }
+
+  void OnFocused(View* gained_focus) override { ++focus_changed_count_; }
+
+  void OnAttemptToReactivateView(View* request_active,
+                                 View* actual_active) override {
+    ++reactivation_count_;
+    reactivation_requested_view_ = request_active;
+    reactivation_actual_view_ = actual_active;
+  }
+
+ private:
+  int activation_changed_count_;
+  int focus_changed_count_;
+  int reactivation_count_;
+  View* reactivation_requested_view_;
+  View* reactivation_actual_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver);
+};
+
+class ViewDestroyer {
+ public:
+  virtual View* GetDestroyedView() = 0;
+
+ protected:
+  virtual ~ViewDestroyer() {}
+};
+
+// FocusNotificationObserver that keeps track of whether it was notified about
+// activation changes or focus changes with a destroyed view.
+class RecordingFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+  RecordingFocusNotificationObserver(FocusController* focus_controller,
+                                     ViewDestroyer* destroyer)
+      : focus_controller_(focus_controller),
+        destroyer_(destroyer),
+        active_(nullptr),
+        focus_(nullptr),
+        was_notified_with_destroyed_view_(false) {
+    focus_controller_->AddObserver(this);
+  }
+  ~RecordingFocusNotificationObserver() override {
+    focus_controller_->RemoveObserver(this);
+  }
+
+  bool was_notified_with_destroyed_view() const {
+    return was_notified_with_destroyed_view_;
+  }
+
+  // Overridden from FocusNotificationObserver:
+  void OnActivated(View* gained_active) override {
+    if (active_ && active_ == destroyer_->GetDestroyedView())
+      was_notified_with_destroyed_view_ = true;
+    active_ = gained_active;
+  }
+
+  void OnFocused(View* gained_focus) override {
+    if (focus_ && focus_ == destroyer_->GetDestroyedView())
+      was_notified_with_destroyed_view_ = true;
+    focus_ = gained_focus;
+  }
+
+ private:
+  FocusController* focus_controller_;
+
+  // Not owned.
+  ViewDestroyer* destroyer_;
+  View* active_;
+  View* focus_;
+
+  // Whether the observer was notified about the loss of activation or the
+  // loss of focus with a view already destroyed by |destroyer_| as the
+  // |lost_active| or |lost_focus| parameter.
+  bool was_notified_with_destroyed_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(RecordingFocusNotificationObserver);
+};
+
+class DestroyOnLoseActivationFocusNotificationObserver
+    : public FocusNotificationObserver,
+      public ViewDestroyer {
+ public:
+  DestroyOnLoseActivationFocusNotificationObserver(
+      FocusController* focus_controller,
+      View* view_to_destroy,
+      View* initial_active)
+      : focus_controller_(focus_controller),
+        view_to_destroy_(view_to_destroy),
+        active_(initial_active),
+        did_destroy_(false) {
+    focus_controller_->AddObserver(this);
+  }
+  ~DestroyOnLoseActivationFocusNotificationObserver() override {
+    focus_controller_->RemoveObserver(this);
+  }
+
+  // Overridden from FocusNotificationObserver:
+  void OnActivated(View* gained_active) override {
+    if (view_to_destroy_ && active_ == view_to_destroy_) {
+      view_to_destroy_->Destroy();
+      did_destroy_ = true;
+    }
+    active_ = gained_active;
+  }
+
+  // Overridden from ViewDestroyer:
+  View* GetDestroyedView() override {
+    return did_destroy_ ? view_to_destroy_ : nullptr;
+  }
+
+ private:
+  FocusController* focus_controller_;
+  View* view_to_destroy_;
+  View* active_;
+  bool did_destroy_;
+
+  DISALLOW_COPY_AND_ASSIGN(DestroyOnLoseActivationFocusNotificationObserver);
+};
+
+class ScopedFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+  ScopedFocusNotificationObserver(FocusController* focus_controller)
+      : focus_controller_(focus_controller) {
+    focus_controller_->AddObserver(this);
+  }
+  ~ScopedFocusNotificationObserver() override {
+    focus_controller_->RemoveObserver(this);
+  }
+
+ private:
+  FocusController* focus_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
+};
+
+// Only responds to events if a message contains |target| as a parameter.
+class ScopedFilteringFocusNotificationObserver
+    : public FocusNotificationObserver {
+ public:
+  ScopedFilteringFocusNotificationObserver(FocusController* focus_controller,
+                                           View* target,
+                                           View* initial_active,
+                                           View* initial_focus)
+      : focus_controller_(focus_controller),
+        target_(target),
+        active_(initial_active),
+        focus_(initial_focus) {
+    focus_controller_->AddObserver(this);
+  }
+  ~ScopedFilteringFocusNotificationObserver() override {
+    focus_controller_->RemoveObserver(this);
+  }
+
+ private:
+  // Overridden from FocusControllerObserver:
+  void OnActivated(View* gained_active) override {
+    if (gained_active == target_ || active_ == target_)
+      FocusNotificationObserver::OnActivated(gained_active);
+    active_ = gained_active;
+  }
+
+  void OnFocused(View* gained_focus) override {
+    if (gained_focus == target_ || focus_ == target_)
+      FocusNotificationObserver::OnFocused(gained_focus);
+    focus_ = gained_focus;
+  }
+
+  void OnAttemptToReactivateView(View* request_active,
+                                 View* actual_active) override {
+    if (request_active == target_ || actual_active == target_) {
+      FocusNotificationObserver::OnAttemptToReactivateView(request_active,
+                                                           actual_active);
+    }
+  }
+
+  FocusController* focus_controller_;
+  View* target_;
+  View* active_;
+  View* focus_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFilteringFocusNotificationObserver);
+};
+
+// Used to fake the handling of events in the pre-target phase.
+class SimpleEventHandler : public ui::EventHandler {
+ public:
+  SimpleEventHandler() {}
+  ~SimpleEventHandler() override {}
+
+  // Overridden from ui::EventHandler:
+  void OnMouseEvent(ui::MouseEvent* event) override {
+    event->SetHandled();
+  }
+  void OnGestureEvent(ui::GestureEvent* event) override {
+    event->SetHandled();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SimpleEventHandler);
+};
+
+class FocusShiftingActivationObserver
+    : public FocusControllerObserver {
+ public:
+  explicit FocusShiftingActivationObserver(FocusController* focus_controller,
+                                           View* activated_view)
+      : focus_controller_(focus_controller),
+        activated_view_(activated_view),
+        shift_focus_to_(NULL) {}
+  ~FocusShiftingActivationObserver() override {}
+
+  void set_shift_focus_to(View* shift_focus_to) {
+    shift_focus_to_ = shift_focus_to;
+  }
+
+ private:
+  // Overridden from FocusControllerObserver:
+  void OnActivated(View* gained_active) override {
+    // Shift focus to a child. This should prevent the default focusing from
+    // occurring in FocusController::FocusView().
+    if (gained_active == activated_view_)
+      focus_controller_->FocusView(shift_focus_to_);
+  }
+
+  void OnFocused(View* gained_focus) override {}
+
+  FocusController* focus_controller_;
+  View* activated_view_;
+  View* shift_focus_to_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver);
+};
+
+// BasicFocusRules subclass that allows basic overrides of focus/activation to
+// be tested. This is intended more as a test that the override system works at
+// all, rather than as an exhaustive set of use cases, those should be covered
+// in tests for those FocusRules implementations.
+class TestFocusRules : public BasicFocusRules {
+ public:
+  TestFocusRules(View* root)
+      : BasicFocusRules(root), focus_restriction_(NULL) {}
+
+  // Restricts focus and activation to this view and its child hierarchy.
+  void set_focus_restriction(View* focus_restriction) {
+    focus_restriction_ = focus_restriction;
+  }
+
+  // Overridden from BasicFocusRules:
+  bool SupportsChildActivation(View* view) const override {
+    // In FocusControllerTests, only the Root has activatable children.
+    return view->GetRoot() == view;
+  }
+  bool CanActivateView(View* view) const override {
+    // Restricting focus to a non-activatable child view means the activatable
+    // parent outside the focus restriction is activatable.
+    bool can_activate =
+        CanFocusOrActivate(view) || view->Contains(focus_restriction_);
+    return can_activate ? BasicFocusRules::CanActivateView(view) : false;
+  }
+  bool CanFocusView(View* view) const override {
+    return CanFocusOrActivate(view) ? BasicFocusRules::CanFocusView(view)
+                                    : false;
+  }
+  View* GetActivatableView(View* view) const override {
+    return BasicFocusRules::GetActivatableView(
+        CanFocusOrActivate(view) ? view : focus_restriction_);
+  }
+  View* GetFocusableView(View* view) const override {
+    return BasicFocusRules::GetFocusableView(
+        CanFocusOrActivate(view) ? view : focus_restriction_);
+  }
+  View* GetNextActivatableView(View* ignore) const override {
+    View* next_activatable = BasicFocusRules::GetNextActivatableView(ignore);
+    return CanFocusOrActivate(next_activatable)
+               ? next_activatable
+               : GetActivatableView(focus_restriction_);
+  }
+
+ private:
+  bool CanFocusOrActivate(View* view) const {
+    return !focus_restriction_ || focus_restriction_->Contains(view);
+  }
+
+  View* focus_restriction_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
+};
+
+// Common infrastructure shared by all FocusController test types.
+class FocusControllerTestBase : public testing::Test {
+ protected:
+  // Hierarchy used by all tests:
+  // root_view
+  //       +-- w1
+  //       |    +-- w11
+  //       |    +-- w12
+  //       +-- w2
+  //       |    +-- w21
+  //       |         +-- w211
+  //       +-- w3
+  FocusControllerTestBase()
+      : root_view_(TestView::Build(0, gfx::Rect(0, 0, 800, 600))),
+        v1(TestView::Build(1, gfx::Rect(0, 0, 50, 50), root_view())),
+        v11(TestView::Build(11, gfx::Rect(5, 5, 10, 10), v1)),
+        v12(TestView::Build(12, gfx::Rect(15, 15, 10, 10), v1)),
+        v2(TestView::Build(2, gfx::Rect(75, 75, 50, 50), root_view())),
+        v21(TestView::Build(21, gfx::Rect(5, 5, 10, 10), v2)),
+        v211(TestView::Build(211, gfx::Rect(1, 1, 5, 5), v21)),
+        v3(TestView::Build(3, gfx::Rect(125, 125, 50, 50), root_view())) {}
+
+  // Overridden from testing::Test:
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    test_focus_rules_ = new TestFocusRules(root_view());
+    focus_controller_.reset(
+        new FocusController(scoped_ptr<FocusRules>(test_focus_rules_)));
+    SetFocusController(root_view(), focus_controller_.get());
+
+    capture_controller_.reset(new CaptureController);
+    SetCaptureController(root_view(), capture_controller_.get());
+
+    ViewTarget* root_target = root_view_->target();
+    root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+    view_event_dispatcher_.reset(new ViewEventDispatcher());
+    view_event_dispatcher_->SetRootViewTarget(root_target);
+
+    GetRootViewTarget()->AddPreTargetHandler(focus_controller_.get());
+  }
+
+  void TearDown() override {
+    GetRootViewTarget()->RemovePreTargetHandler(focus_controller_.get());
+    view_event_dispatcher_.reset();
+
+    root_view_->Destroy();
+
+    capture_controller_.reset();
+    test_focus_rules_ = nullptr;  // Owned by FocusController.
+    focus_controller_.reset();
+
+    testing::Test::TearDown();
+  }
+
+  void FocusView(View* view) { focus_controller_->FocusView(view); }
+  View* GetFocusedView() { return focus_controller_->GetFocusedView(); }
+  int GetFocusedViewId() {
+    View* focused_view = GetFocusedView();
+    return focused_view ? focused_view->id() : -1;
+  }
+  void ActivateView(View* view) { focus_controller_->ActivateView(view); }
+  void DeactivateView(View* view) { focus_controller_->DeactivateView(view); }
+  View* GetActiveView() { return focus_controller_->GetActiveView(); }
+  int GetActiveViewId() {
+    View* active_view = GetActiveView();
+    return active_view ? active_view->id() : -1;
+  }
+
+  View* GetViewById(int id) { return root_view_->GetChildById(id); }
+
+  void ClickLeftButton(View* view) {
+    // Get the center bounds of |target| in |root_view_| coordinate space.
+    gfx::Point center =
+        gfx::Rect(view->bounds().To<gfx::Rect>().size()).CenterPoint();
+    ViewTarget::ConvertPointToTarget(ViewTarget::TargetFromView(view),
+                                     root_view_->target(), &center);
+
+    ui::MouseEvent button_down(ui::ET_MOUSE_PRESSED, center, center,
+                               ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE);
+    ui::EventDispatchDetails details =
+        view_event_dispatcher_->OnEventFromSource(&button_down);
+    CHECK(!details.dispatcher_destroyed);
+
+    ui::MouseEvent button_up(ui::ET_MOUSE_RELEASED, center, center,
+                             ui::EventTimeForNow(),
+                             ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE);
+    details = view_event_dispatcher_->OnEventFromSource(&button_up);
+    CHECK(!details.dispatcher_destroyed);
+  }
+
+  ViewTarget* GetRootViewTarget() {
+    return ViewTarget::TargetFromView(root_view());
+  }
+
+  View* root_view() { return root_view_; }
+  TestFocusRules* test_focus_rules() { return test_focus_rules_; }
+  FocusController* focus_controller() { return focus_controller_.get(); }
+  CaptureController* capture_controller() { return capture_controller_.get(); }
+
+  // Test functions.
+  virtual void BasicFocus() = 0;
+  virtual void BasicActivation() = 0;
+  virtual void FocusEvents() = 0;
+  virtual void DuplicateFocusEvents() {}
+  virtual void ActivationEvents() = 0;
+  virtual void ReactivationEvents() {}
+  virtual void DuplicateActivationEvents() {}
+  virtual void ShiftFocusWithinActiveView() {}
+  virtual void ShiftFocusToChildOfInactiveView() {}
+  virtual void ShiftFocusToParentOfFocusedView() {}
+  virtual void FocusRulesOverride() = 0;
+  virtual void ActivationRulesOverride() = 0;
+  virtual void ShiftFocusOnActivation() {}
+  virtual void ShiftFocusOnActivationDueToHide() {}
+  virtual void NoShiftActiveOnActivation() {}
+  virtual void ChangeFocusWhenNothingFocusedAndCaptured() {}
+  virtual void DontPassDestroyedView() {}
+  // TODO(erg): Also, void FocusedTextInputClient() once we build the IME.
+
+ private:
+  TestView* root_view_;
+  scoped_ptr<FocusController> focus_controller_;
+  TestFocusRules* test_focus_rules_;
+  scoped_ptr<CaptureController> capture_controller_;
+  // TODO(erg): The aura version of this class also keeps track of WMState. Do
+  // we need something analogous here?
+
+  scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+  TestView* v1;
+  TestView* v11;
+  TestView* v12;
+  TestView* v2;
+  TestView* v21;
+  TestView* v211;
+  TestView* v3;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
+};
+
+// Test base for tests where focus is directly set to a target view.
+class FocusControllerDirectTestBase : public FocusControllerTestBase {
+ protected:
+  FocusControllerDirectTestBase() {}
+
+  // Different test types shift focus in different ways.
+  virtual void FocusViewDirect(View* view) = 0;
+  virtual void ActivateViewDirect(View* view) = 0;
+  virtual void DeactivateViewDirect(View* view) = 0;
+
+  // Input events do not change focus if the view can not be focused.
+  virtual bool IsInputEvent() = 0;
+
+  void FocusViewById(int id) {
+    View* view = root_view()->GetChildById(id);
+    DCHECK(view);
+    FocusViewDirect(view);
+  }
+  void ActivateViewById(int id) {
+    View* view = root_view()->GetChildById(id);
+    DCHECK(view);
+    ActivateViewDirect(view);
+  }
+
+  // Overridden from FocusControllerTestBase:
+  void BasicFocus() override {
+    EXPECT_EQ(nullptr, GetFocusedView());
+    FocusViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+    FocusViewById(2);
+    EXPECT_EQ(2, GetFocusedViewId());
+  }
+  void BasicActivation() override {
+    EXPECT_EQ(nullptr, GetActiveView());
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetActiveViewId());
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetActiveViewId());
+    // Verify that attempting to deactivate NULL does not crash and does not
+    // change activation.
+    DeactivateView(nullptr);
+    EXPECT_EQ(2, GetActiveViewId());
+    DeactivateView(GetActiveView());
+    EXPECT_EQ(1, GetActiveViewId());
+  }
+  void FocusEvents() override {
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer1(
+        focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+    ScopedFilteringFocusNotificationObserver observer2(
+        focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+    {
+      SCOPED_TRACE("initial state");
+      root_observer.ExpectCounts(0, 0);
+      observer1.ExpectCounts(0, 0);
+      observer2.ExpectCounts(0, 0);
+    }
+
+    FocusViewById(1);
+    {
+      SCOPED_TRACE("FocusViewById(1)");
+      root_observer.ExpectCounts(1, 1);
+      observer1.ExpectCounts(1, 1);
+      observer2.ExpectCounts(0, 0);
+    }
+
+    FocusViewById(2);
+    {
+      SCOPED_TRACE("FocusViewById(2)");
+      root_observer.ExpectCounts(2, 2);
+      observer1.ExpectCounts(2, 2);
+      observer2.ExpectCounts(1, 1);
+    }
+  }
+  void DuplicateFocusEvents() override {
+    // Focusing an existing focused view should not resend focus events.
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer1(
+        focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+
+    root_observer.ExpectCounts(0, 0);
+    observer1.ExpectCounts(0, 0);
+
+    FocusViewById(1);
+    root_observer.ExpectCounts(1, 1);
+    observer1.ExpectCounts(1, 1);
+
+    FocusViewById(1);
+    root_observer.ExpectCounts(1, 1);
+    observer1.ExpectCounts(1, 1);
+  }
+  void ActivationEvents() override {
+    ActivateViewById(1);
+
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer1(
+        focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+    ScopedFilteringFocusNotificationObserver observer2(
+        focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+    root_observer.ExpectCounts(0, 0);
+    observer1.ExpectCounts(0, 0);
+    observer2.ExpectCounts(0, 0);
+
+    ActivateViewById(2);
+    root_observer.ExpectCounts(1, 1);
+    observer1.ExpectCounts(1, 1);
+    observer2.ExpectCounts(1, 1);
+  }
+  void ReactivationEvents() override {
+    ActivateViewById(1);
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    EXPECT_EQ(0, root_observer.reactivation_count());
+    GetViewById(2)->SetVisible(false);
+    // When we attempt to activate "2", which cannot be activated because it
+    // is not visible, "1" will be reactivated.
+    ActivateViewById(2);
+    EXPECT_EQ(1, root_observer.reactivation_count());
+    EXPECT_EQ(GetViewById(2),
+              root_observer.reactivation_requested_view());
+    EXPECT_EQ(GetViewById(1),
+              root_observer.reactivation_actual_view());
+  }
+  void DuplicateActivationEvents() override {
+    ActivateViewById(1);
+
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer1(
+        focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+    ScopedFilteringFocusNotificationObserver observer2(
+        focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+    root_observer.ExpectCounts(0, 0);
+    observer1.ExpectCounts(0, 0);
+    observer2.ExpectCounts(0, 0);
+
+    ActivateViewById(2);
+    root_observer.ExpectCounts(1, 1);
+    observer1.ExpectCounts(1, 1);
+    observer2.ExpectCounts(1, 1);
+
+    // Activating an existing active view should not resend activation events.
+    ActivateViewById(2);
+    root_observer.ExpectCounts(1, 1);
+    observer1.ExpectCounts(1, 1);
+    observer2.ExpectCounts(1, 1);
+  }
+  void ShiftFocusWithinActiveView() override {
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(1, GetFocusedViewId());
+    FocusViewById(11);
+    EXPECT_EQ(11, GetFocusedViewId());
+    FocusViewById(12);
+    EXPECT_EQ(12, GetFocusedViewId());
+  }
+  void ShiftFocusToChildOfInactiveView() override {
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetActiveViewId());
+    EXPECT_EQ(2, GetFocusedViewId());
+    FocusViewById(11);
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(11, GetFocusedViewId());
+  }
+  void ShiftFocusToParentOfFocusedView() override {
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+    FocusViewById(11);
+    EXPECT_EQ(11, GetFocusedViewId());
+    FocusViewById(1);
+    // Focus should _not_ shift to the parent of the already-focused view.
+    EXPECT_EQ(11, GetFocusedViewId());
+  }
+  void FocusRulesOverride() override {
+    EXPECT_EQ(NULL, GetFocusedView());
+    FocusViewById(11);
+    EXPECT_EQ(11, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(GetViewById(211));
+    FocusViewById(12);
+    // Input events leave focus unchanged; direct API calls will change focus
+    // to the restricted view.
+    int focused_view = IsInputEvent() ? 11 : 211;
+    EXPECT_EQ(focused_view, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(NULL);
+    FocusViewById(12);
+    EXPECT_EQ(12, GetFocusedViewId());
+  }
+  void ActivationRulesOverride() override {
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    View* v3 = GetViewById(3);
+    test_focus_rules()->set_focus_restriction(v3);
+
+    ActivateViewById(2);
+    // Input events leave activation unchanged; direct API calls will activate
+    // the restricted view.
+    int active_view = IsInputEvent() ? 1 : 3;
+    EXPECT_EQ(active_view, GetActiveViewId());
+    EXPECT_EQ(active_view, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(NULL);
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetActiveViewId());
+    EXPECT_EQ(2, GetFocusedViewId());
+  }
+  void ShiftFocusOnActivation() override {
+    // When a view is activated, by default that view is also focused.
+    // An ActivationChangeObserver may shift focus to another view within the
+    // same activatable view.
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetFocusedViewId());
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    ActivateViewById(2);
+
+    View* target = GetViewById(1);
+
+    scoped_ptr<FocusShiftingActivationObserver> observer(
+        new FocusShiftingActivationObserver(focus_controller(), target));
+    observer->set_shift_focus_to(target->GetChildById(11));
+    focus_controller()->AddObserver(observer.get());
+
+    ActivateViewById(1);
+
+    // w1's ActivationChangeObserver shifted focus to this child, pre-empting
+    // FocusController's default setting.
+    EXPECT_EQ(11, GetFocusedViewId());
+
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetFocusedViewId());
+
+    // Simulate a focus reset by the ActivationChangeObserver. This should
+    // trigger the default setting in FocusController.
+    observer->set_shift_focus_to(nullptr);
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    focus_controller()->RemoveObserver(observer.get());
+
+    ActivateViewById(2);
+    EXPECT_EQ(2, GetFocusedViewId());
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+  }
+  void ShiftFocusOnActivationDueToHide() override {
+    // Similar to ShiftFocusOnActivation except the activation change is
+    // triggered by hiding the active view.
+    ActivateViewById(1);
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    // Removes view 3 as candidate for next activatable view.
+    root_view()->GetChildById(3)->SetVisible(false);
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    View* target = root_view()->GetChildById(2);
+
+    scoped_ptr<FocusShiftingActivationObserver> observer(
+        new FocusShiftingActivationObserver(focus_controller(), target));
+    observer->set_shift_focus_to(target->GetChildById(21));
+    focus_controller()->AddObserver(observer.get());
+
+    // Hide the active view.
+    root_view()->GetChildById(1)->SetVisible(false);
+
+    EXPECT_EQ(21, GetFocusedViewId());
+
+    focus_controller()->RemoveObserver(observer.get());
+  }
+  void NoShiftActiveOnActivation() override {
+    // When a view is activated, we need to prevent any change to activation
+    // from being made in response to an activation change notification.
+  }
+
+  // Verifies focus change is honored while capture held.
+  void ChangeFocusWhenNothingFocusedAndCaptured() override {
+    View* v1 = root_view()->GetChildById(1);
+    capture_controller()->SetCapture(v1);
+
+    EXPECT_EQ(-1, GetActiveViewId());
+    EXPECT_EQ(-1, GetFocusedViewId());
+
+    FocusViewById(1);
+
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    capture_controller()->ReleaseCapture(v1);
+  }
+
+  // Verifies if a view that loses activation or focus is destroyed during
+  // observer notification we don't pass the destroyed view to other observers.
+  void DontPassDestroyedView() override {
+    FocusViewById(1);
+
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    {
+      View* to_destroy = root_view()->GetChildById(1);
+      DestroyOnLoseActivationFocusNotificationObserver observer1(
+          focus_controller(), to_destroy, GetActiveView());
+      RecordingFocusNotificationObserver observer2(focus_controller(),
+                                                   &observer1);
+
+      FocusViewById(2);
+
+      EXPECT_EQ(2, GetActiveViewId());
+      EXPECT_EQ(2, GetFocusedViewId());
+
+      EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+      EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+    }
+
+    {
+      View* to_destroy = root_view()->GetChildById(2);
+      DestroyOnLoseActivationFocusNotificationObserver observer1(
+          focus_controller(), to_destroy, GetActiveView());
+      RecordingFocusNotificationObserver observer2(focus_controller(),
+                                                   &observer1);
+
+      FocusViewById(3);
+
+      EXPECT_EQ(3, GetActiveViewId());
+      EXPECT_EQ(3, GetFocusedViewId());
+
+      EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+      EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
+};
+
+// Focus and Activation changes via the FocusController API.
+class FocusControllerApiTest : public FocusControllerDirectTestBase {
+ public:
+  FocusControllerApiTest() {}
+
+ private:
+  // Overridden from FocusControllerTestBase:
+  void FocusViewDirect(View* view) override { FocusView(view); }
+  void ActivateViewDirect(View* view) override { ActivateView(view); }
+  void DeactivateViewDirect(View* view) override { DeactivateView(view); }
+  bool IsInputEvent() override { return false; }
+
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest);
+};
+
+// Focus and Activation changes via input events.
+class FocusControllerMouseEventTest : public FocusControllerDirectTestBase {
+ public:
+  FocusControllerMouseEventTest() {}
+
+  // Tests that a handled mouse event does not trigger a view activation.
+  void IgnoreHandledEvent() {
+    EXPECT_EQ(NULL, GetActiveView());
+    View* v1 = root_view()->GetChildById(1);
+    SimpleEventHandler handler;
+    GetRootViewTarget()->PrependPreTargetHandler(&handler);
+    ClickLeftButton(v1);
+    EXPECT_EQ(NULL, GetActiveView());
+    // TODO(erg): Add gesture testing when we get gestures working.
+    GetRootViewTarget()->RemovePreTargetHandler(&handler);
+    ClickLeftButton(v1);
+    EXPECT_EQ(1, GetActiveViewId());
+  }
+
+ private:
+  // Overridden from FocusControllerTestBase:
+  void FocusViewDirect(View* view) override { ClickLeftButton(view); }
+  void ActivateViewDirect(View* view) override { ClickLeftButton(view); }
+  void DeactivateViewDirect(View* view) override {
+    View* next_activatable = test_focus_rules()->GetNextActivatableView(view);
+    ClickLeftButton(next_activatable);
+  }
+  bool IsInputEvent() override { return true; }
+
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest);
+};
+
+// TODO(erg): Add a FocusControllerGestureEventTest once we have working
+// gesture forwarding and handling.
+
+// Test base for tests where focus is implicitly set to a window as the result
+// of a disposition change to the focused window or the hierarchy that contains
+// it.
+class FocusControllerImplicitTestBase : public FocusControllerTestBase {
+ protected:
+  explicit FocusControllerImplicitTestBase(bool parent) : parent_(parent) {}
+
+  View* GetDispositionView(View* view) {
+    return parent_ ? view->parent() : view;
+  }
+
+  // Change the disposition of |view| in such a way as it will lose focus.
+  virtual void ChangeViewDisposition(View* view) = 0;
+
+  // Allow each disposition change test to add additional post-disposition
+  // change expectations.
+  virtual void PostDispostionChangeExpectations() {}
+
+  // Overridden from FocusControllerTestBase:
+  void BasicFocus() override {
+    EXPECT_EQ(NULL, GetFocusedView());
+
+    View* w211 = root_view()->GetChildById(211);
+    FocusView(w211);
+    EXPECT_EQ(211, GetFocusedViewId());
+
+    ChangeViewDisposition(w211);
+    // BasicFocusRules passes focus to the parent.
+    EXPECT_EQ(parent_ ? 2 : 21, GetFocusedViewId());
+  }
+
+  void BasicActivation() override {
+    DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+    EXPECT_EQ(NULL, GetActiveView());
+
+    View* w2 = root_view()->GetChildById(2);
+    ActivateView(w2);
+    EXPECT_EQ(2, GetActiveViewId());
+
+    ChangeViewDisposition(w2);
+    EXPECT_EQ(3, GetActiveViewId());
+    PostDispostionChangeExpectations();
+  }
+
+  void FocusEvents() override {
+    View* w211 = root_view()->GetChildById(211);
+    FocusView(w211);
+
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer211(
+        focus_controller(), GetViewById(211), GetActiveView(),
+        GetFocusedView());
+
+    {
+      SCOPED_TRACE("first");
+      root_observer.ExpectCounts(0, 0);
+      observer211.ExpectCounts(0, 0);
+    }
+
+    ChangeViewDisposition(w211);
+    {
+      SCOPED_TRACE("second");
+      {
+        SCOPED_TRACE("root_observer");
+        root_observer.ExpectCounts(0, 1);
+      }
+      {
+        SCOPED_TRACE("observer211");
+        observer211.ExpectCounts(0, 1);
+      }
+    }
+  }
+
+  void ActivationEvents() override {
+    DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+    View* w2 = root_view()->GetChildById(2);
+    ActivateView(w2);
+
+    ScopedFocusNotificationObserver root_observer(focus_controller());
+    ScopedFilteringFocusNotificationObserver observer2(
+        focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+    ScopedFilteringFocusNotificationObserver observer3(
+        focus_controller(), GetViewById(3), GetActiveView(), GetFocusedView());
+    root_observer.ExpectCounts(0, 0);
+    observer2.ExpectCounts(0, 0);
+    observer3.ExpectCounts(0, 0);
+
+    ChangeViewDisposition(w2);
+    root_observer.ExpectCounts(1, 1);
+    observer2.ExpectCounts(1, 1);
+    observer3.ExpectCounts(1, 1);
+  }
+
+  void FocusRulesOverride() override {
+    EXPECT_EQ(NULL, GetFocusedView());
+    View* w211 = root_view()->GetChildById(211);
+    FocusView(w211);
+    EXPECT_EQ(211, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(root_view()->GetChildById(11));
+    ChangeViewDisposition(w211);
+    // Normally, focus would shift to the parent (w21) but the override shifts
+    // it to 11.
+    EXPECT_EQ(11, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(NULL);
+  }
+
+  void ActivationRulesOverride() override {
+    DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+    View* w1 = root_view()->GetChildById(1);
+    ActivateView(w1);
+
+    EXPECT_EQ(1, GetActiveViewId());
+    EXPECT_EQ(1, GetFocusedViewId());
+
+    View* w3 = root_view()->GetChildById(3);
+    test_focus_rules()->set_focus_restriction(w3);
+
+    // Normally, activation/focus would move to w2, but since we have a focus
+    // restriction, it should move to w3 instead.
+    ChangeViewDisposition(w1);
+    EXPECT_EQ(3, GetActiveViewId());
+    EXPECT_EQ(3, GetFocusedViewId());
+
+    test_focus_rules()->set_focus_restriction(NULL);
+    ActivateView(root_view()->GetChildById(2));
+    EXPECT_EQ(2, GetActiveViewId());
+    EXPECT_EQ(2, GetFocusedViewId());
+  }
+
+ private:
+  // When true, the disposition change occurs to the parent of the window
+  // instead of to the window. This verifies that changes occurring in the
+  // hierarchy that contains the window affect the window's focus.
+  bool parent_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerImplicitTestBase);
+};
+
+// Focus and Activation changes in response to window visibility changes.
+class FocusControllerHideTest : public FocusControllerImplicitTestBase {
+ public:
+  FocusControllerHideTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+  FocusControllerHideTest(bool parent)
+      : FocusControllerImplicitTestBase(parent) {}
+
+  // Overridden from FocusControllerImplicitTestBase:
+  void ChangeViewDisposition(View* view) override {
+    GetDispositionView(view)->SetVisible(false);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerHideTest);
+};
+
+// Focus and Activation changes in response to window parent visibility
+// changes.
+class FocusControllerParentHideTest : public FocusControllerHideTest {
+ public:
+  FocusControllerParentHideTest() : FocusControllerHideTest(true) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest);
+};
+
+// Focus and Activation changes in response to window destruction.
+class FocusControllerDestructionTest : public FocusControllerImplicitTestBase {
+ public:
+  FocusControllerDestructionTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+  FocusControllerDestructionTest(bool parent)
+      : FocusControllerImplicitTestBase(parent) {}
+
+  // Overridden from FocusControllerImplicitTestBase:
+  void ChangeViewDisposition(View* view) override {
+    GetDispositionView(view)->Destroy();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest);
+};
+
+// Focus and Activation changes in response to window removal.
+class FocusControllerRemovalTest : public FocusControllerImplicitTestBase {
+ public:
+  FocusControllerRemovalTest()
+      : FocusControllerImplicitTestBase(false),
+        window_to_destroy_(nullptr) {}
+
+ protected:
+  FocusControllerRemovalTest(bool parent)
+      : FocusControllerImplicitTestBase(parent),
+        window_to_destroy_(nullptr) {}
+
+  // Overridden from FocusControllerImplicitTestBase:
+  void ChangeViewDisposition(View* view) override {
+    View* disposition_view = GetDispositionView(view);
+    disposition_view->parent()->RemoveChild(disposition_view);
+    window_to_destroy_ = disposition_view;
+  }
+  void TearDown() override {
+    if (window_to_destroy_)
+      window_to_destroy_->Destroy();
+
+    FocusControllerImplicitTestBase::TearDown();
+  }
+
+ private:
+  View* window_to_destroy_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerRemovalTest);
+};
+
+// Focus and Activation changes in response to window parent removal.
+class FocusControllerParentRemovalTest : public FocusControllerRemovalTest {
+ public:
+  FocusControllerParentRemovalTest() : FocusControllerRemovalTest(true) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusControllerParentRemovalTest);
+};
+
+#define FOCUS_CONTROLLER_TEST(TESTCLASS, TESTNAME) \
+  TEST_F(TESTCLASS, TESTNAME) { TESTNAME(); }
+
+// Runs direct focus change tests (input events and API calls).
+//
+// TODO(erg): Enable gesture events in the future.
+#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME)               \
+  FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \
+  FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target.
+#define IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+    FOCUS_CONTROLLER_TEST(FocusControllerHideTest, TESTNAME) \
+    FOCUS_CONTROLLER_TEST(FocusControllerDestructionTest, TESTNAME) \
+    FOCUS_CONTROLLER_TEST(FocusControllerRemovalTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target's parent
+// hierarchy.
+#define IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) \
+    FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \
+    FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME)
+// TODO(erg): FocusControllerParentDestructionTest were commented out in the
+// aura version of this file, and don't work when I tried porting things over.
+
+// Runs all implicit focus change tests (changes to the target and target's
+// parent hierarchy)
+#define IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) \
+    IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+    IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME)
+
+// Runs all possible focus change tests.
+#define ALL_FOCUS_TESTS(TESTNAME) \
+    DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+    IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME)
+
+// Runs focus change tests that apply only to the target. For example,
+// implicit activation changes caused by window disposition changes do not
+// occur when changes to the containing hierarchy happen.
+#define TARGET_FOCUS_TESTS(TESTNAME) \
+    DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+    IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME)
+
+// - Focuses a window, verifies that focus changed.
+ALL_FOCUS_TESTS(BasicFocus);
+
+// - Activates a window, verifies that activation changed.
+TARGET_FOCUS_TESTS(BasicActivation);
+
+// - Focuses a window, verifies that focus events were dispatched.
+ALL_FOCUS_TESTS(FocusEvents);
+
+// - Focuses or activates a window multiple times, verifies that events are only
+//   dispatched when focus/activation actually changes.
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateFocusEvents);
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateActivationEvents);
+
+// - Activates a window, verifies that activation events were dispatched.
+TARGET_FOCUS_TESTS(ActivationEvents);
+
+// - Attempts to active a hidden view, verifies that current view is
+//   attempted to be reactivated and the appropriate event dispatched.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents);
+
+// - Input events/API calls shift focus between focusable views within the
+//   active view.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveView);
+
+// - Input events/API calls to a child view of an inactive view shifts
+//   activation to the activatable parent and focuses the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveView);
+
+// - Input events/API calls to focus the parent of the focused view do not
+//   shift focus away from the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedView);
+
+// - Verifies that FocusRules determine what can be focused.
+ALL_FOCUS_TESTS(FocusRulesOverride);
+
+// - Verifies that FocusRules determine what can be activated.
+TARGET_FOCUS_TESTS(ActivationRulesOverride);
+
+// - Verifies that attempts to change focus or activation from a focus or
+//   activation change observer are ignored.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivation);
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivationDueToHide);
+DIRECT_FOCUS_CHANGE_TESTS(NoShiftActiveOnActivation);
+
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest,
+                      ChangeFocusWhenNothingFocusedAndCaptured);
+
+// See description above DontPassDestroyedView() for details.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDestroyedView);
+
+// TODO(erg): Add the TextInputClient tests here.
+
+// If a mouse event was handled, it should not activate a view.
+FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, IgnoreHandledEvent);
+
+}  // namespace window_manager
diff --git a/services/window_manager/focus_rules.h b/services/window_manager/focus_rules.h
new file mode 100644
index 0000000..82567ba
--- /dev/null
+++ b/services/window_manager/focus_rules.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
+
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/view_manager/cpp/view.h"
+
+namespace window_manager {
+
+// Implemented by an object that establishes the rules about what can be
+// focused or activated.
+class FocusRules {
+ public:
+  virtual ~FocusRules() {}
+
+  // Returns true if the children of |window| can be activated.
+  virtual bool SupportsChildActivation(mojo::View* window) const = 0;
+
+  // Returns true if |view| is a toplevel view. Whether or not a view
+  // is considered toplevel is determined by a similar set of rules that
+  // govern activation and focus. Not all toplevel views are activatable,
+  // call CanActivateView() to determine if a view can be activated.
+  virtual bool IsToplevelView(mojo::View* view) const = 0;
+  // Returns true if |view| can be activated or focused.
+  virtual bool CanActivateView(mojo::View* view) const = 0;
+  // For CanFocusView(), null is supported, because null is a valid focusable
+  // view (in the case of clearing focus).
+  virtual bool CanFocusView(mojo::View* view) const = 0;
+
+  // Returns the toplevel view containing |view|. Not all toplevel views
+  // are activatable, call GetActivatableView() instead to return the
+  // activatable view, which might be in a different hierarchy.
+  // Will return null if |view| is not contained by a view considered to be
+  // a toplevel view.
+  virtual mojo::View* GetToplevelView(mojo::View* view) const = 0;
+  // Returns the activatable or focusable view given an attempt to activate or
+  // focus |view|. Some possible scenarios (not intended to be exhaustive):
+  // - |view| is a child of a non-focusable view and so focus must be set
+  //   according to rules defined by the delegate, e.g. to a parent.
+  // - |view| is an activatable view that is the transient parent of a modal
+  //   view, so attempts to activate |view| should result in the modal
+  //   transient being activated instead.
+  // These methods may return null if they are unable to find an activatable
+  // or focusable view given |view|.
+  virtual mojo::View* GetActivatableView(mojo::View* view) const = 0;
+  virtual mojo::View* GetFocusableView(mojo::View* view) const = 0;
+
+  // Returns the next view to activate in the event that |ignore| is no longer
+  // activatable. This function is called when something is happening to
+  // |ignore| that means it can no longer have focus or activation, including
+  // but not limited to:
+  // - it or its parent hierarchy is being hidden, or removed from the
+  //   RootView.
+  // - it is being destroyed.
+  // - it is being explicitly deactivated.
+  // |ignore| cannot be null.
+  virtual mojo::View* GetNextActivatableView(mojo::View* ignore) const = 0;
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
diff --git a/services/window_manager/hit_test.h b/services/window_manager/hit_test.h
new file mode 100644
index 0000000..93126c5
--- /dev/null
+++ b/services/window_manager/hit_test.h
@@ -0,0 +1,41 @@
+// 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_WINDOW_MANAGER_HIT_TEST_H_
+#define SERVICES_WINDOW_MANAGER_HIT_TEST_H_
+
+// Defines the same symbolic names used by the WM_NCHITTEST Notification under
+// win32 (the integer values are not guaranteed to be equivalent). We do this
+// because we have a whole bunch of code that deals with window resizing and
+// such that requires these values.
+enum HitTestCompat {
+  HTNOWHERE = 0,
+  HTBORDER,
+  HTBOTTOM,
+  HTBOTTOMLEFT,
+  HTBOTTOMRIGHT,
+  HTCAPTION,
+  HTCLIENT,
+  HTCLOSE,
+  HTERROR,
+  HTGROWBOX,
+  HTHELP,
+  HTHSCROLL,
+  HTLEFT,
+  HTMENU,
+  HTMAXBUTTON,
+  HTMINBUTTON,
+  HTREDUCE,
+  HTRIGHT,
+  HTSIZE,
+  HTSYSMENU,
+  HTTOP,
+  HTTOPLEFT,
+  HTTOPRIGHT,
+  HTTRANSPARENT,
+  HTVSCROLL,
+  HTZOOM
+};
+
+#endif  // SERVICES_WINDOW_MANAGER_HIT_TEST_H_
diff --git a/services/window_manager/main.cc b/services/window_manager/main.cc
new file mode 100644
index 0000000..0a29bb4
--- /dev/null
+++ b/services/window_manager/main.cc
@@ -0,0 +1,108 @@
+// 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.
+
+#include "base/memory/scoped_ptr.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_delegate.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/services/view_manager/cpp/view_manager.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "services/window_manager/basic_focus_rules.h"
+#include "services/window_manager/window_manager_app.h"
+#include "services/window_manager/window_manager_delegate.h"
+#include "services/window_manager/window_manager_root.h"
+
+// ApplicationDelegate implementation file for WindowManager users (e.g.
+// core window manager tests) that do not want to provide their own
+// ApplicationDelegate::Create().
+
+using mojo::View;
+using mojo::ViewManager;
+
+namespace window_manager {
+
+class DefaultWindowManagerController : public WindowManagerController {
+ public:
+  DefaultWindowManagerController(WindowManagerRoot* wm_root)
+      : window_manager_root_(wm_root), root_(nullptr), window_offset_(10) {}
+
+  ~DefaultWindowManagerController() override {}
+
+ private:
+  // Overridden from ViewManagerDelegate:
+  void OnEmbed(View* root,
+               mojo::InterfaceRequest<mojo::ServiceProvider> services,
+               mojo::ServiceProviderPtr exposed_services) override {
+    root_ = root;
+    window_manager_root_->InitFocus(
+        make_scoped_ptr(new window_manager::BasicFocusRules(root_)));
+  }
+
+  void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+  // Overridden from WindowManagerDelegate:
+  void Embed(const mojo::String& url,
+             mojo::InterfaceRequest<mojo::ServiceProvider> services,
+             mojo::ServiceProviderPtr exposed_services) override {
+    DCHECK(root_);
+    View* view = root_->view_manager()->CreateView();
+    root_->AddChild(view);
+
+    mojo::Rect rect;
+    rect.x = rect.y = window_offset_;
+    rect.width = rect.height = 100;
+    view->SetBounds(rect);
+    window_offset_ += 10;
+
+    view->SetVisible(true);
+    view->Embed(url, services.Pass(), exposed_services.Pass());
+  }
+
+  WindowManagerRoot* window_manager_root_;
+
+  View* root_;
+  int window_offset_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManagerController);
+};
+
+class DefaultWindowManager : public mojo::ApplicationDelegate,
+                             public WindowManagerControllerFactory {
+ public:
+  DefaultWindowManager() : window_manager_app_(new WindowManagerApp(this)) {}
+
+  // Overridden from mojo::ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* impl) override {
+    tracing_.Initialize(impl);
+    window_manager_app_->Initialize(impl);
+  }
+
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    window_manager_app_->ConfigureIncomingConnection(connection);
+    return true;
+  }
+
+  scoped_ptr<WindowManagerController> CreateWindowManagerController(
+      mojo::ApplicationConnection* connection,
+      window_manager::WindowManagerRoot* wm_root) override {
+    return scoped_ptr<WindowManagerController>(
+        new DefaultWindowManagerController(wm_root));
+  }
+
+ private:
+  scoped_ptr<WindowManagerApp> window_manager_app_;
+  mojo::TracingImpl tracing_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManager);
+};
+
+}  // namespace window_manager
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(
+      new window_manager::DefaultWindowManager);
+  return runner.Run(application_request);
+}
diff --git a/services/window_manager/native_viewport_event_dispatcher_impl.cc b/services/window_manager/native_viewport_event_dispatcher_impl.cc
new file mode 100644
index 0000000..24411be
--- /dev/null
+++ b/services/window_manager/native_viewport_event_dispatcher_impl.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "services/window_manager/native_viewport_event_dispatcher_impl.h"
+
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "services/window_manager/view_event_dispatcher.h"
+#include "services/window_manager/window_manager_root.h"
+
+namespace window_manager {
+
+NativeViewportEventDispatcherImpl::NativeViewportEventDispatcherImpl(
+    WindowManagerRoot* root,
+    mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request)
+    : root_(root), binding_(this, request.Pass()) {
+}
+
+NativeViewportEventDispatcherImpl::~NativeViewportEventDispatcherImpl() {
+}
+
+ui::EventProcessor* NativeViewportEventDispatcherImpl::GetEventProcessor() {
+  return root_->event_dispatcher();
+}
+
+void NativeViewportEventDispatcherImpl::OnEvent(
+    mojo::EventPtr event,
+    const mojo::Callback<void()>& callback) {
+  scoped_ptr<ui::Event> ui_event = event.To<scoped_ptr<ui::Event>>();
+
+  if (ui_event)
+    SendEventToProcessor(ui_event.get());
+
+  callback.Run();
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/native_viewport_event_dispatcher_impl.h b/services/window_manager/native_viewport_event_dispatcher_impl.h
new file mode 100644
index 0000000..6491749
--- /dev/null
+++ b/services/window_manager/native_viewport_event_dispatcher_impl.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
+#define SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "ui/events/event_source.h"
+
+namespace window_manager {
+
+class WindowManagerRoot;
+
+class NativeViewportEventDispatcherImpl
+    : public ui::EventSource,
+      public mojo::NativeViewportEventDispatcher {
+ public:
+  NativeViewportEventDispatcherImpl(
+      WindowManagerRoot* root,
+      mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request);
+  ~NativeViewportEventDispatcherImpl() override;
+
+ private:
+  // ui::EventSource:
+  ui::EventProcessor* GetEventProcessor() override;
+
+  // NativeViewportEventDispatcher:
+  void OnEvent(mojo::EventPtr event,
+               const mojo::Callback<void()>& callback) override;
+
+  WindowManagerRoot* root_;
+
+  mojo::StrongBinding<mojo::NativeViewportEventDispatcher> binding_;
+  DISALLOW_COPY_AND_ASSIGN(NativeViewportEventDispatcherImpl);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
diff --git a/services/window_manager/run_all_unittests.cc b/services/window_manager/run_all_unittests.cc
new file mode 100644
index 0000000..f8f67b6
--- /dev/null
+++ b/services/window_manager/run_all_unittests.cc
@@ -0,0 +1,43 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "ui/gl/gl_surface.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_connection.h"
+#endif
+
+namespace window_manager {
+
+class WindowManagerTestSuite : public base::TestSuite {
+ public:
+  WindowManagerTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+  ~WindowManagerTestSuite() override {}
+
+ protected:
+  void Initialize() override {
+#if defined(USE_X11)
+    // Each test ends up creating a new thread for the native viewport service.
+    // In other words we'll use X on different threads, so tell it that.
+    gfx::InitializeThreadedX11();
+#endif
+    base::TestSuite::Initialize();
+    gfx::GLSurface::InitializeOneOffForTests();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerTestSuite);
+};
+
+}  // namespace window_manager
+
+int main(int argc, char** argv) {
+  window_manager::WindowManagerTestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(
+      argc, argv, base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/services/window_manager/view_event_dispatcher.cc b/services/window_manager/view_event_dispatcher.cc
new file mode 100644
index 0000000..5d2bc29
--- /dev/null
+++ b/services/window_manager/view_event_dispatcher.cc
@@ -0,0 +1,55 @@
+// 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.
+
+#include "services/window_manager/view_event_dispatcher.h"
+
+#include "mojo/services/view_manager/cpp/view.h"
+#include "services/window_manager/view_target.h"
+
+namespace window_manager {
+
+ViewEventDispatcher::ViewEventDispatcher()
+    : event_dispatch_target_(nullptr),
+      old_dispatch_target_(nullptr) {
+}
+
+ViewEventDispatcher::~ViewEventDispatcher() {}
+
+void ViewEventDispatcher::SetRootViewTarget(ViewTarget* root_view_target) {
+  root_view_target_ = root_view_target;
+}
+
+ui::EventTarget* ViewEventDispatcher::GetRootTarget() {
+  return root_view_target_;
+}
+
+void ViewEventDispatcher::OnEventProcessingStarted(ui::Event* event) {
+}
+
+bool ViewEventDispatcher::CanDispatchToTarget(ui::EventTarget* target) {
+  return event_dispatch_target_ == target;
+}
+
+ui::EventDispatchDetails ViewEventDispatcher::PreDispatchEvent(
+    ui::EventTarget* target,
+    ui::Event* event) {
+  // TODO(erg): PreDispatch in aura::WindowEventDispatcher does many, many
+  // things. It, and the functions split off for different event types, are
+  // most of the file.
+  old_dispatch_target_ = event_dispatch_target_;
+  event_dispatch_target_ = static_cast<ViewTarget*>(target);
+  return ui::EventDispatchDetails();
+}
+
+ui::EventDispatchDetails ViewEventDispatcher::PostDispatchEvent(
+    ui::EventTarget* target,
+    const ui::Event& event) {
+  // TODO(erg): Not at all as long as PreDispatchEvent, but still missing core
+  // details.
+  event_dispatch_target_ = old_dispatch_target_;
+  old_dispatch_target_ = nullptr;
+  return ui::EventDispatchDetails();
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/view_event_dispatcher.h b/services/window_manager/view_event_dispatcher.h
new file mode 100644
index 0000000..399939e
--- /dev/null
+++ b/services/window_manager/view_event_dispatcher.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/events/event_processor.h"
+#include "ui/events/event_target.h"
+
+namespace window_manager {
+
+class ViewTarget;
+
+class ViewEventDispatcher : public ui::EventProcessor {
+ public:
+  ViewEventDispatcher();
+  ~ViewEventDispatcher() override;
+
+  void SetRootViewTarget(ViewTarget* root_view_target);
+
+ private:
+  // Overridden from ui::EventProcessor:
+  ui::EventTarget* GetRootTarget() override;
+  void OnEventProcessingStarted(ui::Event* event) override;
+
+  // Overridden from ui::EventDispatcherDelegate.
+  bool CanDispatchToTarget(ui::EventTarget* target) override;
+  ui::EventDispatchDetails PreDispatchEvent(ui::EventTarget* target,
+                                            ui::Event* event) override;
+  ui::EventDispatchDetails PostDispatchEvent(
+      ui::EventTarget* target, const ui::Event& event) override;
+
+  // We keep a weak reference to ViewTarget*, which corresponds to the root of
+  // the mojo::View tree.
+  ViewTarget* root_view_target_;
+
+  ViewTarget* event_dispatch_target_;
+  ViewTarget* old_dispatch_target_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewEventDispatcher);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
diff --git a/services/window_manager/view_target.cc b/services/window_manager/view_target.cc
new file mode 100644
index 0000000..e73dd83
--- /dev/null
+++ b/services/window_manager/view_target.cc
@@ -0,0 +1,167 @@
+// 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.
+
+#include "services/window_manager/view_target.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "mojo/services/view_manager/cpp/view_property.h"
+#include "services/window_manager/view_targeter.h"
+#include "services/window_manager/window_manager_app.h"
+#include "ui/events/event.h"
+#include "ui/events/event_target_iterator.h"
+#include "ui/events/event_targeter.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/point3_f.h"
+#include "ui/gfx/transform.h"
+
+namespace window_manager {
+
+namespace {
+
+DEFINE_OWNED_VIEW_PROPERTY_KEY(ViewTarget, kViewTargetKey, nullptr);
+
+}  // namespace
+
+ViewTarget::~ViewTarget() {
+}
+
+// static
+ViewTarget* ViewTarget::TargetFromView(mojo::View* view) {
+  if (!view)
+    return nullptr;
+
+  ViewTarget* target = view->GetLocalProperty(kViewTargetKey);
+  if (target)
+    return target;
+
+  return new ViewTarget(view);
+}
+
+void ViewTarget::ConvertPointToTarget(const ViewTarget* source,
+                                      const ViewTarget* target,
+                                      gfx::Point* point) {
+  // TODO(erg): Do we need to deal with |source| and |target| being in
+  // different trees?
+  DCHECK_EQ(source->GetRoot(), target->GetRoot());
+  if (source == target)
+    return;
+
+  const ViewTarget* root_target = source->GetRoot();
+  CHECK_EQ(root_target, target->GetRoot());
+
+  if (source != root_target)
+    source->ConvertPointForAncestor(root_target, point);
+  if (target != root_target)
+    target->ConvertPointFromAncestor(root_target, point);
+}
+
+std::vector<ViewTarget*> ViewTarget::GetChildren() const {
+  std::vector<ViewTarget*> targets;
+  for (mojo::View* child : view_->children())
+    targets.push_back(TargetFromView(child));
+  return targets;
+}
+
+const ViewTarget* ViewTarget::GetParent() const {
+  return TargetFromView(view_->parent());
+}
+
+gfx::Rect ViewTarget::GetBounds() const {
+  return view_->bounds().To<gfx::Rect>();
+}
+
+bool ViewTarget::HasParent() const {
+  return !!view_->parent();
+}
+
+bool ViewTarget::IsVisible() const {
+  return view_->visible();
+}
+
+const ViewTarget* ViewTarget::GetRoot() const {
+  const ViewTarget* root = this;
+  for (const ViewTarget* parent = this; parent; parent = parent->GetParent())
+    root = parent;
+  return root;
+}
+
+scoped_ptr<ViewTargeter> ViewTarget::SetEventTargeter(
+    scoped_ptr<ViewTargeter> targeter) {
+  scoped_ptr<ViewTargeter> old_targeter = targeter_.Pass();
+  targeter_ = targeter.Pass();
+  return old_targeter;
+}
+
+bool ViewTarget::CanAcceptEvent(const ui::Event& event) {
+  // We need to make sure that a touch cancel event and any gesture events it
+  // creates can always reach the window. This ensures that we receive a valid
+  // touch / gesture stream.
+  if (event.IsEndingEvent())
+    return true;
+
+  if (!view_->visible())
+    return false;
+
+  // The top-most window can always process an event.
+  if (!view_->parent())
+    return true;
+
+  // In aura, we only accept events if this is a key event or if the user
+  // supplied a TargetHandler, usually the aura::WindowDelegate. Here, we're
+  // just forwarding events to other Views which may be in other processes, so
+  // always accept.
+  return true;
+}
+
+ui::EventTarget* ViewTarget::GetParentTarget() {
+  return TargetFromView(view_->parent());
+}
+
+scoped_ptr<ui::EventTargetIterator> ViewTarget::GetChildIterator() const {
+  return scoped_ptr<ui::EventTargetIterator>(
+      new ui::CopyingEventTargetIteratorImpl<ViewTarget>(GetChildren()));
+}
+
+ui::EventTargeter* ViewTarget::GetEventTargeter() {
+  return targeter_.get();
+}
+
+void ViewTarget::ConvertEventToTarget(ui::EventTarget* target,
+                                      ui::LocatedEvent* event) {
+  event->ConvertLocationToTarget(this, static_cast<ViewTarget*>(target));
+}
+
+ViewTarget::ViewTarget(mojo::View* view_to_wrap) : view_(view_to_wrap) {
+  DCHECK(view_->GetLocalProperty(kViewTargetKey) == nullptr);
+  view_->SetLocalProperty(kViewTargetKey, this);
+}
+
+bool ViewTarget::ConvertPointForAncestor(const ViewTarget* ancestor,
+                                         gfx::Point* point) const {
+  gfx::Vector2d offset;
+  bool result = GetTargetOffsetRelativeTo(ancestor, &offset);
+  *point += offset;
+  return result;
+}
+
+bool ViewTarget::ConvertPointFromAncestor(const ViewTarget* ancestor,
+                                          gfx::Point* point) const {
+  gfx::Vector2d offset;
+  bool result = GetTargetOffsetRelativeTo(ancestor, &offset);
+  *point -= offset;
+  return result;
+}
+
+bool ViewTarget::GetTargetOffsetRelativeTo(const ViewTarget* ancestor,
+                                           gfx::Vector2d* offset) const {
+  const ViewTarget* v = this;
+  for (; v && v != ancestor; v = v->GetParent()) {
+    gfx::Rect bounds = v->GetBounds();
+    *offset += gfx::Vector2d(bounds.x(), bounds.y());
+  }
+  return v == ancestor;
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/view_target.h b/services/window_manager/view_target.h
new file mode 100644
index 0000000..1119e1f
--- /dev/null
+++ b/services/window_manager/view_target.h
@@ -0,0 +1,100 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
+
+#include "ui/events/event_target.h"
+
+namespace gfx {
+class Point;
+class Rect;
+class Vector2d;
+}
+
+namespace ui {
+class EventTargeter;
+}
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+class TestView;
+class ViewTargeter;
+class WindowManagerApp;
+
+// A wrapper class around mojo::View; we can't subclass View to implement the
+// event targeting interfaces, so we create a separate object which observes
+// the View and ties its lifetime to it.
+//
+// We set ourselves as a property of the view passed in, and we are owned by
+// said View.
+class ViewTarget : public ui::EventTarget {
+ public:
+  ~ViewTarget() override;
+
+  // Returns the ViewTarget for a View. ViewTargets are owned by the |view|
+  // passed in, and are created on demand.
+  static ViewTarget* TargetFromView(mojo::View* view);
+
+  // Converts |point| from |source|'s coordinates to |target|'s. If |source| is
+  // NULL, the function returns without modifying |point|. |target| cannot be
+  // NULL.
+  static void ConvertPointToTarget(const ViewTarget* source,
+                                   const ViewTarget* target,
+                                   gfx::Point* point);
+
+  mojo::View* view() { return view_; }
+
+  // TODO(erg): Make this const once we've removed aura from the tree and it's
+  // feasible to change all callers of the EventTargeter interface to pass and
+  // accept const objects. (When that gets done, re-const the
+  // EventTargetIterator::GetNextTarget and EventTarget::GetChildIterator
+  // interfaces.)
+  std::vector<ViewTarget*> GetChildren() const;
+
+  const ViewTarget* GetParent() const;
+  gfx::Rect GetBounds() const;
+  bool HasParent() const;
+  bool IsVisible() const;
+
+  const ViewTarget* GetRoot() const;
+
+  // Sets a new ViewTargeter for the view, and returns the previous
+  // ViewTargeter.
+  scoped_ptr<ViewTargeter> SetEventTargeter(scoped_ptr<ViewTargeter> targeter);
+
+  // Overridden from ui::EventTarget:
+  bool CanAcceptEvent(const ui::Event& event) override;
+  EventTarget* GetParentTarget() override;
+  scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override;
+  ui::EventTargeter* GetEventTargeter() override;
+  void ConvertEventToTarget(ui::EventTarget* target,
+                            ui::LocatedEvent* event) override;
+
+ private:
+  friend class TestView;
+  explicit ViewTarget(mojo::View* view_to_wrap);
+
+  bool ConvertPointForAncestor(const ViewTarget* ancestor,
+                               gfx::Point* point) const;
+  bool ConvertPointFromAncestor(const ViewTarget* ancestor,
+                                gfx::Point* point) const;
+  bool GetTargetOffsetRelativeTo(const ViewTarget* ancestor,
+                                 gfx::Vector2d* offset) const;
+
+  // The mojo::View that we dispatch to.
+  mojo::View* view_;
+
+  scoped_ptr<ViewTargeter> targeter_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewTarget);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
diff --git a/services/window_manager/view_target_unittest.cc b/services/window_manager/view_target_unittest.cc
new file mode 100644
index 0000000..9524759
--- /dev/null
+++ b/services/window_manager/view_target_unittest.cc
@@ -0,0 +1,81 @@
+// 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.
+
+#include "services/window_manager/view_target.h"
+
+#include <set>
+
+#include "mojo/services/view_manager/cpp/view.h"
+#include "services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace window_manager {
+
+using ViewTargetTest = testing::Test;
+
+// V1
+//  +-- V2
+//       +-- V3
+TEST_F(ViewTargetTest, GetRoot) {
+  TestView v1(1, gfx::Rect(20, 20, 400, 400));
+  TestView v2(2, gfx::Rect(10, 10, 350, 350));
+  TestView v3(3, gfx::Rect(10, 10, 100, 100));
+  v1.AddChild(&v2);
+  v2.AddChild(&v3);
+
+  EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+            ViewTarget::TargetFromView(&v1)->GetRoot());
+  EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+            ViewTarget::TargetFromView(&v2)->GetRoot());
+  EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+            ViewTarget::TargetFromView(&v3)->GetRoot());
+}
+
+// V1
+//  +-- V2
+TEST_F(ViewTargetTest, ConvertPointToTarget_Simple) {
+  TestView v1(1, gfx::Rect(20, 20, 400, 400));
+  TestView v2(2, gfx::Rect(10, 10, 350, 350));
+  v1.AddChild(&v2);
+
+  ViewTarget* t1 = v1.target();
+  ViewTarget* t2 = v2.target();
+
+  gfx::Point point1_in_t2_coords(5, 5);
+  ViewTarget::ConvertPointToTarget(t2, t1, &point1_in_t2_coords);
+  gfx::Point point1_in_t1_coords(15, 15);
+  EXPECT_EQ(point1_in_t1_coords, point1_in_t2_coords);
+
+  gfx::Point point2_in_t1_coords(5, 5);
+  ViewTarget::ConvertPointToTarget(t1, t2, &point2_in_t1_coords);
+  gfx::Point point2_in_t2_coords(-5, -5);
+  EXPECT_EQ(point2_in_t2_coords, point2_in_t1_coords);
+}
+
+// V1
+//  +-- V2
+//       +-- V3
+TEST_F(ViewTargetTest, ConvertPointToTarget_Medium) {
+  TestView v1(1, gfx::Rect(20, 20, 400, 400));
+  TestView v2(2, gfx::Rect(10, 10, 350, 350));
+  TestView v3(3, gfx::Rect(10, 10, 100, 100));
+  v1.AddChild(&v2);
+  v2.AddChild(&v3);
+
+  ViewTarget* t1 = v1.target();
+  ViewTarget* t3 = v3.target();
+
+  gfx::Point point1_in_t3_coords(5, 5);
+  ViewTarget::ConvertPointToTarget(t3, t1, &point1_in_t3_coords);
+  gfx::Point point1_in_t1_coords(25, 25);
+  EXPECT_EQ(point1_in_t1_coords, point1_in_t3_coords);
+
+  gfx::Point point2_in_t1_coords(5, 5);
+  ViewTarget::ConvertPointToTarget(t1, t3, &point2_in_t1_coords);
+  gfx::Point point2_in_t3_coords(-15, -15);
+  EXPECT_EQ(point2_in_t3_coords, point2_in_t1_coords);
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/view_targeter.cc b/services/window_manager/view_targeter.cc
new file mode 100644
index 0000000..2b69e6c
--- /dev/null
+++ b/services/window_manager/view_targeter.cc
@@ -0,0 +1,148 @@
+// 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.
+
+#include "services/window_manager/view_targeter.h"
+
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller.h"
+#include "services/window_manager/view_target.h"
+#include "ui/events/event_target_iterator.h"
+
+namespace window_manager {
+
+ViewTargeter::ViewTargeter() {}
+
+ViewTargeter::~ViewTargeter() {}
+
+ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root,
+                                                  ui::Event* event) {
+  ViewTarget* view = static_cast<ViewTarget*>(root);
+  ViewTarget* target;
+  if (event->IsKeyEvent()) {
+    target = FindTargetForKeyEvent(view, *static_cast<ui::KeyEvent*>(event));
+  } else if (event->IsMouseEvent() ||
+      event->IsScrollEvent() ||
+      event->IsTouchEvent() ||
+      event->IsGestureEvent()) {
+    target = static_cast<ViewTarget*>(FindTargetForLocatedEvent(
+      root, static_cast<ui::LocatedEvent*>(event)));
+  } else {
+    target = static_cast<ViewTarget*>(root);
+  }
+
+  // TODO(erg): The aura version of this method does a lot of work to handle
+  // dispatching to a target that isn't a child of |view|. For now, punt on
+  // this.
+  DCHECK_EQ(view->GetRoot(), target->GetRoot());
+
+  return target;
+}
+
+ui::EventTarget* ViewTargeter::FindNextBestTarget(
+  ui::EventTarget* previous_target, ui::Event* event) {
+  return nullptr;
+}
+
+ui::EventTarget* ViewTargeter::FindTargetForLocatedEvent(
+    ui::EventTarget* root,
+    ui::LocatedEvent* event) {
+  ViewTarget* view = static_cast<ViewTarget*>(root);
+  if (!view->HasParent()) {
+    ViewTarget* target = FindTargetInRootView(view, *event);
+    if (target) {
+      view->ConvertEventToTarget(target, event);
+      return target;
+    }
+  }
+  scoped_ptr<ui::EventTargetIterator> iter = root->GetChildIterator();
+  if (iter) {
+    ui::EventTarget* target = root;
+    for (ui::EventTarget* child = iter->GetNextTarget(); child;
+         child = iter->GetNextTarget()) {
+      ViewTargeter* targeter = static_cast<ViewTargeter*>(
+        child->GetEventTargeter());
+      if (!targeter)
+        targeter = this;
+      if (!targeter->SubtreeShouldBeExploredForEvent(child, *event))
+        continue;
+      target->ConvertEventToTarget(child, event);
+      target = child;
+      ui::EventTarget* child_target =
+          targeter->FindTargetForLocatedEvent(child, event);
+      if (child_target)
+        return child_target;
+    }
+    target->ConvertEventToTarget(root, event);
+  }
+  return root->CanAcceptEvent(*event) ? root : NULL;
+}
+
+bool ViewTargeter::SubtreeShouldBeExploredForEvent(
+  ui::EventTarget* target, const ui::LocatedEvent& event) {
+  return SubtreeCanAcceptEvent(target, event) &&
+         EventLocationInsideBounds(target, event);
+}
+
+bool ViewTargeter::SubtreeCanAcceptEvent(ui::EventTarget* target,
+                                         const ui::LocatedEvent& event) const {
+  ViewTarget* view = static_cast<ViewTarget*>(target);
+
+  if (!view->IsVisible())
+    return false;
+
+  // TODO(erg): We may need to keep track of the parent on ViewTarget, because
+  // we have a check here about
+  // WindowDelegate::ShouldDescendIntoChildForEventHandling().
+
+  // TODO(sky): decide if we really want this. If we do, it should be a public
+  // constant and documented.
+  if (view->view()->shared_properties().count("deliver-events-to-parent"))
+    return false;
+
+  return true;
+}
+
+bool ViewTargeter::EventLocationInsideBounds(
+    ui::EventTarget* target,
+    const ui::LocatedEvent& event) const {
+  ViewTarget* view = static_cast<ViewTarget*>(target);
+  gfx::Point point = event.location();
+  const ViewTarget* parent = view->GetParent();
+  if (parent)
+    ViewTarget::ConvertPointToTarget(parent, view, &point);
+  return gfx::Rect(view->GetBounds().size()).Contains(point);
+}
+
+ViewTarget* ViewTargeter::FindTargetForKeyEvent(ViewTarget* view_target,
+                                                const ui::KeyEvent& key) {
+  FocusController* focus_controller = GetFocusController(view_target->view());
+  if (focus_controller) {
+    mojo::View* focused_view = focus_controller->GetFocusedView();
+    if (focused_view)
+      return ViewTarget::TargetFromView(focused_view);
+  }
+  return view_target;
+}
+
+ViewTarget* ViewTargeter::FindTargetInRootView(ViewTarget* root_view,
+                                               const ui::LocatedEvent& event) {
+  // TODO(erg): This here is important because it resolves
+  // mouse_pressed_handler() in the aura version. This is what makes sure
+  // that a view gets both the mouse down and up.
+
+  CaptureController* capture_controller =
+      GetCaptureController(root_view->view());
+  if (capture_controller) {
+    mojo::View* capture_view = capture_controller->GetCapture();
+    if (capture_view)
+      return ViewTarget::TargetFromView(capture_view);
+  }
+
+  // TODO(erg): There's a whole bunch of junk about handling touch events
+  // here. Handle later.
+
+  return nullptr;
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/view_targeter.h b/services/window_manager/view_targeter.h
new file mode 100644
index 0000000..1a7106a
--- /dev/null
+++ b/services/window_manager/view_targeter.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
+
+#include "ui/events/event.h"
+#include "ui/events/event_targeter.h"
+
+namespace window_manager {
+
+class ViewTarget;
+
+class ViewTargeter : public ui::EventTargeter {
+ public:
+  ViewTargeter();
+  ~ViewTargeter() override;
+
+ protected:
+  // ui::EventTargeter:
+  ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
+                                      ui::Event* event) override;
+  ui::EventTarget* FindNextBestTarget(ui::EventTarget* previous_target,
+                                      ui::Event* event) override;
+
+  ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root,
+                                             ui::LocatedEvent* event);
+  bool SubtreeShouldBeExploredForEvent(ui::EventTarget* target,
+                                       const ui::LocatedEvent& event);
+  bool SubtreeCanAcceptEvent(ui::EventTarget* target,
+                             const ui::LocatedEvent& event) const;
+  bool EventLocationInsideBounds(ui::EventTarget* target,
+                                 const ui::LocatedEvent& event) const;
+
+ private:
+  // Targets either the root View or the currently focused view.
+  ViewTarget* FindTargetForKeyEvent(ViewTarget* view, const ui::KeyEvent& key);
+
+  // Deals with cases where the |root_view| needs to change how things are
+  // dispatched. (For example, in the case of capture.)
+  ViewTarget* FindTargetInRootView(ViewTarget* root_view,
+                                   const ui::LocatedEvent& event);
+
+  DISALLOW_COPY_AND_ASSIGN(ViewTargeter);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
diff --git a/services/window_manager/view_targeter_unittest.cc b/services/window_manager/view_targeter_unittest.cc
new file mode 100644
index 0000000..f0b86c4
--- /dev/null
+++ b/services/window_manager/view_targeter_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2013 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/window_manager/view_targeter.h"
+
+#include "base/time/time.h"
+#include "services/window_manager/basic_focus_rules.h"
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller.h"
+#include "services/window_manager/view_event_dispatcher.h"
+#include "services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/test/test_event_handler.h"
+
+namespace window_manager {
+
+class ViewTargeterTest : public testing::Test {
+ public:
+  ViewTargeterTest() {}
+  ~ViewTargeterTest() override {}
+
+  void SetUp() override {
+    view_event_dispatcher_.reset(new ViewEventDispatcher());
+  }
+
+  void TearDown() override {
+    view_event_dispatcher_.reset();
+    testing::Test::TearDown();
+  }
+
+ protected:
+  scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ViewTargeterTest);
+};
+
+TEST_F(ViewTargeterTest, Basic) {
+  // The dispatcher will take ownership of the tree root.
+  TestView root(1, gfx::Rect(0, 0, 100, 100));
+  ViewTarget* root_target = root.target();
+  root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+  view_event_dispatcher_->SetRootViewTarget(root_target);
+
+  CaptureController capture_controller;
+  SetCaptureController(&root, &capture_controller);
+
+  TestView one(2, gfx::Rect(0, 0, 500, 100));
+  TestView two(3, gfx::Rect(501, 0, 500, 1000));
+
+  root.AddChild(&one);
+  root.AddChild(&two);
+
+  ui::test::TestEventHandler handler;
+  one.target()->AddPreTargetHandler(&handler);
+
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(20, 20),
+                       gfx::Point(20, 20), base::TimeDelta(),
+                       ui::EF_NONE, ui::EF_NONE);
+  ui::EventDispatchDetails details =
+      view_event_dispatcher_->OnEventFromSource(&press);
+  ASSERT_FALSE(details.dispatcher_destroyed);
+
+  EXPECT_EQ(1, handler.num_mouse_events());
+
+  one.target()->RemovePreTargetHandler(&handler);
+}
+
+TEST_F(ViewTargeterTest, KeyTest) {
+  // The dispatcher will take ownership of the tree root.
+  TestView root(1, gfx::Rect(0, 0, 100, 100));
+  ViewTarget* root_target = root.target();
+  root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+  view_event_dispatcher_->SetRootViewTarget(root_target);
+
+  CaptureController capture_controller;
+  SetCaptureController(&root, &capture_controller);
+
+  TestView one(2, gfx::Rect(0, 0, 500, 100));
+  TestView two(3, gfx::Rect(501, 0, 500, 1000));
+
+  root.AddChild(&one);
+  root.AddChild(&two);
+
+  ui::test::TestEventHandler one_handler;
+  one.target()->AddPreTargetHandler(&one_handler);
+
+  ui::test::TestEventHandler two_handler;
+  two.target()->AddPreTargetHandler(&two_handler);
+
+  FocusController focus_controller(make_scoped_ptr(new BasicFocusRules(&root)));
+  SetFocusController(&root, &focus_controller);
+
+  // Focus |one|. Then test that it receives a key event.
+  focus_controller.FocusView(&one);
+  ui::KeyEvent key_event_one(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
+  ui::EventDispatchDetails details =
+      view_event_dispatcher_->OnEventFromSource(&key_event_one);
+  ASSERT_FALSE(details.dispatcher_destroyed);
+  EXPECT_EQ(1, one_handler.num_key_events());
+
+  // Focus |two|. Then test that it receives a key event.
+  focus_controller.FocusView(&two);
+  ui::KeyEvent key_event_two(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
+  details = view_event_dispatcher_->OnEventFromSource(&key_event_two);
+  ASSERT_FALSE(details.dispatcher_destroyed);
+  EXPECT_EQ(1, two_handler.num_key_events());
+
+  two.target()->RemovePreTargetHandler(&two_handler);
+  one.target()->RemovePreTargetHandler(&one_handler);
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_app.cc b/services/window_manager/window_manager_app.cc
new file mode 100644
index 0000000..b662c54
--- /dev/null
+++ b/services/window_manager/window_manager_app.cc
@@ -0,0 +1,64 @@
+// 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.
+
+#include "services/window_manager/window_manager_app.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "mojo/services/view_manager/cpp/view_manager.h"
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller.h"
+#include "services/window_manager/focus_rules.h"
+#include "services/window_manager/hit_test.h"
+#include "services/window_manager/view_event_dispatcher.h"
+#include "services/window_manager/view_target.h"
+#include "services/window_manager/view_targeter.h"
+#include "services/window_manager/window_manager_delegate.h"
+#include "services/window_manager/window_manager_root.h"
+
+using mojo::ApplicationConnection;
+using mojo::Id;
+using mojo::ServiceProvider;
+using mojo::View;
+using mojo::WindowManager;
+
+namespace window_manager {
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, public:
+
+WindowManagerApp::WindowManagerApp(
+    WindowManagerControllerFactory* controller_factory)
+    : app_impl_(nullptr), controller_factory_(controller_factory) {}
+
+WindowManagerApp::~WindowManagerApp() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ApplicationDelegate implementation:
+
+void WindowManagerApp::Initialize(mojo::ApplicationImpl* impl) {
+  app_impl_ = impl;
+}
+
+bool WindowManagerApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::WindowManager>(this);
+  return true;
+}
+
+void WindowManagerApp::Create(
+    ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::WindowManager> request) {
+  // WindowManagerRoot manages its own lifetime.
+  new WindowManagerRoot(app_impl_, connection, controller_factory_,
+                        request.Pass());
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_app.h b/services/window_manager/window_manager_app.h
new file mode 100644
index 0000000..a1e0c84
--- /dev/null
+++ b/services/window_manager/window_manager_app.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+#include "services/window_manager/window_manager_delegate.h"
+
+namespace window_manager {
+
+// Implements core window manager functionality that could conceivably be shared
+// across multiple window managers implementing superficially different user
+// experiences.
+// A window manager wishing to use this core should create and own an instance
+// of this object. They may implement the associated ViewManager/WindowManager
+// delegate interfaces exposed by the view manager, this object provides the
+// canonical implementation of said interfaces but will call out to the wrapped
+// instances.
+// Window manager clients should request a WindowManager service to get a new
+// native window.
+class WindowManagerApp : public mojo::ApplicationDelegate,
+                         public mojo::InterfaceFactory<mojo::WindowManager> {
+ public:
+  WindowManagerApp(WindowManagerControllerFactory* controller_factory);
+  ~WindowManagerApp() override;
+
+  // Overridden from ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* impl) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+ private:
+  // InterfaceFactory<WindowManager>:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::WindowManager> request) override;
+
+  mojo::ApplicationImpl* app_impl_;
+  WindowManagerControllerFactory* controller_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerApp);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
diff --git a/services/window_manager/window_manager_apptest.cc b/services/window_manager/window_manager_apptest.cc
new file mode 100644
index 0000000..7eaa27c
--- /dev/null
+++ b/services/window_manager/window_manager_apptest.cc
@@ -0,0 +1,212 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+
+namespace mojo {
+namespace {
+
+// TestApplication's view is embedded by the window manager.
+class TestApplication : public ApplicationDelegate, public ViewManagerDelegate {
+ public:
+  TestApplication() : root_(nullptr) {}
+  ~TestApplication() override {}
+
+  View* root() const { return root_; }
+
+  void set_embed_callback(const base::Closure& callback) {
+    embed_callback_ = callback;
+  }
+
+ private:
+  // ApplicationDelegate:
+  void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  // ViewManagerDelegate:
+  void OnEmbed(View* root,
+               InterfaceRequest<ServiceProvider> services,
+               ServiceProviderPtr exposed_services) override {
+    root_ = root;
+    embed_callback_.Run();
+  }
+  void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+  View* root_;
+  base::Closure embed_callback_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestApplication);
+};
+
+class TestWindowManagerObserver : public WindowManagerObserver {
+ public:
+  explicit TestWindowManagerObserver(
+      InterfaceRequest<WindowManagerObserver> observer_request)
+      : binding_(this, observer_request.Pass()) {}
+  ~TestWindowManagerObserver() override {}
+
+ private:
+  // Overridden from WindowManagerClient:
+  void OnCaptureChanged(Id new_capture_node_id) override {}
+  void OnFocusChanged(Id focused_node_id) override {}
+  void OnActiveWindowChanged(Id active_window) override {}
+
+  Binding<WindowManagerObserver> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindowManagerObserver);
+};
+
+class WindowManagerApplicationTest : public test::ApplicationTestBase {
+ public:
+  WindowManagerApplicationTest() {}
+  ~WindowManagerApplicationTest() override {}
+
+ protected:
+  // ApplicationTestBase:
+  void SetUp() override {
+    ApplicationTestBase::SetUp();
+    application_impl()->ConnectToService("mojo:window_manager",
+                                         &window_manager_);
+  }
+  ApplicationDelegate* GetApplicationDelegate() override {
+    return &test_application_;
+  }
+
+  void EmbedApplicationWithURL(const std::string& url) {
+    window_manager_->Embed(url, nullptr, nullptr);
+
+    base::RunLoop run_loop;
+    test_application_.set_embed_callback(run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  WindowManagerPtr window_manager_;
+  TestApplication test_application_;
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(WindowManagerApplicationTest);
+};
+
+TEST_F(WindowManagerApplicationTest, Embed) {
+  EXPECT_EQ(nullptr, test_application_.root());
+  EmbedApplicationWithURL(application_impl()->url());
+  EXPECT_NE(nullptr, test_application_.root());
+}
+
+struct BoolCallback {
+  BoolCallback(bool* bool_value, base::RunLoop* run_loop)
+      : bool_value(bool_value), run_loop(run_loop) {}
+
+  void Run(bool value) const {
+    *bool_value = value;
+    run_loop->Quit();
+  }
+
+  bool* bool_value;
+  base::RunLoop* run_loop;
+};
+
+TEST_F(WindowManagerApplicationTest, SetCaptureFailsFromNonVM) {
+  EmbedApplicationWithURL(application_impl()->url());
+  bool callback_value = true;
+  base::RunLoop run_loop;
+  window_manager_->SetCapture(test_application_.root()->id(),
+                              BoolCallback(&callback_value, &run_loop));
+  run_loop.Run();
+  // This call only succeeds for WindowManager connections from the ViewManager.
+  EXPECT_FALSE(callback_value);
+}
+
+TEST_F(WindowManagerApplicationTest, FocusWindowFailsFromNonVM) {
+  EmbedApplicationWithURL(application_impl()->url());
+  bool callback_value = true;
+  base::RunLoop run_loop;
+  window_manager_->FocusWindow(test_application_.root()->id(),
+                               BoolCallback(&callback_value, &run_loop));
+  run_loop.Run();
+  // This call only succeeds for WindowManager connections from the ViewManager.
+  EXPECT_FALSE(callback_value);
+}
+
+TEST_F(WindowManagerApplicationTest, ActivateWindowFailsFromNonVM) {
+  EmbedApplicationWithURL(application_impl()->url());
+  bool callback_value = true;
+  base::RunLoop run_loop;
+  window_manager_->ActivateWindow(test_application_.root()->id(),
+                                  BoolCallback(&callback_value, &run_loop));
+  run_loop.Run();
+  // This call only succeeds for WindowManager connections from the ViewManager.
+  EXPECT_FALSE(callback_value);
+}
+
+struct FocusedAndActiveViewsCallback {
+  FocusedAndActiveViewsCallback(uint32* capture_view_id,
+                                uint32* focused_view_id,
+                                uint32* active_view_id,
+                                base::RunLoop* run_loop)
+      : capture_view_id(capture_view_id),
+        focused_view_id(focused_view_id),
+        active_view_id(active_view_id),
+        run_loop(run_loop) {
+  }
+
+  void Run(uint32 capture, uint32 focused, uint32 active) const {
+    *capture_view_id = capture;
+    *focused_view_id = focused;
+    *active_view_id = active;
+    run_loop->Quit();
+  }
+
+  uint32* capture_view_id;
+  uint32* focused_view_id;
+  uint32* active_view_id;
+  base::RunLoop* run_loop;
+};
+
+TEST_F(WindowManagerApplicationTest, GetFocusedAndActiveViewsFailsWithoutFC) {
+  EmbedApplicationWithURL(application_impl()->url());
+  uint32 capture_view_id = -1;
+  uint32 focused_view_id = -1;
+  uint32 active_view_id = -1;
+  base::RunLoop run_loop;
+
+  WindowManagerObserverPtr observer;
+  scoped_ptr<TestWindowManagerObserver> window_manager_observer(
+      new TestWindowManagerObserver(GetProxy(&observer)));
+
+  window_manager_->GetFocusedAndActiveViews(
+      observer.Pass(),
+      FocusedAndActiveViewsCallback(&capture_view_id,
+                                    &focused_view_id,
+                                    &active_view_id,
+                                    &run_loop));
+  run_loop.Run();
+  // This call fails if the WindowManager does not have a FocusController.
+  EXPECT_EQ(0u, capture_view_id);
+  EXPECT_EQ(0u, focused_view_id);
+  EXPECT_EQ(0u, active_view_id);
+}
+
+// TODO(msw): Write tests exercising other WindowManager functionality.
+
+}  // namespace
+}  // namespace mojo
diff --git a/services/window_manager/window_manager_delegate.h b/services/window_manager/window_manager_delegate.h
new file mode 100644
index 0000000..88ebb90
--- /dev/null
+++ b/services/window_manager/window_manager_delegate.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+
+namespace mojo {
+class ApplicationConnection;
+}
+
+namespace window_manager {
+
+class WindowManagerRoot;
+
+class WindowManagerDelegate {
+ public:
+  // See WindowManager::Embed() for details.
+  virtual void Embed(const mojo::String& url,
+                     mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                     mojo::ServiceProviderPtr exposed_services) = 0;
+
+ protected:
+  virtual ~WindowManagerDelegate() {}
+};
+
+// Controls a single instance of WindowManagerRoot, representing one native
+// window and all its Mojo-controlled views inside it. Instances of this object
+// are owned by the WindowManagerRoot they control.
+class WindowManagerController : public WindowManagerDelegate,
+                                public mojo::ViewManagerDelegate {};
+
+// Creates WindowManagerControllers.
+// This class should be implemented by window managers and passed to the
+// WindowManagerApp object.
+class WindowManagerControllerFactory {
+ public:
+  // CreateWindowManagerController() is called by WindowManagerRoot to create
+  // its controller. The WindowManagerRoot passed to this method is guaranteed
+  // to remain valid for the entire lifetime of the created
+  // WindowManagerController.
+  virtual scoped_ptr<WindowManagerController> CreateWindowManagerController(
+      mojo::ApplicationConnection* connection,
+      WindowManagerRoot* wm_root_) = 0;
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/services/window_manager/window_manager_impl.cc b/services/window_manager/window_manager_impl.cc
new file mode 100644
index 0000000..16e71b4
--- /dev/null
+++ b/services/window_manager/window_manager_impl.cc
@@ -0,0 +1,94 @@
+// 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.
+
+#include "services/window_manager/window_manager_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller.h"
+#include "services/window_manager/window_manager_root.h"
+
+using mojo::Callback;
+using mojo::Id;
+
+namespace window_manager {
+
+WindowManagerImpl::WindowManagerImpl(
+    WindowManagerRoot* window_manager,
+    mojo::ScopedMessagePipeHandle window_manager_pipe,
+    bool from_vm)
+    : window_manager_(window_manager),
+      from_vm_(from_vm),
+      binding_(this, window_manager_pipe.Pass()) {
+  binding_.set_connection_error_handler(
+      // WindowManagerRoot::RemoveConnectedService will destroy this object.
+      base::Bind(&WindowManagerRoot::RemoveConnectedService,
+                 base::Unretained(window_manager_), base::Unretained(this)));
+}
+
+WindowManagerImpl::~WindowManagerImpl(){};
+
+void WindowManagerImpl::NotifyViewFocused(Id focused_id) {
+  if (from_vm_ && observer_)
+    observer_->OnFocusChanged(focused_id);
+}
+
+void WindowManagerImpl::NotifyWindowActivated(Id active_id) {
+  if (from_vm_ && observer_)
+    observer_->OnActiveWindowChanged(active_id);
+}
+
+void WindowManagerImpl::NotifyCaptureChanged(Id capture_id) {
+  if (from_vm_ && observer_)
+    observer_->OnCaptureChanged(capture_id);
+}
+
+void WindowManagerImpl::Embed(
+    const mojo::String& url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  window_manager_->Embed(url, services.Pass(), exposed_services.Pass());
+}
+
+void WindowManagerImpl::SetCapture(Id view,
+                                   const Callback<void(bool)>& callback) {
+  callback.Run(from_vm_ && window_manager_->IsReady() &&
+               window_manager_->SetCapture(view));
+}
+
+void WindowManagerImpl::FocusWindow(Id view,
+                                    const Callback<void(bool)>& callback) {
+  callback.Run(from_vm_ && window_manager_->IsReady() &&
+               window_manager_->FocusWindow(view));
+}
+
+void WindowManagerImpl::ActivateWindow(Id view,
+                                       const Callback<void(bool)>& callback) {
+  callback.Run(from_vm_ && window_manager_->IsReady() &&
+               window_manager_->ActivateWindow(view));
+}
+
+void WindowManagerImpl::GetFocusedAndActiveViews(
+    mojo::WindowManagerObserverPtr observer,
+    const mojo::WindowManager::GetFocusedAndActiveViewsCallback& callback) {
+  observer_ = observer.Pass();
+  if (!window_manager_->focus_controller()) {
+    // TODO(sky): add typedef for 0.
+    callback.Run(0, 0, 0);
+    return;
+  }
+  mojo::View* capture_view =
+      window_manager_->capture_controller()->GetCapture();
+  mojo::View* active_view =
+      window_manager_->focus_controller()->GetActiveView();
+  mojo::View* focused_view =
+      window_manager_->focus_controller()->GetFocusedView();
+  // TODO(sky): sanitize ids for client.
+  callback.Run(capture_view ? capture_view->id() : 0,
+               focused_view ? focused_view->id() : 0,
+               active_view ? active_view->id() : 0);
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_impl.h b/services/window_manager/window_manager_impl.h
new file mode 100644
index 0000000..bf0c0ac
--- /dev/null
+++ b/services/window_manager/window_manager_impl.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
+
+namespace window_manager {
+
+class WindowManagerRoot;
+
+class WindowManagerImpl : public mojo::WindowManager {
+ public:
+  // WindowManagerImpl is fully owned by WindowManagerRoot.
+  // |from_vm| is set to true when this service is connected from a view
+  // manager.
+  WindowManagerImpl(WindowManagerRoot* window_manager,
+                    mojo::ScopedMessagePipeHandle window_manager_pipe,
+                    bool from_vm);
+  ~WindowManagerImpl() override;
+
+  void NotifyViewFocused(mojo::Id focused_id);
+  void NotifyWindowActivated(mojo::Id active_id);
+  void NotifyCaptureChanged(mojo::Id capture_id);
+
+ private:
+  // mojo::WindowManager:
+  void Embed(const mojo::String& url,
+             mojo::InterfaceRequest<mojo::ServiceProvider> services,
+             mojo::ServiceProviderPtr exposed_services) override;
+  void SetCapture(uint32_t view_id,
+                  const mojo::Callback<void(bool)>& callback) override;
+  void FocusWindow(uint32_t view_id,
+                   const mojo::Callback<void(bool)>& callback) override;
+  void ActivateWindow(uint32_t view_id,
+                      const mojo::Callback<void(bool)>& callback) override;
+  void GetFocusedAndActiveViews(
+      mojo::WindowManagerObserverPtr observer,
+      const mojo::WindowManager::GetFocusedAndActiveViewsCallback& callback)
+      override;
+
+  WindowManagerRoot* window_manager_;
+
+  // Whether this connection originated from the ViewManager. Connections that
+  // originate from the view manager are expected to have clients. Connections
+  // that don't originate from the view manager do not have clients.
+  const bool from_vm_;
+
+  mojo::Binding<mojo::WindowManager> binding_;
+  mojo::WindowManagerObserverPtr observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerImpl);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
diff --git a/services/window_manager/window_manager_root.cc b/services/window_manager/window_manager_root.cc
new file mode 100644
index 0000000..4c15e43
--- /dev/null
+++ b/services/window_manager/window_manager_root.cc
@@ -0,0 +1,413 @@
+// 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/window_manager/window_manager_root.h"
+
+#include <algorithm>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "mojo/services/view_manager/cpp/view_manager.h"
+#include "services/window_manager/capture_controller.h"
+#include "services/window_manager/focus_controller.h"
+#include "services/window_manager/focus_rules.h"
+#include "services/window_manager/hit_test.h"
+#include "services/window_manager/view_event_dispatcher.h"
+#include "services/window_manager/view_target.h"
+#include "services/window_manager/view_targeter.h"
+#include "services/window_manager/window_manager_delegate.h"
+
+using mojo::ApplicationConnection;
+using mojo::Id;
+using mojo::ServiceProvider;
+using mojo::View;
+using mojo::WindowManager;
+
+namespace window_manager {
+
+namespace {
+
+Id GetIdForView(View* view) {
+  return view ? view->id() : 0;
+}
+
+}  // namespace
+
+// Used for calls to Embed() that occur before we've connected to the
+// ViewManager.
+struct WindowManagerRoot::PendingEmbed {
+  mojo::String url;
+  mojo::InterfaceRequest<ServiceProvider> services;
+  mojo::ServiceProviderPtr exposed_services;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, public:
+
+WindowManagerRoot::WindowManagerRoot(
+    mojo::ApplicationImpl* application_impl,
+    mojo::ApplicationConnection* connection,
+    WindowManagerControllerFactory* controller_factory,
+    mojo::InterfaceRequest<mojo::WindowManager> request)
+    : application_impl_(application_impl),
+      connection_(connection),
+      root_(nullptr) {
+  window_manager_controller_ =
+      controller_factory->CreateWindowManagerController(connection, this);
+  LaunchViewManager(application_impl_);
+
+  // We own WindowManagerImpl. When binding to the request, WindowManagerImpl
+  // registers an error callback to WindowManagerRoot::RemoveConnectedService,
+  // which will delete it.
+  AddConnectedService(make_scoped_ptr(
+      new WindowManagerImpl(this, request.PassMessagePipe(), false)));
+}
+
+WindowManagerRoot::~WindowManagerRoot() {
+  // TODO(msw|sky): Should this destructor explicitly delete the ViewManager?
+  mojo::ViewManager* cached_view_manager = view_manager();
+  for (RegisteredViewIdSet::const_iterator it = registered_view_id_set_.begin();
+       cached_view_manager && it != registered_view_id_set_.end(); ++it) {
+    View* view = cached_view_manager->GetViewById(*it);
+    if (view && view == root_)
+      root_ = nullptr;
+    if (view)
+      view->RemoveObserver(this);
+  }
+  registered_view_id_set_.clear();
+  DCHECK(!root_);
+
+  // connected_services_ destructor will ensure we don't leak any
+  // WindowManagerImpl objects and close all connections.
+}
+
+void WindowManagerRoot::AddConnectedService(
+    scoped_ptr<WindowManagerImpl> connection) {
+  connected_services_.push_back(connection.Pass());
+}
+
+void WindowManagerRoot::RemoveConnectedService(WindowManagerImpl* connection) {
+  ConnectedServices::iterator position = std::find(
+      connected_services_.begin(), connected_services_.end(), connection);
+  DCHECK(position != connected_services_.end());
+
+  connected_services_.erase(position);
+
+  // Having no inbound connection is not a guarantee we are no longer used: we
+  // can be processing an embed request (App 1 asked for a WindowManager, calls
+  // Embed then immediately closes the connection, EmbeddedApp is not yet
+  // started).
+}
+
+bool WindowManagerRoot::SetCapture(Id view_id) {
+  View* view = view_manager()->GetViewById(view_id);
+  return view && SetCaptureImpl(view);
+}
+
+bool WindowManagerRoot::FocusWindow(Id view_id) {
+  View* view = view_manager()->GetViewById(view_id);
+  return view && FocusWindowImpl(view);
+}
+
+bool WindowManagerRoot::ActivateWindow(Id view_id) {
+  View* view = view_manager()->GetViewById(view_id);
+  return view && ActivateWindowImpl(view);
+}
+
+bool WindowManagerRoot::IsReady() const {
+  return root_;
+}
+
+void WindowManagerRoot::InitFocus(scoped_ptr<FocusRules> rules) {
+  DCHECK(root_);
+
+  focus_controller_.reset(new FocusController(rules.Pass()));
+  focus_controller_->AddObserver(this);
+  SetFocusController(root_, focus_controller_.get());
+
+  capture_controller_.reset(new CaptureController);
+  capture_controller_->AddObserver(this);
+  SetCaptureController(root_, capture_controller_.get());
+}
+
+void WindowManagerRoot::Embed(
+    const mojo::String& url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  if (view_manager()) {
+    window_manager_controller_->Embed(url, services.Pass(),
+                                      exposed_services.Pass());
+    return;
+  }
+  scoped_ptr<PendingEmbed> pending_embed(new PendingEmbed);
+  pending_embed->url = url;
+  pending_embed->services = services.Pass();
+  pending_embed->exposed_services = exposed_services.Pass();
+  pending_embeds_.push_back(pending_embed.release());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, ViewManagerDelegate implementation:
+
+void WindowManagerRoot::OnEmbed(
+    View* root,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services) {
+  DCHECK(!root_);
+  root_ = root;
+
+  view_event_dispatcher_.reset(new ViewEventDispatcher);
+
+  RegisterSubtree(root_);
+
+  if (window_manager_controller_.get()) {
+    window_manager_controller_->OnEmbed(root, services.Pass(),
+                                        exposed_services.Pass());
+  }
+
+  for (PendingEmbed* pending_embed : pending_embeds_) {
+    Embed(pending_embed->url, pending_embed->services.Pass(),
+          pending_embed->exposed_services.Pass());
+  }
+  pending_embeds_.clear();
+}
+
+void WindowManagerRoot::OnViewManagerDisconnected(
+    mojo::ViewManager* view_manager) {
+  if (window_manager_controller_.get())
+    window_manager_controller_->OnViewManagerDisconnected(view_manager);
+  delete this;
+}
+
+bool WindowManagerRoot::OnPerformAction(mojo::View* view,
+                                        const std::string& action) {
+  if (!view)
+    return false;
+  if (action == "capture")
+    return SetCaptureImpl(view);
+  if (action == "focus")
+    return FocusWindowImpl(view);
+  else if (action == "activate")
+    return ActivateWindowImpl(view);
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, ViewObserver implementation:
+
+void WindowManagerRoot::OnTreeChanged(
+    const ViewObserver::TreeChangeParams& params) {
+  if (params.receiver != root_)
+    return;
+  DCHECK(params.old_parent || params.new_parent);
+  if (!params.target)
+    return;
+
+  if (params.new_parent) {
+    if (registered_view_id_set_.find(params.target->id()) ==
+        registered_view_id_set_.end()) {
+      RegisteredViewIdSet::const_iterator it =
+          registered_view_id_set_.find(params.new_parent->id());
+      DCHECK(it != registered_view_id_set_.end());
+      RegisterSubtree(params.target);
+    }
+  } else if (params.old_parent) {
+    UnregisterSubtree(params.target);
+  }
+}
+
+void WindowManagerRoot::OnViewDestroying(View* view) {
+  Unregister(view);
+  if (view == root_) {
+    root_ = nullptr;
+    if (focus_controller_)
+      focus_controller_->RemoveObserver(this);
+    if (capture_controller_)
+      capture_controller_->RemoveObserver(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, ui::EventHandler implementation:
+
+void WindowManagerRoot::OnEvent(ui::Event* event) {
+  if (!window_manager_client_)
+    return;
+
+  View* view = static_cast<ViewTarget*>(event->target())->view();
+  if (!view)
+    return;
+
+  if (event->IsKeyEvent()) {
+    const ui::KeyEvent* key_event = static_cast<const ui::KeyEvent*>(event);
+    if (key_event->type() == ui::ET_KEY_PRESSED) {
+      ui::Accelerator accelerator = ConvertEventToAccelerator(key_event);
+      if (accelerator_manager_.Process(accelerator, view))
+        return;
+    }
+  }
+
+  if (focus_controller_)
+    focus_controller_->OnEvent(event);
+
+  window_manager_client_->DispatchInputEventToView(view->id(),
+                                                   mojo::Event::From(*event));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, mojo::FocusControllerObserver implementation:
+
+void WindowManagerRoot::OnFocused(View* gained_focus) {
+  for (ConnectedServices::const_iterator it = connected_services_.begin();
+       it != connected_services_.end(); ++it) {
+    (*it)->NotifyViewFocused(GetIdForView(gained_focus));
+  }
+}
+
+void WindowManagerRoot::OnActivated(View* gained_active) {
+  for (ConnectedServices::const_iterator it = connected_services_.begin();
+       it != connected_services_.end(); ++it) {
+    (*it)->NotifyWindowActivated(GetIdForView(gained_active));
+  }
+  if (gained_active)
+    gained_active->MoveToFront();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, mojo::CaptureControllerObserver implementation:
+
+void WindowManagerRoot::OnCaptureChanged(View* gained_capture) {
+  for (ConnectedServices::const_iterator it = connected_services_.begin();
+       it != connected_services_.end(); ++it) {
+    (*it)->NotifyCaptureChanged(GetIdForView(gained_capture));
+  }
+  if (gained_capture)
+    gained_capture->MoveToFront();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerRoot, private:
+
+bool WindowManagerRoot::SetCaptureImpl(View* view) {
+  CHECK(view);
+  capture_controller_->SetCapture(view);
+  return capture_controller_->GetCapture() == view;
+}
+
+bool WindowManagerRoot::FocusWindowImpl(View* view) {
+  CHECK(view);
+  focus_controller_->FocusView(view);
+  return focus_controller_->GetFocusedView() == view;
+}
+
+bool WindowManagerRoot::ActivateWindowImpl(View* view) {
+  CHECK(view);
+  focus_controller_->ActivateView(view);
+  return focus_controller_->GetActiveView() == view;
+}
+
+void WindowManagerRoot::RegisterSubtree(View* view) {
+  view->AddObserver(this);
+  DCHECK(registered_view_id_set_.find(view->id()) ==
+         registered_view_id_set_.end());
+  // All events pass through the root during dispatch, so we only need a handler
+  // installed there.
+  if (view == root_) {
+    ViewTarget* target = ViewTarget::TargetFromView(view);
+    target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+    target->AddPreTargetHandler(this);
+    view_event_dispatcher_->SetRootViewTarget(target);
+  }
+  registered_view_id_set_.insert(view->id());
+  View::Children::const_iterator it = view->children().begin();
+  for (; it != view->children().end(); ++it)
+    RegisterSubtree(*it);
+}
+
+void WindowManagerRoot::UnregisterSubtree(View* view) {
+  for (View* child : view->children())
+    UnregisterSubtree(child);
+  Unregister(view);
+}
+
+void WindowManagerRoot::Unregister(View* view) {
+  RegisteredViewIdSet::iterator it = registered_view_id_set_.find(view->id());
+  if (it == registered_view_id_set_.end()) {
+    // Because we unregister in OnViewDestroying() we can still get a subsequent
+    // OnTreeChanged for the same view. Ignore this one.
+    return;
+  }
+  view->RemoveObserver(this);
+  DCHECK(it != registered_view_id_set_.end());
+  registered_view_id_set_.erase(it);
+}
+
+void WindowManagerRoot::DispatchInputEventToView(View* view,
+                                                 mojo::EventPtr event) {
+  window_manager_client_->DispatchInputEventToView(view->id(), event.Pass());
+}
+
+void WindowManagerRoot::SetViewportSize(const gfx::Size& size) {
+  window_manager_client_->SetViewportSize(mojo::Size::From(size));
+}
+
+void WindowManagerRoot::LaunchViewManager(mojo::ApplicationImpl* app) {
+  // TODO(sky): figure out logic if this connection goes away.
+  view_manager_client_factory_.reset(
+      new mojo::ViewManagerClientFactory(application_impl_->shell(), this));
+
+  ApplicationConnection* view_manager_app =
+      app->ConnectToApplication("mojo:view_manager");
+  view_manager_app->ConnectToService(&view_manager_service_);
+
+  view_manager_app->AddService<WindowManagerInternal>(this);
+  view_manager_app->AddService<mojo::NativeViewportEventDispatcher>(this);
+
+  view_manager_app->ConnectToService(&window_manager_client_);
+}
+
+void WindowManagerRoot::Create(
+    ApplicationConnection* connection,
+    mojo::InterfaceRequest<WindowManagerInternal> request) {
+  if (wm_internal_binding_.get()) {
+    VLOG(1)
+        << "WindowManager allows only one WindowManagerInternal connection.";
+    return;
+  }
+  wm_internal_binding_.reset(
+      new mojo::Binding<WindowManagerInternal>(this, request.Pass()));
+}
+
+void WindowManagerRoot::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request) {
+  new NativeViewportEventDispatcherImpl(this, request.Pass());
+}
+
+void WindowManagerRoot::CreateWindowManagerForViewManagerClient(
+    uint16_t connection_id,
+    mojo::ScopedMessagePipeHandle window_manager_pipe) {
+  // TODO(sky): pass in |connection_id| for validation.
+  AddConnectedService(make_scoped_ptr(
+      new WindowManagerImpl(this, window_manager_pipe.Pass(), true)));
+}
+
+void WindowManagerRoot::SetViewManagerClient(
+    mojo::ScopedMessagePipeHandle view_manager_client_request) {
+  view_manager_client_.reset(
+      mojo::ViewManagerClientFactory::WeakBindViewManagerToPipe(
+          mojo::MakeRequest<mojo::ViewManagerClient>(
+              view_manager_client_request.Pass()),
+          view_manager_service_.Pass(), application_impl_->shell(), this));
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_root.h b/services/window_manager/window_manager_root.h
new file mode 100644
index 0000000..3b92420
--- /dev/null
+++ b/services/window_manager/window_manager_root.h
@@ -0,0 +1,200 @@
+// 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_WINDOW_MANAGER_WINDOW_MANAGER_ROOT_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_ROOT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/view_manager/cpp/types.h"
+#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
+#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
+#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "mojo/services/window_manager/interfaces/window_manager_internal.mojom.h"
+#include "services/window_manager/capture_controller_observer.h"
+#include "services/window_manager/focus_controller_observer.h"
+#include "services/window_manager/native_viewport_event_dispatcher_impl.h"
+#include "services/window_manager/view_target.h"
+#include "services/window_manager/window_manager_delegate.h"
+#include "services/window_manager/window_manager_impl.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+#include "ui/events/event_handler.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace window_manager {
+
+class CaptureController;
+class FocusController;
+class FocusRules;
+class ViewEventDispatcher;
+class WindowManagerDelegate;
+class WindowManagerImpl;
+
+// Represents the root of a Mojo-managed window hierarchy. Each root has a
+// 1-to-1 link with a native platform window. WindowManagerRoot manages its own
+// lifetime.
+class WindowManagerRoot
+    : public mojo::ViewManagerDelegate,
+      public mojo::ViewObserver,
+      public ui::EventHandler,
+      public FocusControllerObserver,
+      public CaptureControllerObserver,
+      public mojo::InterfaceFactory<mojo::WindowManagerInternal>,
+      public mojo::InterfaceFactory<mojo::NativeViewportEventDispatcher>,
+      public mojo::WindowManagerInternal {
+ public:
+  WindowManagerRoot(mojo::ApplicationImpl* application_impl,
+                    mojo::ApplicationConnection* connection,
+                    WindowManagerControllerFactory* controller_factory,
+                    mojo::InterfaceRequest<mojo::WindowManager> request);
+  ~WindowManagerRoot() override;
+
+  ViewEventDispatcher* event_dispatcher() {
+    return view_event_dispatcher_.get();
+  }
+
+  // Deregisters connections to the window manager service.
+  void RemoveConnectedService(WindowManagerImpl* service);
+
+  // These are canonical implementations of the window manager API methods.
+  bool SetCapture(mojo::Id view);
+  bool FocusWindow(mojo::Id view);
+  bool ActivateWindow(mojo::Id view);
+
+  void DispatchInputEventToView(mojo::View* view, mojo::EventPtr event);
+  void SetViewportSize(const gfx::Size& size);
+
+  bool IsReady() const;
+
+  FocusController* focus_controller() { return focus_controller_.get(); }
+  CaptureController* capture_controller() { return capture_controller_.get(); }
+
+  void InitFocus(scoped_ptr<FocusRules> rules);
+
+  ui::AcceleratorManager* accelerator_manager() {
+    return &accelerator_manager_;
+  }
+
+  // WindowManagerImpl::Embed() forwards to this. If connected to ViewManager
+  // then forwards to delegate, otherwise waits for connection to establish then
+  // forwards.
+  void Embed(const mojo::String& url,
+             mojo::InterfaceRequest<mojo::ServiceProvider> services,
+             mojo::ServiceProviderPtr exposed_services);
+
+ private:
+  typedef ScopedVector<WindowManagerImpl> ConnectedServices;
+  typedef std::set<mojo::Id> RegisteredViewIdSet;
+
+  void AddConnectedService(scoped_ptr<WindowManagerImpl> service);
+
+  struct PendingEmbed;
+  class WindowManagerInternalImpl;
+
+  mojo::ViewManager* view_manager() {
+    return root_ ? root_->view_manager() : nullptr;
+  }
+
+  bool SetCaptureImpl(mojo::View* view);
+  bool FocusWindowImpl(mojo::View* view);
+  bool ActivateWindowImpl(mojo::View* view);
+
+  ui::Accelerator ConvertEventToAccelerator(const ui::KeyEvent* event);
+
+  // Creates an ViewTarget for every view in the hierarchy beneath |view|,
+  // and adds to the registry so that it can be retrieved later via
+  // GetViewTargetForViewId().
+  // TODO(beng): perhaps View should have a property bag.
+  void RegisterSubtree(mojo::View* view);
+
+  // Recursively invokes Unregister() for |view| and all its descendants.
+  void UnregisterSubtree(mojo::View* view);
+
+  // Deletes the ViewTarget associated with the hierarchy beneath |id|,
+  // and removes from the registry.
+  void Unregister(mojo::View* view);
+
+  // Overridden from ViewManagerDelegate:
+  void OnEmbed(mojo::View* root,
+               mojo::InterfaceRequest<mojo::ServiceProvider> services,
+               mojo::ServiceProviderPtr exposed_services) override;
+  void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override;
+  bool OnPerformAction(mojo::View* view, const std::string& action) override;
+
+  // Overridden from ViewObserver:
+  void OnTreeChanged(const ViewObserver::TreeChangeParams& params) override;
+  void OnViewDestroying(mojo::View* view) override;
+
+  // Overridden from ui::EventHandler:
+  void OnEvent(ui::Event* event) override;
+
+  // Overridden from mojo::FocusControllerObserver:
+  void OnFocused(mojo::View* gained_focus) override;
+  void OnActivated(mojo::View* gained_active) override;
+
+  // Overridden from mojo::CaptureControllerObserver:
+  void OnCaptureChanged(mojo::View* gained_capture) override;
+
+  // Creates the connection to the ViewManager.
+  void LaunchViewManager(mojo::ApplicationImpl* app);
+
+  // InterfaceFactory<WindowManagerInternal>:
+  void Create(
+      mojo::ApplicationConnection* connection,
+      mojo::InterfaceRequest<mojo::WindowManagerInternal> request) override;
+
+  // InterfaceFactory<NativeViewportEventDispatcher>:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher>
+                  request) override;
+
+  // WindowManagerInternal:
+  void CreateWindowManagerForViewManagerClient(
+      uint16_t connection_id,
+      mojo::ScopedMessagePipeHandle window_manager_pipe) override;
+  void SetViewManagerClient(
+      mojo::ScopedMessagePipeHandle view_manager_client_request) override;
+
+  mojo::ApplicationImpl* application_impl_;
+  mojo::ApplicationConnection* connection_;
+  scoped_ptr<WindowManagerController> window_manager_controller_;
+
+  ViewManagerDelegate* wrapped_view_manager_delegate_;
+  WindowManagerDelegate* window_manager_delegate_;
+
+  mojo::ViewManagerServicePtr view_manager_service_;
+  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+  mojo::View* root_;
+
+  scoped_ptr<FocusController> focus_controller_;
+  scoped_ptr<CaptureController> capture_controller_;
+
+  ui::AcceleratorManager accelerator_manager_;
+
+  ConnectedServices connected_services_;
+  RegisteredViewIdSet registered_view_id_set_;
+
+  mojo::WindowManagerInternalClientPtr window_manager_client_;
+
+  ScopedVector<PendingEmbed> pending_embeds_;
+
+  scoped_ptr<mojo::ViewManagerClient> view_manager_client_;
+
+  scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+  scoped_ptr<mojo::Binding<WindowManagerInternal>> wm_internal_binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerRoot);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
diff --git a/services/window_manager/window_manager_root_android.cc b/services/window_manager/window_manager_root_android.cc
new file mode 100644
index 0000000..b4c6049
--- /dev/null
+++ b/services/window_manager/window_manager_root_android.cc
@@ -0,0 +1,20 @@
+// 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/window_manager/window_manager_root.h"
+
+#include <android/keycodes.h>
+
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+
+namespace window_manager {
+
+ui::Accelerator WindowManagerRoot::ConvertEventToAccelerator(
+    const ui::KeyEvent* event) {
+  if (event->platform_keycode() == AKEYCODE_BACK)
+    return ui::Accelerator(ui::VKEY_BROWSER_BACK, 0);
+  return ui::Accelerator(event->key_code(), event->flags());
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_root_linux.cc b/services/window_manager/window_manager_root_linux.cc
new file mode 100644
index 0000000..40d137b
--- /dev/null
+++ b/services/window_manager/window_manager_root_linux.cc
@@ -0,0 +1,14 @@
+// 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/window_manager/window_manager_root.h"
+
+namespace window_manager {
+
+ui::Accelerator WindowManagerRoot::ConvertEventToAccelerator(
+    const ui::KeyEvent* event) {
+  return ui::Accelerator(event->key_code(), event->flags());
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_test_util.cc b/services/window_manager/window_manager_test_util.cc
new file mode 100644
index 0000000..3f0a50b
--- /dev/null
+++ b/services/window_manager/window_manager_test_util.cc
@@ -0,0 +1,39 @@
+// 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.
+
+#include "services/window_manager/window_manager_test_util.h"
+
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace window_manager {
+
+TestView::TestView(int id, const gfx::Rect& rect)
+    : target_(new ViewTarget(this)) {
+  mojo::ViewPrivate(this).set_id(id);
+
+  mojo::Rect mojo_rect = *mojo::Rect::From(rect);
+  SetBounds(mojo_rect);
+}
+
+TestView::TestView(int id, const gfx::Rect& rect, View* parent)
+    : TestView(id, rect) {
+  parent->AddChild(this);
+}
+
+TestView::~TestView() {
+}
+
+// static
+TestView* TestView::Build(int id, const gfx::Rect& rect) {
+  return new TestView(id, rect);
+}
+
+// static
+TestView* TestView::Build(int id, const gfx::Rect& rect, mojo::View* parent) {
+  return new TestView(id, rect, parent);
+}
+
+}  // namespace window_manager
diff --git a/services/window_manager/window_manager_test_util.h b/services/window_manager/window_manager_test_util.h
new file mode 100644
index 0000000..fc26bd1
--- /dev/null
+++ b/services/window_manager/window_manager_test_util.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_
+
+#include <set>
+
+#include "mojo/services/view_manager/cpp/lib/view_private.h"
+#include "mojo/services/view_manager/cpp/view.h"
+#include "services/window_manager/view_target.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace window_manager {
+
+// A wrapper around View so we can instantiate these directly without a
+// ViewManager.
+class TestView : public mojo::View {
+ public:
+  TestView(int id, const gfx::Rect& rect);
+  TestView(int id, const gfx::Rect& rect, mojo::View* parent);
+  ~TestView();
+
+  // Builds a child view as a pointer. The caller is responsible for making
+  // sure that the root of any tree allocated this way is Destroy()ed.
+  static TestView* Build(int id, const gfx::Rect& rect);
+  static TestView* Build(int id, const gfx::Rect& rect, View* parent);
+
+  ViewTarget* target() { return target_; }
+
+ private:
+  ViewTarget* target_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+}  // namespace window_manager
+
+#endif  // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 303f907..54d013f 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -81,6 +81,7 @@
                "//gpu/config",
                "//mojo/android:libsystem_java",
                "//mojo/common",
+               "//mojo/services/window_manager/interfaces:interfaces",
                "//services/native_viewport:lib",
                "//shell/application_manager",
                "//ui/gl",
@@ -428,8 +429,10 @@
         "$root_out_dir/device_info.mojo",
         "$root_out_dir/icu_data.mojo",
         "$root_out_dir/java_handler.mojo",
+        "$root_out_dir/kiosk_wm.mojo",
         "$root_out_dir/surfaces_service.mojo",
         "$root_out_dir/tracing.mojo",
+        "$root_out_dir/view_manager.mojo",
       ]
 
       deps += [
@@ -438,8 +441,10 @@
         "//services/dart:dart_content_handler",
         "//services/device_info",
         "//services/icu_data",
+        "//services/kiosk_wm",
         "//services/surfaces",
         "//services/tracing",
+        "//services/view_manager",
       ]
     }
   }
diff --git a/shell/android/main.cc b/shell/android/main.cc
index 40c11de..eb31de5 100644
--- a/shell/android/main.cc
+++ b/shell/android/main.cc
@@ -28,6 +28,7 @@
 #include "mojo/common/binding_set.h"
 #include "mojo/message_pump/message_pump_mojo.h"
 #include "mojo/services/network/interfaces/network_service.mojom.h"
+#include "mojo/services/window_manager/interfaces/window_manager.mojom.h"
 #include "shell/android/android_handler_loader.h"
 #include "shell/android/java_application_loader.h"
 #include "shell/android/native_viewport_application_loader.h"