Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn
new file mode 100644
index 0000000..5b4498c
--- /dev/null
+++ b/mojo/services/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+group("services") {
+  deps = [
+    "//mojo/services/clipboard",
+    "//mojo/services/gles2:bindings",
+    "//mojo/services/html_viewer",
+    "//mojo/services/network",
+    "//mojo/services/public/interfaces/clipboard",
+    "//mojo/services/public/interfaces/content_handler",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/native_viewport",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/public/interfaces/network",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/surfaces",
+    "//mojo/services/test_service:bindings",
+  ]
+  if (!is_android) {
+    deps += ["//mojo/services/native_viewport"]
+  }
+  if (use_aura) {
+    deps += [
+      "//mojo/services/public/interfaces/view_manager",
+      "//mojo/services/public/interfaces/window_manager",
+      "//mojo/services/view_manager",
+      "//mojo/services/window_manager",
+    ]
+  }
+}
diff --git a/mojo/services/DEPS b/mojo/services/DEPS
new file mode 100644
index 0000000..2bbe62d
--- /dev/null
+++ b/mojo/services/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "-mojo",
+  "+mojo/common",
+  "+mojo/public",
+  "+jni",
+
+  # TODO(abarth) Instead of having the services depend on the shell, we
+  # probably should create a layer below the services.
+  "+mojo/shell",
+]
diff --git a/mojo/services/clipboard/BUILD.gn b/mojo/services/clipboard/BUILD.gn
new file mode 100644
index 0000000..5c3b78e
--- /dev/null
+++ b/mojo/services/clipboard/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard
+component("clipboard") {
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_component",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/public/interfaces/clipboard",
+    "//ui/base",
+  ]
+
+  sources = [
+    "clipboard_standalone_impl.cc",
+    "clipboard_standalone_impl.h",
+    "main.cc",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard_unittests
+test("mojo_clipboard_unittests") {
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/application",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/interfaces/clipboard:clipboard",
+    "//mojo/shell:test_support",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "clipboard_standalone_unittest.cc",
+  ]
+}
diff --git a/mojo/services/clipboard/DEPS b/mojo/services/clipboard/DEPS
new file mode 100644
index 0000000..79cfc5d
--- /dev/null
+++ b/mojo/services/clipboard/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+mojo/application",
+  "+mojo/services",
+]
diff --git a/mojo/services/clipboard/clipboard_standalone_impl.cc b/mojo/services/clipboard/clipboard_standalone_impl.cc
new file mode 100644
index 0000000..e1c7e2c
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_impl.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 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/clipboard/clipboard_standalone_impl.h"
+
+namespace mojo {
+
+typedef std::vector<uint8_t> ByteVector;
+
+// ClipboardData contains data copied to the Clipboard for a variety of formats.
+// It mostly just provides APIs to cleanly access and manipulate this data.
+class ClipboardStandaloneImpl::ClipboardData {
+ public:
+  ClipboardData() {}
+  ~ClipboardData() {}
+
+  std::vector<std::string> GetMimeTypes() const {
+    std::vector<std::string> types;
+    for (std::map<std::string, ByteVector>::const_iterator it =
+             data_types_.begin();
+         it != data_types_.end();
+         ++it) {
+      types.push_back(it->first);
+    }
+
+    return types;
+  }
+
+  void SetData(std::map<std::string, ByteVector>* data) {
+    std::swap(data_types_, *data);
+  }
+
+  bool GetData(const std::string& mime_type, ByteVector* data) const {
+    std::map<std::string, ByteVector>::const_iterator it =
+        data_types_.find(mime_type);
+    if (it != data_types_.end()) {
+      *data = it->second;
+      return true;
+    }
+
+    return false;
+  }
+
+ private:
+  std::map<std::string, ByteVector> data_types_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardData);
+};
+
+ClipboardStandaloneImpl::ClipboardStandaloneImpl() {
+  for (int i = 0; i < kNumClipboards; ++i) {
+    sequence_number_[i] = 0;
+    clipboard_state_[i].reset(new ClipboardData);
+  }
+}
+
+ClipboardStandaloneImpl::~ClipboardStandaloneImpl() {
+}
+
+void ClipboardStandaloneImpl::GetSequenceNumber(
+    Clipboard::Type clipboard_type,
+    const mojo::Callback<void(uint64_t)>& callback) {
+  callback.Run(sequence_number_[clipboard_type]);
+}
+
+void ClipboardStandaloneImpl::GetAvailableMimeTypes(
+    Clipboard::Type clipboard_type,
+    const mojo::Callback<void(mojo::Array<mojo::String>)>& callback) {
+  mojo::Array<mojo::String> types = mojo::Array<mojo::String>::From(
+      clipboard_state_[clipboard_type]->GetMimeTypes());
+  callback.Run(types.Pass());
+}
+
+void ClipboardStandaloneImpl::ReadMimeType(
+    Clipboard::Type clipboard_type,
+    const mojo::String& mime_type,
+    const mojo::Callback<void(mojo::Array<uint8_t>)>& callback) {
+  ByteVector mime_data;
+  if (clipboard_state_[clipboard_type]->GetData(
+          mime_type.To<std::string>(), &mime_data)) {
+    callback.Run(mojo::Array<uint8_t>::From(mime_data).Pass());
+    return;
+  }
+
+  callback.Run(mojo::Array<uint8_t>().Pass());
+}
+
+void ClipboardStandaloneImpl::WriteClipboardData(
+    Clipboard::Type clipboard_type,
+    mojo::Array<MimeTypePairPtr> data) {
+  std::map<std::string, ByteVector> mime_data;
+  for (size_t i = 0; i < data.size(); ++i)
+    mime_data[data[i]->mime_type] = data[i]->data;
+
+  sequence_number_[clipboard_type]++;
+  clipboard_state_[clipboard_type]->SetData(&mime_data);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/clipboard/clipboard_standalone_impl.h b/mojo/services/clipboard/clipboard_standalone_impl.h
new file mode 100644
index 0000000..09d7424
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_impl.h
@@ -0,0 +1,58 @@
+// Copyright (c) 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_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
+#define MOJO_SERVICES_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
+
+#include <base/memory/scoped_ptr.h>
+#include <string>
+
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+
+namespace mojo {
+
+// Stub clipboard implementation.
+//
+// Eventually, we'll actually want to interact with the system clipboard, but
+// that's hard today because the system clipboard is asynchronous (on X11), the
+// ui::Clipboard interface is synchronous (which is what we'd use), mojo is
+// asynchronous across processes, and the WebClipboard interface is synchronous
+// (which is at least tractable).
+class ClipboardStandaloneImpl : public InterfaceImpl<mojo::Clipboard> {
+ public:
+  // mojo::Clipboard exposes three possible clipboards.
+  static const int kNumClipboards = 3;
+
+  ClipboardStandaloneImpl();
+  virtual ~ClipboardStandaloneImpl();
+
+  // InterfaceImpl<mojo::Clipboard> implementation.
+  virtual void GetSequenceNumber(
+      Clipboard::Type clipboard_type,
+      const mojo::Callback<void(uint64_t)>& callback) override;
+  virtual void GetAvailableMimeTypes(
+      Clipboard::Type clipboard_types,
+      const mojo::Callback<void(mojo::Array<mojo::String>)>& callback) override;
+  virtual void ReadMimeType(
+      Clipboard::Type clipboard_type,
+      const mojo::String& mime_type,
+      const mojo::Callback<void(mojo::Array<uint8_t>)>& callback) override;
+  virtual void WriteClipboardData(Clipboard::Type clipboard_type,
+                                  mojo::Array<MimeTypePairPtr> data) override;
+
+ private:
+  uint64_t sequence_number_[kNumClipboards];
+
+  // Internal struct which stores the current state of the clipboard.
+  class ClipboardData;
+
+  // The current clipboard state. This is what is read from.
+  scoped_ptr<ClipboardData> clipboard_state_[kNumClipboards];
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardStandaloneImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
diff --git a/mojo/services/clipboard/clipboard_standalone_unittest.cc b/mojo/services/clipboard/clipboard_standalone_unittest.cc
new file mode 100644
index 0000000..c297b94
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 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/at_exit.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CopyUint64AndEndRunloop(uint64_t* output,
+                             base::RunLoop* run_loop,
+                             uint64_t input) {
+  *output = input;
+  run_loop->Quit();
+}
+
+void CopyStringAndEndRunloop(std::string* output,
+                             bool* string_is_null,
+                             base::RunLoop* run_loop,
+                             const mojo::Array<uint8_t>& input) {
+  *string_is_null = input.is_null();
+  *output = input.is_null() ? "" : input.To<std::string>();
+  run_loop->Quit();
+}
+
+void CopyVectorStringAndEndRunloop(std::vector<std::string>* output,
+                                   base::RunLoop* run_loop,
+                                   const mojo::Array<mojo::String>& input) {
+  *output = input.To<std::vector<std::string> >();
+  run_loop->Quit();
+}
+
+const char* kUninitialized = "Uninitialized data";
+const char* kPlainTextData = "Some plain data";
+const char* kHtmlData = "<html>data</html>";
+
+}  // namespace
+
+namespace mojo {
+namespace service {
+
+class ClipboardStandaloneTest : public testing::Test {
+ public:
+  ClipboardStandaloneTest() {}
+  virtual ~ClipboardStandaloneTest() {}
+
+  virtual void SetUp() override {
+    test_helper_.Init();
+
+    test_helper_.application_manager()->ConnectToService(
+        GURL("mojo:mojo_clipboard"), &clipboard_);
+  }
+
+  uint64_t GetSequenceNumber() {
+    base::RunLoop run_loop;
+    uint64_t sequence_num = 999999;
+    clipboard_->GetSequenceNumber(
+        mojo::Clipboard::TYPE_COPY_PASTE,
+        base::Bind(&CopyUint64AndEndRunloop, &sequence_num, &run_loop));
+    run_loop.Run();
+    return sequence_num;
+  }
+
+  std::vector<std::string> GetAvailableFormatMimeTypes() {
+    base::RunLoop run_loop;
+    std::vector<std::string> types;
+    types.push_back(kUninitialized);
+    clipboard_->GetAvailableMimeTypes(
+        mojo::Clipboard::TYPE_COPY_PASTE,
+        base::Bind(&CopyVectorStringAndEndRunloop, &types, &run_loop));
+    run_loop.Run();
+    return types;
+  }
+
+  bool GetDataOfType(const std::string& mime_type, std::string* data) {
+    base::RunLoop run_loop;
+    bool is_null = false;
+    clipboard_->ReadMimeType(
+        mojo::Clipboard::TYPE_COPY_PASTE,
+        mime_type,
+        base::Bind(&CopyStringAndEndRunloop, data, &is_null, &run_loop));
+    run_loop.Run();
+    return !is_null;
+  }
+
+  void SetStringText(const std::string& data) {
+    Array<MimeTypePairPtr> mime_data;
+    MimeTypePairPtr text_data(MimeTypePair::New());
+    text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+    text_data->data = Array<uint8_t>::From(data).Pass();
+    mime_data.push_back(text_data.Pass());
+    clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+                                   mime_data.Pass());
+  }
+
+ protected:
+  base::ShadowingAtExitManager at_exit_;
+  shell::ShellTestHelper test_helper_;
+
+  ClipboardPtr clipboard_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardStandaloneTest);
+};
+
+TEST_F(ClipboardStandaloneTest, EmptyClipboardOK) {
+  EXPECT_EQ(0ul, GetSequenceNumber());
+  EXPECT_TRUE(GetAvailableFormatMimeTypes().empty());
+  std::string data;
+  EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+TEST_F(ClipboardStandaloneTest, CanReadBackText) {
+  std::string data;
+  EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+  EXPECT_EQ(0ul, GetSequenceNumber());
+
+  SetStringText(kPlainTextData);
+  EXPECT_EQ(1ul, GetSequenceNumber());
+
+  EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+  EXPECT_EQ(kPlainTextData, data);
+}
+
+TEST_F(ClipboardStandaloneTest, CanSetMultipleDataTypesAtOnce) {
+  Array<MimeTypePairPtr> mime_data;
+  MimeTypePairPtr text_data(MimeTypePair::New());
+  text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+  text_data->data = Array<uint8_t>::From(std::string(kPlainTextData)).Pass();
+  mime_data.push_back(text_data.Pass());
+
+  MimeTypePairPtr html_data(MimeTypePair::New());
+  html_data->mime_type = mojo::Clipboard::MIME_TYPE_HTML;
+  html_data->data = Array<uint8_t>::From(std::string(kHtmlData)).Pass();
+  mime_data.push_back(html_data.Pass());
+
+  clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+                                 mime_data.Pass());
+
+  EXPECT_EQ(1ul, GetSequenceNumber());
+
+  std::string data;
+  EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+  EXPECT_EQ(kPlainTextData, data);
+  EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_HTML, &data));
+  EXPECT_EQ(kHtmlData, data);
+}
+
+TEST_F(ClipboardStandaloneTest, CanClearClipboardWithNull) {
+  std::string data;
+  SetStringText(kPlainTextData);
+  EXPECT_EQ(1ul, GetSequenceNumber());
+
+  EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+  EXPECT_EQ(kPlainTextData, data);
+
+  Array<MimeTypePairPtr> mime_data;
+  clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+                                 mime_data.Pass());
+
+  EXPECT_EQ(2ul, GetSequenceNumber());
+  EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+TEST_F(ClipboardStandaloneTest, CanClearClipboardWithZeroArray) {
+  std::string data;
+  SetStringText(kPlainTextData);
+  EXPECT_EQ(1ul, GetSequenceNumber());
+
+  EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+  EXPECT_EQ(kPlainTextData, data);
+
+  Array<MimeTypePairPtr> mime_data(0);
+  clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+                                 mime_data.Pass());
+
+  EXPECT_EQ(2ul, GetSequenceNumber());
+  EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/clipboard/main.cc b/mojo/services/clipboard/main.cc
new file mode 100644
index 0000000..fcc33d1
--- /dev/null
+++ b/mojo/services/clipboard/main.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 "base/at_exit.h"
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.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/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/services/clipboard/clipboard_standalone_impl.h"
+
+class Delegate : public mojo::ApplicationDelegate,
+                 public mojo::InterfaceFactory<mojo::Clipboard> {
+ public:
+  Delegate() {}
+  virtual ~Delegate() {}
+
+  // mojo::ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService(this);
+    return true;
+  }
+
+  // mojo::InterfaceFactory<mojo::Clipboard> implementation.
+  virtual void Create(
+      mojo::ApplicationConnection* connection,
+      mojo::InterfaceRequest<mojo::Clipboard> request) override {
+    // TODO(erg): Write native implementations of the clipboard. For now, we
+    // just build a clipboard which doesn't interact with the system.
+    mojo::BindToRequest(new mojo::ClipboardStandaloneImpl(), &request);
+  }
+};
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new Delegate);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/gles2/BUILD.gn b/mojo/services/gles2/BUILD.gn
new file mode 100644
index 0000000..173d7df
--- /dev/null
+++ b/mojo/services/gles2/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_service
+source_set("gles2") {
+  public_deps = [
+    ":bindings",
+  ]
+  deps = [
+    "//base",
+    "//gpu/command_buffer/service",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+  ]
+
+  sources = [
+    "command_buffer_impl.cc",
+    "command_buffer_impl.h",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings
+mojom("interfaces") {
+  sources = [
+    "command_buffer.mojom",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings
+source_set("bindings") {
+  sources = [
+    "command_buffer_type_conversions.cc",
+    "command_buffer_type_conversions.h",
+    "mojo_buffer_backing.cc",
+    "mojo_buffer_backing.h",
+  ]
+
+  public_deps = [
+    ":interfaces",
+  ]
+  deps = [
+    "//base",
+    "//gpu/command_buffer/common",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/gles2:interfaces",
+  ]
+}
diff --git a/mojo/services/gles2/DEPS b/mojo/services/gles2/DEPS
new file mode 100644
index 0000000..acd992d
--- /dev/null
+++ b/mojo/services/gles2/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+gpu/command_buffer",
+  "+ui/gfx",
+  "+ui/gl",
+]
diff --git a/mojo/services/gles2/command_buffer.mojom b/mojo/services/gles2/command_buffer.mojom
new file mode 100644
index 0000000..302dcf4
--- /dev/null
+++ b/mojo/services/gles2/command_buffer.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.
+
+module mojo {
+
+struct CommandBufferState {
+  int32 num_entries;
+  int32 get_offset;
+  int32 put_offset;
+  int32 token;
+  int32 error;  // TODO(piman): enum
+  int32 context_lost_reason;  // TODO(piman): enum
+  uint32 generation;
+};
+
+interface CommandBufferSyncClient {
+  DidInitialize(bool success);
+  DidMakeProgress(CommandBufferState? state);
+};
+
+[Client=CommandBufferClient]
+interface CommandBuffer {
+  Initialize(CommandBufferSyncClient? sync_client,
+             handle<shared_buffer>? shared_state);
+  SetGetBuffer(int32 buffer);
+  Flush(int32 put_offset);
+  MakeProgress(int32 last_get_offset);
+  RegisterTransferBuffer(
+      int32 id, handle<shared_buffer>? transfer_buffer, uint32 size);
+  DestroyTransferBuffer(int32 id);
+  Echo() => ();
+
+  // TODO(piman): sync points
+};
+
+interface CommandBufferClient {
+  DidDestroy();
+  LostContext(int32 lost_reason);  // TODO(piman): enum
+};
+
+}
diff --git a/mojo/services/gles2/command_buffer_impl.cc b/mojo/services/gles2/command_buffer_impl.cc
new file mode 100644
index 0000000..d221750
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_impl.cc
@@ -0,0 +1,204 @@
+// Copyright 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 "mojo/services/gles2/command_buffer_impl.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/gpu_scheduler.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
+#include "mojo/services/gles2/command_buffer_type_conversions.h"
+#include "mojo/services/gles2/mojo_buffer_backing.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mojo {
+
+namespace {
+
+class MemoryTrackerStub : public gpu::gles2::MemoryTracker {
+ public:
+  MemoryTrackerStub() {}
+
+  virtual void TrackMemoryAllocatedChange(size_t old_size,
+                                          size_t new_size,
+                                          gpu::gles2::MemoryTracker::Pool pool)
+      override {}
+
+  virtual bool EnsureGPUMemoryAvailable(size_t size_needed) override {
+    return true;
+  };
+
+ private:
+  virtual ~MemoryTrackerStub() {}
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryTrackerStub);
+};
+
+}  // anonymous namespace
+
+CommandBufferImpl::CommandBufferImpl(
+    gfx::GLShareGroup* share_group,
+    gpu::gles2::MailboxManager* mailbox_manager)
+    : widget_(gfx::kNullAcceleratedWidget),
+      size_(1, 1),
+      share_group_(share_group),
+      mailbox_manager_(mailbox_manager) {
+}
+
+CommandBufferImpl::CommandBufferImpl(
+    gfx::AcceleratedWidget widget,
+    const gfx::Size& size,
+    gfx::GLShareGroup* share_group,
+    gpu::gles2::MailboxManager* mailbox_manager)
+    : widget_(widget),
+      size_(size),
+      share_group_(share_group),
+      mailbox_manager_(mailbox_manager) {
+}
+
+CommandBufferImpl::~CommandBufferImpl() {
+  client()->DidDestroy();
+  if (decoder_) {
+    bool have_context = decoder_->MakeCurrent();
+    decoder_->Destroy(have_context);
+  }
+}
+
+void CommandBufferImpl::Initialize(
+    CommandBufferSyncClientPtr sync_client,
+    mojo::ScopedSharedBufferHandle shared_state) {
+  sync_client_ = sync_client.Pass();
+  sync_client_->DidInitialize(DoInitialize(shared_state.Pass()));
+}
+
+bool CommandBufferImpl::DoInitialize(
+    mojo::ScopedSharedBufferHandle shared_state) {
+  if (widget_ == gfx::kNullAcceleratedWidget)
+    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size_);
+  else
+    surface_ = gfx::GLSurface::CreateViewGLSurface(widget_);
+  if (!surface_.get())
+    return false;
+
+  // TODO(piman): virtual contexts, gpu preference.
+  context_ = gfx::GLContext::CreateGLContext(
+      share_group_.get(), surface_.get(), gfx::PreferIntegratedGpu);
+  if (!context_.get())
+    return false;
+
+  if (!context_->MakeCurrent(surface_.get()))
+    return false;
+
+  // TODO(piman): ShaderTranslatorCache is currently per-ContextGroup but
+  // only needs to be per-thread.
+  scoped_refptr<gpu::gles2::ContextGroup> context_group =
+      new gpu::gles2::ContextGroup(mailbox_manager_.get(),
+                                   new MemoryTrackerStub,
+                                   new gpu::gles2::ShaderTranslatorCache,
+                                   NULL,
+                                   true);
+
+  command_buffer_.reset(
+      new gpu::CommandBufferService(context_group->transfer_buffer_manager()));
+  bool result = command_buffer_->Initialize();
+  DCHECK(result);
+
+  decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group.get()));
+  scheduler_.reset(new gpu::GpuScheduler(
+      command_buffer_.get(), decoder_.get(), decoder_.get()));
+  decoder_->set_engine(scheduler_.get());
+  decoder_->SetResizeCallback(
+      base::Bind(&CommandBufferImpl::OnResize, base::Unretained(this)));
+
+  gpu::gles2::DisallowedFeatures disallowed_features;
+
+  // TODO(piman): attributes.
+  std::vector<int32> attrib_vector;
+  if (!decoder_->Initialize(surface_,
+                            context_,
+                            false /* offscreen */,
+                            size_,
+                            disallowed_features,
+                            attrib_vector))
+    return false;
+
+  command_buffer_->SetPutOffsetChangeCallback(base::Bind(
+      &gpu::GpuScheduler::PutChanged, base::Unretained(scheduler_.get())));
+  command_buffer_->SetGetBufferChangeCallback(base::Bind(
+      &gpu::GpuScheduler::SetGetBuffer, base::Unretained(scheduler_.get())));
+  command_buffer_->SetParseErrorCallback(
+      base::Bind(&CommandBufferImpl::OnParseError, base::Unretained(this)));
+
+  // TODO(piman): other callbacks
+
+  const size_t kSize = sizeof(gpu::CommandBufferSharedState);
+  scoped_ptr<gpu::BufferBacking> backing(
+      gles2::MojoBufferBacking::Create(shared_state.Pass(), kSize));
+  if (!backing)
+    return false;
+
+  command_buffer_->SetSharedStateBuffer(backing.Pass());
+  return true;
+}
+
+void CommandBufferImpl::SetGetBuffer(int32_t buffer) {
+  command_buffer_->SetGetBuffer(buffer);
+}
+
+void CommandBufferImpl::Flush(int32_t put_offset) {
+  if (!context_->MakeCurrent(surface_.get())) {
+    DLOG(WARNING) << "Context lost";
+    client()->LostContext(gpu::error::kUnknown);
+    return;
+  }
+  command_buffer_->Flush(put_offset);
+}
+
+void CommandBufferImpl::MakeProgress(int32_t last_get_offset) {
+  // TODO(piman): handle out-of-order.
+  sync_client_->DidMakeProgress(
+      CommandBufferState::From(command_buffer_->GetLastState()));
+}
+
+void CommandBufferImpl::RegisterTransferBuffer(
+    int32_t id,
+    mojo::ScopedSharedBufferHandle transfer_buffer,
+    uint32_t size) {
+  // Take ownership of the memory and map it into this process.
+  // This validates the size.
+  scoped_ptr<gpu::BufferBacking> backing(
+      gles2::MojoBufferBacking::Create(transfer_buffer.Pass(), size));
+  if (!backing) {
+    DVLOG(0) << "Failed to map shared memory.";
+    return;
+  }
+  command_buffer_->RegisterTransferBuffer(id, backing.Pass());
+}
+
+void CommandBufferImpl::DestroyTransferBuffer(int32_t id) {
+  command_buffer_->DestroyTransferBuffer(id);
+}
+
+void CommandBufferImpl::Echo(const Callback<void()>& callback) {
+  callback.Run();
+}
+
+void CommandBufferImpl::OnParseError() {
+  gpu::CommandBuffer::State state = command_buffer_->GetLastState();
+  client()->LostContext(state.context_lost_reason);
+}
+
+void CommandBufferImpl::OnResize(gfx::Size size, float scale_factor) {
+  surface_->Resize(size);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/gles2/command_buffer_impl.h b/mojo/services/gles2/command_buffer_impl.h
new file mode 100644
index 0000000..5233e90
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_impl.h
@@ -0,0 +1,82 @@
+// Copyright 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.
+
+#ifndef MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
+#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer/timer.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace gpu {
+class CommandBufferService;
+class GpuScheduler;
+class GpuControlService;
+namespace gles2 {
+class GLES2Decoder;
+class MailboxManager;
+}
+}
+
+namespace gfx {
+class GLContext;
+class GLShareGroup;
+class GLSurface;
+}
+
+namespace mojo {
+
+class CommandBufferImpl : public InterfaceImpl<CommandBuffer> {
+ public:
+  // Offscreen.
+  CommandBufferImpl(gfx::GLShareGroup* share_group,
+                    gpu::gles2::MailboxManager* mailbox_manager);
+  // Onscreen.
+  CommandBufferImpl(gfx::AcceleratedWidget widget,
+                    const gfx::Size& size,
+                    gfx::GLShareGroup* share_group,
+                    gpu::gles2::MailboxManager* mailbox_manager);
+  virtual ~CommandBufferImpl();
+
+  virtual void Initialize(CommandBufferSyncClientPtr sync_client,
+                          mojo::ScopedSharedBufferHandle shared_state) override;
+  virtual void SetGetBuffer(int32_t buffer) override;
+  virtual void Flush(int32_t put_offset) override;
+  virtual void MakeProgress(int32_t last_get_offset) override;
+  virtual void RegisterTransferBuffer(
+      int32_t id,
+      mojo::ScopedSharedBufferHandle transfer_buffer,
+      uint32_t size) override;
+  virtual void DestroyTransferBuffer(int32_t id) override;
+  virtual void Echo(const Callback<void()>& callback) override;
+
+ private:
+  bool DoInitialize(mojo::ScopedSharedBufferHandle shared_state);
+
+  void OnResize(gfx::Size size, float scale_factor);
+
+  void OnParseError();
+
+  CommandBufferSyncClientPtr sync_client_;
+
+  gfx::AcceleratedWidget widget_;
+  gfx::Size size_;
+  scoped_ptr<gpu::CommandBufferService> command_buffer_;
+  scoped_ptr<gpu::gles2::GLES2Decoder> decoder_;
+  scoped_ptr<gpu::GpuScheduler> scheduler_;
+  scoped_refptr<gfx::GLContext> context_;
+  scoped_refptr<gfx::GLSurface> surface_;
+  scoped_refptr<gfx::GLShareGroup> share_group_;
+  scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
diff --git a/mojo/services/gles2/command_buffer_type_conversions.cc b/mojo/services/gles2/command_buffer_type_conversions.cc
new file mode 100644
index 0000000..a144f88
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_type_conversions.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 "mojo/services/gles2/command_buffer_type_conversions.h"
+
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace mojo {
+
+CommandBufferStatePtr
+TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State>::Convert(
+    const gpu::CommandBuffer::State& input) {
+  CommandBufferStatePtr result(CommandBufferState::New());
+  result->num_entries = input.num_entries;
+  result->get_offset = input.get_offset;
+  result->put_offset = input.put_offset;
+  result->token = input.token;
+  result->error = input.error;
+  result->context_lost_reason = input.context_lost_reason;
+  result->generation = input.generation;
+  return result.Pass();
+}
+
+gpu::CommandBuffer::State
+TypeConverter<gpu::CommandBuffer::State, CommandBufferStatePtr>::Convert(
+    const CommandBufferStatePtr& input) {
+  gpu::CommandBuffer::State state;
+  state.num_entries = input->num_entries;
+  state.get_offset = input->get_offset;
+  state.put_offset = input->put_offset;
+  state.token = input->token;
+  state.error = static_cast<gpu::error::Error>(input->error);
+  state.context_lost_reason =
+      static_cast<gpu::error::ContextLostReason>(input->context_lost_reason);
+  state.generation = input->generation;
+  return state;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/gles2/command_buffer_type_conversions.h b/mojo/services/gles2/command_buffer_type_conversions.h
new file mode 100644
index 0000000..2579681
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_type_conversions.h
@@ -0,0 +1,28 @@
+// 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_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
+#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
+
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace mojo {
+
+class CommandBufferState;
+
+template <>
+struct TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State> {
+  static CommandBufferStatePtr Convert(const gpu::CommandBuffer::State& input);
+};
+
+template <>
+struct TypeConverter<gpu::CommandBuffer::State, CommandBufferStatePtr> {
+  static gpu::CommandBuffer::State Convert(const CommandBufferStatePtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
diff --git a/mojo/services/gles2/mojo_buffer_backing.cc b/mojo/services/gles2/mojo_buffer_backing.cc
new file mode 100644
index 0000000..3c90878
--- /dev/null
+++ b/mojo/services/gles2/mojo_buffer_backing.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 "mojo/services/gles2/mojo_buffer_backing.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace gles2 {
+
+MojoBufferBacking::MojoBufferBacking(mojo::ScopedSharedBufferHandle handle,
+                                     void* memory,
+                                     size_t size)
+    : handle_(handle.Pass()), memory_(memory), size_(size) {}
+
+MojoBufferBacking::~MojoBufferBacking() { mojo::UnmapBuffer(memory_); }
+
+// static
+scoped_ptr<gpu::BufferBacking> MojoBufferBacking::Create(
+    mojo::ScopedSharedBufferHandle handle,
+    size_t size) {
+  void* memory = NULL;
+  MojoResult result = mojo::MapBuffer(
+      handle.get(), 0, size, &memory, MOJO_MAP_BUFFER_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return scoped_ptr<BufferBacking>();
+  DCHECK(memory);
+  return scoped_ptr<BufferBacking>(
+      new MojoBufferBacking(handle.Pass(), memory, size));
+}
+void* MojoBufferBacking::GetMemory() const { return memory_; }
+size_t MojoBufferBacking::GetSize() const { return size_; }
+
+}  // namespace gles2
+}  // namespace mojo
diff --git a/mojo/services/gles2/mojo_buffer_backing.h b/mojo/services/gles2/mojo_buffer_backing.h
new file mode 100644
index 0000000..9304df7
--- /dev/null
+++ b/mojo/services/gles2/mojo_buffer_backing.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 MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+#define MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace gles2 {
+
+class MojoBufferBacking : public gpu::BufferBacking {
+ public:
+  MojoBufferBacking(mojo::ScopedSharedBufferHandle handle,
+                    void* memory,
+                    size_t size);
+  virtual ~MojoBufferBacking();
+
+  static scoped_ptr<gpu::BufferBacking> Create(
+      mojo::ScopedSharedBufferHandle handle,
+      size_t size);
+
+  virtual void* GetMemory() const override;
+  virtual size_t GetSize() const override;
+
+ private:
+  mojo::ScopedSharedBufferHandle handle_;
+  void* memory_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoBufferBacking);
+};
+
+}  // namespace gles2
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
diff --git a/mojo/services/html_viewer/BUILD.gn b/mojo/services/html_viewer/BUILD.gn
new file mode 100644
index 0000000..ca20a24
--- /dev/null
+++ b/mojo/services/html_viewer/BUILD.gn
@@ -0,0 +1,78 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_html_viewer
+shared_library("html_viewer") {
+  output_name = "mojo_html_viewer"
+
+  sources = [
+    "blink_basic_type_converters.cc",
+    "blink_basic_type_converters.h",
+    "blink_input_events_type_converters.cc",
+    "blink_input_events_type_converters.h",
+    "blink_platform_impl.cc",
+    "blink_platform_impl.h",
+    "blink_url_request_type_converters.cc",
+    "blink_url_request_type_converters.h",
+    "html_viewer.cc",
+    "html_document_view.cc",
+    "html_document_view.h",
+    "webclipboard_impl.cc",
+    "webclipboard_impl.h",
+    "webcookiejar_impl.cc",
+    "webcookiejar_impl.h",
+    "webmediaplayer_factory.cc",
+    "webmediaplayer_factory.h",
+    "webmimeregistry_impl.cc",
+    "webmimeregistry_impl.h",
+    "websockethandle_impl.cc",
+    "websockethandle_impl.h",
+    "webstoragenamespace_impl.cc",
+    "webstoragenamespace_impl.h",
+    "webthemeengine_impl.cc",
+    "webthemeengine_impl.h",
+    "webthread_impl.cc",
+    "webthread_impl.h",
+    "weburlloader_impl.cc",
+    "weburlloader_impl.h",
+    "weblayertreeview_impl.cc",
+    "weblayertreeview_impl.h",
+  ]
+
+  include_dirs = [ "third_party/WebKit" ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//cc",
+    "//cc/blink",
+    "//cc/surfaces",
+    "//media",
+    "//media/audio",
+    "//media/base",
+    "//media/blink",
+    "//mojo/cc",
+    "//mojo/common",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/cpp/network",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/clipboard",
+    "//mojo/services/public/interfaces/content_handler",
+    "//mojo/services/public/interfaces/gpu",
+    "//mojo/services/public/interfaces/input_events:input_events",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/public/interfaces/network",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/cpp/surfaces",
+    "//net",
+    "//skia",
+    "//third_party/WebKit/public:blink",
+    "//ui/native_theme",
+    "//url",
+  ]
+}
diff --git a/mojo/services/html_viewer/DEPS b/mojo/services/html_viewer/DEPS
new file mode 100644
index 0000000..ca538ee
--- /dev/null
+++ b/mojo/services/html_viewer/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+  "+cc",
+  "+media",
+  "+mojo/cc",
+  "+mojo/application",
+  "+mojo/services",
+  "+net/base",
+  "+skia",
+  "+third_party/WebKit/public",
+  "+third_party/skia/include",
+  "+ui/base",
+  "+ui/events",
+  "+ui/native_theme",
+]
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.cc b/mojo/services/html_viewer/blink_basic_type_converters.cc
new file mode 100644
index 0000000..eb52652
--- /dev/null
+++ b/mojo/services/html_viewer/blink_basic_type_converters.cc
@@ -0,0 +1,34 @@
+// 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/html_viewer/blink_basic_type_converters.h"
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+using blink::WebString;
+
+namespace mojo {
+
+// static
+String TypeConverter<String, WebString>::Convert(const WebString& str) {
+  return String(str.utf8());
+}
+
+// static
+WebString TypeConverter<WebString, String>::Convert(const String& str) {
+  return WebString::fromUTF8(str.get());
+}
+
+// static
+Array<uint8_t> TypeConverter<Array<uint8_t>, blink::WebString>::Convert(
+    const blink::WebString& input) {
+  std::string utf8 = input.utf8();
+  Array<uint8_t> result(utf8.size());
+  for (size_t i = 0; i < utf8.size(); ++i)
+    result[i] = utf8[i];
+  return result.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.h b/mojo/services/html_viewer/blink_basic_type_converters.h
new file mode 100644
index 0000000..60ddeee
--- /dev/null
+++ b/mojo/services/html_viewer/blink_basic_type_converters.h
@@ -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.
+
+#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+
+namespace blink {
+class WebString;
+}
+
+namespace mojo {
+class String;
+
+template<>
+struct TypeConverter<String, blink::WebString> {
+  static String Convert(const blink::WebString& str);
+};
+template<>
+struct TypeConverter<blink::WebString, String> {
+  static blink::WebString Convert(const String& str);
+};
+template <>
+struct TypeConverter<Array<uint8_t>, blink::WebString> {
+  static Array<uint8_t> Convert(const blink::WebString& input);
+};
+
+template<typename T, typename U>
+struct TypeConverter<Array<T>, blink::WebVector<U> > {
+  static Array<T> Convert(const blink::WebVector<U>& vector) {
+    Array<T> array(vector.size());
+    for (size_t i = 0; i < vector.size(); ++i)
+      array[i] = TypeConverter<T, U>::Convert(vector[i]);
+    return array.Pass();
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.cc b/mojo/services/html_viewer/blink_input_events_type_converters.cc
new file mode 100644
index 0000000..ff1a2cb
--- /dev/null
+++ b/mojo/services/html_viewer/blink_input_events_type_converters.cc
@@ -0,0 +1,203 @@
+// 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/html_viewer/blink_input_events_type_converters.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "mojo/services/public/interfaces/input_events/input_event_constants.mojom.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace mojo {
+namespace {
+
+// Used for scrolling. This matches Firefox behavior.
+const int kPixelsPerTick = 53;
+
+int EventFlagsToWebEventModifiers(int flags) {
+  int modifiers = 0;
+
+  if (flags & mojo::EVENT_FLAGS_SHIFT_DOWN)
+    modifiers |= blink::WebInputEvent::ShiftKey;
+  if (flags & mojo::EVENT_FLAGS_CONTROL_DOWN)
+    modifiers |= blink::WebInputEvent::ControlKey;
+  if (flags & mojo::EVENT_FLAGS_ALT_DOWN)
+    modifiers |= blink::WebInputEvent::AltKey;
+  // TODO(beng): MetaKey/META_MASK
+  if (flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::LeftButtonDown;
+  if (flags & mojo::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::MiddleButtonDown;
+  if (flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::RightButtonDown;
+  if (flags & mojo::EVENT_FLAGS_CAPS_LOCK_DOWN)
+    modifiers |= blink::WebInputEvent::CapsLockOn;
+  return modifiers;
+}
+
+int EventFlagsToWebInputEventModifiers(int flags) {
+  return
+      (flags & mojo::EVENT_FLAGS_SHIFT_DOWN ?
+       blink::WebInputEvent::ShiftKey : 0) |
+      (flags & mojo::EVENT_FLAGS_CONTROL_DOWN ?
+       blink::WebInputEvent::ControlKey : 0) |
+      (flags & mojo::EVENT_FLAGS_CAPS_LOCK_DOWN ?
+       blink::WebInputEvent::CapsLockOn : 0) |
+      (flags & mojo::EVENT_FLAGS_ALT_DOWN ?
+       blink::WebInputEvent::AltKey : 0);
+}
+
+int GetClickCount(int flags) {
+  if (flags & mojo::MOUSE_EVENT_FLAGS_IS_TRIPLE_CLICK)
+    return 3;
+  else if (flags & mojo::MOUSE_EVENT_FLAGS_IS_DOUBLE_CLICK)
+    return 2;
+
+  return 1;
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebMouseEventFrom(const EventPtr& event) {
+  scoped_ptr<blink::WebMouseEvent> web_event(new blink::WebMouseEvent);
+  web_event->x = event->location_data->in_view_location->x;
+  web_event->y = event->location_data->in_view_location->y;
+
+  // TODO(erg): Remove this if check once we can rely on screen_location
+  // actually being passed to us. As written today, getting the screen
+  // location from ui::Event objects can only be done by querying the
+  // underlying native events, so all synthesized events don't have screen
+  // locations.
+  if (!event->location_data->screen_location.is_null()) {
+    web_event->globalX = event->location_data->screen_location->x;
+    web_event->globalY = event->location_data->screen_location->y;
+  }
+
+  web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
+  web_event->timeStampSeconds =
+      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+  web_event->button = blink::WebMouseEvent::ButtonNone;
+  if (event->flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonLeft;
+  if (event->flags & mojo::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonMiddle;
+  if (event->flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonRight;
+
+  switch (event->action) {
+    case EVENT_TYPE_MOUSE_PRESSED:
+      web_event->type = blink::WebInputEvent::MouseDown;
+      break;
+    case EVENT_TYPE_MOUSE_RELEASED:
+      web_event->type = blink::WebInputEvent::MouseUp;
+      break;
+    case EVENT_TYPE_MOUSE_ENTERED:
+      web_event->type = blink::WebInputEvent::MouseLeave;
+      web_event->button = blink::WebMouseEvent::ButtonNone;
+      break;
+    case EVENT_TYPE_MOUSE_EXITED:
+    case EVENT_TYPE_MOUSE_MOVED:
+    case EVENT_TYPE_MOUSE_DRAGGED:
+      web_event->type = blink::WebInputEvent::MouseMove;
+      break;
+    default:
+      NOTIMPLEMENTED() << "Received unexpected event: " << event->action;
+      break;
+  }
+
+  web_event->clickCount = GetClickCount(event->flags);
+
+  return web_event.PassAs<blink::WebInputEvent>();
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebKeyboardEvent(
+    const EventPtr& event) {
+  scoped_ptr<blink::WebKeyboardEvent> web_event(new blink::WebKeyboardEvent);
+
+  web_event->modifiers = EventFlagsToWebInputEventModifiers(event->flags);
+  web_event->timeStampSeconds =
+      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+  switch (event->action) {
+    case EVENT_TYPE_KEY_PRESSED:
+      web_event->type = event->key_data->is_char ? blink::WebInputEvent::Char :
+          blink::WebInputEvent::RawKeyDown;
+      break;
+    case EVENT_TYPE_KEY_RELEASED:
+      web_event->type = blink::WebInputEvent::KeyUp;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  if (web_event->modifiers & blink::WebInputEvent::AltKey)
+    web_event->isSystemKey = true;
+
+  web_event->windowsKeyCode = event->key_data->windows_key_code;
+  web_event->nativeKeyCode = event->key_data->native_key_code;
+  web_event->text[0] = event->key_data->text;
+  web_event->unmodifiedText[0] = event->key_data->unmodified_text;
+
+  web_event->setKeyIdentifierFromWindowsKeyCode();
+  return web_event.PassAs<blink::WebInputEvent>();
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebMouseWheelEventFrom(
+    const EventPtr& event) {
+  scoped_ptr<blink::WebMouseWheelEvent> web_event(
+      new blink::WebMouseWheelEvent);
+  web_event->type = blink::WebInputEvent::MouseWheel;
+  web_event->button = blink::WebMouseEvent::ButtonNone;
+  web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
+  web_event->timeStampSeconds =
+      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+  web_event->x = event->location_data->in_view_location->x;
+  web_event->y = event->location_data->in_view_location->y;
+
+  // TODO(erg): Remove this null check as parallel to above.
+  if (!event->location_data->screen_location.is_null()) {
+    web_event->globalX = event->location_data->screen_location->x;
+    web_event->globalY = event->location_data->screen_location->y;
+  }
+
+  if ((event->flags & mojo::EVENT_FLAGS_SHIFT_DOWN) != 0 &&
+      event->wheel_data->x_offset == 0) {
+    web_event->deltaX = event->wheel_data->y_offset;
+    web_event->deltaY = 0;
+  } else {
+    web_event->deltaX = event->wheel_data->x_offset;
+    web_event->deltaY = event->wheel_data->y_offset;
+  }
+
+  web_event->wheelTicksX = web_event->deltaX / kPixelsPerTick;
+  web_event->wheelTicksY = web_event->deltaY / kPixelsPerTick;
+
+  return web_event.PassAs<blink::WebInputEvent>();
+}
+
+}  // namespace
+
+// static
+scoped_ptr<blink::WebInputEvent>
+TypeConverter<scoped_ptr<blink::WebInputEvent>, EventPtr>::Convert(
+    const EventPtr& event) {
+  if (event->action == EVENT_TYPE_MOUSE_PRESSED ||
+      event->action == EVENT_TYPE_MOUSE_RELEASED ||
+      event->action == EVENT_TYPE_MOUSE_ENTERED ||
+      event->action == EVENT_TYPE_MOUSE_EXITED ||
+      event->action == EVENT_TYPE_MOUSE_MOVED ||
+      event->action == EVENT_TYPE_MOUSE_DRAGGED) {
+    return BuildWebMouseEventFrom(event);
+  } else if ((event->action == EVENT_TYPE_KEY_PRESSED ||
+              event->action == EVENT_TYPE_KEY_RELEASED) &&
+             event->key_data) {
+    return BuildWebKeyboardEvent(event);
+  } else if (event->action == EVENT_TYPE_MOUSEWHEEL) {
+    return BuildWebMouseWheelEventFrom(event);
+  }
+
+  return scoped_ptr<blink::WebInputEvent>();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.h b/mojo/services/html_viewer/blink_input_events_type_converters.h
new file mode 100644
index 0000000..3d5860d
--- /dev/null
+++ b/mojo/services/html_viewer/blink_input_events_type_converters.h
@@ -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.
+
+#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace mojo {
+
+template <>
+struct TypeConverter<scoped_ptr<blink::WebInputEvent>, EventPtr> {
+  static scoped_ptr<blink::WebInputEvent> Convert(const EventPtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_platform_impl.cc b/mojo/services/html_viewer/blink_platform_impl.cc
new file mode 100644
index 0000000..8a7555b
--- /dev/null
+++ b/mojo/services/html_viewer/blink_platform_impl.cc
@@ -0,0 +1,251 @@
+// 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/html_viewer/blink_platform_impl.h"
+
+#include <cmath>
+
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/html_viewer/webclipboard_impl.h"
+#include "mojo/services/html_viewer/webcookiejar_impl.h"
+#include "mojo/services/html_viewer/websockethandle_impl.h"
+#include "mojo/services/html_viewer/webthread_impl.h"
+#include "mojo/services/html_viewer/weburlloader_impl.h"
+#include "net/base/data_url.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/public/platform/WebWaitableEvent.h"
+
+namespace mojo {
+namespace {
+
+// TODO(darin): Figure out what our UA should really be.
+const char kUserAgentString[] =
+  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
+  "Chrome/35.0.1916.153 Safari/537.36";
+
+class WebWaitableEventImpl : public blink::WebWaitableEvent {
+ public:
+  WebWaitableEventImpl() : impl_(new base::WaitableEvent(false, false)) {}
+  virtual ~WebWaitableEventImpl() {}
+
+  virtual void wait() { impl_->Wait(); }
+  virtual void signal() { impl_->Signal(); }
+
+  base::WaitableEvent* impl() {
+    return impl_.get();
+  }
+
+ private:
+  scoped_ptr<base::WaitableEvent> impl_;
+  DISALLOW_COPY_AND_ASSIGN(WebWaitableEventImpl);
+};
+
+}  // namespace
+
+BlinkPlatformImpl::BlinkPlatformImpl(ApplicationImpl* app)
+    : main_loop_(base::MessageLoop::current()),
+      shared_timer_func_(NULL),
+      shared_timer_fire_time_(0.0),
+      shared_timer_fire_time_was_set_while_suspended_(false),
+      shared_timer_suspended_(0),
+      current_thread_slot_(&DestroyCurrentThread) {
+  app->ConnectToService("mojo:mojo_network_service", &network_service_);
+
+  CookieStorePtr cookie_store;
+  network_service_->GetCookieStore(Get(&cookie_store));
+  cookie_jar_.reset(new WebCookieJarImpl(cookie_store.Pass()));
+
+  ClipboardPtr clipboard;
+  app->ConnectToService("mojo:mojo_clipboard", &clipboard);
+  clipboard_.reset(new WebClipboardImpl(clipboard.Pass()));
+}
+
+BlinkPlatformImpl::~BlinkPlatformImpl() {
+}
+
+blink::WebCookieJar* BlinkPlatformImpl::cookieJar() {
+  return cookie_jar_.get();
+}
+
+blink::WebClipboard* BlinkPlatformImpl::clipboard() {
+  return clipboard_.get();
+}
+
+blink::WebMimeRegistry* BlinkPlatformImpl::mimeRegistry() {
+  return &mime_registry_;
+}
+
+blink::WebThemeEngine* BlinkPlatformImpl::themeEngine() {
+  return &theme_engine_;
+}
+
+blink::WebString BlinkPlatformImpl::defaultLocale() {
+  return blink::WebString::fromUTF8("en-US");
+}
+
+double BlinkPlatformImpl::currentTime() {
+  return base::Time::Now().ToDoubleT();
+}
+
+double BlinkPlatformImpl::monotonicallyIncreasingTime() {
+  return base::TimeTicks::Now().ToInternalValue() /
+      static_cast<double>(base::Time::kMicrosecondsPerSecond);
+}
+
+void BlinkPlatformImpl::cryptographicallyRandomValues(unsigned char* buffer,
+                                                      size_t length) {
+  base::RandBytes(buffer, length);
+}
+
+void BlinkPlatformImpl::setSharedTimerFiredFunction(void (*func)()) {
+  shared_timer_func_ = func;
+}
+
+void BlinkPlatformImpl::setSharedTimerFireInterval(
+    double interval_seconds) {
+  shared_timer_fire_time_ = interval_seconds + monotonicallyIncreasingTime();
+  if (shared_timer_suspended_) {
+    shared_timer_fire_time_was_set_while_suspended_ = true;
+    return;
+  }
+
+  // By converting between double and int64 representation, we run the risk
+  // of losing precision due to rounding errors. Performing computations in
+  // microseconds reduces this risk somewhat. But there still is the potential
+  // of us computing a fire time for the timer that is shorter than what we
+  // need.
+  // As the event loop will check event deadlines prior to actually firing
+  // them, there is a risk of needlessly rescheduling events and of
+  // needlessly looping if sleep times are too short even by small amounts.
+  // This results in measurable performance degradation unless we use ceil() to
+  // always round up the sleep times.
+  int64 interval = static_cast<int64>(
+      ceil(interval_seconds * base::Time::kMillisecondsPerSecond)
+      * base::Time::kMicrosecondsPerMillisecond);
+
+  if (interval < 0)
+    interval = 0;
+
+  shared_timer_.Stop();
+  shared_timer_.Start(FROM_HERE, base::TimeDelta::FromMicroseconds(interval),
+                      this, &BlinkPlatformImpl::DoTimeout);
+}
+
+void BlinkPlatformImpl::stopSharedTimer() {
+  shared_timer_.Stop();
+}
+
+void BlinkPlatformImpl::callOnMainThread(
+    void (*func)(void*), void* context) {
+  main_loop_->PostTask(FROM_HERE, base::Bind(func, context));
+}
+
+bool BlinkPlatformImpl::isThreadedCompositingEnabled() {
+  return true;
+}
+
+blink::WebCompositorSupport* BlinkPlatformImpl::compositorSupport() {
+  return &compositor_support_;
+}
+
+blink::WebScrollbarBehavior* BlinkPlatformImpl::scrollbarBehavior() {
+  return &scrollbar_behavior_;
+}
+
+const unsigned char* BlinkPlatformImpl::getTraceCategoryEnabledFlag(
+    const char* category_name) {
+  static const unsigned char buf[] = "*";
+  return buf;
+}
+
+blink::WebURLLoader* BlinkPlatformImpl::createURLLoader() {
+  return new WebURLLoaderImpl(network_service_.get());
+}
+
+blink::WebSocketHandle* BlinkPlatformImpl::createWebSocketHandle() {
+  return new WebSocketHandleImpl(network_service_.get());
+}
+
+blink::WebString BlinkPlatformImpl::userAgent() {
+  return blink::WebString::fromUTF8(kUserAgentString);
+}
+
+blink::WebData BlinkPlatformImpl::parseDataURL(
+    const blink::WebURL& url,
+    blink::WebString& mimetype_out,
+    blink::WebString& charset_out) {
+  std::string mimetype, charset, data;
+  if (net::DataURL::Parse(url, &mimetype, &charset, &data)
+      && net::IsSupportedMimeType(mimetype)) {
+    mimetype_out = blink::WebString::fromUTF8(mimetype);
+    charset_out = blink::WebString::fromUTF8(charset);
+    return data;
+  }
+  return blink::WebData();
+}
+
+blink::WebURLError BlinkPlatformImpl::cancelledError(const blink::WebURL& url)
+    const {
+  blink::WebURLError error;
+  error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+  error.reason = net::ERR_ABORTED;
+  error.unreachableURL = url;
+  error.staleCopyInCache = false;
+  error.isCancellation = true;
+  return error;
+}
+
+blink::WebThread* BlinkPlatformImpl::createThread(const char* name) {
+  return new WebThreadImpl(name);
+}
+
+blink::WebThread* BlinkPlatformImpl::currentThread() {
+  WebThreadImplForMessageLoop* thread =
+      static_cast<WebThreadImplForMessageLoop*>(current_thread_slot_.Get());
+  if (thread)
+    return (thread);
+
+  scoped_refptr<base::MessageLoopProxy> message_loop =
+      base::MessageLoopProxy::current();
+  if (!message_loop.get())
+    return NULL;
+
+  thread = new WebThreadImplForMessageLoop(message_loop.get());
+  current_thread_slot_.Set(thread);
+  return thread;
+}
+
+void BlinkPlatformImpl::yieldCurrentThread() {
+  base::PlatformThread::YieldCurrentThread();
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::createWaitableEvent() {
+  return new WebWaitableEventImpl();
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::waitMultipleEvents(
+    const blink::WebVector<blink::WebWaitableEvent*>& web_events) {
+  std::vector<base::WaitableEvent*> events;
+  for (size_t i = 0; i < web_events.size(); ++i)
+    events.push_back(static_cast<WebWaitableEventImpl*>(web_events[i])->impl());
+  size_t idx = base::WaitableEvent::WaitMany(
+      vector_as_array(&events), events.size());
+  DCHECK_LT(idx, web_events.size());
+  return web_events[idx];
+}
+
+// static
+void BlinkPlatformImpl::DestroyCurrentThread(void* thread) {
+  WebThreadImplForMessageLoop* impl =
+      static_cast<WebThreadImplForMessageLoop*>(thread);
+  delete impl;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/blink_platform_impl.h b/mojo/services/html_viewer/blink_platform_impl.h
new file mode 100644
index 0000000..9abd336
--- /dev/null
+++ b/mojo/services/html_viewer/blink_platform_impl.h
@@ -0,0 +1,93 @@
+// 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_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/timer/timer.h"
+#include "cc/blink/web_compositor_support_impl.h"
+#include "mojo/services/html_viewer/webmimeregistry_impl.h"
+#include "mojo/services/html_viewer/webthemeengine_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebScrollbarBehavior.h"
+
+namespace mojo {
+class ApplicationImpl;
+class WebClipboardImpl;
+class WebCookieJarImpl;
+
+class BlinkPlatformImpl : public blink::Platform {
+ public:
+  explicit BlinkPlatformImpl(ApplicationImpl* app);
+  virtual ~BlinkPlatformImpl();
+
+  // blink::Platform methods:
+  virtual blink::WebCookieJar* cookieJar();
+  virtual blink::WebClipboard* clipboard();
+  virtual blink::WebMimeRegistry* mimeRegistry();
+  virtual blink::WebThemeEngine* themeEngine();
+  virtual blink::WebString defaultLocale();
+  virtual double currentTime();
+  virtual double monotonicallyIncreasingTime();
+  virtual void cryptographicallyRandomValues(
+      unsigned char* buffer, size_t length);
+  virtual void setSharedTimerFiredFunction(void (*func)());
+  virtual void setSharedTimerFireInterval(double interval_seconds);
+  virtual void stopSharedTimer();
+  virtual void callOnMainThread(void (*func)(void*), void* context);
+  virtual bool isThreadedCompositingEnabled();
+  virtual blink::WebCompositorSupport* compositorSupport();
+  virtual blink::WebURLLoader* createURLLoader();
+  virtual blink::WebSocketHandle* createWebSocketHandle();
+  virtual blink::WebString userAgent();
+  virtual blink::WebData parseDataURL(
+      const blink::WebURL& url, blink::WebString& mime_type,
+      blink::WebString& charset);
+  virtual blink::WebURLError cancelledError(const blink::WebURL& url) const;
+  virtual blink::WebThread* createThread(const char* name);
+  virtual blink::WebThread* currentThread();
+  virtual void yieldCurrentThread();
+  virtual blink::WebWaitableEvent* createWaitableEvent();
+  virtual blink::WebWaitableEvent* waitMultipleEvents(
+      const blink::WebVector<blink::WebWaitableEvent*>& events);
+  virtual blink::WebScrollbarBehavior* scrollbarBehavior();
+  virtual const unsigned char* getTraceCategoryEnabledFlag(
+      const char* category_name);
+
+ private:
+  void SuspendSharedTimer();
+  void ResumeSharedTimer();
+
+  void DoTimeout() {
+    if (shared_timer_func_ && !shared_timer_suspended_)
+      shared_timer_func_();
+  }
+
+  static void DestroyCurrentThread(void*);
+
+  NetworkServicePtr network_service_;
+  base::MessageLoop* main_loop_;
+  base::OneShotTimer<BlinkPlatformImpl> shared_timer_;
+  void (*shared_timer_func_)();
+  double shared_timer_fire_time_;
+  bool shared_timer_fire_time_was_set_while_suspended_;
+  int shared_timer_suspended_;  // counter
+  base::ThreadLocalStorage::Slot current_thread_slot_;
+  cc_blink::WebCompositorSupportImpl compositor_support_;
+  WebThemeEngineImpl theme_engine_;
+  scoped_ptr<WebCookieJarImpl> cookie_jar_;
+  scoped_ptr<WebClipboardImpl> clipboard_;
+  WebMimeRegistryImpl mime_registry_;
+  blink::WebScrollbarBehavior scrollbar_behavior_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlinkPlatformImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.cc b/mojo/services/html_viewer/blink_url_request_type_converters.cc
new file mode 100644
index 0000000..9428d92
--- /dev/null
+++ b/mojo/services/html_viewer/blink_url_request_type_converters.cc
@@ -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.
+
+#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
+
+#include "base/strings/string_util.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+
+namespace mojo {
+namespace {
+
+// Ripped from web_url_loader_impl.cc.
+class HeaderFlattener : public blink::WebHTTPHeaderVisitor {
+ public:
+  HeaderFlattener() : has_accept_header_(false) {}
+
+  virtual void visitHeader(const blink::WebString& name,
+                           const blink::WebString& value) {
+    // Headers are latin1.
+    const std::string& name_latin1 = name.latin1();
+    const std::string& value_latin1 = value.latin1();
+
+    // Skip over referrer headers found in the header map because we already
+    // pulled it out as a separate parameter.
+    if (LowerCaseEqualsASCII(name_latin1, "referer"))
+      return;
+
+    if (LowerCaseEqualsASCII(name_latin1, "accept"))
+      has_accept_header_ = true;
+
+    buffer_.push_back(name_latin1 + ": " + value_latin1);
+  }
+
+  Array<String> GetBuffer() {
+    // In some cases, WebKit doesn't add an Accept header, but not having the
+    // header confuses some web servers.  See bug 808613.
+    if (!has_accept_header_) {
+      buffer_.push_back("Accept: */*");
+      has_accept_header_ = true;
+    }
+    return buffer_.Pass();
+  }
+
+ private:
+  Array<String> buffer_;
+  bool has_accept_header_;
+};
+
+void AddRequestBody(URLRequest* url_request,
+                    const blink::WebURLRequest& request) {
+  if (request.httpBody().isNull())
+    return;
+
+  uint32_t i = 0;
+  blink::WebHTTPBody::Element element;
+  while (request.httpBody().elementAt(i++, element)) {
+    switch (element.type) {
+      case blink::WebHTTPBody::Element::TypeData:
+        if (!element.data.isEmpty()) {
+          // WebKit sometimes gives up empty data to append. These aren't
+          // necessary so we just optimize those out here.
+          uint32_t num_bytes = static_cast<uint32_t>(element.data.size());
+          MojoCreateDataPipeOptions options;
+          options.struct_size = sizeof(MojoCreateDataPipeOptions);
+          options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+          options.element_num_bytes = 1;
+          options.capacity_num_bytes = num_bytes;
+          DataPipe data_pipe(options);
+          url_request->body.push_back(
+              data_pipe.consumer_handle.Pass());
+          WriteDataRaw(data_pipe.producer_handle.get(),
+                       element.data.data(),
+                       &num_bytes,
+                       MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+        }
+        break;
+      case blink::WebHTTPBody::Element::TypeFile:
+      case blink::WebHTTPBody::Element::TypeFileSystemURL:
+      case blink::WebHTTPBody::Element::TypeBlob:
+        // TODO(mpcomplete): handle these.
+        NOTIMPLEMENTED();
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+}
+
+} // namespace
+
+URLRequestPtr TypeConverter<URLRequestPtr, blink::WebURLRequest>::Convert(
+    const blink::WebURLRequest& request) {
+  URLRequestPtr url_request(URLRequest::New());
+  url_request->url = request.url().string().utf8();
+  url_request->method = request.httpMethod().utf8();
+
+  HeaderFlattener flattener;
+  request.visitHTTPHeaderFields(&flattener);
+  url_request->headers = flattener.GetBuffer().Pass();
+
+  AddRequestBody(url_request.get(), request);
+
+  return url_request.Pass();
+}
+
+}  // namespace mojo
+
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.h b/mojo/services/html_viewer/blink_url_request_type_converters.h
new file mode 100644
index 0000000..583d0ba
--- /dev/null
+++ b/mojo/services/html_viewer/blink_url_request_type_converters.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 MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
+
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace blink {
+class WebURLRequest;
+}
+
+namespace mojo {
+
+template <>
+struct TypeConverter<URLRequestPtr, blink::WebURLRequest> {
+  static URLRequestPtr Convert(const blink::WebURLRequest& request);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/html_document_view.cc b/mojo/services/html_viewer/html_document_view.cc
new file mode 100644
index 0000000..7056501
--- /dev/null
+++ b/mojo/services/html_viewer/html_document_view.cc
@@ -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.
+
+#include "mojo/services/html_viewer/html_document_view.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/html_viewer/blink_input_events_type_converters.h"
+#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
+#include "mojo/services/html_viewer/weblayertreeview_impl.h"
+#include "mojo/services/html_viewer/webmediaplayer_factory.h"
+#include "mojo/services/html_viewer/webstoragenamespace_impl.h"
+#include "mojo/services/html_viewer/weburlloader_impl.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/web/WebConsoleMessage.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebSettings.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace mojo {
+namespace {
+
+void ConfigureSettings(blink::WebSettings* settings) {
+  settings->setCookieEnabled(true);
+  settings->setDefaultFixedFontSize(13);
+  settings->setDefaultFontSize(16);
+  settings->setLoadsImagesAutomatically(true);
+  settings->setJavaScriptEnabled(true);
+}
+
+Target WebNavigationPolicyToNavigationTarget(
+    blink::WebNavigationPolicy policy) {
+  switch (policy) {
+    case blink::WebNavigationPolicyCurrentTab:
+      return TARGET_SOURCE_NODE;
+    case blink::WebNavigationPolicyNewBackgroundTab:
+    case blink::WebNavigationPolicyNewForegroundTab:
+    case blink::WebNavigationPolicyNewWindow:
+    case blink::WebNavigationPolicyNewPopup:
+      return TARGET_NEW_NODE;
+    default:
+      return TARGET_DEFAULT;
+  }
+}
+
+bool CanNavigateLocally(blink::WebFrame* frame,
+                        const blink::WebURLRequest& request) {
+  // For now, we just load child frames locally.
+  // TODO(aa): In the future, this should use embedding to connect to a
+  // different instance of Blink if the frame is cross-origin.
+  if (frame->parent())
+    return true;
+
+  // If we have extraData() it means we already have the url response
+  // (presumably because we are being called via Navigate()). In that case we
+  // can go ahead and navigate locally.
+  if (request.extraData())
+    return true;
+
+  // Otherwise we don't know if we're the right app to handle this request. Ask
+  // host to do the navigation for us.
+  return false;
+}
+
+}  // namespace
+
+HTMLDocumentView::HTMLDocumentView(
+    URLResponsePtr response,
+    InterfaceRequest<ServiceProvider> service_provider_request,
+    Shell* shell,
+    scoped_refptr<base::MessageLoopProxy> compositor_thread,
+    WebMediaPlayerFactory* web_media_player_factory)
+    : shell_(shell),
+      web_view_(NULL),
+      root_(NULL),
+      view_manager_client_factory_(shell, this),
+      compositor_thread_(compositor_thread),
+      web_media_player_factory_(web_media_player_factory),
+      weak_factory_(this) {
+  ServiceProviderImpl* exported_services = new ServiceProviderImpl();
+  exported_services->AddService(&view_manager_client_factory_);
+  BindToRequest(exported_services, &service_provider_request);
+  Load(response.Pass());
+}
+
+HTMLDocumentView::~HTMLDocumentView() {
+  if (web_view_)
+    web_view_->close();
+  if (root_)
+    root_->RemoveObserver(this);
+}
+
+void HTMLDocumentView::OnEmbed(
+    ViewManager* view_manager,
+    View* root,
+    ServiceProviderImpl* embedee_service_provider_impl,
+    scoped_ptr<ServiceProvider> embedder_service_provider) {
+  root_ = root;
+  embedder_service_provider_ = embedder_service_provider.Pass();
+  navigator_host_.set_service_provider(embedder_service_provider_.get());
+
+  web_view_->resize(root_->bounds().size());
+  web_layer_tree_view_impl_->setViewportSize(root_->bounds().size());
+  web_layer_tree_view_impl_->set_view(root_);
+  root_->AddObserver(this);
+}
+
+void HTMLDocumentView::OnViewManagerDisconnected(ViewManager* view_manager) {
+  // TODO(aa): Need to figure out how shutdown works.
+}
+
+void HTMLDocumentView::Load(URLResponsePtr response) {
+  web_view_ = blink::WebView::create(this);
+  web_layer_tree_view_impl_->set_widget(web_view_);
+  ConfigureSettings(web_view_->settings());
+  web_view_->setMainFrame(blink::WebLocalFrame::create(this));
+
+  GURL url(response->url);
+
+  WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
+  extra_data->synthetic_response = response.Pass();
+
+  blink::WebURLRequest web_request;
+  web_request.initialize();
+  web_request.setURL(url);
+  web_request.setExtraData(extra_data);
+
+  web_view_->mainFrame()->loadRequest(web_request);
+}
+
+blink::WebStorageNamespace* HTMLDocumentView::createSessionStorageNamespace() {
+  return new WebStorageNamespaceImpl();
+}
+
+void HTMLDocumentView::initializeLayerTreeView() {
+  ServiceProviderPtr surfaces_service_provider;
+  shell_->ConnectToApplication("mojo:mojo_surfaces_service",
+                               Get(&surfaces_service_provider));
+  InterfacePtr<SurfacesService> surfaces_service;
+  ConnectToService(surfaces_service_provider.get(), &surfaces_service);
+
+  ServiceProviderPtr gpu_service_provider;
+  // TODO(jamesr): Should be mojo:mojo_gpu_service
+  shell_->ConnectToApplication("mojo:mojo_native_viewport_service",
+                               Get(&gpu_service_provider));
+  InterfacePtr<Gpu> gpu_service;
+  ConnectToService(gpu_service_provider.get(), &gpu_service);
+  web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
+      compositor_thread_, surfaces_service.Pass(), gpu_service.Pass()));
+}
+
+blink::WebLayerTreeView* HTMLDocumentView::layerTreeView() {
+  return web_layer_tree_view_impl_.get();
+}
+
+blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
+    blink::WebLocalFrame* frame,
+    const blink::WebURL& url,
+    blink::WebMediaPlayerClient* client) {
+  return web_media_player_factory_->CreateMediaPlayer(frame, url, client);
+}
+
+blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
+    blink::WebLocalFrame* frame,
+    const blink::WebURL& url,
+    blink::WebMediaPlayerClient* client,
+    blink::WebContentDecryptionModule* initial_cdm) {
+  return createMediaPlayer(frame, url, client);
+}
+
+blink::WebFrame* HTMLDocumentView::createChildFrame(
+    blink::WebLocalFrame* parent,
+    const blink::WebString& frameName) {
+  blink::WebLocalFrame* web_frame = blink::WebLocalFrame::create(this);
+  parent->appendChild(web_frame);
+  return web_frame;
+}
+
+void HTMLDocumentView::frameDetached(blink::WebFrame* frame) {
+  if (frame->parent())
+    frame->parent()->removeChild(frame);
+
+  // |frame| is invalid after here.
+  frame->close();
+}
+
+blink::WebCookieJar* HTMLDocumentView::cookieJar(blink::WebLocalFrame* frame) {
+  // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
+  // Either it should, as it once did, or we should find another solution here.
+  return blink::Platform::current()->cookieJar();
+}
+
+blink::WebNavigationPolicy HTMLDocumentView::decidePolicyForNavigation(
+    blink::WebLocalFrame* frame, blink::WebDataSource::ExtraData* data,
+    const blink::WebURLRequest& request, blink::WebNavigationType nav_type,
+    blink::WebNavigationPolicy default_policy, bool is_redirect) {
+  if (CanNavigateLocally(frame, request))
+    return default_policy;
+
+  navigator_host_->RequestNavigate(
+      WebNavigationPolicyToNavigationTarget(default_policy),
+      URLRequest::From(request).Pass());
+
+  return blink::WebNavigationPolicyIgnore;
+}
+
+void HTMLDocumentView::didAddMessageToConsole(
+    const blink::WebConsoleMessage& message,
+    const blink::WebString& source_name,
+    unsigned source_line,
+    const blink::WebString& stack_trace) {
+}
+
+void HTMLDocumentView::didNavigateWithinPage(
+    blink::WebLocalFrame* frame, const blink::WebHistoryItem& history_item,
+    blink::WebHistoryCommitType commit_type) {
+  navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
+}
+
+void HTMLDocumentView::OnViewBoundsChanged(View* view,
+                                           const gfx::Rect& old_bounds,
+                                           const gfx::Rect& new_bounds) {
+  DCHECK_EQ(view, root_);
+  web_view_->resize(view->bounds().size());
+}
+
+void HTMLDocumentView::OnViewDestroyed(View* view) {
+  DCHECK_EQ(view, root_);
+  view->RemoveObserver(this);
+  root_ = NULL;
+}
+
+void HTMLDocumentView::OnViewInputEvent(View* view, const EventPtr& event) {
+  scoped_ptr<blink::WebInputEvent> web_event =
+      event.To<scoped_ptr<blink::WebInputEvent> >();
+  if (web_event)
+    web_view_->handleInputEvent(*web_event);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/html_document_view.h b/mojo/services/html_viewer/html_document_view.h
new file mode 100644
index 0000000..e2407bd
--- /dev/null
+++ b/mojo/services/html_viewer/html_document_view.h
@@ -0,0 +1,122 @@
+// 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_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+#define MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/application/lazy_interface_ptr.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/web/WebFrameClient.h"
+#include "third_party/WebKit/public/web/WebViewClient.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace mojo {
+
+class WebMediaPlayerFactory;
+class ViewManager;
+class View;
+class WebLayerTreeViewImpl;
+
+// A view for a single HTML document.
+class HTMLDocumentView : public blink::WebViewClient,
+                         public blink::WebFrameClient,
+                         public ViewManagerDelegate,
+                         public ViewObserver {
+ public:
+  // Load a new HTMLDocument with |response|.
+  //
+  // |service_provider_request| should be used to implement a
+  // ServiceProvider which exposes services to the connecting application.
+  // Commonly, the connecting application is the ViewManager and it will
+  // request ViewManagerClient.
+  //
+  // |shell| is the Shell connection for this mojo::Application.
+  HTMLDocumentView(URLResponsePtr response,
+                   InterfaceRequest<ServiceProvider> service_provider_request,
+                   Shell* shell,
+                   scoped_refptr<base::MessageLoopProxy> compositor_thread,
+                   WebMediaPlayerFactory* web_media_player_factory);
+  virtual ~HTMLDocumentView();
+
+ private:
+  // WebViewClient methods:
+  virtual blink::WebStorageNamespace* createSessionStorageNamespace();
+
+  // WebWidgetClient methods:
+  virtual void initializeLayerTreeView();
+  virtual blink::WebLayerTreeView* layerTreeView();
+
+  // WebFrameClient methods:
+  virtual blink::WebMediaPlayer* createMediaPlayer(
+      blink::WebLocalFrame* frame,
+      const blink::WebURL& url,
+      blink::WebMediaPlayerClient* client);
+  virtual blink::WebMediaPlayer* createMediaPlayer(
+      blink::WebLocalFrame* frame,
+      const blink::WebURL& url,
+      blink::WebMediaPlayerClient* client,
+      blink::WebContentDecryptionModule* initial_cdm);
+  virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent,
+                                            const blink::WebString& frameName);
+  virtual void frameDetached(blink::WebFrame*);
+  virtual blink::WebCookieJar* cookieJar(blink::WebLocalFrame* frame);
+  virtual blink::WebNavigationPolicy decidePolicyForNavigation(
+      blink::WebLocalFrame* frame, blink::WebDataSource::ExtraData* data,
+      const blink::WebURLRequest& request, blink::WebNavigationType nav_type,
+      blink::WebNavigationPolicy default_policy, bool isRedirect);
+  virtual void didAddMessageToConsole(
+      const blink::WebConsoleMessage& message,
+      const blink::WebString& source_name,
+      unsigned source_line,
+      const blink::WebString& stack_trace);
+  virtual void didNavigateWithinPage(
+      blink::WebLocalFrame* frame,
+      const blink::WebHistoryItem& history_item,
+      blink::WebHistoryCommitType commit_type);
+
+  // ViewManagerDelegate methods:
+  virtual void OnEmbed(
+      ViewManager* view_manager,
+      View* root,
+      ServiceProviderImpl* embedee_service_provider_impl,
+      scoped_ptr<ServiceProvider> embedder_service_provider) override;
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override;
+
+  // ViewObserver methods:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override;
+  virtual void OnViewDestroyed(View* view) override;
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) override;
+
+  void Load(URLResponsePtr response);
+
+  URLResponsePtr response_;
+  scoped_ptr<ServiceProvider> embedder_service_provider_;
+  Shell* shell_;
+  LazyInterfacePtr<NavigatorHost> navigator_host_;
+  blink::WebView* web_view_;
+  View* root_;
+  ViewManagerClientFactory view_manager_client_factory_;
+  scoped_ptr<WebLayerTreeViewImpl> web_layer_tree_view_impl_;
+  scoped_refptr<base::MessageLoopProxy> compositor_thread_;
+  WebMediaPlayerFactory* web_media_player_factory_;
+
+  base::WeakPtrFactory<HTMLDocumentView> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(HTMLDocumentView);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc
new file mode 100644
index 0000000..ebde85f
--- /dev/null
+++ b/mojo/services/html_viewer/html_viewer.cc
@@ -0,0 +1,117 @@
+// 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/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.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/interface_factory_impl.h"
+#include "mojo/services/html_viewer/blink_platform_impl.h"
+#include "mojo/services/html_viewer/html_document_view.h"
+#include "mojo/services/html_viewer/webmediaplayer_factory.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+
+#if !defined(COMPONENT_BUILD)
+#include "base/i18n/icu_util.h"
+#include "base/path_service.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#endif
+
+namespace mojo {
+
+class HTMLViewer;
+
+class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
+ public:
+  ContentHandlerImpl(Shell* shell,
+                     scoped_refptr<base::MessageLoopProxy> compositor_thread,
+                     WebMediaPlayerFactory* web_media_player_factory)
+      : shell_(shell),
+        compositor_thread_(compositor_thread),
+        web_media_player_factory_(web_media_player_factory) {}
+  virtual ~ContentHandlerImpl() {}
+
+ private:
+  // Overridden from ContentHandler:
+  virtual void OnConnect(
+      const mojo::String& url,
+      URLResponsePtr response,
+      InterfaceRequest<ServiceProvider> service_provider_request) override {
+    new HTMLDocumentView(response.Pass(),
+                         service_provider_request.Pass(),
+                         shell_,
+                         compositor_thread_,
+                         web_media_player_factory_);
+  }
+
+  Shell* shell_;
+  scoped_refptr<base::MessageLoopProxy> compositor_thread_;
+  WebMediaPlayerFactory* web_media_player_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl);
+};
+
+class HTMLViewer : public ApplicationDelegate,
+                   public InterfaceFactory<ContentHandler> {
+ public:
+  HTMLViewer() : compositor_thread_("compositor thread") {}
+
+  virtual ~HTMLViewer() { blink::shutdown(); }
+
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    shell_ = app->shell();
+    blink_platform_impl_.reset(new BlinkPlatformImpl(app));
+    blink::initialize(blink_platform_impl_.get());
+#if !defined(COMPONENT_BUILD)
+  base::i18n::InitializeICU();
+
+  ui::RegisterPathProvider();
+
+  base::FilePath ui_test_pak_path;
+  CHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+  ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+#endif
+
+    compositor_thread_.Start();
+    web_media_player_factory_.reset(new WebMediaPlayerFactory(
+        compositor_thread_.message_loop_proxy()));
+  }
+
+  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+      override {
+    connection->AddService(this);
+    return true;
+  }
+
+  // Overridden from InterfaceFactory<ContentHandler>
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<ContentHandler> request) override {
+    BindToRequest(
+        new ContentHandlerImpl(shell_, compositor_thread_.message_loop_proxy(),
+                               web_media_player_factory_.get()),
+        &request);
+  }
+
+  scoped_ptr<BlinkPlatformImpl> blink_platform_impl_;
+  Shell* shell_;
+  base::Thread compositor_thread_;
+  scoped_ptr<WebMediaPlayerFactory> web_media_player_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(HTMLViewer);
+};
+
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::HTMLViewer);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/html_viewer/webclipboard_impl.cc b/mojo/services/html_viewer/webclipboard_impl.cc
new file mode 100644
index 0000000..94b16ca
--- /dev/null
+++ b/mojo/services/html_viewer/webclipboard_impl.cc
@@ -0,0 +1,222 @@
+// 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/html_viewer/webclipboard_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/html_viewer/blink_basic_type_converters.h"
+
+namespace mojo {
+namespace {
+
+void CopyUint64(uint64_t* output, uint64_t input) {
+  *output = input;
+}
+
+void CopyWebString(blink::WebString* output,
+                   const mojo::Array<uint8_t>& input) {
+  // blink does not differentiate between the requested data type not existing
+  // and the empty string.
+  if (input.is_null()) {
+    output->reset();
+  } else {
+    *output = blink::WebString::fromUTF8(
+        reinterpret_cast<const char*>(&input.front()),
+        input.size());
+  }
+}
+
+void CopyURL(blink::WebURL* pageURL,
+             const mojo::Array<uint8_t>& input) {
+  if (input.is_null()) {
+    *pageURL = blink::WebURL();
+  } else {
+    *pageURL = GURL(std::string(reinterpret_cast<const char*>(&input.front()),
+                                input.size()));
+  }
+}
+
+void CopyVectorString(std::vector<std::string>* output,
+                      const Array<String>& input) {
+  *output = input.To<std::vector<std::string> >();
+}
+
+template <typename T, typename U>
+bool Contains(const std::vector<T>& v, const U& item) {
+  return std::find(v.begin(), v.end(), item) != v.end();
+}
+
+const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
+
+}  // namespace
+
+WebClipboardImpl::WebClipboardImpl(ClipboardPtr clipboard)
+    : clipboard_(clipboard.Pass()) {
+}
+
+WebClipboardImpl::~WebClipboardImpl() {
+}
+
+uint64_t WebClipboardImpl::sequenceNumber(Buffer buffer) {
+  mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+  uint64_t number = 0;
+  clipboard_->GetSequenceNumber(clipboard_type,
+                                base::Bind(&CopyUint64, &number));
+
+  // Force this to be synchronous.
+  clipboard_.WaitForIncomingMethodCall();
+  return number;
+}
+
+bool WebClipboardImpl::isFormatAvailable(Format format, Buffer buffer) {
+  mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+  std::vector<std::string> types;
+  clipboard_->GetAvailableMimeTypes(
+      clipboard_type, base::Bind(&CopyVectorString, &types));
+
+  // Force this to be synchronous.
+  clipboard_.WaitForIncomingMethodCall();
+
+  switch (format) {
+    case FormatPlainText:
+      return Contains(types, mojo::Clipboard::MIME_TYPE_TEXT);
+    case FormatHTML:
+      return Contains(types, mojo::Clipboard::MIME_TYPE_HTML);
+    case FormatSmartPaste:
+      return Contains(types, kMimeTypeWebkitSmartPaste);
+    case FormatBookmark:
+      // This might be difficult.
+      return false;
+  }
+
+  return false;
+}
+
+blink::WebVector<blink::WebString> WebClipboardImpl::readAvailableTypes(
+    Buffer buffer,
+    bool* containsFilenames) {
+  mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+  std::vector<std::string> types;
+  clipboard_->GetAvailableMimeTypes(
+      clipboard_type, base::Bind(&CopyVectorString, &types));
+
+  // Force this to be synchronous.
+  clipboard_.WaitForIncomingMethodCall();
+
+  // AFAICT, every instance of setting containsFilenames is false.
+  *containsFilenames = false;
+
+  blink::WebVector<blink::WebString> output(types.size());
+  for (size_t i = 0; i < types.size(); ++i) {
+    output[i] = blink::WebString::fromUTF8(types[i]);
+  }
+
+  return output;
+}
+
+blink::WebString WebClipboardImpl::readPlainText(Buffer buffer) {
+  mojo::Clipboard::Type type = ConvertBufferType(buffer);
+
+  blink::WebString text;
+  clipboard_->ReadMimeType(
+      type, mojo::Clipboard::MIME_TYPE_TEXT, base::Bind(&CopyWebString, &text));
+
+  // Force this to be synchronous.
+  clipboard_.WaitForIncomingMethodCall();
+
+  return text;
+}
+
+blink::WebString WebClipboardImpl::readHTML(Buffer buffer,
+                                            blink::WebURL* pageURL,
+                                            unsigned* fragmentStart,
+                                            unsigned* fragmentEnd) {
+  mojo::Clipboard::Type type = ConvertBufferType(buffer);
+
+  blink::WebString html;
+  clipboard_->ReadMimeType(
+      type, mojo::Clipboard::MIME_TYPE_HTML, base::Bind(&CopyWebString, &html));
+  clipboard_.WaitForIncomingMethodCall();
+
+  *fragmentStart = 0;
+  *fragmentEnd = static_cast<unsigned>(html.length());
+
+  clipboard_->ReadMimeType(
+      type, mojo::Clipboard::MIME_TYPE_URL, base::Bind(&CopyURL, pageURL));
+  clipboard_.WaitForIncomingMethodCall();
+
+  return html;
+}
+
+blink::WebString WebClipboardImpl::readCustomData(
+    Buffer buffer,
+    const blink::WebString& mime_type) {
+  mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+  blink::WebString data;
+  clipboard_->ReadMimeType(
+      clipboard_type, mime_type.utf8(), base::Bind(&CopyWebString, &data));
+
+  // Force this to be synchronous.
+  clipboard_.WaitForIncomingMethodCall();
+
+  return data;
+}
+
+void WebClipboardImpl::writePlainText(const blink::WebString& text) {
+  Array<MimeTypePairPtr> data;
+  MimeTypePairPtr text_data(MimeTypePair::New());
+  text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+  text_data->data = Array<uint8_t>::From(text).Pass();
+  data.push_back(text_data.Pass());
+
+  clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE, data.Pass());
+}
+
+void WebClipboardImpl::writeHTML(const blink::WebString& htmlText,
+                                 const blink::WebURL& url,
+                                 const blink::WebString& plainText,
+                                 bool writeSmartPaste) {
+  Array<MimeTypePairPtr> data;
+  MimeTypePairPtr text_data(MimeTypePair::New());
+  text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+  text_data->data = Array<uint8_t>::From(plainText).Pass();
+  data.push_back(text_data.Pass());
+
+  MimeTypePairPtr html_data(MimeTypePair::New());
+  text_data->mime_type = mojo::Clipboard::MIME_TYPE_HTML;
+  text_data->data = Array<uint8_t>::From(htmlText).Pass();
+  data.push_back(html_data.Pass());
+
+  MimeTypePairPtr url_data(MimeTypePair::New());
+  url_data->mime_type = mojo::Clipboard::MIME_TYPE_URL;
+  url_data->data = Array<uint8_t>::From(url.string()).Pass();
+  data.push_back(url_data.Pass());
+
+  if (writeSmartPaste) {
+    MimeTypePairPtr smart_paste(MimeTypePair::New());
+    url_data->mime_type = kMimeTypeWebkitSmartPaste;
+    url_data->data = Array<uint8_t>::From(blink::WebString()).Pass();
+    data.push_back(smart_paste.Pass());
+  }
+
+  clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE, data.Pass());
+}
+
+mojo::Clipboard::Type WebClipboardImpl::ConvertBufferType(Buffer buffer) {
+  switch (buffer) {
+    case BufferStandard:
+      return mojo::Clipboard::TYPE_COPY_PASTE;
+    case BufferSelection:
+      return mojo::Clipboard::TYPE_SELECTION;
+  }
+
+  NOTREACHED();
+  return mojo::Clipboard::TYPE_COPY_PASTE;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webclipboard_impl.h b/mojo/services/html_viewer/webclipboard_impl.h
new file mode 100644
index 0000000..0c307c5
--- /dev/null
+++ b/mojo/services/html_viewer/webclipboard_impl.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 MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
+
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+#include "third_party/WebKit/public/platform/WebClipboard.h"
+
+namespace mojo {
+
+class WebClipboardImpl : public blink::WebClipboard {
+ public:
+  WebClipboardImpl(ClipboardPtr clipboard);
+  virtual ~WebClipboardImpl();
+
+  // blink::WebClipboard methods:
+  virtual uint64_t sequenceNumber(Buffer);
+  virtual bool isFormatAvailable(Format, Buffer);
+  virtual blink::WebVector<blink::WebString> readAvailableTypes(
+      Buffer,
+      bool* containsFilenames);
+  virtual blink::WebString readPlainText(Buffer);
+  virtual blink::WebString readHTML(Buffer buffer,
+                                    blink::WebURL* pageURL,
+                                    unsigned* fragmentStart,
+                                    unsigned* fragmentEnd);
+  // TODO(erg): readImage()
+  virtual blink::WebString readCustomData(Buffer, const blink::WebString& type);
+  virtual void writePlainText(const blink::WebString&);
+  virtual void writeHTML(const blink::WebString& htmlText,
+                         const blink::WebURL&,
+                         const blink::WebString& plainText,
+                         bool writeSmartPaste);
+
+ private:
+  // Changes webkit buffers to mojo Clipboard::Types.
+  mojo::Clipboard::Type ConvertBufferType(Buffer buffer);
+
+  ClipboardPtr clipboard_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebClipboardImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
diff --git a/mojo/services/html_viewer/webcookiejar_impl.cc b/mojo/services/html_viewer/webcookiejar_impl.cc
new file mode 100644
index 0000000..0d86480
--- /dev/null
+++ b/mojo/services/html_viewer/webcookiejar_impl.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 "mojo/services/html_viewer/webcookiejar_impl.h"
+
+#include "base/bind.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+
+namespace mojo {
+namespace {
+
+void CopyBool(bool* output, bool input) {
+  *output = input;
+}
+
+void CopyString(String* output, const String& input) {
+  *output = input;
+}
+
+}  // namespace
+
+WebCookieJarImpl::WebCookieJarImpl(CookieStorePtr store)
+    : store_(store.Pass()) {
+}
+
+WebCookieJarImpl::~WebCookieJarImpl() {
+}
+
+void WebCookieJarImpl::setCookie(const blink::WebURL& url,
+                                 const blink::WebURL& first_party_for_cookies,
+                                 const blink::WebString& cookie) {
+  bool success;
+  store_->Set(url.string().utf8(), cookie.utf8(),
+              base::Bind(&CopyBool, &success));
+
+  // Wait to ensure the cookie was set before advancing. That way any
+  // subsequent URL request will see the changes to the cookie store.
+  //
+  // TODO(darin): Consider using associated message pipes for the CookieStore
+  // and URLLoader, so that we could let this method call run asynchronously
+  // without suffering an ordering problem. See crbug/386825.
+  //
+  store_.WaitForIncomingMethodCall();
+}
+
+blink::WebString WebCookieJarImpl::cookies(
+    const blink::WebURL& url,
+    const blink::WebURL& first_party_for_cookies) {
+  String result;
+  store_->Get(url.string().utf8(), base::Bind(&CopyString, &result));
+
+  // Wait for the result. Since every outbound request we make to the cookie
+  // store is followed up with WaitForIncomingMethodCall, we can be sure that
+  // the next incoming method call will be the response to our request.
+  store_.WaitForIncomingMethodCall();
+  if (!result)
+    return blink::WebString();
+
+  return blink::WebString::fromUTF8(result);
+}
+
+blink::WebString WebCookieJarImpl::cookieRequestHeaderFieldValue(
+    const blink::WebURL& url,
+    const blink::WebURL& first_party_for_cookies) {
+  return cookies(url, first_party_for_cookies);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webcookiejar_impl.h b/mojo/services/html_viewer/webcookiejar_impl.h
new file mode 100644
index 0000000..cfa0055
--- /dev/null
+++ b/mojo/services/html_viewer/webcookiejar_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
+
+#include "mojo/services/public/interfaces/network/cookie_store.mojom.h"
+#include "third_party/WebKit/public/platform/WebCookieJar.h"
+
+namespace mojo {
+
+class WebCookieJarImpl : public blink::WebCookieJar {
+ public:
+  explicit WebCookieJarImpl(CookieStorePtr store);
+  virtual ~WebCookieJarImpl();
+
+  // blink::WebCookieJar methods:
+  virtual void setCookie(
+      const blink::WebURL& url,
+      const blink::WebURL& first_party_for_cookies,
+      const blink::WebString& cookie);
+  virtual blink::WebString cookies(
+      const blink::WebURL& url,
+      const blink::WebURL& first_party_for_cookies);
+  virtual blink::WebString cookieRequestHeaderFieldValue(
+      const blink::WebURL& url,
+      const blink::WebURL& first_party_for_cookies);
+
+ private:
+  CookieStorePtr store_;
+  DISALLOW_COPY_AND_ASSIGN(WebCookieJarImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.cc b/mojo/services/html_viewer/weblayertreeview_impl.cc
new file mode 100644
index 0000000..56ebc34
--- /dev/null
+++ b/mojo/services/html_viewer/weblayertreeview_impl.cc
@@ -0,0 +1,243 @@
+// 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/html_viewer/weblayertreeview_impl.h"
+
+#include "base/message_loop/message_loop_proxy.h"
+#include "cc/blink/web_layer_impl.h"
+#include "cc/layers/layer.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/cc/context_provider_mojo.h"
+#include "mojo/cc/output_surface_mojo.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "third_party/WebKit/public/web/WebWidget.h"
+
+namespace mojo {
+
+WebLayerTreeViewImpl::WebLayerTreeViewImpl(
+    scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy,
+    SurfacesServicePtr surfaces_service,
+    GpuPtr gpu_service)
+    : widget_(NULL),
+      view_(NULL),
+      surfaces_service_(surfaces_service.Pass()),
+      gpu_service_(gpu_service.Pass()),
+      main_thread_compositor_task_runner_(base::MessageLoopProxy::current()),
+      weak_factory_(this) {
+  main_thread_bound_weak_ptr_ = weak_factory_.GetWeakPtr();
+  surfaces_service_->CreateSurfaceConnection(
+      base::Bind(&WebLayerTreeViewImpl::OnSurfaceConnectionCreated,
+                 main_thread_bound_weak_ptr_));
+
+  cc::LayerTreeSettings settings;
+
+  // For web contents, layer transforms should scale up the contents of layers
+  // to keep content always crisp when possible.
+  settings.layer_transforms_should_scale_layer_contents = true;
+
+  cc::SharedBitmapManager* shared_bitmap_manager = NULL;
+
+  layer_tree_host_ =
+      cc::LayerTreeHost::CreateThreaded(this,
+                                        shared_bitmap_manager,
+                                        settings,
+                                        base::MessageLoopProxy::current(),
+                                        compositor_message_loop_proxy);
+  DCHECK(layer_tree_host_);
+}
+
+WebLayerTreeViewImpl::~WebLayerTreeViewImpl() {
+}
+
+void WebLayerTreeViewImpl::WillBeginMainFrame(int frame_id) {
+}
+
+void WebLayerTreeViewImpl::DidBeginMainFrame() {
+}
+
+void WebLayerTreeViewImpl::BeginMainFrame(const cc::BeginFrameArgs& args) {
+  VLOG(2) << "WebLayerTreeViewImpl::BeginMainFrame";
+  double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
+  double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF();
+  double interval_sec = args.interval.InSecondsF();
+  blink::WebBeginFrameArgs web_begin_frame_args(
+      frame_time_sec, deadline_sec, interval_sec);
+  widget_->beginFrame(web_begin_frame_args);
+}
+
+void WebLayerTreeViewImpl::Layout() {
+  widget_->layout();
+}
+
+void WebLayerTreeViewImpl::ApplyViewportDeltas(
+    const gfx::Vector2d& inner_delta,
+    const gfx::Vector2d& outer_delta,
+    float page_scale,
+    float top_controls_delta) {
+  widget_->applyViewportDeltas(
+      inner_delta,
+      outer_delta,
+      page_scale,
+      top_controls_delta);
+}
+
+void WebLayerTreeViewImpl::ApplyViewportDeltas(
+    const gfx::Vector2d& scroll_delta,
+    float page_scale,
+    float top_controls_delta) {
+  widget_->applyViewportDeltas(scroll_delta, page_scale, top_controls_delta);
+}
+
+void WebLayerTreeViewImpl::RequestNewOutputSurface(bool fallback) {
+  layer_tree_host_->SetOutputSurface(output_surface_.Pass());
+}
+
+void WebLayerTreeViewImpl::DidInitializeOutputSurface() {
+}
+
+void WebLayerTreeViewImpl::WillCommit() {
+}
+
+void WebLayerTreeViewImpl::DidCommit() {
+  widget_->didCommitFrameToCompositor();
+}
+
+void WebLayerTreeViewImpl::DidCommitAndDrawFrame() {
+}
+
+void WebLayerTreeViewImpl::DidCompleteSwapBuffers() {
+}
+
+void WebLayerTreeViewImpl::setSurfaceReady() {
+}
+
+void WebLayerTreeViewImpl::setRootLayer(const blink::WebLayer& layer) {
+  layer_tree_host_->SetRootLayer(
+      static_cast<const cc_blink::WebLayerImpl*>(&layer)->layer());
+}
+
+void WebLayerTreeViewImpl::clearRootLayer() {
+  layer_tree_host_->SetRootLayer(scoped_refptr<cc::Layer>());
+}
+
+void WebLayerTreeViewImpl::setViewportSize(
+    const blink::WebSize& device_viewport_size) {
+  layer_tree_host_->SetViewportSize(device_viewport_size);
+}
+
+blink::WebSize WebLayerTreeViewImpl::deviceViewportSize() const {
+  return layer_tree_host_->device_viewport_size();
+}
+
+void WebLayerTreeViewImpl::setDeviceScaleFactor(float device_scale_factor) {
+  layer_tree_host_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float WebLayerTreeViewImpl::deviceScaleFactor() const {
+  return layer_tree_host_->device_scale_factor();
+}
+
+void WebLayerTreeViewImpl::setBackgroundColor(blink::WebColor color) {
+  layer_tree_host_->set_background_color(color);
+}
+
+void WebLayerTreeViewImpl::setHasTransparentBackground(
+    bool has_transparent_background) {
+  layer_tree_host_->set_has_transparent_background(has_transparent_background);
+}
+
+void WebLayerTreeViewImpl::setOverhangBitmap(const SkBitmap& bitmap) {
+  layer_tree_host_->SetOverhangBitmap(bitmap);
+}
+
+void WebLayerTreeViewImpl::setVisible(bool visible) {
+  layer_tree_host_->SetVisible(visible);
+}
+
+void WebLayerTreeViewImpl::setPageScaleFactorAndLimits(float page_scale_factor,
+                                                       float minimum,
+                                                       float maximum) {
+  layer_tree_host_->SetPageScaleFactorAndLimits(
+      page_scale_factor, minimum, maximum);
+}
+
+void WebLayerTreeViewImpl::registerForAnimations(blink::WebLayer* layer) {
+  cc::Layer* cc_layer = static_cast<cc_blink::WebLayerImpl*>(layer)->layer();
+  cc_layer->layer_animation_controller()->SetAnimationRegistrar(
+      layer_tree_host_->animation_registrar());
+}
+
+void WebLayerTreeViewImpl::registerViewportLayers(
+    const blink::WebLayer* pageScaleLayer,
+    const blink::WebLayer* innerViewportScrollLayer,
+    const blink::WebLayer* outerViewportScrollLayer) {
+  layer_tree_host_->RegisterViewportLayers(
+      static_cast<const cc_blink::WebLayerImpl*>(pageScaleLayer)->layer(),
+      static_cast<const cc_blink::WebLayerImpl*>(innerViewportScrollLayer)
+          ->layer(),
+      // The outer viewport layer will only exist when using pinch virtual
+      // viewports.
+      outerViewportScrollLayer ? static_cast<const cc_blink::WebLayerImpl*>(
+                                     outerViewportScrollLayer)->layer()
+                               : NULL);
+}
+
+void WebLayerTreeViewImpl::clearViewportLayers() {
+  layer_tree_host_->RegisterViewportLayers(scoped_refptr<cc::Layer>(),
+                                           scoped_refptr<cc::Layer>(),
+                                           scoped_refptr<cc::Layer>());
+}
+
+void WebLayerTreeViewImpl::startPageScaleAnimation(
+    const blink::WebPoint& destination,
+    bool use_anchor,
+    float new_page_scale,
+    double duration_sec) {
+  base::TimeDelta duration = base::TimeDelta::FromMicroseconds(
+      duration_sec * base::Time::kMicrosecondsPerSecond);
+  layer_tree_host_->StartPageScaleAnimation(
+      gfx::Vector2d(destination.x, destination.y),
+      use_anchor,
+      new_page_scale,
+      duration);
+}
+
+void WebLayerTreeViewImpl::setNeedsAnimate() {
+  layer_tree_host_->SetNeedsAnimate();
+}
+
+bool WebLayerTreeViewImpl::commitRequested() const {
+  return layer_tree_host_->CommitRequested();
+}
+
+void WebLayerTreeViewImpl::finishAllRendering() {
+  layer_tree_host_->FinishAllRendering();
+}
+
+void WebLayerTreeViewImpl::OnSurfaceConnectionCreated(SurfacePtr surface,
+                                                      uint32_t id_namespace) {
+  CommandBufferPtr cb;
+  gpu_service_->CreateOffscreenGLES2Context(Get(&cb));
+  scoped_refptr<cc::ContextProvider> context_provider(
+      new ContextProviderMojo(cb.PassMessagePipe()));
+  output_surface_.reset(new OutputSurfaceMojo(
+      this, context_provider, surface.Pass(), id_namespace));
+  layer_tree_host_->SetLayerTreeHostClientReady();
+}
+
+void WebLayerTreeViewImpl::DidCreateSurface(cc::SurfaceId id) {
+  main_thread_compositor_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&WebLayerTreeViewImpl::DidCreateSurfaceOnMainThread,
+                 main_thread_bound_weak_ptr_,
+                 id));
+}
+
+void WebLayerTreeViewImpl::DidCreateSurfaceOnMainThread(cc::SurfaceId id) {
+  view_->SetSurfaceId(SurfaceId::From(id));
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.h b/mojo/services/html_viewer/weblayertreeview_impl.h
new file mode 100644
index 0000000..5221902
--- /dev/null
+++ b/mojo/services/html_viewer/weblayertreeview_impl.h
@@ -0,0 +1,136 @@
+// 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_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/trees/layer_tree_host_client.h"
+#include "mojo/cc/output_surface_mojo.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebLayerTreeView.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace blink {
+class WebWidget;
+}
+
+namespace cc {
+class LayerTreeHost;
+}
+
+namespace mojo {
+class View;
+
+class WebLayerTreeViewImpl : public blink::WebLayerTreeView,
+                             public cc::LayerTreeHostClient,
+                             public OutputSurfaceMojoClient {
+ public:
+  WebLayerTreeViewImpl(
+      scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy,
+      SurfacesServicePtr surfaces_service,
+      GpuPtr gpu_service);
+  virtual ~WebLayerTreeViewImpl();
+
+  void set_widget(blink::WebWidget* widget) { widget_ = widget; }
+  void set_view(View* view) { view_ = view; }
+
+  // cc::LayerTreeHostClient implementation.
+  virtual void WillBeginMainFrame(int frame_id) override;
+  virtual void DidBeginMainFrame() override;
+  virtual void BeginMainFrame(const cc::BeginFrameArgs& args) override;
+  virtual void Layout() override;
+  virtual void ApplyViewportDeltas(const gfx::Vector2d& inner_delta,
+                                   const gfx::Vector2d& outer_delta,
+                                   float page_scale,
+                                   float top_controls_delta) override;
+  virtual void ApplyViewportDeltas(const gfx::Vector2d& scroll_delta,
+                                   float page_scale,
+                                   float top_controls_delta) override;
+  virtual void RequestNewOutputSurface(bool fallback) override;
+  virtual void DidInitializeOutputSurface() override;
+  virtual void WillCommit() override;
+  virtual void DidCommit() override;
+  virtual void DidCommitAndDrawFrame() override;
+  virtual void DidCompleteSwapBuffers() override;
+  virtual void RateLimitSharedMainThreadContext() override {}
+
+  // blink::WebLayerTreeView implementation.
+  virtual void setSurfaceReady() override;
+  virtual void setRootLayer(const blink::WebLayer& layer) override;
+  virtual void clearRootLayer() override;
+  virtual void setViewportSize(
+      const blink::WebSize& device_viewport_size) override;
+  virtual blink::WebSize deviceViewportSize() const override;
+  virtual void setDeviceScaleFactor(float) override;
+  virtual float deviceScaleFactor() const override;
+  virtual void setBackgroundColor(blink::WebColor color) override;
+  virtual void setHasTransparentBackground(
+      bool has_transparent_background) override;
+  virtual void setOverhangBitmap(const SkBitmap& bitmap) override;
+  virtual void setVisible(bool visible) override;
+  virtual void setPageScaleFactorAndLimits(float page_scale_factor,
+                                           float minimum,
+                                           float maximum) override;
+  virtual void startPageScaleAnimation(const blink::WebPoint& destination,
+                                       bool use_anchor,
+                                       float new_page_scale,
+                                       double duration_sec) override;
+  virtual void heuristicsForGpuRasterizationUpdated(bool matches_heuristic) {}
+  virtual void setTopControlsContentOffset(float offset) {}
+  virtual void setNeedsAnimate() override;
+  virtual bool commitRequested() const override;
+  virtual void didStopFlinging() {}
+  virtual void compositeAndReadbackAsync(
+      blink::WebCompositeAndReadbackAsyncCallback* callback) {}
+  virtual void finishAllRendering() override;
+  virtual void setDeferCommits(bool defer_commits) {}
+  virtual void registerForAnimations(blink::WebLayer* layer) override;
+  virtual void registerViewportLayers(
+      const blink::WebLayer* page_scale_layer,
+      const blink::WebLayer* inner_viewport_scroll_layer,
+      const blink::WebLayer* outer_viewport_scroll_layer) override;
+  virtual void clearViewportLayers() override;
+  virtual void registerSelection(const blink::WebSelectionBound& start,
+                                 const blink::WebSelectionBound& end) {}
+  virtual void clearSelection() {}
+  virtual void setShowFPSCounter(bool) {}
+  virtual void setShowPaintRects(bool) {}
+  virtual void setShowDebugBorders(bool) {}
+  virtual void setContinuousPaintingEnabled(bool) {}
+  virtual void setShowScrollBottleneckRects(bool) {}
+
+  // OutputSurfaceMojoClient implementation.
+  virtual void DidCreateSurface(cc::SurfaceId id) override;
+
+ private:
+  void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  void DidCreateSurfaceOnMainThread(cc::SurfaceId id);
+
+  // widget_ and view_ will outlive us.
+  blink::WebWidget* widget_;
+  View* view_;
+  scoped_ptr<cc::LayerTreeHost> layer_tree_host_;
+  SurfacesServicePtr surfaces_service_;
+  scoped_ptr<cc::OutputSurface> output_surface_;
+  GpuPtr gpu_service_;
+  scoped_refptr<base::SingleThreadTaskRunner>
+      main_thread_compositor_task_runner_;
+  base::WeakPtr<WebLayerTreeViewImpl> main_thread_bound_weak_ptr_;
+
+  base::WeakPtrFactory<WebLayerTreeViewImpl> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(WebLayerTreeViewImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.cc b/mojo/services/html_viewer/webmediaplayer_factory.cc
new file mode 100644
index 0000000..bbf8c81
--- /dev/null
+++ b/mojo/services/html_viewer/webmediaplayer_factory.cc
@@ -0,0 +1,87 @@
+// 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/html_viewer/webmediaplayer_factory.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/threading/thread.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/null_audio_sink.h"
+#include "media/base/audio_hardware_config.h"
+#include "media/base/media.h"
+#include "media/base/media_log.h"
+#include "media/blink/null_encrypted_media_player_support.h"
+#include "media/blink/webmediaplayer_impl.h"
+#include "media/blink/webmediaplayer_params.h"
+#include "media/filters/gpu_video_accelerator_factories.h"
+
+namespace mojo {
+
+WebMediaPlayerFactory::WebMediaPlayerFactory(
+    const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner)
+    : compositor_task_runner_(compositor_task_runner),
+      media_thread_("Media"),
+      audio_manager_(media::AudioManager::Create(&fake_audio_log_factory_)),
+      audio_hardware_config_(
+          audio_manager_->GetInputStreamParameters(
+              media::AudioManagerBase::kDefaultDeviceId),
+          audio_manager_->GetDefaultOutputStreamParameters()) {
+
+  if (!media::IsMediaLibraryInitialized()) {
+    base::FilePath module_dir;
+    CHECK(PathService::Get(base::DIR_EXE, &module_dir));
+    CHECK(media::InitializeMediaLibrary(module_dir));
+  }
+}
+
+WebMediaPlayerFactory::~WebMediaPlayerFactory() {
+}
+
+blink::WebMediaPlayer* WebMediaPlayerFactory::CreateMediaPlayer(
+    blink::WebLocalFrame* frame,
+    const blink::WebURL& url,
+    blink::WebMediaPlayerClient* client) {
+#if defined(OS_ANDROID)
+  return NULL;
+#else
+
+  media::WebMediaPlayerParams params(
+      media::WebMediaPlayerParams::DeferLoadCB(),
+      CreateAudioRendererSink(),
+      GetAudioHardwareConfig(),
+      new media::MediaLog(),
+      scoped_refptr<media::GpuVideoAcceleratorFactories>(),
+      GetMediaThreadTaskRunner(),
+      compositor_task_runner_,
+      base::Bind(&media::NullEncryptedMediaPlayerSupport::Create),
+      NULL);
+  base::WeakPtr<media::WebMediaPlayerDelegate> delegate;
+
+  return new media::WebMediaPlayerImpl(frame, client, delegate, params);
+#endif
+}
+
+const media::AudioHardwareConfig&
+WebMediaPlayerFactory::GetAudioHardwareConfig() {
+  return audio_hardware_config_;
+}
+
+scoped_refptr<media::AudioRendererSink>
+WebMediaPlayerFactory::CreateAudioRendererSink() {
+  // TODO(acolwell): Replace this with an AudioRendererSink implementation
+  // that actually talks to the audio device or an audio mojo service.
+  return new media::NullAudioSink(GetMediaThreadTaskRunner());
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WebMediaPlayerFactory::GetMediaThreadTaskRunner() {
+  if (!media_thread_.IsRunning())
+    media_thread_.Start();
+
+  return media_thread_.message_loop_proxy();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.h b/mojo/services/html_viewer/webmediaplayer_factory.h
new file mode 100644
index 0000000..db85370
--- /dev/null
+++ b/mojo/services/html_viewer/webmediaplayer_factory.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 MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/base/audio_hardware_config.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
+class WebMediaPlayer;
+class WebLocalFrame;
+class WebURL;
+class WebMediaPlayerClient;
+}
+
+namespace media {
+class AudioManager;
+class AudioRendererSink;
+}
+
+namespace mojo {
+
+// Helper class used to create blink::WebMediaPlayer objects.
+// This class stores the "global state" shared across all WebMediaPlayer
+// instances.
+class WebMediaPlayerFactory {
+ public:
+  explicit WebMediaPlayerFactory(const scoped_refptr<
+      base::SingleThreadTaskRunner>& compositor_task_runner);
+  ~WebMediaPlayerFactory();
+
+  blink::WebMediaPlayer* CreateMediaPlayer(blink::WebLocalFrame* frame,
+                                           const blink::WebURL& url,
+                                           blink::WebMediaPlayerClient* client);
+
+ private:
+  const media::AudioHardwareConfig& GetAudioHardwareConfig();
+  scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink();
+  scoped_refptr<base::SingleThreadTaskRunner> GetMediaThreadTaskRunner();
+
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+  base::Thread media_thread_;
+  media::FakeAudioLogFactory fake_audio_log_factory_;
+  scoped_ptr<media::AudioManager> audio_manager_;
+  media::AudioHardwareConfig audio_hardware_config_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerFactory);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.cc b/mojo/services/html_viewer/webmimeregistry_impl.cc
new file mode 100644
index 0000000..2faac17
--- /dev/null
+++ b/mojo/services/html_viewer/webmimeregistry_impl.cc
@@ -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.
+
+#include "mojo/services/html_viewer/webmimeregistry_impl.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace mojo {
+namespace {
+
+std::string ToASCIIOrEmpty(const blink::WebString& string) {
+  return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
+                                     : std::string();
+}
+
+}  // namespace
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMIMEType(
+    const blink::WebString& mime_type) {
+  return net::IsSupportedMimeType(ToASCIIOrEmpty(mime_type)) ?
+      blink::WebMimeRegistry::IsSupported :
+      blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsImageMIMEType(
+    const blink::WebString& mime_type) {
+  return net::IsSupportedImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+      blink::WebMimeRegistry::IsSupported :
+      blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType
+WebMimeRegistryImpl::supportsImagePrefixedMIMEType(
+    const blink::WebString& mime_type) {
+  std::string ascii_mime_type = ToASCIIOrEmpty(mime_type);
+  return (net::IsSupportedImageMimeType(ascii_mime_type) ||
+          (StartsWithASCII(ascii_mime_type, "image/", true) &&
+           net::IsSupportedNonImageMimeType(ascii_mime_type)))
+             ? WebMimeRegistry::IsSupported
+             : WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType
+    WebMimeRegistryImpl::supportsJavaScriptMIMEType(
+    const blink::WebString& mime_type) {
+  return net::IsSupportedJavascriptMimeType(ToASCIIOrEmpty(mime_type)) ?
+      blink::WebMimeRegistry::IsSupported :
+      blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMediaMIMEType(
+    const blink::WebString& mime_type,
+    const blink::WebString& codecs,
+    const blink::WebString& key_system) {
+  const std::string mime_type_ascii = ToASCIIOrEmpty(mime_type);
+  // Not supporting the container is a flat-out no.
+  if (!net::IsSupportedMediaMimeType(mime_type_ascii))
+    return IsNotSupported;
+
+  // Mojo does not currently support any key systems.
+  if (!key_system.isEmpty())
+    return IsNotSupported;
+
+  // Check list of strict codecs to see if it is supported.
+  if (net::IsStrictMediaMimeType(mime_type_ascii)) {
+    // Check if the codecs are a perfect match.
+    std::vector<std::string> strict_codecs;
+    net::ParseCodecString(ToASCIIOrEmpty(codecs), &strict_codecs, false);
+    return static_cast<WebMimeRegistry::SupportsType>(
+        net::IsSupportedStrictMediaMimeType(mime_type_ascii, strict_codecs));
+  }
+
+  // If we don't recognize the codec, it's possible we support it.
+  std::vector<std::string> parsed_codecs;
+  net::ParseCodecString(ToASCIIOrEmpty(codecs), &parsed_codecs, true);
+  if (!net::AreSupportedMediaCodecs(parsed_codecs))
+    return MayBeSupported;
+
+  // Otherwise we have a perfect match.
+  return IsSupported;
+}
+
+bool WebMimeRegistryImpl::supportsMediaSourceMIMEType(
+    const blink::WebString& mime_type,
+    const blink::WebString& codecs) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebMimeRegistryImpl::supportsEncryptedMediaMIMEType(
+    const blink::WebString& key_system,
+    const blink::WebString& mime_type,
+    const blink::WebString& codecs) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+blink::WebMimeRegistry::SupportsType
+    WebMimeRegistryImpl::supportsNonImageMIMEType(
+    const blink::WebString& mime_type) {
+  return net::IsSupportedNonImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+      blink::WebMimeRegistry::IsSupported :
+      blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeForExtension(
+    const blink::WebString& file_extension) {
+  NOTIMPLEMENTED();
+  return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::wellKnownMimeTypeForExtension(
+    const blink::WebString& file_extension) {
+  NOTIMPLEMENTED();
+  return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeFromFile(
+    const blink::WebString& file_path) {
+  NOTIMPLEMENTED();
+  return blink::WebString();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.h b/mojo/services/html_viewer/webmimeregistry_impl.h
new file mode 100644
index 0000000..ba25ec9
--- /dev/null
+++ b/mojo/services/html_viewer/webmimeregistry_impl.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 MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "third_party/WebKit/public/platform/WebMimeRegistry.h"
+
+namespace mojo {
+
+class WebMimeRegistryImpl : public blink::WebMimeRegistry {
+ public:
+  WebMimeRegistryImpl() {}
+  virtual ~WebMimeRegistryImpl() {}
+
+  // WebMimeRegistry methods:
+  virtual blink::WebMimeRegistry::SupportsType supportsMIMEType(
+      const blink::WebString& mime_type);
+  virtual blink::WebMimeRegistry::SupportsType supportsImageMIMEType(
+      const blink::WebString& mime_type);
+  virtual blink::WebMimeRegistry::SupportsType supportsImagePrefixedMIMEType(
+      const blink::WebString& mime_type);
+  virtual blink::WebMimeRegistry::SupportsType supportsJavaScriptMIMEType(
+      const blink::WebString& mime_type);
+  virtual blink::WebMimeRegistry::SupportsType supportsMediaMIMEType(
+      const blink::WebString& mime_type,
+      const blink::WebString& codecs,
+      const blink::WebString& key_system);
+  virtual bool supportsMediaSourceMIMEType(
+      const blink::WebString& mime_type,
+      const blink::WebString& codecs);
+  virtual bool supportsEncryptedMediaMIMEType(
+      const blink::WebString& key_system,
+      const blink::WebString& mime_type,
+      const blink::WebString& codecs);
+  virtual blink::WebMimeRegistry::SupportsType supportsNonImageMIMEType(
+      const blink::WebString& mime_type);
+  virtual blink::WebString mimeTypeForExtension(
+      const blink::WebString& extension);
+  virtual blink::WebString wellKnownMimeTypeForExtension(
+      const blink::WebString& extension);
+  virtual blink::WebString mimeTypeFromFile(
+      const blink::WebString& path);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
diff --git a/mojo/services/html_viewer/websockethandle_impl.cc b/mojo/services/html_viewer/websockethandle_impl.cc
new file mode 100644
index 0000000..b927b10
--- /dev/null
+++ b/mojo/services/html_viewer/websockethandle_impl.cc
@@ -0,0 +1,218 @@
+// 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/html_viewer/websockethandle_impl.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/services/html_viewer/blink_basic_type_converters.h"
+#include "mojo/services/public/cpp/network/web_socket_read_queue.h"
+#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
+#include "third_party/WebKit/public/platform/WebSocketHandleClient.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+
+using blink::WebSerializedOrigin;
+using blink::WebSocketHandle;
+using blink::WebSocketHandleClient;
+using blink::WebString;
+using blink::WebURL;
+using blink::WebVector;
+
+namespace mojo {
+
+template<>
+struct TypeConverter<WebSocket::MessageType, WebSocketHandle::MessageType> {
+  static WebSocket::MessageType Convert(WebSocketHandle::MessageType type) {
+    DCHECK(type == WebSocketHandle::MessageTypeContinuation ||
+           type == WebSocketHandle::MessageTypeText ||
+           type == WebSocketHandle::MessageTypeBinary);
+    typedef WebSocket::MessageType MessageType;
+    COMPILE_ASSERT(
+        static_cast<MessageType>(WebSocketHandle::MessageTypeContinuation) ==
+            WebSocket::MESSAGE_TYPE_CONTINUATION,
+        enum_values_must_match_for_message_type);
+    COMPILE_ASSERT(
+        static_cast<MessageType>(WebSocketHandle::MessageTypeText) ==
+            WebSocket::MESSAGE_TYPE_TEXT,
+        enum_values_must_match_for_message_type);
+    COMPILE_ASSERT(
+        static_cast<MessageType>(WebSocketHandle::MessageTypeBinary) ==
+            WebSocket::MESSAGE_TYPE_BINARY,
+        enum_values_must_match_for_message_type);
+    return static_cast<WebSocket::MessageType>(type);
+  }
+};
+
+template<>
+struct TypeConverter<WebSocketHandle::MessageType, WebSocket::MessageType> {
+  static WebSocketHandle::MessageType Convert(WebSocket::MessageType type) {
+    DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION ||
+           type == WebSocket::MESSAGE_TYPE_TEXT ||
+           type == WebSocket::MESSAGE_TYPE_BINARY);
+    return static_cast<WebSocketHandle::MessageType>(type);
+  }
+};
+
+// This class forms a bridge from the mojo WebSocketClient interface and the
+// Blink WebSocketHandleClient interface.
+class WebSocketClientImpl : public InterfaceImpl<WebSocketClient> {
+ public:
+  explicit WebSocketClientImpl(WebSocketHandleImpl* handle,
+                               blink::WebSocketHandleClient* client)
+      : handle_(handle), client_(client) {}
+  virtual ~WebSocketClientImpl() {}
+
+ private:
+  // WebSocketClient methods:
+  virtual void DidConnect(bool fail,
+                          const String& selected_subprotocol,
+                          const String& extensions,
+                          ScopedDataPipeConsumerHandle receive_stream)
+      override {
+    blink::WebSocketHandleClient* client = client_;
+    WebSocketHandleImpl* handle = handle_;
+    receive_stream_ = receive_stream.Pass();
+    read_queue_.reset(new WebSocketReadQueue(receive_stream_.get()));
+    if (fail)
+      handle->Disconnect();  // deletes |this|
+    client->didConnect(handle,
+                       fail,
+                       selected_subprotocol.To<WebString>(),
+                       extensions.To<WebString>());
+    // |handle| can be deleted here.
+  }
+
+  virtual void DidReceiveData(bool fin,
+                              WebSocket::MessageType type,
+                              uint32_t num_bytes) override {
+    read_queue_->Read(num_bytes,
+                      base::Bind(&WebSocketClientImpl::DidReadFromReceiveStream,
+                                 base::Unretained(this),
+                                 fin, type, num_bytes));
+  }
+
+  virtual void DidReceiveFlowControl(int64_t quota) override {
+    client_->didReceiveFlowControl(handle_, quota);
+    // |handle| can be deleted here.
+  }
+
+  virtual void DidFail(const String& message) override {
+    blink::WebSocketHandleClient* client = client_;
+    WebSocketHandleImpl* handle = handle_;
+    handle->Disconnect();  // deletes |this|
+    client->didFail(handle, message.To<WebString>());
+    // |handle| can be deleted here.
+  }
+
+  virtual void DidClose(bool was_clean,
+                        uint16_t code,
+                        const String& reason) override {
+    blink::WebSocketHandleClient* client = client_;
+    WebSocketHandleImpl* handle = handle_;
+    handle->Disconnect();  // deletes |this|
+    client->didClose(handle, was_clean, code, reason.To<WebString>());
+    // |handle| can be deleted here.
+  }
+
+  void DidReadFromReceiveStream(bool fin,
+                                WebSocket::MessageType type,
+                                uint32_t num_bytes,
+                                const char* data) {
+    client_->didReceiveData(handle_,
+                            fin,
+                            ConvertTo<WebSocketHandle::MessageType>(type),
+                            data,
+                            num_bytes);
+    // |handle_| can be deleted here.
+  }
+
+  // |handle_| owns this object, so it is guaranteed to outlive us.
+  WebSocketHandleImpl* handle_;
+  blink::WebSocketHandleClient* client_;
+  ScopedDataPipeConsumerHandle receive_stream_;
+  scoped_ptr<WebSocketReadQueue> read_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketClientImpl);
+};
+
+WebSocketHandleImpl::WebSocketHandleImpl(NetworkService* network_service)
+    : did_close_(false) {
+  network_service->CreateWebSocket(Get(&web_socket_));
+}
+
+WebSocketHandleImpl::~WebSocketHandleImpl() {
+  if (!did_close_) {
+    // The connection is abruptly disconnected by the renderer without
+    // closing handshake.
+    web_socket_->Close(WebSocket::kAbnormalCloseCode, String());
+  }
+}
+
+void WebSocketHandleImpl::connect(const WebURL& url,
+                                  const WebVector<WebString>& protocols,
+                                  const WebSerializedOrigin& origin,
+                                  WebSocketHandleClient* client) {
+  client_.reset(new WebSocketClientImpl(this, client));
+  WebSocketClientPtr client_ptr;
+  // TODO(mpcomplete): Is this the right ownership model? Or should mojo own
+  // |client_|?
+  WeakBindToProxy(client_.get(), &client_ptr);
+
+  DataPipe data_pipe;
+  send_stream_ = data_pipe.producer_handle.Pass();
+  write_queue_.reset(new WebSocketWriteQueue(send_stream_.get()));
+  web_socket_->Connect(url.string().utf8(),
+                       Array<String>::From(protocols),
+                       origin.string().utf8(),
+                       data_pipe.consumer_handle.Pass(),
+                       client_ptr.Pass());
+}
+
+void WebSocketHandleImpl::send(bool fin,
+                               WebSocketHandle::MessageType type,
+                               const char* data,
+                               size_t size) {
+  if (!client_)
+    return;
+
+  uint32_t size32 = static_cast<uint32_t>(size);
+  write_queue_->Write(
+      data, size32,
+      base::Bind(&WebSocketHandleImpl::DidWriteToSendStream,
+                 base::Unretained(this),
+                 fin, type, size32));
+}
+
+void WebSocketHandleImpl::flowControl(int64_t quota) {
+  if (!client_)
+    return;
+
+  web_socket_->FlowControl(quota);
+}
+
+void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) {
+  web_socket_->Close(code, reason.utf8());
+}
+
+void WebSocketHandleImpl::DidWriteToSendStream(
+    bool fin,
+    WebSocketHandle::MessageType type,
+    uint32_t num_bytes,
+    const char* data) {
+  web_socket_->Send(fin, ConvertTo<WebSocket::MessageType>(type), num_bytes);
+}
+
+void WebSocketHandleImpl::Disconnect() {
+  did_close_ = true;
+  client_.reset();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/websockethandle_impl.h b/mojo/services/html_viewer/websockethandle_impl.h
new file mode 100644
index 0000000..f035dd9
--- /dev/null
+++ b/mojo/services/html_viewer/websockethandle_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 MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/public/interfaces/network/web_socket.mojom.h"
+#include "third_party/WebKit/public/platform/WebSocketHandle.h"
+
+namespace mojo {
+class NetworkService;
+class WebSocketClientImpl;
+class WebSocketWriteQueue;
+
+// Implements WebSocketHandle by talking to the mojo WebSocket interface.
+class WebSocketHandleImpl : public blink::WebSocketHandle {
+ public:
+  explicit WebSocketHandleImpl(NetworkService* network_service);
+
+ private:
+  friend class WebSocketClientImpl;
+
+  virtual ~WebSocketHandleImpl();
+
+  // blink::WebSocketHandle methods:
+  virtual void connect(const blink::WebURL& url,
+                       const blink::WebVector<blink::WebString>& protocols,
+                       const blink::WebSerializedOrigin& origin,
+                       blink::WebSocketHandleClient*) override;
+  virtual void send(bool fin,
+                    MessageType,
+                    const char* data,
+                    size_t size) override;
+  virtual void flowControl(int64_t quota) override;
+  virtual void close(unsigned short code,
+                     const blink::WebString& reason) override;
+
+  // Called when we finished writing to |send_stream_|.
+  void DidWriteToSendStream(bool fin,
+                            WebSocketHandle::MessageType type,
+                            uint32_t num_bytes,
+                            const char* data);
+
+  // Called when the socket is closed.
+  void Disconnect();
+
+  WebSocketPtr web_socket_;
+  scoped_ptr<WebSocketClientImpl> client_;
+  ScopedDataPipeProducerHandle send_stream_;
+  scoped_ptr<WebSocketWriteQueue> write_queue_;
+  // True if close() was called.
+  bool did_close_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHandleImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.cc b/mojo/services/html_viewer/webstoragenamespace_impl.cc
new file mode 100644
index 0000000..ea1f0eb
--- /dev/null
+++ b/mojo/services/html_viewer/webstoragenamespace_impl.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 "mojo/services/html_viewer/webstoragenamespace_impl.h"
+
+#include <stdio.h>
+
+#include "third_party/WebKit/public/platform/WebStorageArea.h"
+
+namespace mojo {
+namespace {
+
+class DummyWebStorageAreaImpl : public blink::WebStorageArea {
+ public:
+  virtual unsigned length() {
+    return 0;
+  }
+  virtual blink::WebString key(unsigned index) {
+    return blink::WebString();
+  }
+  virtual blink::WebString getItem(const blink::WebString& key) {
+    return blink::WebString();
+  }
+};
+
+}  // namespace
+
+WebStorageNamespaceImpl::WebStorageNamespaceImpl() {
+}
+
+WebStorageNamespaceImpl::~WebStorageNamespaceImpl() {
+}
+
+blink::WebStorageArea* WebStorageNamespaceImpl::createStorageArea(
+    const blink::WebString& origin) {
+  return new DummyWebStorageAreaImpl();
+}
+
+bool WebStorageNamespaceImpl::isSameNamespace(
+    const blink::WebStorageNamespace&) const {
+  return false;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.h b/mojo/services/html_viewer/webstoragenamespace_impl.h
new file mode 100644
index 0000000..7e11069
--- /dev/null
+++ b/mojo/services/html_viewer/webstoragenamespace_impl.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_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
+
+#include "base/macros.h"
+#include "third_party/WebKit/public/platform/WebStorageNamespace.h"
+
+namespace mojo {
+
+class WebStorageNamespaceImpl : public blink::WebStorageNamespace {
+ public:
+  WebStorageNamespaceImpl();
+
+ private:
+  virtual ~WebStorageNamespaceImpl();
+
+  // blink::WebStorageNamespace methods:
+  virtual blink::WebStorageArea* createStorageArea(
+      const blink::WebString& origin);
+  virtual bool isSameNamespace(const blink::WebStorageNamespace&) const;
+
+  DISALLOW_COPY_AND_ASSIGN(WebStorageNamespaceImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthemeengine_impl.cc b/mojo/services/html_viewer/webthemeengine_impl.cc
new file mode 100644
index 0000000..24906e5
--- /dev/null
+++ b/mojo/services/html_viewer/webthemeengine_impl.cc
@@ -0,0 +1,202 @@
+// 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/html_viewer/webthemeengine_impl.h"
+
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/WebKit/public/platform/WebSize.h"
+#include "ui/native_theme/native_theme.h"
+
+using blink::WebCanvas;
+using blink::WebColor;
+using blink::WebRect;
+using blink::WebThemeEngine;
+
+namespace mojo {
+
+static ui::NativeTheme::Part NativeThemePart(
+    WebThemeEngine::Part part) {
+  switch (part) {
+    case WebThemeEngine::PartScrollbarDownArrow:
+      return ui::NativeTheme::kScrollbarDownArrow;
+    case WebThemeEngine::PartScrollbarLeftArrow:
+      return ui::NativeTheme::kScrollbarLeftArrow;
+    case WebThemeEngine::PartScrollbarRightArrow:
+      return ui::NativeTheme::kScrollbarRightArrow;
+    case WebThemeEngine::PartScrollbarUpArrow:
+      return ui::NativeTheme::kScrollbarUpArrow;
+    case WebThemeEngine::PartScrollbarHorizontalThumb:
+      return ui::NativeTheme::kScrollbarHorizontalThumb;
+    case WebThemeEngine::PartScrollbarVerticalThumb:
+      return ui::NativeTheme::kScrollbarVerticalThumb;
+    case WebThemeEngine::PartScrollbarHorizontalTrack:
+      return ui::NativeTheme::kScrollbarHorizontalTrack;
+    case WebThemeEngine::PartScrollbarVerticalTrack:
+      return ui::NativeTheme::kScrollbarVerticalTrack;
+    case WebThemeEngine::PartScrollbarCorner:
+      return ui::NativeTheme::kScrollbarCorner;
+    case WebThemeEngine::PartCheckbox:
+      return ui::NativeTheme::kCheckbox;
+    case WebThemeEngine::PartRadio:
+      return ui::NativeTheme::kRadio;
+    case WebThemeEngine::PartButton:
+      return ui::NativeTheme::kPushButton;
+    case WebThemeEngine::PartTextField:
+      return ui::NativeTheme::kTextField;
+    case WebThemeEngine::PartMenuList:
+      return ui::NativeTheme::kMenuList;
+    case WebThemeEngine::PartSliderTrack:
+      return ui::NativeTheme::kSliderTrack;
+    case WebThemeEngine::PartSliderThumb:
+      return ui::NativeTheme::kSliderThumb;
+    case WebThemeEngine::PartInnerSpinButton:
+      return ui::NativeTheme::kInnerSpinButton;
+    case WebThemeEngine::PartProgressBar:
+      return ui::NativeTheme::kProgressBar;
+    default:
+      return ui::NativeTheme::kScrollbarDownArrow;
+  }
+}
+
+static ui::NativeTheme::State NativeThemeState(
+    WebThemeEngine::State state) {
+  switch (state) {
+    case WebThemeEngine::StateDisabled:
+      return ui::NativeTheme::kDisabled;
+    case WebThemeEngine::StateHover:
+      return ui::NativeTheme::kHovered;
+    case WebThemeEngine::StateNormal:
+      return ui::NativeTheme::kNormal;
+    case WebThemeEngine::StatePressed:
+      return ui::NativeTheme::kPressed;
+    default:
+      return ui::NativeTheme::kDisabled;
+  }
+}
+
+static void GetNativeThemeExtraParams(
+    WebThemeEngine::Part part,
+    WebThemeEngine::State state,
+    const WebThemeEngine::ExtraParams* extra_params,
+    ui::NativeTheme::ExtraParams* native_theme_extra_params) {
+  switch (part) {
+    case WebThemeEngine::PartScrollbarHorizontalTrack:
+    case WebThemeEngine::PartScrollbarVerticalTrack:
+      native_theme_extra_params->scrollbar_track.track_x =
+          extra_params->scrollbarTrack.trackX;
+      native_theme_extra_params->scrollbar_track.track_y =
+          extra_params->scrollbarTrack.trackY;
+      native_theme_extra_params->scrollbar_track.track_width =
+          extra_params->scrollbarTrack.trackWidth;
+      native_theme_extra_params->scrollbar_track.track_height =
+          extra_params->scrollbarTrack.trackHeight;
+      break;
+    case WebThemeEngine::PartCheckbox:
+      native_theme_extra_params->button.checked = extra_params->button.checked;
+      native_theme_extra_params->button.indeterminate =
+          extra_params->button.indeterminate;
+      break;
+    case WebThemeEngine::PartRadio:
+      native_theme_extra_params->button.checked = extra_params->button.checked;
+      break;
+    case WebThemeEngine::PartButton:
+      native_theme_extra_params->button.is_default =
+          extra_params->button.isDefault;
+      native_theme_extra_params->button.has_border =
+          extra_params->button.hasBorder;
+      // Native buttons have a different focus style.
+      native_theme_extra_params->button.is_focused = false;
+      native_theme_extra_params->button.background_color =
+          extra_params->button.backgroundColor;
+      break;
+    case WebThemeEngine::PartTextField:
+      native_theme_extra_params->text_field.is_text_area =
+          extra_params->textField.isTextArea;
+      native_theme_extra_params->text_field.is_listbox =
+          extra_params->textField.isListbox;
+      native_theme_extra_params->text_field.background_color =
+          extra_params->textField.backgroundColor;
+      break;
+    case WebThemeEngine::PartMenuList:
+      native_theme_extra_params->menu_list.has_border =
+          extra_params->menuList.hasBorder;
+      native_theme_extra_params->menu_list.has_border_radius =
+          extra_params->menuList.hasBorderRadius;
+      native_theme_extra_params->menu_list.arrow_x =
+          extra_params->menuList.arrowX;
+      native_theme_extra_params->menu_list.arrow_y =
+          extra_params->menuList.arrowY;
+      native_theme_extra_params->menu_list.background_color =
+          extra_params->menuList.backgroundColor;
+      break;
+    case WebThemeEngine::PartSliderTrack:
+    case WebThemeEngine::PartSliderThumb:
+      native_theme_extra_params->slider.vertical =
+          extra_params->slider.vertical;
+      native_theme_extra_params->slider.in_drag = extra_params->slider.inDrag;
+      break;
+    case WebThemeEngine::PartInnerSpinButton:
+      native_theme_extra_params->inner_spin.spin_up =
+          extra_params->innerSpin.spinUp;
+      native_theme_extra_params->inner_spin.read_only =
+          extra_params->innerSpin.readOnly;
+      break;
+    case WebThemeEngine::PartProgressBar:
+      native_theme_extra_params->progress_bar.determinate =
+          extra_params->progressBar.determinate;
+      native_theme_extra_params->progress_bar.value_rect_x =
+          extra_params->progressBar.valueRectX;
+      native_theme_extra_params->progress_bar.value_rect_y =
+          extra_params->progressBar.valueRectY;
+      native_theme_extra_params->progress_bar.value_rect_width =
+          extra_params->progressBar.valueRectWidth;
+      native_theme_extra_params->progress_bar.value_rect_height =
+          extra_params->progressBar.valueRectHeight;
+      break;
+    default:
+      break;  // Parts that have no extra params get here.
+  }
+}
+
+blink::WebSize WebThemeEngineImpl::getSize(WebThemeEngine::Part part) {
+  ui::NativeTheme::ExtraParams extra;
+  return ui::NativeTheme::instance()->GetPartSize(NativeThemePart(part),
+                                                   ui::NativeTheme::kNormal,
+                                                   extra);
+}
+
+void WebThemeEngineImpl::paint(
+    blink::WebCanvas* canvas,
+    WebThemeEngine::Part part,
+    WebThemeEngine::State state,
+    const blink::WebRect& rect,
+    const WebThemeEngine::ExtraParams* extra_params) {
+  ui::NativeTheme::ExtraParams native_theme_extra_params;
+  GetNativeThemeExtraParams(
+      part, state, extra_params, &native_theme_extra_params);
+  ui::NativeTheme::instance()->Paint(
+      canvas,
+      NativeThemePart(part),
+      NativeThemeState(state),
+      gfx::Rect(rect),
+      native_theme_extra_params);
+}
+
+void WebThemeEngineImpl::paintStateTransition(blink::WebCanvas* canvas,
+                                              WebThemeEngine::Part part,
+                                              WebThemeEngine::State startState,
+                                              WebThemeEngine::State endState,
+                                              double progress,
+                                              const blink::WebRect& rect) {
+  ui::NativeTheme::instance()->PaintStateTransition(
+      canvas,
+      NativeThemePart(part),
+      NativeThemeState(startState),
+      NativeThemeState(endState),
+      progress,
+      gfx::Rect(rect));
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webthemeengine_impl.h b/mojo/services/html_viewer/webthemeengine_impl.h
new file mode 100644
index 0000000..a3eda9e
--- /dev/null
+++ b/mojo/services/html_viewer/webthemeengine_impl.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 MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
+
+#include "third_party/WebKit/public/platform/WebThemeEngine.h"
+
+namespace mojo {
+
+class WebThemeEngineImpl : public blink::WebThemeEngine {
+ public:
+  // WebThemeEngine methods:
+  virtual blink::WebSize getSize(blink::WebThemeEngine::Part);
+  virtual void paint(
+      blink::WebCanvas* canvas,
+      blink::WebThemeEngine::Part part,
+      blink::WebThemeEngine::State state,
+      const blink::WebRect& rect,
+      const blink::WebThemeEngine::ExtraParams* extra_params);
+  virtual void paintStateTransition(blink::WebCanvas* canvas,
+                                    blink::WebThemeEngine::Part part,
+                                    blink::WebThemeEngine::State startState,
+                                    blink::WebThemeEngine::State endState,
+                                    double progress,
+                                    const blink::WebRect& rect);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthread_impl.cc b/mojo/services/html_viewer/webthread_impl.cc
new file mode 100644
index 0000000..678d22e
--- /dev/null
+++ b/mojo/services/html_viewer/webthread_impl.cc
@@ -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.
+
+// An implementation of WebThread in terms of base::MessageLoop and
+// base::Thread
+
+#include "mojo/services/html_viewer/webthread_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/threading/platform_thread.h"
+
+namespace mojo {
+
+WebThreadBase::WebThreadBase() {}
+WebThreadBase::~WebThreadBase() {}
+
+class WebThreadBase::TaskObserverAdapter
+    : public base::MessageLoop::TaskObserver {
+ public:
+  TaskObserverAdapter(WebThread::TaskObserver* observer)
+      : observer_(observer) {}
+
+  virtual void WillProcessTask(const base::PendingTask& pending_task) override {
+    observer_->willProcessTask();
+  }
+
+  virtual void DidProcessTask(const base::PendingTask& pending_task) override {
+    observer_->didProcessTask();
+  }
+
+private:
+  WebThread::TaskObserver* observer_;
+};
+
+void WebThreadBase::addTaskObserver(TaskObserver* observer) {
+  CHECK(isCurrentThread());
+  std::pair<TaskObserverMap::iterator, bool> result = task_observer_map_.insert(
+      std::make_pair(observer, static_cast<TaskObserverAdapter*>(NULL)));
+  if (result.second)
+    result.first->second = new TaskObserverAdapter(observer);
+  base::MessageLoop::current()->AddTaskObserver(result.first->second);
+}
+
+void WebThreadBase::removeTaskObserver(TaskObserver* observer) {
+  CHECK(isCurrentThread());
+  TaskObserverMap::iterator iter = task_observer_map_.find(observer);
+  if (iter == task_observer_map_.end())
+    return;
+  base::MessageLoop::current()->RemoveTaskObserver(iter->second);
+  delete iter->second;
+  task_observer_map_.erase(iter);
+}
+
+WebThreadImpl::WebThreadImpl(const char* name)
+    : thread_(new base::Thread(name)) {
+  thread_->Start();
+}
+
+void WebThreadImpl::postTask(Task* task) {
+  thread_->message_loop()->PostTask(
+      FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImpl::postDelayedTask(Task* task, long long delay_ms) {
+  thread_->message_loop()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+      base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImpl::enterRunLoop() {
+  CHECK(isCurrentThread());
+  CHECK(!thread_->message_loop()->is_running());  // We don't support nesting.
+  thread_->message_loop()->Run();
+}
+
+void WebThreadImpl::exitRunLoop() {
+  CHECK(isCurrentThread());
+  CHECK(thread_->message_loop()->is_running());
+  thread_->message_loop()->Quit();
+}
+
+bool WebThreadImpl::isCurrentThread() const {
+  return thread_->thread_id() == base::PlatformThread::CurrentId();
+}
+
+blink::PlatformThreadId WebThreadImpl::threadId() const {
+  return thread_->thread_id();
+}
+
+WebThreadImpl::~WebThreadImpl() {
+  thread_->Stop();
+}
+
+WebThreadImplForMessageLoop::WebThreadImplForMessageLoop(
+    base::MessageLoopProxy* message_loop)
+    : message_loop_(message_loop) {}
+
+void WebThreadImplForMessageLoop::postTask(Task* task) {
+  message_loop_->PostTask(
+      FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImplForMessageLoop::postDelayedTask(Task* task,
+                                                  long long delay_ms) {
+  message_loop_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+      base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImplForMessageLoop::enterRunLoop() {
+  CHECK(isCurrentThread());
+  // We don't support nesting.
+  CHECK(!base::MessageLoop::current()->is_running());
+  base::MessageLoop::current()->Run();
+}
+
+void WebThreadImplForMessageLoop::exitRunLoop() {
+  CHECK(isCurrentThread());
+  CHECK(base::MessageLoop::current()->is_running());
+  base::MessageLoop::current()->Quit();
+}
+
+bool WebThreadImplForMessageLoop::isCurrentThread() const {
+  return message_loop_->BelongsToCurrentThread();
+}
+
+blink::PlatformThreadId WebThreadImplForMessageLoop::threadId() const {
+  return thread_id_;
+}
+
+WebThreadImplForMessageLoop::~WebThreadImplForMessageLoop() {}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/webthread_impl.h b/mojo/services/html_viewer/webthread_impl.h
new file mode 100644
index 0000000..93d7aeb
--- /dev/null
+++ b/mojo/services/html_viewer/webthread_impl.h
@@ -0,0 +1,78 @@
+// 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_HTML_VIEWER_WEBTHREAD_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "third_party/WebKit/public/platform/WebThread.h"
+
+namespace mojo {
+
+class WebThreadBase : public blink::WebThread {
+ public:
+  virtual ~WebThreadBase();
+
+  virtual void addTaskObserver(TaskObserver* observer);
+  virtual void removeTaskObserver(TaskObserver* observer);
+
+  virtual bool isCurrentThread() const = 0;
+  virtual blink::PlatformThreadId threadId() const = 0;
+
+ protected:
+  WebThreadBase();
+
+ private:
+  class TaskObserverAdapter;
+
+  typedef std::map<TaskObserver*, TaskObserverAdapter*> TaskObserverMap;
+  TaskObserverMap task_observer_map_;
+};
+
+class WebThreadImpl : public WebThreadBase {
+ public:
+  explicit WebThreadImpl(const char* name);
+  virtual ~WebThreadImpl();
+
+  virtual void postTask(Task* task);
+  virtual void postDelayedTask(Task* task, long long delay_ms);
+
+  virtual void enterRunLoop();
+  virtual void exitRunLoop();
+
+  base::MessageLoop* message_loop() const { return thread_->message_loop(); }
+
+  virtual bool isCurrentThread() const;
+  virtual blink::PlatformThreadId threadId() const;
+
+ private:
+  scoped_ptr<base::Thread> thread_;
+};
+
+class WebThreadImplForMessageLoop : public WebThreadBase {
+ public:
+  explicit WebThreadImplForMessageLoop(
+      base::MessageLoopProxy* message_loop);
+  virtual ~WebThreadImplForMessageLoop();
+
+  virtual void postTask(Task* task);
+  virtual void postDelayedTask(Task* task, long long delay_ms);
+
+  virtual void enterRunLoop();
+  virtual void exitRunLoop();
+
+ private:
+  virtual bool isCurrentThread() const;
+  virtual blink::PlatformThreadId threadId() const;
+
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
+  blink::PlatformThreadId thread_id_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_
diff --git a/mojo/services/html_viewer/weburlloader_impl.cc b/mojo/services/html_viewer/weburlloader_impl.cc
new file mode 100644
index 0000000..fb82bcf
--- /dev/null
+++ b/mojo/services/html_viewer/weburlloader_impl.cc
@@ -0,0 +1,224 @@
+// 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/html_viewer/weburlloader_impl.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
+#include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
+#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+
+namespace mojo {
+namespace {
+
+static blink::WebURLResponse::HTTPVersion StatusLineToHTTPVersion(
+    const String& status_line) {
+  if (status_line.is_null())
+    return blink::WebURLResponse::HTTP_0_9;
+
+  if (StartsWithASCII(status_line, "HTTP/1.0", true))
+    return blink::WebURLResponse::HTTP_1_0;
+
+  if (StartsWithASCII(status_line, "HTTP/1.1", true))
+    return blink::WebURLResponse::HTTP_1_1;
+
+  return blink::WebURLResponse::Unknown;
+}
+
+blink::WebURLResponse ToWebURLResponse(const URLResponsePtr& url_response) {
+  blink::WebURLResponse result;
+  result.initialize();
+  result.setURL(GURL(url_response->url));
+  result.setMIMEType(blink::WebString::fromUTF8(url_response->mime_type));
+  result.setTextEncodingName(blink::WebString::fromUTF8(url_response->charset));
+  result.setHTTPVersion(StatusLineToHTTPVersion(url_response->status_line));
+  result.setHTTPStatusCode(url_response->status_code);
+
+  // TODO(darin): Initialize timing properly.
+  blink::WebURLLoadTiming timing;
+  timing.initialize();
+  result.setLoadTiming(timing);
+
+  for (size_t i = 0; i < url_response->headers.size(); ++i) {
+    const std::string& header_line = url_response->headers[i];
+    size_t first_colon = header_line.find(":");
+
+    if (first_colon == std::string::npos || first_colon == 0)
+      continue;
+
+    std::string value;
+    TrimWhitespaceASCII(header_line.substr(first_colon + 1),
+                        base::TRIM_LEADING,
+                        &value);
+    result.setHTTPHeaderField(
+        blink::WebString::fromUTF8(header_line.substr(0, first_colon)),
+        blink::WebString::fromUTF8(value));
+  }
+
+  return result;
+}
+
+}  // namespace
+
+WebURLRequestExtraData::WebURLRequestExtraData() {
+}
+
+WebURLRequestExtraData::~WebURLRequestExtraData() {
+}
+
+WebURLLoaderImpl::WebURLLoaderImpl(NetworkService* network_service)
+    : client_(NULL),
+      weak_factory_(this) {
+  network_service->CreateURLLoader(Get(&url_loader_));
+}
+
+WebURLLoaderImpl::~WebURLLoaderImpl() {
+}
+
+void WebURLLoaderImpl::loadSynchronously(
+    const blink::WebURLRequest& request,
+    blink::WebURLResponse& response,
+    blink::WebURLError& error,
+    blink::WebData& data) {
+  NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request,
+                                          blink::WebURLLoaderClient* client) {
+  client_ = client;
+  url_ = request.url();
+
+  URLRequestPtr url_request = URLRequest::From(request);
+  url_request->auto_follow_redirects = false;
+
+  if (request.extraData()) {
+    WebURLRequestExtraData* extra_data =
+        static_cast<WebURLRequestExtraData*>(request.extraData());
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+                   weak_factory_.GetWeakPtr(),
+                   base::Passed(&extra_data->synthetic_response)));
+  } else {
+    url_loader_->Start(url_request.Pass(),
+                       base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+                                  weak_factory_.GetWeakPtr()));
+  }
+}
+
+void WebURLLoaderImpl::cancel() {
+  url_loader_.reset();
+  response_body_stream_.reset();
+
+  URLResponsePtr failed_response(URLResponse::New());
+  failed_response->url = String::From(url_);
+  failed_response->error = NetworkError::New();
+  failed_response->error->code = net::ERR_ABORTED;
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+                 weak_factory_.GetWeakPtr(),
+                 base::Passed(&failed_response)));
+}
+
+void WebURLLoaderImpl::setDefersLoading(bool defers_loading) {
+  NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::OnReceivedResponse(URLResponsePtr url_response) {
+  url_ = GURL(url_response->url);
+
+  if (url_response->error) {
+    OnReceivedError(url_response.Pass());
+  } else if (url_response->redirect_url) {
+    OnReceivedRedirect(url_response.Pass());
+  } else {
+    base::WeakPtr<WebURLLoaderImpl> self(weak_factory_.GetWeakPtr());
+    client_->didReceiveResponse(this, ToWebURLResponse(url_response));
+
+    // We may have been deleted during didReceiveResponse.
+    if (!self)
+      return;
+
+    // Start streaming data
+    response_body_stream_ = url_response->body.Pass();
+    ReadMore();
+  }
+}
+
+void WebURLLoaderImpl::OnReceivedError(URLResponsePtr url_response) {
+  blink::WebURLError web_error;
+  web_error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+  web_error.reason = url_response->error->code;
+  web_error.unreachableURL = GURL(url_response->url);
+  web_error.staleCopyInCache = false;
+  web_error.isCancellation =
+      url_response->error->code == net::ERR_ABORTED ? true : false;
+
+  client_->didFail(this, web_error);
+}
+
+void WebURLLoaderImpl::OnReceivedRedirect(URLResponsePtr url_response) {
+  blink::WebURLRequest new_request;
+  new_request.initialize();
+  new_request.setURL(GURL(url_response->redirect_url));
+  new_request.setHTTPMethod(
+      blink::WebString::fromUTF8(url_response->redirect_method));
+
+  client_->willSendRequest(this, new_request, ToWebURLResponse(url_response));
+  // TODO(darin): Check if new_request was rejected.
+
+  url_loader_->FollowRedirect(
+      base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void WebURLLoaderImpl::ReadMore() {
+  const void* buf;
+  uint32_t buf_size;
+  MojoResult rv = BeginReadDataRaw(response_body_stream_.get(),
+                                   &buf,
+                                   &buf_size,
+                                   MOJO_READ_DATA_FLAG_NONE);
+  if (rv == MOJO_RESULT_OK) {
+    client_->didReceiveData(this, static_cast<const char*>(buf), buf_size, -1);
+    EndReadDataRaw(response_body_stream_.get(), buf_size);
+    WaitToReadMore();
+  } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
+    WaitToReadMore();
+  } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+    // We reached end-of-file.
+    double finish_time = base::Time::Now().ToDoubleT();
+    client_->didFinishLoading(
+        this,
+        finish_time,
+        blink::WebURLLoaderClient::kUnknownEncodedDataLength);
+  } else {
+    // TODO(darin): Oops!
+  }
+}
+
+void WebURLLoaderImpl::WaitToReadMore() {
+  handle_watcher_.Start(
+      response_body_stream_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_DEADLINE_INDEFINITE,
+      base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+  ReadMore();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/html_viewer/weburlloader_impl.h b/mojo/services/html_viewer/weburlloader_impl.h
new file mode 100644
index 0000000..6bb30df
--- /dev/null
+++ b/mojo/services/html_viewer/weburlloader_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 MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/platform/WebURLLoader.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+
+namespace mojo {
+class NetworkService;
+
+// The concrete type of WebURLRequest::ExtraData.
+class WebURLRequestExtraData : public blink::WebURLRequest::ExtraData {
+ public:
+  WebURLRequestExtraData();
+  virtual ~WebURLRequestExtraData();
+
+  URLResponsePtr synthetic_response;
+};
+
+class WebURLLoaderImpl : public blink::WebURLLoader {
+ public:
+  explicit WebURLLoaderImpl(NetworkService* network_service);
+
+ private:
+  virtual ~WebURLLoaderImpl();
+
+  // blink::WebURLLoader methods:
+  virtual void loadSynchronously(
+      const blink::WebURLRequest& request, blink::WebURLResponse& response,
+      blink::WebURLError& error, blink::WebData& data) override;
+  virtual void loadAsynchronously(
+      const blink::WebURLRequest&, blink::WebURLLoaderClient* client) override;
+  virtual void cancel() override;
+  virtual void setDefersLoading(bool defers_loading) override;
+
+  void OnReceivedResponse(URLResponsePtr response);
+  void OnReceivedError(URLResponsePtr response);
+  void OnReceivedRedirect(URLResponsePtr response);
+  void ReadMore();
+  void WaitToReadMore();
+  void OnResponseBodyStreamReady(MojoResult result);
+
+  blink::WebURLLoaderClient* client_;
+  GURL url_;
+  URLLoaderPtr url_loader_;
+  ScopedDataPipeConsumerHandle response_body_stream_;
+  common::HandleWatcher handle_watcher_;
+
+  base::WeakPtrFactory<WebURLLoaderImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebURLLoaderImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_
diff --git a/mojo/services/native_viewport/BUILD.gn b/mojo/services/native_viewport/BUILD.gn
new file mode 100644
index 0000000..036e8e9
--- /dev/null
+++ b/mojo/services/native_viewport/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+if (!is_android) {
+  shared_library("native_viewport") {
+    output_name = "mojo_native_viewport_service"
+
+    deps = [
+      ":lib",
+      "//base",
+      "//mojo/application",
+      "//mojo/public/c/system:for_shared_library",
+      "//mojo/public/cpp/bindings:bindings",
+      "//mojo/services/public/interfaces/native_viewport",
+      "//ui/gl",
+    ]
+
+    sources = [ "main.cc" ]
+  }
+}
+
+source_set("lib") {
+  deps = [
+    "//base",
+    "//cc/surfaces",
+    "//gpu/command_buffer/service",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/services/gles2",
+    "//mojo/services/gles2:interfaces",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/native_viewport",
+    "//mojo/services/public/interfaces/surfaces",
+    "//ui/events",
+    "//ui/events/platform",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//ui/platform_window",
+  ]
+
+  sources = [
+    "gpu_impl.cc",
+    "gpu_impl.h",
+    "native_viewport_impl.cc",
+    "native_viewport_impl.h",
+    "platform_viewport.h",
+    "platform_viewport_android.cc",
+    "platform_viewport_android.h",
+    "platform_viewport_mac.mm",
+    "platform_viewport_headless.cc",
+    "platform_viewport_headless.h",
+    "platform_viewport_win.cc",
+    "viewport_surface.cc",
+    "viewport_surface.h",
+  ]
+
+  if (is_ios) {
+    sources += [ "platform_viewport_stub.cc" ]
+  }
+
+  if (is_android) {
+    deps += [ "//mojo:jni_headers" ]
+  }
+
+  if (use_x11) {
+    sources += [ "platform_viewport_x11.cc" ]
+    deps += [
+      "//ui/events/platform/x11",
+      "//ui/platform_window/x11",
+    ]
+  }
+
+  if (use_ozone) {
+    sources += [ "platform_viewport_ozone.cc" ]
+  }
+}
diff --git a/mojo/services/native_viewport/DEPS b/mojo/services/native_viewport/DEPS
new file mode 100644
index 0000000..abf7ef8
--- /dev/null
+++ b/mojo/services/native_viewport/DEPS
@@ -0,0 +1,18 @@
+include_rules = [
+  "+cc/surfaces",
+  "+gpu/command_buffer/service/mailbox_manager.h",
+  "+mojo/application",
+  "+mojo/services/public/cpp/geometry",
+  "+mojo/services/public/cpp/input_events",
+  "+mojo/services/public/cpp/surfaces",
+  "+mojo/services/public/interfaces/gpu",
+  "+mojo/services/public/interfaces/native_viewport",
+  "+mojo/services/public/interfaces/geometry",
+  "+mojo/services/public/interfaces/surfaces",
+  "+mojo/services/gles2",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/gl",
+  "+ui/ozone/public",
+  "+ui/platform_window",
+]
diff --git a/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
new file mode 100644
index 0000000..c3f8075
--- /dev/null
+++ b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
@@ -0,0 +1,91 @@
+// Copyright 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.
+
+package org.chromium.mojo;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Exposes SurfaceView to native code.
+ */
+@JNINamespace("mojo")
+public class PlatformViewportAndroid extends SurfaceView {
+
+    private long mNativeMojoViewport;
+    private final SurfaceHolder.Callback mSurfaceCallback;
+
+    @SuppressWarnings("unused")
+    @CalledByNative
+    public static void createForActivity(Activity activity, long nativeViewport) {
+        activity.setContentView(new PlatformViewportAndroid(activity, nativeViewport));
+    }
+
+    public PlatformViewportAndroid(Context context, long nativeViewport) {
+        super(context);
+
+        mNativeMojoViewport = nativeViewport;
+        assert mNativeMojoViewport != 0;
+
+        mSurfaceCallback = new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+                assert mNativeMojoViewport != 0;
+                nativeSurfaceSetSize(mNativeMojoViewport, width, height);
+            }
+
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+                assert mNativeMojoViewport != 0;
+                nativeSurfaceCreated(mNativeMojoViewport, holder.getSurface());
+            }
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+                assert mNativeMojoViewport != 0;
+                nativeSurfaceDestroyed(mNativeMojoViewport);
+            }
+        };
+        getHolder().addCallback(mSurfaceCallback);
+
+    }
+
+    // TODO(abarth): Someone needs to call destroy at some point.
+    public void destroy() {
+        getHolder().removeCallback(mSurfaceCallback);
+        nativeDestroy(mNativeMojoViewport);
+        mNativeMojoViewport = 0;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return nativeTouchEvent(mNativeMojoViewport,
+                                event.getPointerId(0),
+                                event.getAction(),
+                                event.getX(), event.getY(),
+                                event.getEventTime());
+    }
+
+    private static native void nativeDestroy(long nativePlatformViewportAndroid);
+    private static native void nativeSurfaceCreated(
+        long nativePlatformViewportAndroid, Surface surface);
+    private static native void nativeSurfaceDestroyed(
+        long nativePlatformViewportAndroid);
+    private static native void nativeSurfaceSetSize(
+        long nativePlatformViewportAndroid,
+        int width, int height);
+    private static native boolean nativeTouchEvent(
+        long nativePlatformViewportAndroid,
+        int pointerId,
+        int action,
+        float x, float y,
+        long timeMs);
+};
diff --git a/mojo/services/native_viewport/gpu_impl.cc b/mojo/services/native_viewport/gpu_impl.cc
new file mode 100644
index 0000000..de0b69e
--- /dev/null
+++ b/mojo/services/native_viewport/gpu_impl.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 "mojo/services/native_viewport/gpu_impl.h"
+
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "mojo/services/gles2/command_buffer_impl.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "ui/gl/gl_share_group.h"
+
+namespace mojo {
+
+GpuImpl::GpuImpl(
+    const scoped_refptr<gfx::GLShareGroup>& share_group,
+    const scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager)
+    : share_group_(share_group), mailbox_manager_(mailbox_manager) {
+}
+
+GpuImpl::~GpuImpl() {
+}
+
+void GpuImpl::CreateOnscreenGLES2Context(
+    uint64_t native_viewport_id,
+    SizePtr size,
+    InterfaceRequest<CommandBuffer> command_buffer_request) {
+  gfx::AcceleratedWidget widget = bit_cast<gfx::AcceleratedWidget>(
+      static_cast<uintptr_t>(native_viewport_id));
+  BindToRequest(new CommandBufferImpl(widget,
+                                      size.To<gfx::Size>(),
+                                      share_group_.get(),
+                                      mailbox_manager_.get()),
+                &command_buffer_request);
+}
+
+void GpuImpl::CreateOffscreenGLES2Context(
+    InterfaceRequest<CommandBuffer> command_buffer_request) {
+  BindToRequest(
+      new CommandBufferImpl(share_group_.get(), mailbox_manager_.get()),
+      &command_buffer_request);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/gpu_impl.h b/mojo/services/native_viewport/gpu_impl.h
new file mode 100644
index 0000000..ac0f435
--- /dev/null
+++ b/mojo/services/native_viewport/gpu_impl.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.
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+
+namespace gfx {
+class GLShareGroup;
+}
+
+namespace gpu {
+namespace gles2 {
+class MailboxManager;
+}
+}
+
+namespace mojo {
+
+class GpuImpl : public InterfaceImpl<Gpu> {
+ public:
+  GpuImpl(const scoped_refptr<gfx::GLShareGroup>& share_group,
+          const scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager);
+
+  virtual ~GpuImpl();
+
+  virtual void CreateOnscreenGLES2Context(
+      uint64_t native_viewport_id,
+      SizePtr size,
+      InterfaceRequest<CommandBuffer> command_buffer_request) override;
+
+  virtual void CreateOffscreenGLES2Context(
+      InterfaceRequest<CommandBuffer> command_buffer_request) override;
+
+ private:
+  // We need to share these across all NativeViewport instances so that contexts
+  // they create can share resources with each other via mailboxes.
+  scoped_refptr<gfx::GLShareGroup> share_group_;
+  scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuImpl);
+};
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/main.cc b/mojo/services/native_viewport/main.cc
new file mode 100644
index 0000000..0185e67
--- /dev/null
+++ b/mojo/services/native_viewport/main.cc
@@ -0,0 +1,111 @@
+// 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/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "gpu/command_buffer/service/mailbox_manager.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/interface_factory_impl.h"
+#include "mojo/services/native_viewport/gpu_impl.h"
+#include "mojo/services/native_viewport/native_viewport_impl.h"
+#include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mojo {
+
+class NativeViewportAppDelegate
+    : public ApplicationDelegate,
+      public InterfaceFactory<NativeViewport>,
+      public InterfaceFactory<Gpu>,
+      public InterfaceFactory<NativeViewportConfig> {
+ public:
+  NativeViewportAppDelegate()
+      : share_group_(new gfx::GLShareGroup),
+        mailbox_manager_(new gpu::gles2::MailboxManager),
+        is_test_(false),
+        is_headless_(false),
+        is_initialized_(false) {}
+  virtual ~NativeViewportAppDelegate() {}
+
+ private:
+  class NativeViewportConfigImpl : public InterfaceImpl<NativeViewportConfig> {
+   public:
+    NativeViewportConfigImpl(NativeViewportAppDelegate* app_delegate)
+      : app_delegate_(app_delegate) {}
+
+    virtual void UseTestConfig(
+        const Callback<void()>& callback) override {
+      app_delegate_->is_test_ = true;
+      callback.Run();
+    }
+
+    virtual void UseHeadlessConfig(
+        const Callback<void()>& callback) override {
+      app_delegate_->is_headless_ = true;
+      callback.Run();
+    }
+
+   private:
+    NativeViewportAppDelegate* app_delegate_;
+  };
+
+  // ApplicationDelegate implementation.
+  virtual void Initialize(ApplicationImpl* application) override {
+    app_ = application;
+  }
+
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService<NativeViewport>(this);
+    connection->AddService<Gpu>(this);
+    connection->AddService<NativeViewportConfig>(this);
+    return true;
+  }
+
+  // InterfaceFactory<NativeViewport> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NativeViewport> request) override {
+#if !defined(COMPONENT_BUILD)
+    if (!is_initialized_) {
+      if (is_test_)
+        gfx::GLSurface::InitializeOneOffForTests();
+      else
+        gfx::GLSurface::InitializeOneOff();
+      is_initialized_ = true;
+    }
+#endif
+    BindToRequest(new NativeViewportImpl(app_, is_headless_), &request);
+  }
+
+  // InterfaceFactory<Gpu> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Gpu> request) override {
+    BindToRequest(new GpuImpl(share_group_.get(), mailbox_manager_.get()),
+                  &request);
+  }
+
+  // InterfaceFactory<NVTestConfig> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NativeViewportConfig> request) override {
+    BindToRequest(new NativeViewportConfigImpl(this), &request);
+  }
+
+  ApplicationImpl* app_;
+  scoped_refptr<gfx::GLShareGroup> share_group_;
+  scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+  bool is_test_;
+  bool is_headless_;
+  bool is_initialized_;
+  DISALLOW_COPY_AND_ASSIGN(NativeViewportAppDelegate);
+};
+}
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::NativeViewportAppDelegate);
+  runner.set_message_loop_type(base::MessageLoop::TYPE_UI);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/native_viewport/native_viewport_impl.cc b/mojo/services/native_viewport/native_viewport_impl.cc
new file mode 100644
index 0000000..3006612
--- /dev/null
+++ b/mojo/services/native_viewport/native_viewport_impl.cc
@@ -0,0 +1,160 @@
+// 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/native_viewport/native_viewport_impl.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/native_viewport/platform_viewport_headless.h"
+#include "mojo/services/native_viewport/viewport_surface.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+namespace {
+
+bool IsRateLimitedEventType(ui::Event* event) {
+  return event->type() == ui::ET_MOUSE_MOVED ||
+         event->type() == ui::ET_MOUSE_DRAGGED ||
+         event->type() == ui::ET_TOUCH_MOVED;
+}
+
+}  // namespace
+
+NativeViewportImpl::NativeViewportImpl(ApplicationImpl* app, bool is_headless)
+    : is_headless_(is_headless),
+      widget_id_(0u),
+      waiting_for_event_ack_(false),
+      weak_factory_(this) {
+  app->ConnectToService("mojo:mojo_surfaces_service", &surfaces_service_);
+  // TODO(jamesr): Should be mojo_gpu_service
+  app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+}
+
+NativeViewportImpl::~NativeViewportImpl() {
+  // Destroy the NativeViewport early on as it may call us back during
+  // destruction and we want to be in a known state.
+  platform_viewport_.reset();
+}
+
+void NativeViewportImpl::Create(SizePtr size,
+                                const Callback<void(uint64_t)>& callback) {
+  create_callback_ = callback;
+  size_ = size.To<gfx::Size>();
+  if (is_headless_)
+    platform_viewport_ = PlatformViewportHeadless::Create(this);
+  else
+    platform_viewport_ = PlatformViewport::Create(this);
+  platform_viewport_->Init(gfx::Rect(size.To<gfx::Size>()));
+}
+
+void NativeViewportImpl::Show() {
+  platform_viewport_->Show();
+}
+
+void NativeViewportImpl::Hide() {
+  platform_viewport_->Hide();
+}
+
+void NativeViewportImpl::Close() {
+  DCHECK(platform_viewport_);
+  platform_viewport_->Close();
+}
+
+void NativeViewportImpl::SetSize(SizePtr size) {
+  platform_viewport_->SetBounds(gfx::Rect(size.To<gfx::Size>()));
+}
+
+void NativeViewportImpl::SubmittedFrame(SurfaceIdPtr child_surface_id) {
+  if (child_surface_id_.is_null()) {
+    // If this is the first indication that the client will use surfaces,
+    // initialize that system.
+    // TODO(jamesr): When everything is converted to surfaces initialize this
+    // eagerly.
+    viewport_surface_.reset(
+        new ViewportSurface(surfaces_service_.get(),
+                            gpu_service_.get(),
+                            size_,
+                            child_surface_id.To<cc::SurfaceId>()));
+    if (widget_id_)
+      viewport_surface_->SetWidgetId(widget_id_);
+  }
+  child_surface_id_ = child_surface_id.To<cc::SurfaceId>();
+  if (viewport_surface_)
+    viewport_surface_->SetChildId(child_surface_id_);
+}
+
+void NativeViewportImpl::OnBoundsChanged(const gfx::Rect& bounds) {
+  if (size_ == bounds.size())
+    return;
+
+  size_ = bounds.size();
+
+  // Wait for the accelerated widget before telling the client of the bounds.
+  if (create_callback_.is_null())
+    ProcessOnBoundsChanged();
+}
+
+void NativeViewportImpl::OnAcceleratedWidgetAvailable(
+    gfx::AcceleratedWidget widget) {
+  widget_id_ = static_cast<uint64_t>(bit_cast<uintptr_t>(widget));
+  // TODO(jamesr): Remove once everything is converted to surfaces.
+  create_callback_.Run(widget_id_);
+  create_callback_.reset();
+  // Immediately tell the client of the size. The size may be wrong, if so we'll
+  // get the right one in the next OnBoundsChanged() call.
+  ProcessOnBoundsChanged();
+  if (viewport_surface_)
+    viewport_surface_->SetWidgetId(widget_id_);
+}
+
+bool NativeViewportImpl::OnEvent(ui::Event* ui_event) {
+  // Must not return early before updating capture.
+  switch (ui_event->type()) {
+    case ui::ET_MOUSE_PRESSED:
+    case ui::ET_TOUCH_PRESSED:
+      platform_viewport_->SetCapture();
+      break;
+    case ui::ET_MOUSE_RELEASED:
+    case ui::ET_TOUCH_RELEASED:
+      platform_viewport_->ReleaseCapture();
+      break;
+    default:
+      break;
+  }
+
+  if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event))
+    return false;
+
+  client()->OnEvent(
+      Event::From(*ui_event),
+      base::Bind(&NativeViewportImpl::AckEvent, weak_factory_.GetWeakPtr()));
+  waiting_for_event_ack_ = true;
+  return false;
+}
+
+void NativeViewportImpl::OnDestroyed() {
+  client()->OnDestroyed();
+}
+
+void NativeViewportImpl::AckEvent() {
+  waiting_for_event_ack_ = false;
+}
+
+void NativeViewportImpl::ProcessOnBoundsChanged() {
+  client()->OnSizeChanged(Size::From(size_));
+  if (viewport_surface_)
+    viewport_surface_->SetSize(size_);
+}
+
+}  // namespace mojo
+
diff --git a/mojo/services/native_viewport/native_viewport_impl.h b/mojo/services/native_viewport/native_viewport_impl.h
new file mode 100644
index 0000000..03918cb
--- /dev/null
+++ b/mojo/services/native_viewport/native_viewport_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/native_viewport/platform_viewport.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+class ApplicationImpl;
+class ViewportSurface;
+
+class NativeViewportImpl : public InterfaceImpl<NativeViewport>,
+                           public PlatformViewport::Delegate {
+ public:
+  NativeViewportImpl(ApplicationImpl* app, bool is_headless);
+  virtual ~NativeViewportImpl();
+
+  // InterfaceImpl<NativeViewport> implementation.
+  virtual void Create(SizePtr size,
+                      const Callback<void(uint64_t)>& callback) override;
+  virtual void Show() override;
+  virtual void Hide() override;
+  virtual void Close() override;
+  virtual void SetSize(SizePtr size) override;
+  virtual void SubmittedFrame(SurfaceIdPtr surface_id) override;
+
+  // PlatformViewport::Delegate implementation.
+  virtual void OnBoundsChanged(const gfx::Rect& bounds) override;
+  virtual void OnAcceleratedWidgetAvailable(
+      gfx::AcceleratedWidget widget) override;
+  virtual bool OnEvent(ui::Event* ui_event) override;
+  virtual void OnDestroyed() override;
+
+  void AckEvent();
+
+ private:
+  void ProcessOnBoundsChanged();
+
+  bool is_headless_;
+  scoped_ptr<PlatformViewport> platform_viewport_;
+  scoped_ptr<ViewportSurface> viewport_surface_;
+  uint64_t widget_id_;
+  gfx::Size size_;
+  GpuPtr gpu_service_;
+  SurfacesServicePtr surfaces_service_;
+  cc::SurfaceId child_surface_id_;
+  bool waiting_for_event_ack_;
+  Callback<void(uint64_t)> create_callback_;
+  base::WeakPtrFactory<NativeViewportImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeViewportImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_
diff --git a/mojo/services/native_viewport/platform_viewport.h b/mojo/services/native_viewport/platform_viewport.h
new file mode 100644
index 0000000..a3ba3dc
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport.h
@@ -0,0 +1,53 @@
+// Copyright 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+// Encapsulation of platform-specific Viewport.
+class PlatformViewport {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    virtual void OnBoundsChanged(const gfx::Rect& rect) = 0;
+    virtual void OnAcceleratedWidgetAvailable(
+        gfx::AcceleratedWidget widget) = 0;
+    virtual bool OnEvent(ui::Event* ui_event) = 0;
+    virtual void OnDestroyed() = 0;
+  };
+
+  virtual ~PlatformViewport() {}
+
+  virtual void Init(const gfx::Rect& bounds) = 0;
+  virtual void Show() = 0;
+  virtual void Hide() = 0;
+  virtual void Close() = 0;
+  virtual gfx::Size GetSize() = 0;
+  virtual void SetBounds(const gfx::Rect& bounds) = 0;
+
+  virtual void SetCapture() = 0;
+  virtual void ReleaseCapture() = 0;
+
+  static scoped_ptr<PlatformViewport> Create(Delegate* delegate);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_
diff --git a/mojo/services/native_viewport/platform_viewport_android.cc b/mojo/services/native_viewport/platform_viewport_android.cc
new file mode 100644
index 0000000..bbe22c9
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_android.cc
@@ -0,0 +1,156 @@
+// Copyright 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 "mojo/services/native_viewport/platform_viewport_android.h"
+
+#include <android/input.h>
+#include <android/native_window_jni.h>
+
+#include "base/android/jni_android.h"
+#include "jni/PlatformViewportAndroid_jni.h"
+#include "ui/events/event.h"
+#include "ui/gfx/point.h"
+
+namespace mojo {
+
+ui::EventType MotionEventActionToEventType(jint action) {
+  switch (action) {
+    case AMOTION_EVENT_ACTION_DOWN:
+      return ui::ET_TOUCH_PRESSED;
+    case AMOTION_EVENT_ACTION_MOVE:
+      return ui::ET_TOUCH_MOVED;
+    case AMOTION_EVENT_ACTION_UP:
+      return ui::ET_TOUCH_RELEASED;
+    default:
+      NOTREACHED();
+  }
+  return ui::ET_UNKNOWN;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, public:
+
+// static
+bool PlatformViewportAndroid::Register(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+PlatformViewportAndroid::PlatformViewportAndroid(Delegate* delegate)
+    : delegate_(delegate),
+      window_(NULL),
+      id_generator_(0),
+      weak_factory_(this) {
+}
+
+PlatformViewportAndroid::~PlatformViewportAndroid() {
+  if (window_)
+    ReleaseWindow();
+}
+
+void PlatformViewportAndroid::Destroy(JNIEnv* env, jobject obj) {
+  delegate_->OnDestroyed();
+}
+
+void PlatformViewportAndroid::SurfaceCreated(JNIEnv* env,
+                                             jobject obj,
+                                             jobject jsurface) {
+  base::android::ScopedJavaLocalRef<jobject> protector(env, jsurface);
+  // Note: This ensures that any local references used by
+  // ANativeWindow_fromSurface are released immediately. This is needed as a
+  // workaround for https://code.google.com/p/android/issues/detail?id=68174
+  {
+    base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
+    window_ = ANativeWindow_fromSurface(env, jsurface);
+  }
+  delegate_->OnAcceleratedWidgetAvailable(window_);
+}
+
+void PlatformViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) {
+  DCHECK(window_);
+  ReleaseWindow();
+}
+
+void PlatformViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj,
+                                             jint width, jint height) {
+  bounds_ = gfx::Rect(width, height);
+  delegate_->OnBoundsChanged(bounds_);
+}
+
+bool PlatformViewportAndroid::TouchEvent(JNIEnv* env, jobject obj,
+                                         jint pointer_id,
+                                         jint action,
+                                         jfloat x, jfloat y,
+                                         jlong time_ms) {
+  gfx::Point location(static_cast<int>(x), static_cast<int>(y));
+  ui::TouchEvent event(MotionEventActionToEventType(action), location,
+                       id_generator_.GetGeneratedID(pointer_id),
+                       base::TimeDelta::FromMilliseconds(time_ms));
+  // TODO(beng): handle multiple touch-points.
+  delegate_->OnEvent(&event);
+  if (action == ui::ET_TOUCH_RELEASED)
+    id_generator_.ReleaseNumber(pointer_id);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, PlatformViewport implementation:
+
+void PlatformViewportAndroid::Init(const gfx::Rect& bounds) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_PlatformViewportAndroid_createForActivity(
+      env,
+      base::android::GetApplicationContext(),
+      reinterpret_cast<jlong>(this));
+}
+
+void PlatformViewportAndroid::Show() {
+  // Nothing to do. View is created visible.
+}
+
+void PlatformViewportAndroid::Hide() {
+  // Nothing to do. View is always visible.
+}
+
+void PlatformViewportAndroid::Close() {
+  // TODO(beng): close activity containing MojoView?
+
+  // TODO(beng): perform this in response to view destruction.
+  delegate_->OnDestroyed();
+}
+
+gfx::Size PlatformViewportAndroid::GetSize() {
+  return bounds_.size();
+}
+
+void PlatformViewportAndroid::SetBounds(const gfx::Rect& bounds) {
+  NOTIMPLEMENTED();
+}
+
+void PlatformViewportAndroid::SetCapture() {
+  NOTIMPLEMENTED();
+}
+
+void PlatformViewportAndroid::ReleaseCapture() {
+  NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, private:
+
+void PlatformViewportAndroid::ReleaseWindow() {
+  ANativeWindow_release(window_);
+  window_ = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewport, public:
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(
+      new PlatformViewportAndroid(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_android.h b/mojo/services/native_viewport/platform_viewport_android.h
new file mode 100644
index 0000000..31b0312
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_android.h
@@ -0,0 +1,66 @@
+// Copyright 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/native_viewport/platform_viewport.h"
+#include "ui/events/event_constants.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/sequential_id_generator.h"
+#include "ui/gfx/size.h"
+
+namespace gpu {
+class GLInProcessContext;
+}
+
+struct ANativeWindow;
+
+namespace mojo {
+
+class PlatformViewportAndroid : public PlatformViewport {
+ public:
+  static bool Register(JNIEnv* env);
+
+  explicit PlatformViewportAndroid(Delegate* delegate);
+  virtual ~PlatformViewportAndroid();
+
+  void Destroy(JNIEnv* env, jobject obj);
+  void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface);
+  void SurfaceDestroyed(JNIEnv* env, jobject obj);
+  void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height);
+  bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action,
+                  jfloat x, jfloat y, jlong time_ms);
+
+ private:
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) override;
+  virtual void Show() override;
+  virtual void Hide() override;
+  virtual void Close() override;
+  virtual gfx::Size GetSize() override;
+  virtual void SetBounds(const gfx::Rect& bounds) override;
+  virtual void SetCapture() override;
+  virtual void ReleaseCapture() override;
+
+  void ReleaseWindow();
+
+  Delegate* delegate_;
+  ANativeWindow* window_;
+  gfx::Rect bounds_;
+  ui::SequentialIDGenerator id_generator_;
+
+  base::WeakPtrFactory<PlatformViewportAndroid> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportAndroid);
+};
+
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_
diff --git a/mojo/services/native_viewport/platform_viewport_headless.cc b/mojo/services/native_viewport/platform_viewport_headless.cc
new file mode 100644
index 0000000..1c34711
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_headless.cc
@@ -0,0 +1,52 @@
+// Copyright 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 "mojo/services/native_viewport/platform_viewport_headless.h"
+
+namespace mojo {
+
+PlatformViewportHeadless::PlatformViewportHeadless(Delegate* delegate)
+    : delegate_(delegate) {
+}
+
+PlatformViewportHeadless::~PlatformViewportHeadless() {
+}
+
+void PlatformViewportHeadless::Init(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+}
+
+void PlatformViewportHeadless::Show() {
+}
+
+void PlatformViewportHeadless::Hide() {
+}
+
+void PlatformViewportHeadless::Close() {
+  delegate_->OnDestroyed();
+}
+
+gfx::Size PlatformViewportHeadless::GetSize() {
+  return bounds_.size();
+}
+
+void PlatformViewportHeadless::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds_);
+}
+
+void PlatformViewportHeadless::SetCapture() {
+}
+
+void PlatformViewportHeadless::ReleaseCapture() {
+}
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewportHeadless::Create(
+    Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(
+      new PlatformViewportHeadless(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_headless.h b/mojo/services/native_viewport/platform_viewport_headless.h
new file mode 100644
index 0000000..9b9743f
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_headless.h
@@ -0,0 +1,36 @@
+// Copyright 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 "base/macros.h"
+#include "mojo/services/native_viewport/platform_viewport.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+
+class PlatformViewportHeadless : public PlatformViewport {
+ public:
+  virtual ~PlatformViewportHeadless();
+
+  static scoped_ptr<PlatformViewport> Create(Delegate* delegate);
+
+ private:
+  explicit PlatformViewportHeadless(Delegate* delegate);
+
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) override;
+  virtual void Show() override;
+  virtual void Hide() override;
+  virtual void Close() override;
+  virtual gfx::Size GetSize() override;
+  virtual void SetBounds(const gfx::Rect& bounds) override;
+  virtual void SetCapture() override;
+  virtual void ReleaseCapture() override;
+
+  Delegate* delegate_;
+  gfx::Rect bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportHeadless);
+};
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_mac.mm b/mojo/services/native_viewport/platform_viewport_mac.mm
new file mode 100644
index 0000000..9d11052
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_mac.mm
@@ -0,0 +1,84 @@
+// Copyright 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 "mojo/services/native_viewport/platform_viewport.h"
+
+#import <AppKit/NSApplication.h>
+#import <AppKit/NSView.h>
+#import <AppKit/NSWindow.h>
+
+#include "base/bind.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+
+class PlatformViewportMac : public PlatformViewport {
+ public:
+  PlatformViewportMac(Delegate* delegate)
+      : delegate_(delegate),
+        window_(nil) {
+  }
+
+  virtual ~PlatformViewportMac() {
+    [window_ orderOut:nil];
+    [window_ close];
+  }
+
+ private:
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+    [NSApplication sharedApplication];
+
+    rect_ = bounds;
+    window_ = [[NSWindow alloc]
+                  initWithContentRect:NSRectFromCGRect(rect_.ToCGRect())
+                            styleMask:NSTitledWindowMask
+                              backing:NSBackingStoreBuffered
+                                defer:NO];
+    delegate_->OnAcceleratedWidgetAvailable([window_ contentView]);
+    delegate_->OnBoundsChanged(rect_);
+  }
+
+  virtual void Show() OVERRIDE {
+    [window_ orderFront:nil];
+  }
+
+  virtual void Hide() OVERRIDE {
+    [window_ orderOut:nil];
+  }
+
+  virtual void Close() OVERRIDE {
+    // TODO(beng): perform this in response to NSWindow destruction.
+    delegate_->OnDestroyed();
+  }
+
+  virtual gfx::Size GetSize() OVERRIDE {
+    return rect_.size();
+  }
+
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+    NOTIMPLEMENTED();
+  }
+
+  virtual void SetCapture() OVERRIDE {
+    NOTIMPLEMENTED();
+  }
+
+  virtual void ReleaseCapture() OVERRIDE {
+    NOTIMPLEMENTED();
+  }
+
+  Delegate* delegate_;
+  NSWindow* window_;
+  gfx::Rect rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportMac);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(new PlatformViewportMac(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_ozone.cc b/mojo/services/native_viewport/platform_viewport_ozone.cc
new file mode 100644
index 0000000..b8076fb
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_ozone.cc
@@ -0,0 +1,95 @@
+// 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/native_viewport/platform_viewport.h"
+
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace mojo {
+
+// TODO(spang): Deduplicate with PlatformViewportX11.. but there's a hack
+// in there that prevents this.
+class PlatformViewportOzone : public PlatformViewport,
+                              public ui::PlatformWindowDelegate {
+ public:
+  explicit PlatformViewportOzone(Delegate* delegate) : delegate_(delegate) {
+    ui::OzonePlatform::InitializeForUI();
+  }
+
+  virtual ~PlatformViewportOzone() {
+    // Destroy the platform-window while |this| is still alive.
+    platform_window_.reset();
+  }
+
+ private:
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+    platform_window_ =
+        ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
+  }
+
+  virtual void Show() OVERRIDE { platform_window_->Show(); }
+
+  virtual void Hide() OVERRIDE { platform_window_->Hide(); }
+
+  virtual void Close() OVERRIDE { platform_window_->Close(); }
+
+  virtual gfx::Size GetSize() OVERRIDE {
+    return platform_window_->GetBounds().size();
+  }
+
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+    platform_window_->SetBounds(bounds);
+  }
+
+  virtual void SetCapture() OVERRIDE { platform_window_->SetCapture(); }
+
+  virtual void ReleaseCapture() OVERRIDE { platform_window_->ReleaseCapture(); }
+
+  // ui::PlatformWindowDelegate:
+  virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+    delegate_->OnBoundsChanged(new_bounds);
+  }
+
+  virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {}
+
+  virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+    delegate_->OnEvent(event);
+  }
+
+  virtual void OnCloseRequest() OVERRIDE { platform_window_->Close(); }
+
+  virtual void OnClosed() OVERRIDE { delegate_->OnDestroyed(); }
+
+  virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {}
+
+  virtual void OnLostCapture() OVERRIDE {}
+
+  virtual void OnAcceleratedWidgetAvailable(
+      gfx::AcceleratedWidget widget) OVERRIDE {
+    delegate_->OnAcceleratedWidgetAvailable(widget);
+  }
+
+  virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+  scoped_ptr<ui::PlatformWindow> platform_window_;
+  Delegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportOzone);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(
+      new PlatformViewportOzone(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_stub.cc b/mojo/services/native_viewport/platform_viewport_stub.cc
new file mode 100644
index 0000000..80a7e94
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_stub.cc
@@ -0,0 +1,14 @@
+// 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/native_viewport/platform_viewport_headless.h"
+
+namespace mojo {
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return PlatformViewportHeadless::Create(delegate);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_win.cc b/mojo/services/native_viewport/platform_viewport_win.cc
new file mode 100644
index 0000000..2227112
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_win.cc
@@ -0,0 +1,104 @@
+// Copyright 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 "mojo/services/native_viewport/platform_viewport.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/rect.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/win/win_window.h"
+
+namespace mojo {
+
+class PlatformViewportWin : public PlatformViewport,
+                            public ui::PlatformWindowDelegate {
+ public:
+  explicit PlatformViewportWin(Delegate* delegate)
+      : delegate_(delegate) {
+  }
+
+  virtual ~PlatformViewportWin() {
+    // Destroy the platform-window while |this| is still alive.
+    platform_window_.reset();
+  }
+
+ private:
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+    platform_window_.reset(new ui::WinWindow(this, bounds));
+  }
+
+  virtual void Show() OVERRIDE {
+    platform_window_->Show();
+  }
+
+  virtual void Hide() OVERRIDE {
+    platform_window_->Hide();
+  }
+
+  virtual void Close() OVERRIDE {
+    platform_window_->Close();
+  }
+
+  virtual gfx::Size GetSize() OVERRIDE {
+    return platform_window_->GetBounds().size();
+  }
+
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+    platform_window_->SetBounds(bounds);
+  }
+
+  virtual void SetCapture() OVERRIDE {
+    platform_window_->SetCapture();
+  }
+
+  virtual void ReleaseCapture() OVERRIDE {
+    platform_window_->ReleaseCapture();
+  }
+
+  // ui::PlatformWindowDelegate:
+  virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+    delegate_->OnBoundsChanged(new_bounds);
+  }
+
+  virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {
+  }
+
+  virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+    delegate_->OnEvent(event);
+  }
+
+  virtual void OnCloseRequest() OVERRIDE {
+    platform_window_->Close();
+  }
+
+  virtual void OnClosed() OVERRIDE {
+    delegate_->OnDestroyed();
+  }
+
+  virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {
+  }
+
+  virtual void OnLostCapture() OVERRIDE {
+  }
+
+  virtual void OnAcceleratedWidgetAvailable(
+      gfx::AcceleratedWidget widget) OVERRIDE {
+    delegate_->OnAcceleratedWidgetAvailable(widget);
+  }
+
+  virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+  scoped_ptr<ui::PlatformWindow> platform_window_;
+  Delegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportWin);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(new PlatformViewportWin(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_x11.cc b/mojo/services/native_viewport/platform_viewport_x11.cc
new file mode 100644
index 0000000..d4914c7
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_x11.cc
@@ -0,0 +1,150 @@
+// Copyright 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 "mojo/services/native_viewport/platform_viewport.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/rect.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/x11/x11_window.h"
+
+namespace mojo {
+
+class PlatformViewportX11 : public PlatformViewport,
+                            public ui::PlatformWindowDelegate {
+ public:
+  explicit PlatformViewportX11(Delegate* delegate) : delegate_(delegate) {
+  }
+
+  virtual ~PlatformViewportX11() {
+    // Destroy the platform-window while |this| is still alive.
+    platform_window_.reset();
+  }
+
+ private:
+  // Overridden from PlatformViewport:
+  virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+    CHECK(!event_source_);
+    CHECK(!platform_window_);
+
+    event_source_ = ui::PlatformEventSource::CreateDefault();
+
+    platform_window_.reset(new ui::X11Window(this));
+    platform_window_->SetBounds(bounds);
+  }
+
+  virtual void Show() OVERRIDE {
+    platform_window_->Show();
+  }
+
+  virtual void Hide() OVERRIDE {
+    platform_window_->Hide();
+  }
+
+  virtual void Close() OVERRIDE {
+    platform_window_->Close();
+  }
+
+  virtual gfx::Size GetSize() OVERRIDE {
+    return bounds_.size();
+  }
+
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+    platform_window_->SetBounds(bounds);
+  }
+
+  virtual void SetCapture() OVERRIDE {
+    platform_window_->SetCapture();
+  }
+
+  virtual void ReleaseCapture() OVERRIDE {
+    platform_window_->ReleaseCapture();
+  }
+
+  // ui::PlatformWindowDelegate:
+  virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+    bounds_ = new_bounds;
+    delegate_->OnBoundsChanged(new_bounds);
+  }
+
+  virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {
+  }
+
+  virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+    delegate_->OnEvent(event);
+
+    // We want to emulate the WM_CHAR generation behaviour of Windows.
+    //
+    // On Linux, we've previously inserted characters by having
+    // InputMethodAuraLinux take all key down events and send a character event
+    // to the TextInputClient. This causes a mismatch in code that has to be
+    // shared between Windows and Linux, including blink code. Now that we're
+    // trying to have one way of doing things, we need to standardize on and
+    // emulate Windows character events.
+    //
+    // This is equivalent to what we're doing in the current Linux port, but
+    // done once instead of done multiple times in different places.
+    if (event->type() == ui::ET_KEY_PRESSED) {
+      ui::KeyEvent* key_press_event = static_cast<ui::KeyEvent*>(event);
+      ui::KeyEvent char_event(key_press_event->GetCharacter(),
+                              key_press_event->key_code(),
+                              key_press_event->flags());
+
+      DCHECK_EQ(key_press_event->GetCharacter(), char_event.GetCharacter());
+      DCHECK_EQ(key_press_event->key_code(), char_event.key_code());
+      DCHECK_EQ(key_press_event->flags(), char_event.flags());
+
+      char_event.SetExtendedKeyEventData(scoped_ptr<ui::ExtendedKeyEventData>(
+          new MojoExtendedKeyEventData(
+              key_press_event->GetLocatedWindowsKeyboardCode(),
+              key_press_event->GetText(),
+              key_press_event->GetUnmodifiedText())));
+      char_event.set_platform_keycode(key_press_event->platform_keycode());
+
+      delegate_->OnEvent(&char_event);
+    }
+  }
+
+  virtual void OnCloseRequest() OVERRIDE {
+    platform_window_->Close();
+  }
+
+  virtual void OnClosed() OVERRIDE {
+    delegate_->OnDestroyed();
+  }
+
+  virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {
+  }
+
+  virtual void OnLostCapture() OVERRIDE {
+  }
+
+  virtual void OnAcceleratedWidgetAvailable(
+      gfx::AcceleratedWidget widget) OVERRIDE {
+    delegate_->OnAcceleratedWidgetAvailable(widget);
+  }
+
+  virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+  scoped_ptr<ui::PlatformEventSource> event_source_;
+  scoped_ptr<ui::PlatformWindow> platform_window_;
+  Delegate* delegate_;
+  gfx::Rect bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportX11);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(new PlatformViewportX11(delegate)).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/viewport_surface.cc b/mojo/services/native_viewport/viewport_surface.cc
new file mode 100644
index 0000000..8786e8c
--- /dev/null
+++ b/mojo/services/native_viewport/viewport_surface.cc
@@ -0,0 +1,111 @@
+// 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/native_viewport/viewport_surface.h"
+
+#include "base/bind.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+ViewportSurface::ViewportSurface(SurfacesService* surfaces_service,
+                                 Gpu* gpu_service,
+                                 const gfx::Size& size,
+                                 cc::SurfaceId child_id)
+    : gpu_service_(gpu_service),
+      widget_id_(0u),
+      size_(size),
+      child_id_(child_id),
+      weak_factory_(this) {
+  surfaces_service->CreateSurfaceConnection(
+      base::Bind(&ViewportSurface::OnSurfaceConnectionCreated,
+                 weak_factory_.GetWeakPtr()));
+}
+
+ViewportSurface::~ViewportSurface() {
+}
+
+void ViewportSurface::SetWidgetId(uint64_t widget_id) {
+  widget_id_ = widget_id;
+  if (id_allocator_)
+    BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::SetSize(const gfx::Size& size) {
+  if (size_ == size)
+    return;
+
+  if (id_.is_null())
+    return;
+
+  surface_->DestroySurface(SurfaceId::From(id_));
+  if (widget_id_)
+    BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::SetChildId(cc::SurfaceId child_id) {
+  child_id_ = child_id;
+  SubmitFrame();
+}
+
+void ViewportSurface::OnSurfaceConnectionCreated(SurfacePtr surface,
+                                                 uint32_t id_namespace) {
+  surface_ = surface.Pass();
+  surface_.set_client(this);
+  id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+  if (widget_id_ != 0u)
+    BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::BindSurfaceToNativeViewport() {
+  CommandBufferPtr cb;
+  gpu_service_->CreateOnscreenGLES2Context(
+      widget_id_, Size::From(size_), Get(&cb));
+
+  id_ = id_allocator_->GenerateId();
+  surface_->CreateGLES2BoundSurface(
+      cb.Pass(), SurfaceId::From(id_), Size::From(size_));
+
+  SubmitFrame();
+}
+
+void ViewportSurface::SubmitFrame() {
+  if (child_id_.is_null() || id_.is_null())
+    return;
+
+  SurfaceQuadStatePtr surface_quad_state = SurfaceQuadState::New();
+  surface_quad_state->surface = SurfaceId::From(child_id_);
+
+  gfx::Rect bounds(size_);
+
+  QuadPtr surface_quad = Quad::New();
+  surface_quad->material = Material::MATERIAL_SURFACE_CONTENT;
+  surface_quad->rect = Rect::From(bounds);
+  surface_quad->opaque_rect = Rect::From(bounds);
+  surface_quad->visible_rect = Rect::From(bounds);
+  surface_quad->needs_blending = true;
+  surface_quad->shared_quad_state_index = 0;
+  surface_quad->surface_quad_state = surface_quad_state.Pass();
+
+  PassPtr pass = CreateDefaultPass(1, bounds);
+
+  pass->quads.push_back(surface_quad.Pass());
+  pass->shared_quad_states.push_back(CreateDefaultSQS(size_));
+
+  FramePtr frame = Frame::New();
+  frame->passes.push_back(pass.Pass());
+  frame->resources.resize(0u);
+  surface_->SubmitFrame(SurfaceId::From(id_), frame.Pass());
+}
+
+void ViewportSurface::ReturnResources(Array<ReturnedResourcePtr> resources) {
+  // We never submit resources so we should never get any back.
+  DCHECK_EQ(0u, resources.size());
+}
+
+}  // namespace mojo
diff --git a/mojo/services/native_viewport/viewport_surface.h b/mojo/services/native_viewport/viewport_surface.h
new file mode 100644
index 0000000..2ca8105
--- /dev/null
+++ b/mojo/services/native_viewport/viewport_surface.h
@@ -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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+
+// This manages the surface that draws to a particular NativeViewport instance.
+class ViewportSurface : public SurfaceClient {
+ public:
+  ViewportSurface(SurfacesService* surfaces_service,
+                  Gpu* gpu_service,
+                  const gfx::Size& size,
+                  cc::SurfaceId child_id);
+  virtual ~ViewportSurface();
+
+  void SetWidgetId(uint64_t widget_id);
+  void SetSize(const gfx::Size& size);
+  void SetChildId(cc::SurfaceId child_id);
+
+ private:
+  void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  void BindSurfaceToNativeViewport();
+  void SubmitFrame();
+
+  // SurfaceClient implementation.
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+  SurfacePtr surface_;
+  Gpu* gpu_service_;
+  uint64_t widget_id_;
+  gfx::Size size_;
+  scoped_ptr<cc::SurfaceIdAllocator> id_allocator_;
+  cc::SurfaceId id_;
+  cc::SurfaceId child_id_;
+  base::WeakPtrFactory<ViewportSurface> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewportSurface);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn
new file mode 100644
index 0000000..d6ae427
--- /dev/null
+++ b/mojo/services/network/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_service
+shared_library("network") {
+  output_name = "mojo_network_service"
+
+  deps = [
+    ":lib",
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/public/cpp/network",
+    "//mojo/services/public/interfaces/network",
+  ]
+
+  sources = [ "main.cc" ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_service_lib
+source_set("lib") {
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/cpp/network",
+    "//mojo/services/public/interfaces/network",
+    "//net",
+    "//url",
+  ]
+
+  sources = [
+    "cookie_store_impl.cc",
+    "cookie_store_impl.h",
+    "network_context.cc",
+    "network_context.h",
+    "network_service_impl.cc",
+    "network_service_impl.h",
+    "url_loader_impl.cc",
+    "url_loader_impl.h",
+    "web_socket_impl.cc",
+    "web_socket_impl.h",
+  ]
+}
diff --git a/mojo/services/network/DEPS b/mojo/services/network/DEPS
new file mode 100644
index 0000000..cb237df
--- /dev/null
+++ b/mojo/services/network/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+mojo/application",
+  "+mojo/services",
+  "+net",
+]
diff --git a/mojo/services/network/cookie_store_impl.cc b/mojo/services/network/cookie_store_impl.cc
new file mode 100644
index 0000000..0340933
--- /dev/null
+++ b/mojo/services/network/cookie_store_impl.cc
@@ -0,0 +1,66 @@
+// 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/network/cookie_store_impl.h"
+
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/network/network_context.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_request_context.h"
+
+namespace mojo {
+namespace {
+
+void AdaptGetCookiesCallback(const Callback<void(String)>& callback,
+                             const std::string& cookies) {
+  callback.Run(cookies);
+}
+
+void AdaptSetCookieCallback(const Callback<void(bool)>& callback,
+                            bool success) {
+  callback.Run(success);
+}
+
+}  // namespace
+
+CookieStoreImpl::CookieStoreImpl(NetworkContext* context,
+                                 const GURL& origin)
+    : context_(context),
+      origin_(origin) {
+}
+
+CookieStoreImpl::~CookieStoreImpl() {
+}
+
+void CookieStoreImpl::Get(const String& url,
+                          const Callback<void(String)>& callback) {
+  // TODO(darin): Perform origin restriction.
+  net::CookieStore* store = context_->url_request_context()->cookie_store();
+  if (!store) {
+    callback.Run(String());
+    return;
+  }
+  store->GetCookiesWithOptionsAsync(
+      url.To<GURL>(),
+      net::CookieOptions(),
+      base::Bind(&AdaptGetCookiesCallback, callback));
+}
+
+void CookieStoreImpl::Set(const String& url,
+                          const String& cookie,
+                          const Callback<void(bool)>& callback) {
+  // TODO(darin): Perform origin restriction.
+  net::CookieStore* store = context_->url_request_context()->cookie_store();
+  if (!store) {
+    callback.Run(false);
+    return;
+  }
+  store->SetCookieWithOptionsAsync(
+      url.To<GURL>(),
+      cookie,
+      net::CookieOptions(),
+      base::Bind(&AdaptSetCookieCallback, callback));
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/cookie_store_impl.h b/mojo/services/network/cookie_store_impl.h
new file mode 100644
index 0000000..a2ff0b2
--- /dev/null
+++ b/mojo/services/network/cookie_store_impl.h
@@ -0,0 +1,34 @@
+// 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_NETWORK_COOKIE_STORE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_
+
+#include "mojo/services/public/interfaces/network/cookie_store.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+class NetworkContext;
+
+class CookieStoreImpl : public InterfaceImpl<CookieStore> {
+ public:
+  CookieStoreImpl(NetworkContext* context, const GURL& origin);
+  virtual ~CookieStoreImpl();
+
+ private:
+  // CookieStore methods:
+  virtual void Get(const String& url,
+                   const Callback<void(String)>& callback) OVERRIDE;
+  virtual void Set(const String& url, const String& cookie,
+                   const Callback<void(bool)>& callback) OVERRIDE;
+
+  NetworkContext* context_;
+  GURL origin_;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieStoreImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_
diff --git a/mojo/services/network/main.cc b/mojo/services/network/main.cc
new file mode 100644
index 0000000..8d637ac
--- /dev/null
+++ b/mojo/services/network/main.cc
@@ -0,0 +1,56 @@
+// 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/at_exit.h"
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.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/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/network/network_service_impl.h"
+
+class Delegate : public mojo::ApplicationDelegate,
+                 public mojo::InterfaceFactory<mojo::NetworkService> {
+ public:
+  Delegate() {}
+
+  virtual void Initialize(mojo::ApplicationImpl* app) OVERRIDE {
+    base::FilePath base_path;
+    CHECK(PathService::Get(base::DIR_TEMP, &base_path));
+    base_path = base_path.Append(FILE_PATH_LITERAL("network_service"));
+    context_.reset(new mojo::NetworkContext(base_path));
+  }
+
+  // mojo::ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) OVERRIDE {
+    DCHECK(context_);
+    connection->AddService(this);
+    return true;
+  }
+
+  // mojo::InterfaceFactory<mojo::NetworkService> implementation.
+  virtual void Create(
+      mojo::ApplicationConnection* connection,
+      mojo::InterfaceRequest<mojo::NetworkService> request) OVERRIDE {
+    mojo::BindToRequest(
+        new mojo::NetworkServiceImpl(connection, context_.get()), &request);
+  }
+
+ private:
+  scoped_ptr<mojo::NetworkContext> context_;
+};
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new Delegate);
+  runner.set_message_loop_type(base::MessageLoop::TYPE_IO);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/network/network_context.cc b/mojo/services/network/network_context.cc
new file mode 100644
index 0000000..a996522
--- /dev/null
+++ b/mojo/services/network/network_context.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 "mojo/services/network/network_context.h"
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "net/proxy/proxy_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace mojo {
+
+NetworkContext::NetworkContext(const base::FilePath& base_path) {
+  net::URLRequestContextBuilder builder;
+  builder.set_accept_language("en-us,en");
+  // TODO(darin): This is surely the wrong UA string.
+  builder.set_user_agent("Mojo/0.1");
+  builder.set_proxy_service(net::ProxyService::CreateDirect());
+  builder.set_transport_security_persister_path(base_path);
+
+  net::URLRequestContextBuilder::HttpCacheParams cache_params;
+  cache_params.path = base_path.Append(FILE_PATH_LITERAL("Cache"));
+  cache_params.type = net::URLRequestContextBuilder::HttpCacheParams::DISK;
+  builder.EnableHttpCache(cache_params);
+
+  builder.set_file_enabled(true);
+
+  url_request_context_.reset(builder.Build());
+}
+
+NetworkContext::~NetworkContext() {
+  // TODO(darin): Be careful about destruction order of member variables?
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/network_context.h b/mojo/services/network/network_context.h
new file mode 100644
index 0000000..19245f4
--- /dev/null
+++ b/mojo/services/network/network_context.h
@@ -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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace net {
+class URLRequestContext;
+}
+
+namespace mojo {
+
+class NetworkContext {
+ public:
+  explicit NetworkContext(const base::FilePath& base_path);
+  ~NetworkContext();
+
+  net::URLRequestContext* url_request_context() {
+    return url_request_context_.get();
+  }
+
+ private:
+  scoped_ptr<net::URLRequestContext> url_request_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkContext);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
diff --git a/mojo/services/network/network_service_impl.cc b/mojo/services/network/network_service_impl.cc
new file mode 100644
index 0000000..a576975
--- /dev/null
+++ b/mojo/services/network/network_service_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 "mojo/services/network/network_service_impl.h"
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/network/cookie_store_impl.h"
+#include "mojo/services/network/url_loader_impl.h"
+#include "mojo/services/network/web_socket_impl.h"
+
+namespace mojo {
+
+NetworkServiceImpl::NetworkServiceImpl(ApplicationConnection* connection,
+                                       NetworkContext* context)
+    : context_(context),
+      origin_(GURL(connection->GetRemoteApplicationURL()).GetOrigin()) {
+}
+
+NetworkServiceImpl::~NetworkServiceImpl() {
+}
+
+void NetworkServiceImpl::CreateURLLoader(InterfaceRequest<URLLoader> loader) {
+  // TODO(darin): Plumb origin_. Use for CORS.
+  BindToRequest(new URLLoaderImpl(context_), &loader);
+}
+
+void NetworkServiceImpl::GetCookieStore(InterfaceRequest<CookieStore> store) {
+  BindToRequest(new CookieStoreImpl(context_, origin_), &store);
+}
+
+void NetworkServiceImpl::CreateWebSocket(InterfaceRequest<WebSocket> socket) {
+  BindToRequest(new WebSocketImpl(context_), &socket);
+}
+
+void NetworkServiceImpl::CreateTCPBoundSocket(
+    NetAddressPtr local_address,
+    InterfaceRequest<TCPBoundSocket> bound_socket,
+    const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) {
+  // TODO(brettw) implement this.
+  callback.Run(NetworkErrorPtr(), NetAddressPtr());
+}
+
+void NetworkServiceImpl::CreateTCPClientSocket(
+    NetAddressPtr remote_address,
+    ScopedDataPipeConsumerHandle send_stream,
+    ScopedDataPipeProducerHandle receive_stream,
+    InterfaceRequest<TCPClientSocket> client_socket,
+    const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) {
+  // TODO(brettw) implement this.
+  callback.Run(NetworkErrorPtr(), NetAddressPtr());
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/network_service_impl.h b/mojo/services/network/network_service_impl.h
new file mode 100644
index 0000000..9088e36
--- /dev/null
+++ b/mojo/services/network/network_service_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+class ApplicationConnection;
+class NetworkContext;
+
+class NetworkServiceImpl : public InterfaceImpl<NetworkService> {
+ public:
+  NetworkServiceImpl(ApplicationConnection* connection,
+                     NetworkContext* context);
+  virtual ~NetworkServiceImpl();
+
+  // NetworkService methods:
+  virtual void CreateURLLoader(InterfaceRequest<URLLoader> loader) override;
+  virtual void GetCookieStore(InterfaceRequest<CookieStore> store) override;
+  virtual void CreateWebSocket(InterfaceRequest<WebSocket> socket) override;
+  virtual void CreateTCPBoundSocket(
+      NetAddressPtr local_address,
+      InterfaceRequest<TCPBoundSocket> bound_socket,
+      const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) override;
+  virtual void CreateTCPClientSocket(
+      NetAddressPtr remote_address,
+      ScopedDataPipeConsumerHandle send_stream,
+      ScopedDataPipeProducerHandle receive_stream,
+      InterfaceRequest<TCPClientSocket> client_socket,
+      const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) override;
+
+ private:
+  NetworkContext* context_;
+  GURL origin_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc
new file mode 100644
index 0000000..b5cbd0f
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.cc
@@ -0,0 +1,384 @@
+// 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/network/url_loader_impl.h"
+
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/network/network_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request_context.h"
+
+namespace mojo {
+namespace {
+
+const uint32_t kMaxReadSize = 64 * 1024;
+
+// Generates an URLResponsePtr from the response state of a net::URLRequest.
+URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
+  URLResponsePtr response(URLResponse::New());
+  response->url = String::From(url_request->url());
+
+  const net::HttpResponseHeaders* headers = url_request->response_headers();
+  if (headers) {
+    response->status_code = headers->response_code();
+    response->status_line = headers->GetStatusLine();
+
+    std::vector<String> header_lines;
+    void* iter = NULL;
+    std::string name, value;
+    while (headers->EnumerateHeaderLines(&iter, &name, &value))
+      header_lines.push_back(name + ": " + value);
+    if (!header_lines.empty())
+      response->headers.Swap(&header_lines);
+  }
+
+  std::string mime_type;
+  url_request->GetMimeType(&mime_type);
+  response->mime_type = mime_type;
+
+  std::string charset;
+  url_request->GetCharset(&charset);
+  response->charset = charset;
+
+  return response.Pass();
+}
+
+NetworkErrorPtr MakeNetworkError(int error_code) {
+  NetworkErrorPtr error = NetworkError::New();
+  error->code = error_code;
+  error->description = net::ErrorToString(error_code);
+  return error.Pass();
+}
+
+// Reads the request body upload data from a DataPipe.
+class UploadDataPipeElementReader : public net::UploadElementReader {
+ public:
+  UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
+      : pipe_(pipe.Pass()), num_bytes_(0) {}
+  virtual ~UploadDataPipeElementReader() {}
+
+  // UploadElementReader overrides:
+  virtual int Init(const net::CompletionCallback& callback) OVERRIDE {
+    offset_ = 0;
+    ReadDataRaw(pipe_.get(), NULL, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
+    return net::OK;
+  }
+  virtual uint64 GetContentLength() const OVERRIDE {
+    return num_bytes_;
+  }
+  virtual uint64 BytesRemaining() const OVERRIDE {
+    return num_bytes_ - offset_;
+  }
+  virtual bool IsInMemory() const OVERRIDE {
+    return false;
+  }
+  virtual int Read(net::IOBuffer* buf,
+                   int buf_length,
+                   const net::CompletionCallback& callback) OVERRIDE {
+    uint32_t bytes_read =
+        std::min(static_cast<uint32_t>(BytesRemaining()),
+                 static_cast<uint32_t>(buf_length));
+    if (bytes_read > 0) {
+      ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
+                  MOJO_READ_DATA_FLAG_NONE);
+    }
+
+    offset_ += bytes_read;
+    return bytes_read;
+  }
+
+ private:
+  ScopedDataPipeConsumerHandle pipe_;
+  uint32_t num_bytes_;
+  uint32_t offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
+};
+
+}  // namespace
+
+// Keeps track of a pending two-phase write on a DataPipeProducerHandle.
+class URLLoaderImpl::PendingWriteToDataPipe :
+    public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
+ public:
+  explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
+      : handle_(handle.Pass()),
+        buffer_(NULL) {
+  }
+
+  MojoResult BeginWrite(uint32_t* num_bytes) {
+    MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
+                                          MOJO_WRITE_DATA_FLAG_NONE);
+    if (*num_bytes > kMaxReadSize)
+      *num_bytes = kMaxReadSize;
+
+    return result;
+  }
+
+  ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
+    EndWriteDataRaw(handle_.get(), num_bytes);
+    buffer_ = NULL;
+    return handle_.Pass();
+  }
+
+  char* buffer() { return static_cast<char*>(buffer_); }
+
+ private:
+  friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
+
+  ~PendingWriteToDataPipe() {
+    if (handle_.is_valid())
+      EndWriteDataRaw(handle_.get(), 0);
+  }
+
+  ScopedDataPipeProducerHandle handle_;
+  void* buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
+};
+
+// Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
+// and makes its buffer available as a net::IOBuffer.
+class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
+ public:
+  DependentIOBuffer(PendingWriteToDataPipe* pending_write)
+      : net::WrappedIOBuffer(pending_write->buffer()),
+        pending_write_(pending_write) {
+  }
+ private:
+  virtual ~DependentIOBuffer() {}
+  scoped_refptr<PendingWriteToDataPipe> pending_write_;
+};
+
+URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
+    : context_(context),
+      response_body_buffer_size_(0),
+      auto_follow_redirects_(true),
+      weak_ptr_factory_(this) {
+}
+
+URLLoaderImpl::~URLLoaderImpl() {
+}
+
+void URLLoaderImpl::Start(URLRequestPtr request,
+                          const Callback<void(URLResponsePtr)>& callback) {
+  if (url_request_) {
+    SendError(net::ERR_UNEXPECTED, callback);
+    return;
+  }
+
+  if (!request) {
+    SendError(net::ERR_INVALID_ARGUMENT, callback);
+    return;
+  }
+
+  url_request_ = context_->url_request_context()->CreateRequest(
+      GURL(request->url),
+      net::DEFAULT_PRIORITY,
+      this,
+      NULL);
+  url_request_->set_method(request->method);
+  if (request->headers) {
+    net::HttpRequestHeaders headers;
+    for (size_t i = 0; i < request->headers.size(); ++i)
+      headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
+    url_request_->SetExtraRequestHeaders(headers);
+  }
+  if (request->body) {
+    ScopedVector<net::UploadElementReader> element_readers;
+    for (size_t i = 0; i < request->body.size(); ++i) {
+      element_readers.push_back(
+          new UploadDataPipeElementReader(request->body[i].Pass()));
+    }
+    url_request_->set_upload(make_scoped_ptr(
+        new net::UploadDataStream(element_readers.Pass(), 0)));
+  }
+  if (request->bypass_cache)
+    url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
+
+  callback_ = callback;
+  response_body_buffer_size_ = request->response_body_buffer_size;
+  auto_follow_redirects_ = request->auto_follow_redirects;
+
+  url_request_->Start();
+}
+
+void URLLoaderImpl::FollowRedirect(
+    const Callback<void(URLResponsePtr)>& callback) {
+  if (!url_request_) {
+    SendError(net::ERR_UNEXPECTED, callback);
+    return;
+  }
+
+  if (auto_follow_redirects_) {
+    DLOG(ERROR) << "Spurious call to FollowRedirect";
+    SendError(net::ERR_UNEXPECTED, callback);
+    return;
+  }
+
+  // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
+  url_request_->FollowDeferredRedirect();
+}
+
+void URLLoaderImpl::QueryStatus(
+    const Callback<void(URLLoaderStatusPtr)>& callback) {
+  URLLoaderStatusPtr status(URLLoaderStatus::New());
+  if (url_request_) {
+    status->is_loading = url_request_->is_pending();
+    if (!url_request_->status().is_success())
+      status->error = MakeNetworkError(url_request_->status().error());
+  } else {
+    status->is_loading = false;
+  }
+  // TODO(darin): Populate more status fields.
+  callback.Run(status.Pass());
+}
+
+void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
+                                       const net::RedirectInfo& redirect_info,
+                                       bool* defer_redirect) {
+  DCHECK(url_request == url_request_.get());
+  DCHECK(url_request->status().is_success());
+
+  if (auto_follow_redirects_)
+    return;
+
+  // Send the redirect response to the client, allowing them to inspect it and
+  // optionally follow the redirect.
+  *defer_redirect = true;
+
+  URLResponsePtr response = MakeURLResponse(url_request);
+  response->redirect_method = redirect_info.new_method;
+  response->redirect_url = String::From(redirect_info.new_url);
+
+  SendResponse(response.Pass());
+}
+
+void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
+  DCHECK(url_request == url_request_.get());
+
+  if (!url_request->status().is_success()) {
+    SendError(url_request->status().error(), callback_);
+    callback_ = Callback<void(URLResponsePtr)>();
+    return;
+  }
+
+  // TODO(darin): Add support for optional MIME sniffing.
+
+  DataPipe data_pipe;
+  // TODO(darin): Honor given buffer size.
+
+  URLResponsePtr response = MakeURLResponse(url_request);
+  response->body = data_pipe.consumer_handle.Pass();
+  response_body_stream_ = data_pipe.producer_handle.Pass();
+
+  SendResponse(response.Pass());
+
+  // Start reading...
+  ReadMore();
+}
+
+void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
+                                    int bytes_read) {
+  DCHECK(url_request == url_request_.get());
+
+  if (url_request->status().is_success()) {
+    DidRead(static_cast<uint32_t>(bytes_read), false);
+  } else {
+    pending_write_ = NULL;  // This closes the data pipe.
+  }
+}
+
+void URLLoaderImpl::SendError(
+    int error_code,
+    const Callback<void(URLResponsePtr)>& callback) {
+  URLResponsePtr response(URLResponse::New());
+  if (url_request_)
+    response->url = String::From(url_request_->url());
+  response->error = MakeNetworkError(error_code);
+  callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::SendResponse(URLResponsePtr response) {
+  Callback<void(URLResponsePtr)> callback;
+  std::swap(callback_, callback);
+  callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+  // TODO(darin): Handle a bad |result| value.
+  ReadMore();
+}
+
+void URLLoaderImpl::WaitToReadMore() {
+  handle_watcher_.Start(response_body_stream_.get(),
+                        MOJO_HANDLE_SIGNAL_WRITABLE,
+                        MOJO_DEADLINE_INDEFINITE,
+                        base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
+                                   weak_ptr_factory_.GetWeakPtr()));
+}
+
+void URLLoaderImpl::ReadMore() {
+  DCHECK(!pending_write_.get());
+
+  pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
+
+  uint32_t num_bytes;
+  MojoResult result = pending_write_->BeginWrite(&num_bytes);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    // The pipe is full. We need to wait for it to have more space.
+    response_body_stream_ = pending_write_->Complete(num_bytes);
+    pending_write_ = NULL;
+    WaitToReadMore();
+    return;
+  }
+  if (result != MOJO_RESULT_OK) {
+    // The response body stream is in a bad state. Bail.
+    // TODO(darin): How should this be communicated to our client?
+    return;
+  }
+  CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
+
+  scoped_refptr<net::IOBuffer> buf =
+      new DependentIOBuffer(pending_write_.get());
+
+  int bytes_read;
+  url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read);
+
+  // Drop our reference to the buffer.
+  buf = NULL;
+
+  if (url_request_->status().is_io_pending()) {
+    // Wait for OnReadCompleted.
+  } else if (url_request_->status().is_success() && bytes_read > 0) {
+    DidRead(static_cast<uint32_t>(bytes_read), true);
+  } else {
+    pending_write_->Complete(0);
+    pending_write_ = NULL;  // This closes the data pipe.
+  }
+}
+
+void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
+  DCHECK(url_request_->status().is_success());
+
+  response_body_stream_ = pending_write_->Complete(num_bytes);
+  pending_write_ = NULL;
+
+  if (completed_synchronously) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
+  } else {
+    ReadMore();
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h
new file mode 100644
index 0000000..5c5af8a
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.h
@@ -0,0 +1,70 @@
+// 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_NETWORK_URL_LOADER_IMPL_H_
+#define MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+
+namespace mojo {
+class NetworkContext;
+
+class URLLoaderImpl : public InterfaceImpl<URLLoader>,
+                      public net::URLRequest::Delegate {
+ public:
+  explicit URLLoaderImpl(NetworkContext* context);
+  virtual ~URLLoaderImpl();
+
+ private:
+  class PendingWriteToDataPipe;
+  class DependentIOBuffer;
+
+  // URLLoader methods:
+  virtual void Start(
+      URLRequestPtr request,
+      const Callback<void(URLResponsePtr)>& callback) OVERRIDE;
+  virtual void FollowRedirect(
+      const Callback<void(URLResponsePtr)>& callback) OVERRIDE;
+  virtual void QueryStatus(
+      const Callback<void(URLLoaderStatusPtr)>& callback) OVERRIDE;
+
+  // net::URLRequest::Delegate methods:
+  virtual void OnReceivedRedirect(net::URLRequest* url_request,
+                                  const net::RedirectInfo& redirect_info,
+                                  bool* defer_redirect) OVERRIDE;
+  virtual void OnResponseStarted(net::URLRequest* url_request) OVERRIDE;
+  virtual void OnReadCompleted(net::URLRequest* url_request, int bytes_read)
+      OVERRIDE;
+
+  void SendError(
+      int error,
+      const Callback<void(URLResponsePtr)>& callback);
+  void SendResponse(URLResponsePtr response);
+  void OnResponseBodyStreamReady(MojoResult result);
+  void WaitToReadMore();
+  void ReadMore();
+  void DidRead(uint32_t num_bytes, bool completed_synchronously);
+
+  NetworkContext* context_;
+  scoped_ptr<net::URLRequest> url_request_;
+  Callback<void(URLResponsePtr)> callback_;
+  ScopedDataPipeProducerHandle response_body_stream_;
+  scoped_refptr<PendingWriteToDataPipe> pending_write_;
+  common::HandleWatcher handle_watcher_;
+  uint32 response_body_buffer_size_;
+  bool auto_follow_redirects_;
+
+  base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
diff --git a/mojo/services/network/web_socket_impl.cc b/mojo/services/network/web_socket_impl.cc
new file mode 100644
index 0000000..7ba9b22
--- /dev/null
+++ b/mojo/services/network/web_socket_impl.cc
@@ -0,0 +1,239 @@
+// 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/network/web_socket_impl.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/public/cpp/network/web_socket_read_queue.h"
+#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
+#include "net/websockets/websocket_channel.h"
+#include "net/websockets/websocket_errors.h"
+#include "net/websockets/websocket_event_interface.h"
+#include "net/websockets/websocket_frame.h"  // for WebSocketFrameHeader::OpCode
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
+#include "url/origin.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<net::WebSocketFrameHeader::OpCode,
+                     WebSocket::MessageType> {
+  static net::WebSocketFrameHeader::OpCode Convert(
+      WebSocket::MessageType type) {
+    DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION ||
+           type == WebSocket::MESSAGE_TYPE_TEXT ||
+           type == WebSocket::MESSAGE_TYPE_BINARY);
+    typedef net::WebSocketFrameHeader::OpCode OpCode;
+    // These compile asserts verify that the same underlying values are used for
+    // both types, so we can simply cast between them.
+    COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_CONTINUATION) ==
+                       net::WebSocketFrameHeader::kOpCodeContinuation,
+                   enum_values_must_match_for_opcode_continuation);
+    COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_TEXT) ==
+                       net::WebSocketFrameHeader::kOpCodeText,
+                   enum_values_must_match_for_opcode_text);
+    COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_BINARY) ==
+                       net::WebSocketFrameHeader::kOpCodeBinary,
+                   enum_values_must_match_for_opcode_binary);
+    return static_cast<OpCode>(type);
+  }
+};
+
+template <>
+struct TypeConverter<WebSocket::MessageType,
+                     net::WebSocketFrameHeader::OpCode> {
+  static WebSocket::MessageType Convert(
+      net::WebSocketFrameHeader::OpCode type) {
+    DCHECK(type == net::WebSocketFrameHeader::kOpCodeContinuation ||
+           type == net::WebSocketFrameHeader::kOpCodeText ||
+           type == net::WebSocketFrameHeader::kOpCodeBinary);
+    return static_cast<WebSocket::MessageType>(type);
+  }
+};
+
+namespace {
+
+typedef net::WebSocketEventInterface::ChannelState ChannelState;
+
+struct WebSocketEventHandler : public net::WebSocketEventInterface {
+ public:
+  WebSocketEventHandler(WebSocketClientPtr client)
+      : client_(client.Pass()) {
+  }
+  virtual ~WebSocketEventHandler() {}
+
+ private:
+  // net::WebSocketEventInterface methods:
+  virtual ChannelState OnAddChannelResponse(
+      bool fail,
+      const std::string& selected_subprotocol,
+      const std::string& extensions) OVERRIDE;
+  virtual ChannelState OnDataFrame(bool fin,
+                                   WebSocketMessageType type,
+                                   const std::vector<char>& data) OVERRIDE;
+  virtual ChannelState OnClosingHandshake() OVERRIDE;
+  virtual ChannelState OnFlowControl(int64 quota) OVERRIDE;
+  virtual ChannelState OnDropChannel(bool was_clean,
+                                     uint16 code,
+                                     const std::string& reason) OVERRIDE;
+  virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE;
+  virtual ChannelState OnStartOpeningHandshake(
+      scoped_ptr<net::WebSocketHandshakeRequestInfo> request) OVERRIDE;
+  virtual ChannelState OnFinishOpeningHandshake(
+      scoped_ptr<net::WebSocketHandshakeResponseInfo> response) OVERRIDE;
+  virtual ChannelState OnSSLCertificateError(
+      scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+      const GURL& url,
+      const net::SSLInfo& ssl_info,
+      bool fatal) OVERRIDE;
+
+  // Called once we've written to |receive_stream_|.
+  void DidWriteToReceiveStream(bool fin,
+                               net::WebSocketFrameHeader::OpCode type,
+                               uint32_t num_bytes,
+                               const char* buffer);
+  WebSocketClientPtr client_;
+  ScopedDataPipeProducerHandle receive_stream_;
+  scoped_ptr<WebSocketWriteQueue> write_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketEventHandler);
+};
+
+ChannelState WebSocketEventHandler::OnAddChannelResponse(
+    bool fail,
+    const std::string& selected_protocol,
+    const std::string& extensions) {
+  DataPipe data_pipe;
+  receive_stream_ = data_pipe.producer_handle.Pass();
+  write_queue_.reset(new WebSocketWriteQueue(receive_stream_.get()));
+  client_->DidConnect(
+      fail, selected_protocol, extensions, data_pipe.consumer_handle.Pass());
+  if (fail)
+    return WebSocketEventInterface::CHANNEL_DELETED;
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnDataFrame(
+    bool fin,
+    net::WebSocketFrameHeader::OpCode type,
+    const std::vector<char>& data) {
+  uint32_t size = static_cast<uint32_t>(data.size());
+  write_queue_->Write(
+      &data[0], size,
+      base::Bind(&WebSocketEventHandler::DidWriteToReceiveStream,
+                 base::Unretained(this),
+                 fin, type, size));
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnClosingHandshake() {
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnFlowControl(int64 quota) {
+  client_->DidReceiveFlowControl(quota);
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnDropChannel(bool was_clean,
+                                                  uint16 code,
+                                                  const std::string& reason) {
+  client_->DidClose(was_clean, code, reason);
+  return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+ChannelState WebSocketEventHandler::OnFailChannel(const std::string& message) {
+  client_->DidFail(message);
+  return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+ChannelState WebSocketEventHandler::OnStartOpeningHandshake(
+    scoped_ptr<net::WebSocketHandshakeRequestInfo> request) {
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnFinishOpeningHandshake(
+    scoped_ptr<net::WebSocketHandshakeResponseInfo> response) {
+  return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnSSLCertificateError(
+    scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+    const GURL& url,
+    const net::SSLInfo& ssl_info,
+    bool fatal) {
+  client_->DidFail("SSL Error");
+  return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+void WebSocketEventHandler::DidWriteToReceiveStream(
+    bool fin,
+    net::WebSocketFrameHeader::OpCode type,
+    uint32_t num_bytes,
+    const char* buffer) {
+  client_->DidReceiveData(
+      fin, ConvertTo<WebSocket::MessageType>(type), num_bytes);
+}
+
+}  // namespace mojo
+
+WebSocketImpl::WebSocketImpl(NetworkContext* context) : context_(context) {
+}
+
+WebSocketImpl::~WebSocketImpl() {
+}
+
+void WebSocketImpl::Connect(const String& url,
+                            Array<String> protocols,
+                            const String& origin,
+                            ScopedDataPipeConsumerHandle send_stream,
+                            WebSocketClientPtr client) {
+  DCHECK(!channel_);
+  send_stream_ = send_stream.Pass();
+  read_queue_.reset(new WebSocketReadQueue(send_stream_.get()));
+  scoped_ptr<net::WebSocketEventInterface> event_interface(
+      new WebSocketEventHandler(client.Pass()));
+  channel_.reset(new net::WebSocketChannel(event_interface.Pass(),
+                                           context_->url_request_context()));
+  channel_->SendAddChannelRequest(GURL(url.get()),
+                                  protocols.To<std::vector<std::string> >(),
+                                  url::Origin(origin.get()));
+}
+
+void WebSocketImpl::Send(bool fin,
+                         WebSocket::MessageType type,
+                         uint32_t num_bytes) {
+  DCHECK(channel_);
+  read_queue_->Read(num_bytes,
+                    base::Bind(&WebSocketImpl::DidReadFromSendStream,
+                               base::Unretained(this),
+                               fin, type, num_bytes));
+}
+
+void WebSocketImpl::FlowControl(int64_t quota) {
+  DCHECK(channel_);
+  channel_->SendFlowControl(quota);
+}
+
+void WebSocketImpl::Close(uint16_t code, const String& reason) {
+  DCHECK(channel_);
+  channel_->StartClosingHandshake(code, reason);
+}
+
+void WebSocketImpl::DidReadFromSendStream(bool fin,
+                                          WebSocket::MessageType type,
+                                          uint32_t num_bytes,
+                                          const char* data) {
+  std::vector<char> buffer(num_bytes);
+  memcpy(&buffer[0], data, num_bytes);
+  DCHECK(channel_);
+  channel_->SendFrame(
+      fin, ConvertTo<net::WebSocketFrameHeader::OpCode>(type), buffer);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/web_socket_impl.h b/mojo/services/network/web_socket_impl.h
new file mode 100644
index 0000000..bd8a27d
--- /dev/null
+++ b/mojo/services/network/web_socket_impl.h
@@ -0,0 +1,56 @@
+// 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_NETWORK_WEB_SOCKET_IMPL_H_
+#define MOJO_SERVICES_NETWORK_WEB_SOCKET_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/web_socket.mojom.h"
+
+namespace net {
+class WebSocketChannel;
+}  // namespace net
+
+namespace mojo {
+class NetworkContext;
+class WebSocketReadQueue;
+
+// Forms a bridge between the WebSocket mojo interface and the net::WebSocket
+// implementation.
+class WebSocketImpl : public InterfaceImpl<WebSocket> {
+ public:
+  explicit WebSocketImpl(NetworkContext* context);
+  virtual ~WebSocketImpl();
+
+ private:
+  // WebSocket methods:
+  virtual void Connect(const String& url,
+                       Array<String> protocols,
+                       const String& origin,
+                       ScopedDataPipeConsumerHandle send_stream,
+                       WebSocketClientPtr client) OVERRIDE;
+  virtual void Send(bool fin,
+                    WebSocket::MessageType type,
+                    uint32_t num_bytes) OVERRIDE;
+  virtual void FlowControl(int64_t quota) OVERRIDE;
+  virtual void Close(uint16_t code, const String& reason) OVERRIDE;
+
+  // Called with the data to send once it has been read from |send_stream_|.
+  void DidReadFromSendStream(bool fin,
+                             WebSocket::MessageType type,
+                             uint32_t num_bytes,
+                             const char* data);
+
+  // The channel we use to send events to the network.
+  scoped_ptr<net::WebSocketChannel> channel_;
+  ScopedDataPipeConsumerHandle send_stream_;
+  scoped_ptr<WebSocketReadQueue> read_queue_;
+  NetworkContext* context_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_WEB_SOCKET_IMPL_H_
diff --git a/mojo/services/public/cpp/geometry/BUILD.gn b/mojo/services/public/cpp/geometry/BUILD.gn
new file mode 100644
index 0000000..c87adf3
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/BUILD.gn
@@ -0,0 +1,29 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_geometry_lib
+component("geometry") {
+  output_name = "mojo_geometry_lib"
+
+  public_deps = [
+    "//ui/gfx",
+  ]
+  deps = [
+    "//skia",
+    "//ui/gfx/geometry",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_component",
+    "//mojo/services/public/interfaces/geometry",
+  ]
+
+  defines = [
+    "MOJO_GEOMETRY_IMPLEMENTATION",
+  ]
+
+  sources = [
+    "lib/geometry_type_converters.cc",
+    "geometry_type_converters.h",
+    "mojo_geometry_export.h",
+  ]
+}
diff --git a/mojo/services/public/cpp/geometry/DEPS b/mojo/services/public/cpp/geometry/DEPS
new file mode 100644
index 0000000..3e03810
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ui/gfx/geometry",
+  "+ui/gfx/transform.h",
+]
diff --git a/mojo/services/public/cpp/geometry/geometry_type_converters.h b/mojo/services/public/cpp/geometry/geometry_type_converters.h
new file mode 100644
index 0000000..2f2cc1b
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/geometry_type_converters.h
@@ -0,0 +1,74 @@
+// 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_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
+
+#include "mojo/services/public/cpp/geometry/mojo_geometry_export.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<PointPtr, gfx::Point> {
+  static PointPtr Convert(const gfx::Point& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Point, PointPtr> {
+  static gfx::Point Convert(const PointPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<PointFPtr, gfx::PointF> {
+  static PointFPtr Convert(const gfx::PointF& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::PointF, PointFPtr> {
+  static gfx::PointF Convert(const PointFPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<SizePtr, gfx::Size> {
+  static SizePtr Convert(const gfx::Size& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Size, SizePtr> {
+  static gfx::Size Convert(const SizePtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<RectPtr, gfx::Rect> {
+  static RectPtr Convert(const gfx::Rect& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Rect, RectPtr> {
+  static gfx::Rect Convert(const RectPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<RectFPtr, gfx::RectF> {
+  static RectFPtr Convert(const gfx::RectF& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::RectF, RectFPtr> {
+  static gfx::RectF Convert(const RectFPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<TransformPtr, gfx::Transform> {
+  static TransformPtr Convert(const gfx::Transform& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Transform, TransformPtr> {
+  static gfx::Transform Convert(const TransformPtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc b/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc
new file mode 100644
index 0000000..9a40c15
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/lib/geometry_type_converters.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 "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+
+namespace mojo {
+
+// static
+PointPtr TypeConverter<PointPtr, gfx::Point>::Convert(const gfx::Point& input) {
+  PointPtr point(Point::New());
+  point->x = input.x();
+  point->y = input.y();
+  return point.Pass();
+}
+
+// static
+gfx::Point TypeConverter<gfx::Point, PointPtr>::Convert(const PointPtr& input) {
+  if (input.is_null())
+    return gfx::Point();
+  return gfx::Point(input->x, input->y);
+}
+
+// static
+PointFPtr TypeConverter<PointFPtr, gfx::PointF>::Convert(
+    const gfx::PointF& input) {
+  PointFPtr point(PointF::New());
+  point->x = input.x();
+  point->y = input.y();
+  return point.Pass();
+}
+
+// static
+gfx::PointF TypeConverter<gfx::PointF, PointFPtr>::Convert(
+    const PointFPtr& input) {
+  if (input.is_null())
+    return gfx::PointF();
+  return gfx::PointF(input->x, input->y);
+}
+
+// static
+SizePtr TypeConverter<SizePtr, gfx::Size>::Convert(const gfx::Size& input) {
+  SizePtr size(Size::New());
+  size->width = input.width();
+  size->height = input.height();
+  return size.Pass();
+}
+
+// static
+gfx::Size TypeConverter<gfx::Size, SizePtr>::Convert(const SizePtr& input) {
+  if (input.is_null())
+    return gfx::Size();
+  return gfx::Size(input->width, input->height);
+}
+
+// static
+RectPtr TypeConverter<RectPtr, gfx::Rect>::Convert(const gfx::Rect& input) {
+  RectPtr rect(Rect::New());
+  rect->x = input.x();
+  rect->y = input.y();
+  rect->width = input.width();
+  rect->height = input.height();
+  return rect.Pass();
+}
+
+// static
+gfx::Rect TypeConverter<gfx::Rect, RectPtr>::Convert(const RectPtr& input) {
+  if (input.is_null())
+    return gfx::Rect();
+  return gfx::Rect(input->x, input->y, input->width, input->height);
+}
+
+// static
+RectFPtr TypeConverter<RectFPtr, gfx::RectF>::Convert(const gfx::RectF& input) {
+  RectFPtr rect(RectF::New());
+  rect->x = input.x();
+  rect->y = input.y();
+  rect->width = input.width();
+  rect->height = input.height();
+  return rect.Pass();
+}
+
+// static
+gfx::RectF TypeConverter<gfx::RectF, RectFPtr>::Convert(const RectFPtr& input) {
+  if (input.is_null())
+    return gfx::RectF();
+  return gfx::RectF(input->x, input->y, input->width, input->height);
+}
+
+// static
+TransformPtr TypeConverter<TransformPtr, gfx::Transform>::Convert(
+    const gfx::Transform& input) {
+  std::vector<float> storage(16);
+  input.matrix().asRowMajorf(&storage[0]);
+  mojo::Array<float> matrix;
+  matrix.Swap(&storage);
+  TransformPtr transform(Transform::New());
+  transform->matrix = matrix.Pass();
+  return transform.Pass();
+}
+
+// static
+gfx::Transform TypeConverter<gfx::Transform, TransformPtr>::Convert(
+    const TransformPtr& input) {
+  if (input.is_null())
+    return gfx::Transform();
+  gfx::Transform transform(gfx::Transform::kSkipInitialization);
+  transform.matrix().setRowMajorf(&input->matrix.storage()[0]);
+  return transform;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/geometry/mojo_geometry_export.h b/mojo/services/public/cpp/geometry/mojo_geometry_export.h
new file mode 100644
index 0000000..f21aebc
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/mojo_geometry_export.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 MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_GEOMETRY_IMPLEMENTATION)
+#define MOJO_GEOMETRY_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GEOMETRY_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_GEOMETRY_IMPLEMENTATION)
+#define MOJO_GEOMETRY_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GEOMETRY_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_GEOMETRY_EXPORT
+#endif
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
diff --git a/mojo/services/public/cpp/input_events/BUILD.gn b/mojo/services/public/cpp/input_events/BUILD.gn
new file mode 100644
index 0000000..2b47ece
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/BUILD.gn
@@ -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.
+
+component("input_events") {
+  sources = [
+    "lib/input_events_type_converters.cc",
+    "lib/mojo_extended_key_event_data.cc",
+    "lib/mojo_extended_key_event_data.h",
+    "mojo_input_events_export.h",
+  ]
+
+  defines = [
+    "MOJO_INPUT_EVENTS_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//base",
+    "//ui/events",
+    "//ui/gfx/geometry",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_component",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+  ]
+}
diff --git a/mojo/services/public/cpp/input_events/DEPS b/mojo/services/public/cpp/input_events/DEPS
new file mode 100644
index 0000000..fe1d98e
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/events",
+]
diff --git a/mojo/services/public/cpp/input_events/input_events_type_converters.h b/mojo/services/public/cpp/input_events/input_events_type_converters.h
new file mode 100644
index 0000000..bbbbe10
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/input_events_type_converters.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_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/cpp/input_events/mojo_input_events_export.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventType, ui::EventType> {
+  static EventType Convert(ui::EventType type);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<ui::EventType, EventType> {
+  static ui::EventType Convert(EventType type);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::Event> {
+  static EventPtr Convert(const ui::Event& input);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::KeyEvent> {
+  static EventPtr Convert(const ui::KeyEvent& input);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<scoped_ptr<ui::Event>, EventPtr> {
+  static scoped_ptr<ui::Event> Convert(const EventPtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/input_events/lib/input_event_names.h b/mojo/services/public/cpp/input_events/lib/input_event_names.h
new file mode 100644
index 0000000..3fd6ef6
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/input_event_names.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.
+
+MOJO_INPUT_EVENT_NAME(UNKNOWN);
+MOJO_INPUT_EVENT_NAME(MOUSE_PRESSED);
+MOJO_INPUT_EVENT_NAME(MOUSE_DRAGGED);
+MOJO_INPUT_EVENT_NAME(MOUSE_RELEASED);
+MOJO_INPUT_EVENT_NAME(MOUSE_MOVED);
+MOJO_INPUT_EVENT_NAME(MOUSE_ENTERED);
+MOJO_INPUT_EVENT_NAME(MOUSE_EXITED);
+MOJO_INPUT_EVENT_NAME(KEY_PRESSED);
+MOJO_INPUT_EVENT_NAME(KEY_RELEASED);
+MOJO_INPUT_EVENT_NAME(MOUSEWHEEL);
+MOJO_INPUT_EVENT_NAME(MOUSE_CAPTURE_CHANGED);
+MOJO_INPUT_EVENT_NAME(TOUCH_RELEASED);
+MOJO_INPUT_EVENT_NAME(TOUCH_PRESSED);
+MOJO_INPUT_EVENT_NAME(TOUCH_MOVED);
+MOJO_INPUT_EVENT_NAME(TOUCH_CANCELLED);
+MOJO_INPUT_EVENT_NAME(DROP_TARGET_EVENT);
+MOJO_INPUT_EVENT_NAME(TRANSLATED_KEY_PRESS);
+MOJO_INPUT_EVENT_NAME(TRANSLATED_KEY_RELEASE);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_UPDATE);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_DOWN);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_CANCEL);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_UNCONFIRMED);
+MOJO_INPUT_EVENT_NAME(GESTURE_DOUBLE_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_TWO_FINGER_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_UPDATE);
+MOJO_INPUT_EVENT_NAME(GESTURE_LONG_PRESS);
+MOJO_INPUT_EVENT_NAME(GESTURE_LONG_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_SWIPE);
+MOJO_INPUT_EVENT_NAME(GESTURE_SHOW_PRESS);
+MOJO_INPUT_EVENT_NAME(GESTURE_WIN8_EDGE_SWIPE);
+MOJO_INPUT_EVENT_NAME(SCROLL);
+MOJO_INPUT_EVENT_NAME(SCROLL_FLING_START);
+MOJO_INPUT_EVENT_NAME(SCROLL_FLING_CANCEL);
+MOJO_INPUT_EVENT_NAME(CANCEL_MODE);
+MOJO_INPUT_EVENT_NAME(UMA_DATA);
diff --git a/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc b/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
new file mode 100644
index 0000000..340e200
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
@@ -0,0 +1,245 @@
+// 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/public/cpp/input_events/input_events_type_converters.h"
+
+#if defined(USE_X11)
+#include <X11/extensions/XInput2.h>
+#include <X11/Xlib.h>
+#endif
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace mojo {
+
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_NONE) ==
+               static_cast<int32>(ui::EF_NONE),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_CAPS_LOCK_DOWN) ==
+               static_cast<int32>(ui::EF_CAPS_LOCK_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_SHIFT_DOWN) ==
+               static_cast<int32>(ui::EF_SHIFT_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_CONTROL_DOWN) ==
+               static_cast<int32>(ui::EF_CONTROL_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_ALT_DOWN) ==
+               static_cast<int32>(ui::EF_ALT_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_LEFT_MOUSE_BUTTON) ==
+               static_cast<int32>(ui::EF_LEFT_MOUSE_BUTTON),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_MIDDLE_MOUSE_BUTTON) ==
+               static_cast<int32>(ui::EF_MIDDLE_MOUSE_BUTTON),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_RIGHT_MOUSE_BUTTON) ==
+               static_cast<int32>(ui::EF_RIGHT_MOUSE_BUTTON),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_COMMAND_DOWN) ==
+               static_cast<int32>(ui::EF_COMMAND_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_EXTENDED) ==
+               static_cast<int32>(ui::EF_EXTENDED),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_IS_SYNTHESIZED) ==
+               static_cast<int32>(ui::EF_IS_SYNTHESIZED),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_ALTGR_DOWN) ==
+               static_cast<int32>(ui::EF_ALTGR_DOWN),
+               event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_MOD3_DOWN) ==
+               static_cast<int32>(ui::EF_MOD3_DOWN),
+               event_flags_should_match);
+
+
+// static
+EventType TypeConverter<EventType, ui::EventType>::Convert(ui::EventType type) {
+#define MOJO_INPUT_EVENT_NAME(name) case ui::ET_##name: return EVENT_TYPE_##name
+
+  switch (type) {
+#include "mojo/services/public/cpp/input_events/lib/input_event_names.h"
+    case ui::ET_LAST:
+      NOTREACHED();
+      break;
+  }
+
+#undef MOJO_INPUT_EVENT_NAME
+
+  NOTREACHED();
+  return EVENT_TYPE_UNKNOWN;
+}
+
+// static
+ui::EventType TypeConverter<ui::EventType, EventType>::Convert(EventType type) {
+#define MOJO_INPUT_EVENT_NAME(name) case EVENT_TYPE_##name: return ui::ET_##name
+
+  switch (type) {
+#include "mojo/services/public/cpp/input_events/lib/input_event_names.h"
+  }
+
+#undef MOJO_INPUT_EVENT_NAME
+
+  NOTREACHED();
+  return ui::ET_UNKNOWN;
+}
+
+// static
+EventPtr TypeConverter<EventPtr, ui::Event>::Convert(const ui::Event& input) {
+  EventPtr event(Event::New());
+  event->action = ConvertTo<EventType>(input.type());
+  event->flags = EventFlags(input.flags());
+  event->time_stamp = input.time_stamp().ToInternalValue();
+
+  if (input.IsMouseEvent() || input.IsTouchEvent()) {
+    const ui::LocatedEvent* located_event =
+        static_cast<const ui::LocatedEvent*>(&input);
+
+    LocationDataPtr location_data(LocationData::New());
+    location_data->in_view_location = Point::From(located_event->location());
+    if (input.HasNativeEvent()) {
+      location_data->screen_location =
+          Point::From(ui::EventSystemLocationFromNative(input.native_event()));
+    }
+
+    event->location_data = location_data.Pass();
+  }
+
+  if (input.IsTouchEvent()) {
+    const ui::TouchEvent* touch_event =
+        static_cast<const ui::TouchEvent*>(&input);
+    TouchDataPtr touch_data(TouchData::New());
+    touch_data->pointer_id = touch_event->touch_id();
+    event->touch_data = touch_data.Pass();
+  } else if (input.IsKeyEvent()) {
+    const ui::KeyEvent* key_event = static_cast<const ui::KeyEvent*>(&input);
+    KeyDataPtr key_data(KeyData::New());
+    key_data->key_code = key_event->GetConflatedWindowsKeyCode();
+    key_data->native_key_code = key_event->platform_keycode();
+    key_data->is_char = key_event->is_char();
+    key_data->character = key_event->GetCharacter();
+
+    if (key_event->extended_key_event_data()) {
+      const MojoExtendedKeyEventData* data =
+          static_cast<const MojoExtendedKeyEventData*>(
+              key_event->extended_key_event_data());
+      key_data->windows_key_code = static_cast<mojo::KeyboardCode>(
+          data->windows_key_code());
+      key_data->text = data->text();
+      key_data->unmodified_text = data->unmodified_text();
+    } else {
+      key_data->windows_key_code = static_cast<mojo::KeyboardCode>(
+          key_event->GetLocatedWindowsKeyboardCode());
+      key_data->text = key_event->GetText();
+      key_data->unmodified_text = key_event->GetUnmodifiedText();
+    }
+
+    event->key_data = key_data.Pass();
+  } else if (input.IsMouseWheelEvent()) {
+    const ui::MouseWheelEvent* wheel_event =
+        static_cast<const ui::MouseWheelEvent*>(&input);
+    MouseWheelDataPtr wheel_data(MouseWheelData::New());
+    wheel_data->x_offset = wheel_event->x_offset();
+    wheel_data->y_offset = wheel_event->y_offset();
+    event->wheel_data = wheel_data.Pass();
+  }
+  return event.Pass();
+}
+
+// static
+EventPtr TypeConverter<EventPtr, ui::KeyEvent>::Convert(
+    const ui::KeyEvent& input) {
+  return Event::From(static_cast<const ui::Event&>(input));
+}
+
+// static
+scoped_ptr<ui::Event> TypeConverter<scoped_ptr<ui::Event>, EventPtr>::Convert(
+    const EventPtr& input) {
+  scoped_ptr<ui::Event> ui_event;
+  ui::EventType ui_event_type = ConvertTo<ui::EventType>(input->action);
+
+  gfx::Point location;
+  if (!input->location_data.is_null() &&
+      !input->location_data->in_view_location.is_null()) {
+    location = input->location_data->in_view_location.To<gfx::Point>();
+  }
+
+  switch (input->action) {
+    case ui::ET_KEY_PRESSED:
+    case ui::ET_KEY_RELEASED: {
+      scoped_ptr<ui::KeyEvent> key_event;
+      if (input->key_data->is_char) {
+        key_event.reset(new ui::KeyEvent(
+            static_cast<base::char16>(input->key_data->character),
+            static_cast<ui::KeyboardCode>(
+                input->key_data->key_code),
+            input->flags));
+      } else {
+        key_event.reset(new ui::KeyEvent(
+            ui_event_type,
+            static_cast<ui::KeyboardCode>(
+                input->key_data->key_code),
+            input->flags));
+      }
+      key_event->SetExtendedKeyEventData(scoped_ptr<ui::ExtendedKeyEventData>(
+          new MojoExtendedKeyEventData(
+              static_cast<int32_t>(input->key_data->windows_key_code),
+              input->key_data->text,
+              input->key_data->unmodified_text)));
+      key_event->set_platform_keycode(input->key_data->native_key_code);
+      ui_event = key_event.PassAs<ui::KeyEvent>();
+      break;
+    }
+    case EVENT_TYPE_MOUSE_PRESSED:
+    case EVENT_TYPE_MOUSE_DRAGGED:
+    case EVENT_TYPE_MOUSE_RELEASED:
+    case EVENT_TYPE_MOUSE_MOVED:
+    case EVENT_TYPE_MOUSE_ENTERED:
+    case EVENT_TYPE_MOUSE_EXITED: {
+      // TODO: last flags isn't right. Need to send changed_flags.
+      ui_event.reset(new ui::MouseEvent(
+                         ui_event_type,
+                         location,
+                         location,
+                         ui::EventFlags(input->flags),
+                         ui::EventFlags(input->flags)));
+      break;
+    }
+    case EVENT_TYPE_MOUSEWHEEL: {
+      const gfx::Vector2d offset(input->wheel_data->x_offset,
+                                 input->wheel_data->y_offset);
+      ui_event.reset(new ui::MouseWheelEvent(offset,
+                                             location,
+                                             location,
+                                             ui::EventFlags(input->flags),
+                                             ui::EventFlags(input->flags)));
+      break;
+    }
+    case EVENT_TYPE_TOUCH_MOVED:
+    case EVENT_TYPE_TOUCH_PRESSED:
+    case EVENT_TYPE_TOUCH_CANCELLED:
+    case EVENT_TYPE_TOUCH_RELEASED: {
+      ui_event.reset(new ui::TouchEvent(
+                         ui_event_type,
+                         location,
+                         ui::EventFlags(input->flags),
+                         input->touch_data->pointer_id,
+                         base::TimeDelta::FromInternalValue(input->time_stamp),
+                         0.f, 0.f, 0.f, 0.f));
+      break;
+    }
+    default:
+      // TODO: support other types.
+      // NOTIMPLEMENTED();
+      ;
+  }
+  // TODO: need to support time_stamp.
+  return ui_event.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc
new file mode 100644
index 0000000..fa93751
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc
@@ -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.
+
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+
+namespace mojo {
+
+MojoExtendedKeyEventData::MojoExtendedKeyEventData(int32_t windows_key_code,
+                                                   uint16_t text,
+                                                   uint16_t unmodified_text)
+    : windows_key_code_(windows_key_code),
+      text_(text),
+      unmodified_text_(unmodified_text) {
+}
+
+MojoExtendedKeyEventData::~MojoExtendedKeyEventData() {}
+
+ui::ExtendedKeyEventData* MojoExtendedKeyEventData::Clone() const {
+  return new MojoExtendedKeyEventData(windows_key_code_,
+                                      text_,
+                                      unmodified_text_);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h
new file mode 100644
index 0000000..cd5be0f
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h
@@ -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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
+
+#include "ui/events/event.h"
+#include "mojo/services/public/cpp/input_events/mojo_input_events_export.h"
+
+namespace mojo {
+
+// A structure to store all mojo specific data on a KeyEvent.
+class MOJO_INPUT_EVENTS_EXPORT MojoExtendedKeyEventData
+    : public ui::ExtendedKeyEventData {
+ public:
+  MojoExtendedKeyEventData(int32_t windows_key_code,
+                           uint16_t text,
+                           uint16_t unmodified_text);
+  virtual ~MojoExtendedKeyEventData();
+
+  int32_t windows_key_code() const { return windows_key_code_; }
+  uint16_t text() const { return text_; }
+  uint16_t unmodified_text() const { return unmodified_text_; }
+
+  // ui::ExtendedKeyEventData:
+  virtual ui::ExtendedKeyEventData* Clone() const OVERRIDE;
+
+ private:
+  const int32_t windows_key_code_;
+  const uint16_t text_;
+  const uint16_t unmodified_text_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoExtendedKeyEventData);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
diff --git a/mojo/services/public/cpp/input_events/mojo_input_events_export.h b/mojo/services/public/cpp/input_events/mojo_input_events_export.h
new file mode 100644
index 0000000..d7b193e
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/mojo_input_events_export.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 MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION)
+#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllexport)
+#else
+#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION)
+#define MOJO_INPUT_EVENTS_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_INPUT_EVENTS_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_INPUT_EVENTS_EXPORT
+#endif
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
diff --git a/mojo/services/public/cpp/network/BUILD.gn b/mojo/services/public/cpp/network/BUILD.gn
new file mode 100644
index 0000000..81e64b4
--- /dev/null
+++ b/mojo/services/public/cpp/network/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_utility
+source_set("network") {
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_component",
+  ]
+
+  sources = [
+    "web_socket_read_queue.cc",
+    "web_socket_read_queue.h",
+    "web_socket_write_queue.cc",
+    "web_socket_write_queue.h",
+  ]
+}
diff --git a/mojo/services/public/cpp/network/web_socket_read_queue.cc b/mojo/services/public/cpp/network/web_socket_read_queue.cc
new file mode 100644
index 0000000..66b5a66
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_read_queue.cc
@@ -0,0 +1,70 @@
+// 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/public/cpp/network/web_socket_read_queue.h"
+
+#include "base/bind.h"
+
+namespace mojo {
+
+struct WebSocketReadQueue::Operation {
+  uint32_t num_bytes_;
+  base::Callback<void(const char*)> callback_;
+};
+
+WebSocketReadQueue::WebSocketReadQueue(DataPipeConsumerHandle handle)
+    : handle_(handle), is_waiting_(false) {
+}
+
+WebSocketReadQueue::~WebSocketReadQueue() {
+}
+
+void WebSocketReadQueue::Read(uint32_t num_bytes,
+                              base::Callback<void(const char*)> callback) {
+  Operation* op = new Operation;
+  op->num_bytes_ = num_bytes;
+  op->callback_ = callback;
+  queue_.push_back(op);
+
+  if (!is_waiting_)
+    TryToRead();
+}
+
+void WebSocketReadQueue::TryToRead() {
+  Operation* op = queue_[0];
+  const void* buffer = NULL;
+  uint32_t bytes_read = op->num_bytes_;
+  MojoResult result = BeginReadDataRaw(
+      handle_, &buffer, &bytes_read, MOJO_READ_DATA_FLAG_ALL_OR_NONE);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    EndReadDataRaw(handle_, bytes_read);
+    Wait();
+    return;
+  }
+
+  // Ensure |op| is deleted, whether or not |this| goes away.
+  scoped_ptr<Operation> op_deleter(op);
+  queue_.weak_erase(queue_.begin());
+  if (result != MOJO_RESULT_OK)
+    return;
+  DataPipeConsumerHandle handle = handle_;
+  op->callback_.Run(static_cast<const char*>(buffer));  // may delete |this|
+  EndReadDataRaw(handle, bytes_read);
+}
+
+void WebSocketReadQueue::Wait() {
+  is_waiting_ = true;
+  handle_watcher_.Start(
+      handle_,
+      MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_DEADLINE_INDEFINITE,
+      base::Bind(&WebSocketReadQueue::OnHandleReady, base::Unretained(this)));
+}
+
+void WebSocketReadQueue::OnHandleReady(MojoResult result) {
+  is_waiting_ = false;
+  TryToRead();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/network/web_socket_read_queue.h b/mojo/services/public/cpp/network/web_socket_read_queue.h
new file mode 100644
index 0000000..6108e49
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_read_queue.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_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace mojo {
+
+// This class simplifies the handling of multiple Reads on a DataPipe. It reads
+// the data in the expected chunk size, calling the callback once a full chunk
+// is ready. Callbacks are owned by this class, and are guaranteed not to be
+// called after this class is destroyed.
+// See also: WebSocketWriteQueue
+class WebSocketReadQueue {
+ public:
+  WebSocketReadQueue(DataPipeConsumerHandle handle);
+  ~WebSocketReadQueue();
+
+  void Read(uint32_t num_bytes, base::Callback<void(const char*)> callback);
+
+ private:
+  struct Operation;
+
+  void TryToRead();
+  void Wait();
+  void OnHandleReady(MojoResult result);
+
+  DataPipeConsumerHandle handle_;
+  common::HandleWatcher handle_watcher_;
+  ScopedVector<Operation> queue_;
+  bool is_waiting_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
diff --git a/mojo/services/public/cpp/network/web_socket_write_queue.cc b/mojo/services/public/cpp/network/web_socket_write_queue.cc
new file mode 100644
index 0000000..fe3f674
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_write_queue.cc
@@ -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.
+
+#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
+
+#include "base/bind.h"
+
+namespace mojo {
+
+struct WebSocketWriteQueue::Operation {
+  uint32_t num_bytes_;
+  base::Callback<void(const char*)> callback_;
+
+  const char* data_;
+  // Only initialized if the initial Write fails. This saves a copy in
+  // the common case.
+  std::vector<char> data_copy_;
+};
+
+WebSocketWriteQueue::WebSocketWriteQueue(DataPipeProducerHandle handle)
+    : handle_(handle), is_waiting_(false) {
+}
+
+WebSocketWriteQueue::~WebSocketWriteQueue() {
+}
+
+void WebSocketWriteQueue::Write(const char* data,
+                                uint32_t num_bytes,
+                                base::Callback<void(const char*)> callback) {
+  Operation* op = new Operation;
+  op->num_bytes_ = num_bytes;
+  op->callback_ = callback;
+  op->data_ = data;
+  queue_.push_back(op);
+
+  MojoResult result = MOJO_RESULT_SHOULD_WAIT;
+  if (!is_waiting_)
+    result = TryToWrite();
+
+  // If we have to wait, make a local copy of the data so we know it will
+  // live until we need it.
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    op->data_copy_.resize(num_bytes);
+    memcpy(&op->data_copy_[0], data, num_bytes);
+    op->data_ = &op->data_copy_[0];
+  }
+}
+
+MojoResult WebSocketWriteQueue::TryToWrite() {
+  Operation* op = queue_[0];
+  uint32_t bytes_written = op->num_bytes_;
+  MojoResult result = WriteDataRaw(
+      handle_, op->data_, &bytes_written, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    Wait();
+    return result;
+  }
+
+  // Ensure |op| is deleted, whether or not |this| goes away.
+  scoped_ptr<Operation> op_deleter(op);
+  queue_.weak_erase(queue_.begin());
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  op->callback_.Run(op->data_);  // may delete |this|
+  return result;
+}
+
+void WebSocketWriteQueue::Wait() {
+  is_waiting_ = true;
+  handle_watcher_.Start(handle_,
+                        MOJO_HANDLE_SIGNAL_WRITABLE,
+                        MOJO_DEADLINE_INDEFINITE,
+                        base::Bind(&WebSocketWriteQueue::OnHandleReady,
+                                   base::Unretained(this)));
+}
+
+void WebSocketWriteQueue::OnHandleReady(MojoResult result) {
+  is_waiting_ = false;
+  TryToWrite();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/network/web_socket_write_queue.h b/mojo/services/public/cpp/network/web_socket_write_queue.h
new file mode 100644
index 0000000..7d5f5fa
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_write_queue.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 MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace mojo {
+
+// This class simplifies the handling of multiple Writes on a DataPipe. It
+// writes each chunk all at once (or waits until the pipe is ready before
+// writing), calling the callback when finished. Callbacks are owned by this
+// class, and are guaranteed not to be called after this class is destroyed.
+// See also: WebSocketReadQueue
+class WebSocketWriteQueue {
+ public:
+  WebSocketWriteQueue(DataPipeProducerHandle handle);
+  ~WebSocketWriteQueue();
+
+  void Write(const char* data,
+             uint32_t num_bytes,
+             base::Callback<void(const char*)> callback);
+
+ private:
+  struct Operation;
+
+  MojoResult TryToWrite();
+  void Wait();
+  void OnHandleReady(MojoResult result);
+
+  DataPipeProducerHandle handle_;
+  common::HandleWatcher handle_watcher_;
+  ScopedVector<Operation> queue_;
+  bool is_waiting_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
diff --git a/mojo/services/public/cpp/surfaces/BUILD.gn b/mojo/services/public/cpp/surfaces/BUILD.gn
new file mode 100644
index 0000000..5bd6729
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_lib
+component("surfaces") {
+  output_name = "mojo_surfaces_lib"
+
+  sources = [
+    "lib/surfaces_type_converters.cc",
+    "lib/surfaces_utils.cc",
+    "mojo_surfaces_export.h",
+    "surfaces_type_converters.h",
+    "surfaces_utils.h",
+  ]
+
+  defines = [ "MOJO_SURFACES_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//mojo/services/public/cpp/geometry",
+    "//ui/gfx",
+  ]
+
+  deps = [
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//gpu",
+    "//ui/gfx/geometry",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_component",
+    "//mojo/services/public/interfaces/surfaces:surface_id",
+    "//mojo/services/public/interfaces/surfaces",
+  ]
+}
diff --git a/mojo/services/public/cpp/surfaces/DEPS b/mojo/services/public/cpp/surfaces/DEPS
new file mode 100644
index 0000000..bce2d34
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+cc/output",
+  "+cc/quads",
+  "+cc/resources",
+  "+cc/surfaces",
+  "+gpu/command_buffer/common/mailbox.h",
+  "+gpu/command_buffer/common/mailbox_holder.h",
+  "+third_party/skia/include",
+  "+ui/gfx/geometry",
+  "+ui/gfx/transform.h",
+]
diff --git a/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc
new file mode 100644
index 0000000..7d1798e
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc
@@ -0,0 +1,576 @@
+// 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/public/cpp/surfaces/surfaces_type_converters.h"
+
+#include "base/macros.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/draw_quad.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/quads/tile_draw_quad.h"
+#include "cc/quads/yuv_video_draw_quad.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+
+namespace mojo {
+
+#define ASSERT_ENUM_VALUES_EQUAL(value)                                      \
+  COMPILE_ASSERT(cc::DrawQuad::value == static_cast<cc::DrawQuad::Material>( \
+                                            MATERIAL_##value),     \
+                 value##_enum_value_matches)
+
+ASSERT_ENUM_VALUES_EQUAL(CHECKERBOARD);
+ASSERT_ENUM_VALUES_EQUAL(DEBUG_BORDER);
+ASSERT_ENUM_VALUES_EQUAL(IO_SURFACE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(PICTURE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(RENDER_PASS);
+ASSERT_ENUM_VALUES_EQUAL(SOLID_COLOR);
+ASSERT_ENUM_VALUES_EQUAL(STREAM_VIDEO_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(SURFACE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(TEXTURE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(TILED_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(YUV_VIDEO_CONTENT);
+
+COMPILE_ASSERT(
+    cc::YUVVideoDrawQuad::REC_601 ==
+        static_cast<cc::YUVVideoDrawQuad::ColorSpace>(YUV_COLOR_SPACE_REC_601),
+    rec_601_enum_matches);
+COMPILE_ASSERT(cc::YUVVideoDrawQuad::REC_601_JPEG ==
+                   static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
+                       YUV_COLOR_SPACE_REC_601_JPEG),
+               rec_601_jpeg_enum_matches);
+
+namespace {
+
+cc::SharedQuadState* ConvertSharedQuadState(const SharedQuadStatePtr& input,
+                                            cc::RenderPass* render_pass) {
+  cc::SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
+  state->SetAll(input->content_to_target_transform.To<gfx::Transform>(),
+                input->content_bounds.To<gfx::Size>(),
+                input->visible_content_rect.To<gfx::Rect>(),
+                input->clip_rect.To<gfx::Rect>(),
+                input->is_clipped,
+                input->opacity,
+                static_cast<::SkXfermode::Mode>(input->blend_mode),
+                input->sorting_context_id);
+  return state;
+}
+
+bool ConvertDrawQuad(const QuadPtr& input,
+                     cc::SharedQuadState* sqs,
+                     cc::RenderPass* render_pass) {
+  switch (input->material) {
+    case MATERIAL_RENDER_PASS: {
+      cc::RenderPassDrawQuad* render_pass_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::RenderPassDrawQuad>();
+      RenderPassQuadState* render_pass_quad_state =
+          input->render_pass_quad_state.get();
+      gfx::PointF filter_scale_as_point =
+          render_pass_quad_state->filters_scale.To<gfx::PointF>();
+      render_pass_quad->SetAll(
+          sqs,
+          input->rect.To<gfx::Rect>(),
+          input->opaque_rect.To<gfx::Rect>(),
+          input->visible_rect.To<gfx::Rect>(),
+          input->needs_blending,
+          render_pass_quad_state->render_pass_id.To<cc::RenderPassId>(),
+          render_pass_quad_state->mask_resource_id,
+          render_pass_quad_state->mask_uv_rect.To<gfx::RectF>(),
+          cc::FilterOperations(),  // TODO(jamesr): filters
+          gfx::Vector2dF(filter_scale_as_point.x(), filter_scale_as_point.y()),
+          cc::FilterOperations());  // TODO(jamesr): background_filters
+      break;
+    }
+    case MATERIAL_SOLID_COLOR: {
+      if (input->solid_color_quad_state.is_null())
+        return false;
+      cc::SolidColorDrawQuad* color_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+      color_quad->SetAll(
+          sqs,
+          input->rect.To<gfx::Rect>(),
+          input->opaque_rect.To<gfx::Rect>(),
+          input->visible_rect.To<gfx::Rect>(),
+          input->needs_blending,
+          input->solid_color_quad_state->color.To<SkColor>(),
+          input->solid_color_quad_state->force_anti_aliasing_off);
+      break;
+    }
+    case MATERIAL_SURFACE_CONTENT: {
+      if (input->surface_quad_state.is_null())
+        return false;
+      cc::SurfaceDrawQuad* surface_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+      surface_quad->SetAll(
+          sqs,
+          input->rect.To<gfx::Rect>(),
+          input->opaque_rect.To<gfx::Rect>(),
+          input->visible_rect.To<gfx::Rect>(),
+          input->needs_blending,
+          input->surface_quad_state->surface.To<cc::SurfaceId>());
+      break;
+    }
+    case MATERIAL_TEXTURE_CONTENT: {
+      TextureQuadStatePtr& texture_quad_state =
+          input->texture_quad_state;
+      if (texture_quad_state.is_null() ||
+          texture_quad_state->vertex_opacity.is_null() ||
+          texture_quad_state->background_color.is_null())
+        return false;
+      cc::TextureDrawQuad* texture_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+      texture_quad->SetAll(
+          sqs,
+          input->rect.To<gfx::Rect>(),
+          input->opaque_rect.To<gfx::Rect>(),
+          input->visible_rect.To<gfx::Rect>(),
+          input->needs_blending,
+          texture_quad_state->resource_id,
+          texture_quad_state->premultiplied_alpha,
+          texture_quad_state->uv_top_left.To<gfx::PointF>(),
+          texture_quad_state->uv_bottom_right.To<gfx::PointF>(),
+          texture_quad_state->background_color.To<SkColor>(),
+          &texture_quad_state->vertex_opacity.storage()[0],
+          texture_quad_state->flipped);
+      break;
+    }
+    case MATERIAL_TILED_CONTENT: {
+      TileQuadStatePtr& tile_state = input->tile_quad_state;
+      if (tile_state.is_null())
+        return false;
+      cc::TileDrawQuad* tile_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::TileDrawQuad>();
+      tile_quad->SetAll(sqs,
+                        input->rect.To<gfx::Rect>(),
+                        input->opaque_rect.To<gfx::Rect>(),
+                        input->visible_rect.To<gfx::Rect>(),
+                        input->needs_blending,
+                        tile_state->resource_id,
+                        tile_state->tex_coord_rect.To<gfx::RectF>(),
+                        tile_state->texture_size.To<gfx::Size>(),
+                        tile_state->swizzle_contents);
+      break;
+    }
+    case MATERIAL_YUV_VIDEO_CONTENT: {
+      YUVVideoQuadStatePtr& yuv_state = input->yuv_video_quad_state;
+      if (yuv_state.is_null())
+        return false;
+      cc::YUVVideoDrawQuad* yuv_quad =
+          render_pass->CreateAndAppendDrawQuad<cc::YUVVideoDrawQuad>();
+      yuv_quad->SetAll(sqs,
+                       input->rect.To<gfx::Rect>(),
+                       input->opaque_rect.To<gfx::Rect>(),
+                       input->visible_rect.To<gfx::Rect>(),
+                       input->needs_blending,
+                       yuv_state->tex_coord_rect.To<gfx::RectF>(),
+                       yuv_state->y_plane_resource_id,
+                       yuv_state->u_plane_resource_id,
+                       yuv_state->v_plane_resource_id,
+                       yuv_state->a_plane_resource_id,
+                       static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
+                           yuv_state->color_space));
+      break;
+    }
+    default:
+      NOTREACHED() << "Unsupported material " << input->material;
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+// static
+SurfaceIdPtr TypeConverter<SurfaceIdPtr, cc::SurfaceId>::Convert(
+    const cc::SurfaceId& input) {
+  SurfaceIdPtr id(SurfaceId::New());
+  id->id = input.id;
+  return id.Pass();
+}
+
+// static
+cc::SurfaceId TypeConverter<cc::SurfaceId, SurfaceIdPtr>::Convert(
+    const SurfaceIdPtr& input) {
+  return cc::SurfaceId(input->id);
+}
+
+// static
+ColorPtr TypeConverter<ColorPtr, SkColor>::Convert(const SkColor& input) {
+  ColorPtr color(Color::New());
+  color->rgba = input;
+  return color.Pass();
+}
+
+// static
+SkColor TypeConverter<SkColor, ColorPtr>::Convert(const ColorPtr& input) {
+  return input->rgba;
+}
+
+// static
+RenderPassIdPtr TypeConverter<RenderPassIdPtr, cc::RenderPassId>::Convert(
+    const cc::RenderPassId& input) {
+  RenderPassIdPtr pass_id(RenderPassId::New());
+  pass_id->layer_id = input.layer_id;
+  pass_id->index = input.index;
+  return pass_id.Pass();
+}
+
+// static
+cc::RenderPassId TypeConverter<cc::RenderPassId, RenderPassIdPtr>::Convert(
+    const RenderPassIdPtr& input) {
+  return cc::RenderPassId(input->layer_id, input->index);
+}
+
+// static
+QuadPtr TypeConverter<QuadPtr, cc::DrawQuad>::Convert(
+    const cc::DrawQuad& input) {
+  QuadPtr quad = Quad::New();
+  quad->material = static_cast<Material>(input.material);
+  quad->rect = Rect::From(input.rect);
+  quad->opaque_rect = Rect::From(input.opaque_rect);
+  quad->visible_rect = Rect::From(input.visible_rect);
+  quad->needs_blending = input.needs_blending;
+  // This is intentionally left set to an invalid value here. It's set when
+  // converting an entire pass since it's an index into the pass' shared quad
+  // state list.
+  quad->shared_quad_state_index = -1;
+  switch (input.material) {
+    case cc::DrawQuad::RENDER_PASS: {
+      const cc::RenderPassDrawQuad* render_pass_quad =
+          cc::RenderPassDrawQuad::MaterialCast(&input);
+      RenderPassQuadStatePtr pass_state = RenderPassQuadState::New();
+      pass_state->render_pass_id =
+          RenderPassId::From(render_pass_quad->render_pass_id);
+      pass_state->mask_resource_id = render_pass_quad->mask_resource_id;
+      pass_state->mask_uv_rect = RectF::From(render_pass_quad->mask_uv_rect);
+      // TODO(jamesr): pass_state->filters
+      pass_state->filters_scale = PointF::From(
+          gfx::PointAtOffsetFromOrigin(render_pass_quad->filters_scale));
+      // TODO(jamesr): pass_state->background_filters
+      quad->render_pass_quad_state = pass_state.Pass();
+      break;
+    }
+    case cc::DrawQuad::SOLID_COLOR: {
+      const cc::SolidColorDrawQuad* color_quad =
+          cc::SolidColorDrawQuad::MaterialCast(&input);
+      SolidColorQuadStatePtr color_state = SolidColorQuadState::New();
+      color_state->color = Color::From(color_quad->color);
+      color_state->force_anti_aliasing_off =
+          color_quad->force_anti_aliasing_off;
+      quad->solid_color_quad_state = color_state.Pass();
+      break;
+    }
+    case cc::DrawQuad::SURFACE_CONTENT: {
+      const cc::SurfaceDrawQuad* surface_quad =
+          cc::SurfaceDrawQuad::MaterialCast(&input);
+      SurfaceQuadStatePtr surface_state =
+          SurfaceQuadState::New();
+      surface_state->surface = SurfaceId::From(surface_quad->surface_id);
+      quad->surface_quad_state = surface_state.Pass();
+      break;
+    }
+    case cc::DrawQuad::TEXTURE_CONTENT: {
+      const cc::TextureDrawQuad* texture_quad =
+          cc::TextureDrawQuad::MaterialCast(&input);
+      TextureQuadStatePtr texture_state = TextureQuadState::New();
+      texture_state->resource_id = texture_quad->resource_id;
+      texture_state->premultiplied_alpha = texture_quad->premultiplied_alpha;
+      texture_state->uv_top_left = PointF::From(texture_quad->uv_top_left);
+      texture_state->uv_bottom_right =
+          PointF::From(texture_quad->uv_bottom_right);
+      texture_state->background_color =
+          Color::From(texture_quad->background_color);
+      Array<float> vertex_opacity(4);
+      for (size_t i = 0; i < 4; ++i) {
+        vertex_opacity[i] = texture_quad->vertex_opacity[i];
+      }
+      texture_state->vertex_opacity = vertex_opacity.Pass();
+      texture_state->flipped = texture_quad->flipped;
+      quad->texture_quad_state = texture_state.Pass();
+      break;
+    }
+    case cc::DrawQuad::TILED_CONTENT: {
+      const cc::TileDrawQuad* tile_quad =
+          cc::TileDrawQuad::MaterialCast(&input);
+      TileQuadStatePtr tile_state = TileQuadState::New();
+      tile_state->tex_coord_rect = RectF::From(tile_quad->tex_coord_rect);
+      tile_state->texture_size = Size::From(tile_quad->texture_size);
+      tile_state->swizzle_contents = tile_quad->swizzle_contents;
+      tile_state->resource_id = tile_quad->resource_id;
+      quad->tile_quad_state = tile_state.Pass();
+      break;
+    }
+    case cc::DrawQuad::YUV_VIDEO_CONTENT: {
+      const cc::YUVVideoDrawQuad* yuv_quad =
+          cc::YUVVideoDrawQuad::MaterialCast(&input);
+      YUVVideoQuadStatePtr yuv_state = YUVVideoQuadState::New();
+      yuv_state->tex_coord_rect = RectF::From(yuv_quad->tex_coord_rect);
+      yuv_state->y_plane_resource_id = yuv_quad->y_plane_resource_id;
+      yuv_state->u_plane_resource_id = yuv_quad->u_plane_resource_id;
+      yuv_state->v_plane_resource_id = yuv_quad->v_plane_resource_id;
+      yuv_state->a_plane_resource_id = yuv_quad->a_plane_resource_id;
+      yuv_state->color_space =
+          static_cast<YUVColorSpace>(yuv_quad->color_space);
+      quad->yuv_video_quad_state = yuv_state.Pass();
+      break;
+    }
+
+    default:
+      NOTREACHED() << "Unsupported material " << input.material;
+  }
+  return quad.Pass();
+}
+
+// static
+SharedQuadStatePtr
+TypeConverter<SharedQuadStatePtr, cc::SharedQuadState>::Convert(
+    const cc::SharedQuadState& input) {
+  SharedQuadStatePtr state = SharedQuadState::New();
+  state->content_to_target_transform =
+      Transform::From(input.content_to_target_transform);
+  state->content_bounds = Size::From(input.content_bounds);
+  state->visible_content_rect = Rect::From(input.visible_content_rect);
+  state->clip_rect = Rect::From(input.clip_rect);
+  state->is_clipped = input.is_clipped;
+  state->opacity = input.opacity;
+  state->blend_mode = static_cast<SkXfermode>(input.blend_mode);
+  state->sorting_context_id = input.sorting_context_id;
+  return state.Pass();
+}
+
+// static
+PassPtr TypeConverter<PassPtr, cc::RenderPass>::Convert(
+    const cc::RenderPass& input) {
+  PassPtr pass = Pass::New();
+  pass->id = input.id.index;
+  pass->output_rect = Rect::From(input.output_rect);
+  pass->damage_rect = Rect::From(input.damage_rect);
+  pass->transform_to_root_target =
+      Transform::From(input.transform_to_root_target);
+  pass->has_transparent_background = input.has_transparent_background;
+  Array<QuadPtr> quads(input.quad_list.size());
+  Array<SharedQuadStatePtr> shared_quad_state(
+      input.shared_quad_state_list.size());
+  int sqs_i = -1;
+  const cc::SharedQuadState* last_sqs = NULL;
+  size_t i = 0;
+  for (cc::QuadList::ConstIterator iter = input.quad_list.begin();
+       iter != input.quad_list.end();
+       ++iter) {
+    const cc::DrawQuad& quad = *iter;
+    quads[i] = Quad::From(quad);
+    if (quad.shared_quad_state != last_sqs) {
+      sqs_i++;
+      shared_quad_state[sqs_i] =
+          SharedQuadState::From(*input.shared_quad_state_list[sqs_i]);
+      last_sqs = quad.shared_quad_state;
+    }
+    quads[i]->shared_quad_state_index = sqs_i;
+    ++i;
+  }
+  // We should copy all shared quad states.
+  DCHECK_EQ(static_cast<size_t>(sqs_i + 1), shared_quad_state.size());
+  pass->quads = quads.Pass();
+  pass->shared_quad_states = shared_quad_state.Pass();
+  return pass.Pass();
+}
+
+// static
+scoped_ptr<cc::RenderPass>
+TypeConverter<scoped_ptr<cc::RenderPass>, PassPtr>::Convert(
+    const PassPtr& input) {
+  // TODO(weiliangc): RenderPass will have a constructor that takes in preset
+  // size of quad list and shared quad state list size in upcoming CL.
+  scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+  pass->SetAll(cc::RenderPassId(1, input->id),
+               input->output_rect.To<gfx::Rect>(),
+               input->damage_rect.To<gfx::Rect>(),
+               input->transform_to_root_target.To<gfx::Transform>(),
+               input->has_transparent_background);
+  cc::SharedQuadStateList& sqs_list = pass->shared_quad_state_list;
+  sqs_list.reserve(input->shared_quad_states.size());
+  for (size_t i = 0; i < input->shared_quad_states.size(); ++i) {
+    ConvertSharedQuadState(input->shared_quad_states[i], pass.get());
+  }
+  for (size_t i = 0; i < input->quads.size(); ++i) {
+    QuadPtr quad = input->quads[i].Pass();
+    if (!ConvertDrawQuad(
+            quad, sqs_list[quad->shared_quad_state_index], pass.get()))
+      return scoped_ptr<cc::RenderPass>();
+  }
+  return pass.Pass();
+}
+
+// static
+MailboxPtr TypeConverter<MailboxPtr, gpu::Mailbox>::Convert(
+    const gpu::Mailbox& input) {
+  Array<int8_t> name(64);
+  for (int i = 0; i < 64; ++i) {
+    name[i] = input.name[i];
+  }
+  MailboxPtr mailbox(Mailbox::New());
+  mailbox->name = name.Pass();
+  return mailbox.Pass();
+}
+
+// static
+gpu::Mailbox TypeConverter<gpu::Mailbox, MailboxPtr>::Convert(
+    const MailboxPtr& input) {
+  gpu::Mailbox mailbox;
+  if (!input->name.is_null())
+    mailbox.SetName(&input->name.storage()[0]);
+  return mailbox;
+}
+
+// static
+MailboxHolderPtr TypeConverter<MailboxHolderPtr, gpu::MailboxHolder>::Convert(
+    const gpu::MailboxHolder& input) {
+  MailboxHolderPtr holder(MailboxHolder::New());
+  holder->mailbox = Mailbox::From<gpu::Mailbox>(input.mailbox);
+  holder->texture_target = input.texture_target;
+  holder->sync_point = input.sync_point;
+  return holder.Pass();
+}
+
+// static
+gpu::MailboxHolder TypeConverter<gpu::MailboxHolder, MailboxHolderPtr>::Convert(
+    const MailboxHolderPtr& input) {
+  gpu::MailboxHolder holder;
+  holder.mailbox = input->mailbox.To<gpu::Mailbox>();
+  holder.texture_target = input->texture_target;
+  holder.sync_point = input->sync_point;
+  return holder;
+}
+
+// static
+TransferableResourcePtr
+TypeConverter<TransferableResourcePtr, cc::TransferableResource>::Convert(
+    const cc::TransferableResource& input) {
+  TransferableResourcePtr transferable = TransferableResource::New();
+  transferable->id = input.id;
+  transferable->format = static_cast<ResourceFormat>(input.format);
+  transferable->filter = input.filter;
+  transferable->size = Size::From(input.size);
+  transferable->mailbox_holder = MailboxHolder::From(input.mailbox_holder);
+  transferable->is_repeated = input.is_repeated;
+  transferable->is_software = input.is_software;
+  return transferable.Pass();
+}
+
+// static
+cc::TransferableResource
+TypeConverter<cc::TransferableResource, TransferableResourcePtr>::Convert(
+    const TransferableResourcePtr& input) {
+  cc::TransferableResource transferable;
+  transferable.id = input->id;
+  transferable.format = static_cast<cc::ResourceFormat>(input->format);
+  transferable.filter = input->filter;
+  transferable.size = input->size.To<gfx::Size>();
+  transferable.mailbox_holder = input->mailbox_holder.To<gpu::MailboxHolder>();
+  transferable.is_repeated = input->is_repeated;
+  transferable.is_software = input->is_software;
+  return transferable;
+}
+
+// static
+Array<TransferableResourcePtr> TypeConverter<
+    Array<TransferableResourcePtr>,
+    cc::TransferableResourceArray>::Convert(const cc::TransferableResourceArray&
+                                                input) {
+  Array<TransferableResourcePtr> resources(input.size());
+  for (size_t i = 0; i < input.size(); ++i) {
+    resources[i] = TransferableResource::From(input[i]);
+  }
+  return resources.Pass();
+}
+
+// static
+cc::TransferableResourceArray
+TypeConverter<cc::TransferableResourceArray, Array<TransferableResourcePtr> >::
+    Convert(const Array<TransferableResourcePtr>& input) {
+  cc::TransferableResourceArray resources(input.size());
+  for (size_t i = 0; i < input.size(); ++i) {
+    resources[i] = input[i].To<cc::TransferableResource>();
+  }
+  return resources;
+}
+
+// static
+ReturnedResourcePtr
+TypeConverter<ReturnedResourcePtr, cc::ReturnedResource>::Convert(
+    const cc::ReturnedResource& input) {
+  ReturnedResourcePtr returned = ReturnedResource::New();
+  returned->id = input.id;
+  returned->sync_point = input.sync_point;
+  returned->count = input.count;
+  returned->lost = input.lost;
+  return returned.Pass();
+}
+
+// static
+cc::ReturnedResource
+TypeConverter<cc::ReturnedResource, ReturnedResourcePtr>::Convert(
+    const ReturnedResourcePtr& input) {
+  cc::ReturnedResource returned;
+  returned.id = input->id;
+  returned.sync_point = input->sync_point;
+  returned.count = input->count;
+  returned.lost = input->lost;
+  return returned;
+}
+
+// static
+Array<ReturnedResourcePtr>
+TypeConverter<Array<ReturnedResourcePtr>, cc::ReturnedResourceArray>::Convert(
+    const cc::ReturnedResourceArray& input) {
+  Array<ReturnedResourcePtr> resources(input.size());
+  for (size_t i = 0; i < input.size(); ++i) {
+    resources[i] = ReturnedResource::From(input[i]);
+  }
+  return resources.Pass();
+}
+
+// static
+FramePtr TypeConverter<FramePtr, cc::CompositorFrame>::Convert(
+    const cc::CompositorFrame& input) {
+  FramePtr frame = Frame::New();
+  DCHECK(input.delegated_frame_data);
+  cc::DelegatedFrameData* frame_data = input.delegated_frame_data.get();
+  frame->resources =
+      Array<TransferableResourcePtr>::From(frame_data->resource_list);
+  const cc::RenderPassList& pass_list = frame_data->render_pass_list;
+  frame->passes = Array<PassPtr>::New(pass_list.size());
+  for (size_t i = 0; i < pass_list.size(); ++i) {
+    frame->passes[i] = Pass::From(*pass_list[i]);
+  }
+  return frame.Pass();
+}
+
+// static
+scoped_ptr<cc::CompositorFrame>
+TypeConverter<scoped_ptr<cc::CompositorFrame>, FramePtr>::Convert(
+    const FramePtr& input) {
+  scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData);
+  frame_data->device_scale_factor = 1.f;
+  frame_data->resource_list =
+      input->resources.To<cc::TransferableResourceArray>();
+  frame_data->render_pass_list.reserve(input->passes.size());
+  for (size_t i = 0; i < input->passes.size(); ++i) {
+    scoped_ptr<cc::RenderPass> pass =
+        input->passes[i].To<scoped_ptr<cc::RenderPass> >();
+    if (!pass)
+      return scoped_ptr<cc::CompositorFrame>();
+    frame_data->render_pass_list.push_back(pass.Pass());
+  }
+  scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
+  frame->delegated_frame_data = frame_data.Pass();
+  return frame.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/surfaces/lib/surfaces_utils.cc b/mojo/services/public/cpp/surfaces/lib/surfaces_utils.cc
new file mode 100644
index 0000000..c35a77a
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/lib/surfaces_utils.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 "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+SharedQuadStatePtr CreateDefaultSQS(const gfx::Size& size) {
+  SharedQuadStatePtr sqs = SharedQuadState::New();
+  sqs->content_to_target_transform = Transform::From(gfx::Transform());
+  sqs->content_bounds = Size::From(size);
+  sqs->visible_content_rect = Rect::From(gfx::Rect(size));
+  sqs->clip_rect = Rect::From(gfx::Rect(size));
+  sqs->is_clipped = false;
+  sqs->opacity = 1.f;
+  sqs->blend_mode = mojo::SK_XFERMODE_kSrc_Mode;
+  sqs->sorting_context_id = 0;
+  return sqs.Pass();
+}
+
+PassPtr CreateDefaultPass(int id, const gfx::Rect& rect) {
+  PassPtr pass = Pass::New();
+  pass->id = id;
+  pass->output_rect = Rect::From(rect);
+  pass->damage_rect = Rect::From(rect);
+  pass->transform_to_root_target = Transform::From(gfx::Transform());
+  pass->has_transparent_background = false;
+  return pass.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/surfaces/mojo_surfaces_export.h b/mojo/services/public/cpp/surfaces/mojo_surfaces_export.h
new file mode 100644
index 0000000..31b408f
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/mojo_surfaces_export.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 MOJO_SERVICES_PUBLIC_CPP_SURFACES_MOJO_SURFACES_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_MOJO_SURFACES_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_SURFACES_IMPLEMENTATION)
+#define MOJO_SURFACES_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SURFACES_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_SURFACES_IMPLEMENTATION)
+#define MOJO_SURFACES_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SURFACES_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_SURFACES_EXPORT
+#endif
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_SURFACES_MOJO_SURFACES_EXPORT_H_
diff --git a/mojo/services/public/cpp/surfaces/surfaces_type_converters.h b/mojo/services/public/cpp/surfaces/surfaces_type_converters.h
new file mode 100644
index 0000000..4ef0e2c
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/surfaces_type_converters.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 MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/resources/transferable_resource.h"
+#include "cc/surfaces/surface_id.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/services/public/cpp/surfaces/mojo_surfaces_export.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace cc {
+class CompositorFrame;
+class DrawQuad;
+class RenderPass;
+class RenderPassId;
+class SharedQuadState;
+}  // namespace cc
+
+namespace mojo {
+
+// Types from surface_id.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<SurfaceIdPtr, cc::SurfaceId> {
+  static SurfaceIdPtr Convert(const cc::SurfaceId& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<cc::SurfaceId, SurfaceIdPtr> {
+  static cc::SurfaceId Convert(const SurfaceIdPtr& input);
+};
+
+// Types from quads.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<ColorPtr, SkColor> {
+  static ColorPtr Convert(const SkColor& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<SkColor, ColorPtr> {
+  static SkColor Convert(const ColorPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<RenderPassIdPtr, cc::RenderPassId> {
+  static RenderPassIdPtr Convert(const cc::RenderPassId& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<cc::RenderPassId, RenderPassIdPtr> {
+  static cc::RenderPassId Convert(const RenderPassIdPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<QuadPtr, cc::DrawQuad> {
+  static QuadPtr Convert(const cc::DrawQuad& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<SharedQuadStatePtr, cc::SharedQuadState> {
+  static SharedQuadStatePtr Convert(const cc::SharedQuadState& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<PassPtr, cc::RenderPass> {
+  static PassPtr Convert(const cc::RenderPass& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<scoped_ptr<cc::RenderPass>, PassPtr> {
+  static scoped_ptr<cc::RenderPass> Convert(const PassPtr& input);
+};
+
+// Types from surfaces.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<MailboxPtr, gpu::Mailbox> {
+  static MailboxPtr Convert(const gpu::Mailbox& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<gpu::Mailbox, MailboxPtr> {
+  static gpu::Mailbox Convert(const MailboxPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<MailboxHolderPtr, gpu::MailboxHolder> {
+  static MailboxHolderPtr Convert(const gpu::MailboxHolder& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<gpu::MailboxHolder, MailboxHolderPtr> {
+  static gpu::MailboxHolder Convert(const MailboxHolderPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<TransferableResourcePtr, cc::TransferableResource> {
+  static TransferableResourcePtr Convert(const cc::TransferableResource& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::TransferableResource, TransferableResourcePtr> {
+  static cc::TransferableResource Convert(const TransferableResourcePtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<Array<TransferableResourcePtr>, cc::TransferableResourceArray> {
+  static Array<TransferableResourcePtr> Convert(
+      const cc::TransferableResourceArray& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::TransferableResourceArray, Array<TransferableResourcePtr> > {
+  static cc::TransferableResourceArray Convert(
+      const Array<TransferableResourcePtr>& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<ReturnedResourcePtr, cc::ReturnedResource> {
+  static ReturnedResourcePtr Convert(const cc::ReturnedResource& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::ReturnedResource, ReturnedResourcePtr> {
+  static cc::ReturnedResource Convert(const ReturnedResourcePtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<Array<ReturnedResourcePtr>, cc::ReturnedResourceArray> {
+  static Array<ReturnedResourcePtr> Convert(
+      const cc::ReturnedResourceArray& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<FramePtr, cc::CompositorFrame> {
+  static FramePtr Convert(const cc::CompositorFrame& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<scoped_ptr<cc::CompositorFrame>, FramePtr> {
+  static scoped_ptr<cc::CompositorFrame> Convert(const FramePtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/surfaces/surfaces_utils.h b/mojo/services/public/cpp/surfaces/surfaces_utils.h
new file mode 100644
index 0000000..e4c3c83
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/surfaces_utils.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_PUBLIC_CPP_SURFACES_SURFACES_UTILS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_UTILS_H_
+
+#include "mojo/services/public/cpp/surfaces/mojo_surfaces_export.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace mojo {
+
+MOJO_SURFACES_EXPORT SharedQuadStatePtr CreateDefaultSQS(const gfx::Size& size);
+
+// Constructs a pass with the given id, output_rect and damage_rect set to rect,
+// transform_to_root_target set to identity and has_transparent_background set
+// to false.
+MOJO_SURFACES_EXPORT PassPtr CreateDefaultPass(int id, const gfx::Rect& rect);
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_UTILS_H_
diff --git a/mojo/services/public/cpp/surfaces/tests/BUILD.gn b/mojo/services/public/cpp/surfaces/tests/BUILD.gn
new file mode 100644
index 0000000..10810fe
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/tests/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_lib_unittests
+test("mojo_surfaces_lib_unittests") {
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//cc",
+    "//cc/surfaces",
+    "//gpu",
+    "//skia",
+    "//testing/gtest",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gfx:test_support",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/common/test:run_all_unittests",
+  ]
+
+  sources = [ "surface_unittest.cc" ]
+}
diff --git a/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc
new file mode 100644
index 0000000..93cf571
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc
@@ -0,0 +1,460 @@
+// 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 "cc/quads/render_pass.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+
+namespace mojo {
+namespace {
+
+TEST(SurfaceLibTest, SurfaceIdConverterNullId) {
+  cc::SurfaceId null_id;
+  cc::SurfaceId round_trip = SurfaceId::From(null_id).To<cc::SurfaceId>();
+  EXPECT_TRUE(round_trip.is_null());
+}
+
+TEST(SurfaceLibTest, SurfaceIdConverterValidId) {
+  cc::SurfaceId valid_id(7);
+  cc::SurfaceId round_trip = SurfaceId::From(valid_id).To<cc::SurfaceId>();
+  EXPECT_FALSE(round_trip.is_null());
+  EXPECT_EQ(valid_id, round_trip);
+}
+
+TEST(SurfaceLibTest, Color) {
+  SkColor arbitrary_color = SK_ColorMAGENTA;
+  SkColor round_trip = Color::From(arbitrary_color).To<SkColor>();
+  EXPECT_EQ(arbitrary_color, round_trip);
+}
+
+class SurfaceLibQuadTest : public testing::Test {
+ public:
+  SurfaceLibQuadTest()
+      : rect(5, 7, 13, 19),
+        opaque_rect(rect),
+        visible_rect(9, 11, 5, 7),
+        needs_blending(false) {
+    pass = cc::RenderPass::Create();
+    sqs = pass->CreateAndAppendSharedQuadState();
+  }
+
+ protected:
+  gfx::Rect rect;
+  gfx::Rect opaque_rect;
+  gfx::Rect visible_rect;
+  bool needs_blending;
+  scoped_ptr<cc::RenderPass> pass;
+  cc::SharedQuadState* sqs;
+};
+
+TEST_F(SurfaceLibQuadTest, ColorQuad) {
+  cc::SolidColorDrawQuad* color_quad =
+      pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+  SkColor arbitrary_color = SK_ColorGREEN;
+  bool force_anti_aliasing_off = true;
+  color_quad->SetAll(sqs,
+                     rect,
+                     opaque_rect,
+                     visible_rect,
+                     needs_blending,
+                     arbitrary_color,
+                     force_anti_aliasing_off);
+
+  QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*color_quad);
+  ASSERT_FALSE(mojo_quad.is_null());
+  EXPECT_EQ(MATERIAL_SOLID_COLOR, mojo_quad->material);
+  EXPECT_EQ(Rect::From(rect), mojo_quad->rect);
+  EXPECT_EQ(Rect::From(opaque_rect), mojo_quad->opaque_rect);
+  EXPECT_EQ(Rect::From(visible_rect), mojo_quad->visible_rect);
+  EXPECT_EQ(needs_blending, mojo_quad->needs_blending);
+  ASSERT_TRUE(mojo_quad->solid_color_quad_state);
+  SolidColorQuadStatePtr& mojo_color_state = mojo_quad->solid_color_quad_state;
+  EXPECT_EQ(Color::From(arbitrary_color), mojo_color_state->color);
+  EXPECT_EQ(force_anti_aliasing_off, mojo_color_state->force_anti_aliasing_off);
+}
+
+TEST_F(SurfaceLibQuadTest, SurfaceQuad) {
+  cc::SurfaceDrawQuad* surface_quad =
+      pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+  cc::SurfaceId arbitrary_id(5);
+  surface_quad->SetAll(
+      sqs, rect, opaque_rect, visible_rect, needs_blending, arbitrary_id);
+
+  QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*surface_quad);
+  ASSERT_FALSE(mojo_quad.is_null());
+  EXPECT_EQ(MATERIAL_SURFACE_CONTENT, mojo_quad->material);
+  ASSERT_TRUE(mojo_quad->surface_quad_state);
+  SurfaceQuadStatePtr& mojo_surface_state = mojo_quad->surface_quad_state;
+  EXPECT_EQ(SurfaceId::From(arbitrary_id),
+            mojo_surface_state->surface);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuad) {
+  cc::TextureDrawQuad* texture_quad =
+      pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+  unsigned resource_id = 9;
+  bool premultiplied_alpha = true;
+  gfx::PointF uv_top_left(1.7f, 2.1f);
+  gfx::PointF uv_bottom_right(-7.f, 16.3f);
+  SkColor background_color = SK_ColorYELLOW;
+  float vertex_opacity[4] = {0.1f, 0.5f, 0.4f, 0.8f};
+  bool flipped = false;
+  texture_quad->SetAll(sqs,
+                       rect,
+                       opaque_rect,
+                       visible_rect,
+                       needs_blending,
+                       resource_id,
+                       premultiplied_alpha,
+                       uv_top_left,
+                       uv_bottom_right,
+                       background_color,
+                       vertex_opacity,
+                       flipped);
+
+  QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*texture_quad);
+  ASSERT_FALSE(mojo_quad.is_null());
+  EXPECT_EQ(MATERIAL_TEXTURE_CONTENT, mojo_quad->material);
+  ASSERT_TRUE(mojo_quad->texture_quad_state);
+  TextureQuadStatePtr& mojo_texture_state = mojo_quad->texture_quad_state;
+  EXPECT_EQ(resource_id, mojo_texture_state->resource_id);
+  EXPECT_EQ(premultiplied_alpha, mojo_texture_state->premultiplied_alpha);
+  EXPECT_EQ(PointF::From(uv_top_left), mojo_texture_state->uv_top_left);
+  EXPECT_EQ(PointF::From(uv_bottom_right), mojo_texture_state->uv_bottom_right);
+  EXPECT_EQ(Color::From(background_color),
+            mojo_texture_state->background_color);
+  for (size_t i = 0; i < 4; ++i) {
+    EXPECT_EQ(vertex_opacity[i], mojo_texture_state->vertex_opacity[i]) << i;
+  }
+  EXPECT_EQ(flipped, mojo_texture_state->flipped);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuadEmptyVertexOpacity) {
+  QuadPtr mojo_texture_quad = Quad::New();
+  mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT;
+  TextureQuadStatePtr mojo_texture_state = TextureQuadState::New();
+  mojo_texture_state->background_color = Color::New();
+  mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass();
+  PassPtr mojo_pass = Pass::New();
+  mojo_pass->quads.push_back(mojo_texture_quad.Pass());
+  SharedQuadStatePtr mojo_sqs = SharedQuadState::New();
+  mojo_pass->shared_quad_states.push_back(mojo_sqs.Pass());
+
+  scoped_ptr<cc::RenderPass> pass = mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+
+  EXPECT_FALSE(pass);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuadEmptyBackgroundColor) {
+  QuadPtr mojo_texture_quad = Quad::New();
+  mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT;
+  TextureQuadStatePtr mojo_texture_state = TextureQuadState::New();
+  mojo_texture_state->vertex_opacity = mojo::Array<float>::New(4);
+  mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass();
+  PassPtr mojo_pass = Pass::New();
+  mojo_pass->quads.push_back(mojo_texture_quad.Pass());
+  SharedQuadStatePtr mojo_sqs = SharedQuadState::New();
+  mojo_pass->shared_quad_states.push_back(mojo_sqs.Pass());
+
+  scoped_ptr<cc::RenderPass> pass = mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+  EXPECT_FALSE(pass);
+}
+
+TEST(SurfaceLibTest, SharedQuadState) {
+  gfx::Transform content_to_target_transform;
+  content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+  gfx::Size content_bounds(57, 39);
+  gfx::Rect visible_content_rect(3, 7, 28, 42);
+  gfx::Rect clip_rect(9, 12, 21, 31);
+  bool is_clipped = true;
+  float opacity = 0.65f;
+  int sorting_context_id = 13;
+  ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
+  scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+  cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+  sqs->SetAll(content_to_target_transform,
+              content_bounds,
+              visible_content_rect,
+              clip_rect,
+              is_clipped,
+              opacity,
+              blend_mode,
+              sorting_context_id);
+
+  SharedQuadStatePtr mojo_sqs = SharedQuadState::From(*sqs);
+  ASSERT_FALSE(mojo_sqs.is_null());
+  EXPECT_EQ(Transform::From(content_to_target_transform),
+            mojo_sqs->content_to_target_transform);
+  EXPECT_EQ(Size::From(content_bounds), mojo_sqs->content_bounds);
+  EXPECT_EQ(Rect::From(visible_content_rect), mojo_sqs->visible_content_rect);
+  EXPECT_EQ(Rect::From(clip_rect), mojo_sqs->clip_rect);
+  EXPECT_EQ(is_clipped, mojo_sqs->is_clipped);
+  EXPECT_EQ(opacity, mojo_sqs->opacity);
+  EXPECT_EQ(sorting_context_id, mojo_sqs->sorting_context_id);
+}
+
+TEST(SurfaceLibTest, RenderPass) {
+  scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+  cc::RenderPassId pass_id(1, 6);
+  gfx::Rect output_rect(4, 9, 13, 71);
+  gfx::Rect damage_rect(9, 17, 41, 45);
+  gfx::Transform transform_to_root_target;
+  transform_to_root_target.SkewY(43.0);
+  bool has_transparent_background = false;
+  pass->SetAll(pass_id,
+               output_rect,
+               damage_rect,
+               transform_to_root_target,
+               has_transparent_background);
+
+  gfx::Transform content_to_target_transform;
+  content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+  gfx::Size content_bounds(57, 39);
+  gfx::Rect visible_content_rect(3, 7, 28, 42);
+  gfx::Rect clip_rect(9, 12, 21, 31);
+  bool is_clipped = true;
+  float opacity = 0.65f;
+  int sorting_context_id = 13;
+  ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
+  cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+  sqs->SetAll(content_to_target_transform,
+              content_bounds,
+              visible_content_rect,
+              clip_rect,
+              is_clipped,
+              opacity,
+              blend_mode,
+              sorting_context_id);
+
+  gfx::Rect rect(5, 7, 13, 19);
+  gfx::Rect opaque_rect(rect);
+  gfx::Rect visible_rect(9, 11, 5, 7);
+  bool needs_blending = false;
+
+  cc::SolidColorDrawQuad* color_quad =
+      pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+  SkColor arbitrary_color = SK_ColorGREEN;
+  bool force_anti_aliasing_off = true;
+  color_quad->SetAll(pass->shared_quad_state_list.back(),
+                     rect,
+                     opaque_rect,
+                     visible_rect,
+                     needs_blending,
+                     arbitrary_color,
+                     force_anti_aliasing_off);
+
+  cc::SurfaceDrawQuad* surface_quad =
+      pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+  cc::SurfaceId arbitrary_id(5);
+  surface_quad->SetAll(
+      sqs, rect, opaque_rect, visible_rect, needs_blending, arbitrary_id);
+
+  cc::TextureDrawQuad* texture_quad =
+      pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+  unsigned resource_id = 9;
+  bool premultiplied_alpha = true;
+  gfx::PointF uv_top_left(1.7f, 2.1f);
+  gfx::PointF uv_bottom_right(-7.f, 16.3f);
+  SkColor background_color = SK_ColorYELLOW;
+  float vertex_opacity[4] = {0.1f, 0.5f, 0.4f, 0.8f};
+  bool flipped = false;
+  texture_quad->SetAll(sqs,
+                       rect,
+                       opaque_rect,
+                       visible_rect,
+                       needs_blending,
+                       resource_id,
+                       premultiplied_alpha,
+                       uv_top_left,
+                       uv_bottom_right,
+                       background_color,
+                       vertex_opacity,
+                       flipped);
+
+  PassPtr mojo_pass = Pass::From(*pass);
+  ASSERT_FALSE(mojo_pass.is_null());
+  EXPECT_EQ(6, mojo_pass->id);
+  EXPECT_EQ(Rect::From(output_rect), mojo_pass->output_rect);
+  EXPECT_EQ(Rect::From(damage_rect), mojo_pass->damage_rect);
+  EXPECT_EQ(Transform::From(transform_to_root_target),
+            mojo_pass->transform_to_root_target);
+  EXPECT_EQ(has_transparent_background, mojo_pass->has_transparent_background);
+  ASSERT_EQ(1u, mojo_pass->shared_quad_states.size());
+  ASSERT_EQ(3u, mojo_pass->quads.size());
+  EXPECT_EQ(0, mojo_pass->quads[0]->shared_quad_state_index);
+
+  scoped_ptr<cc::RenderPass> round_trip_pass =
+      mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+  EXPECT_EQ(pass_id, round_trip_pass->id);
+  EXPECT_EQ(output_rect, round_trip_pass->output_rect);
+  EXPECT_EQ(damage_rect, round_trip_pass->damage_rect);
+  EXPECT_EQ(transform_to_root_target,
+            round_trip_pass->transform_to_root_target);
+  EXPECT_EQ(has_transparent_background,
+            round_trip_pass->has_transparent_background);
+  ASSERT_EQ(1u, round_trip_pass->shared_quad_state_list.size());
+  ASSERT_EQ(3u, round_trip_pass->quad_list.size());
+  EXPECT_EQ(round_trip_pass->shared_quad_state_list.front(),
+            round_trip_pass->quad_list.front()->shared_quad_state);
+
+  cc::SharedQuadState* round_trip_sqs =
+      round_trip_pass->shared_quad_state_list.front();
+  EXPECT_EQ(content_to_target_transform,
+            round_trip_sqs->content_to_target_transform);
+  EXPECT_EQ(content_bounds, round_trip_sqs->content_bounds);
+  EXPECT_EQ(visible_content_rect, round_trip_sqs->visible_content_rect);
+  EXPECT_EQ(clip_rect, round_trip_sqs->clip_rect);
+  EXPECT_EQ(is_clipped, round_trip_sqs->is_clipped);
+  EXPECT_EQ(opacity, round_trip_sqs->opacity);
+  EXPECT_EQ(sorting_context_id, round_trip_sqs->sorting_context_id);
+
+  cc::QuadList::Iterator dq_iter = round_trip_pass->quad_list.begin();
+  // First is solid color quad.
+  ASSERT_EQ(cc::DrawQuad::SOLID_COLOR, dq_iter->material);
+  EXPECT_EQ(rect, dq_iter->rect);
+  EXPECT_EQ(opaque_rect, dq_iter->opaque_rect);
+  EXPECT_EQ(visible_rect, dq_iter->visible_rect);
+  EXPECT_EQ(needs_blending, dq_iter->needs_blending);
+  const cc::SolidColorDrawQuad* round_trip_color_quad =
+      cc::SolidColorDrawQuad::MaterialCast(&*dq_iter);
+  EXPECT_EQ(arbitrary_color, round_trip_color_quad->color);
+  EXPECT_EQ(force_anti_aliasing_off,
+            round_trip_color_quad->force_anti_aliasing_off);
+
+  ++dq_iter;
+  // Second is surface quad.
+  ASSERT_EQ(cc::DrawQuad::SURFACE_CONTENT, dq_iter->material);
+  const cc::SurfaceDrawQuad* round_trip_surface_quad =
+      cc::SurfaceDrawQuad::MaterialCast(&*dq_iter);
+  EXPECT_EQ(arbitrary_id, round_trip_surface_quad->surface_id);
+
+  ++dq_iter;
+  // Third is texture quad.
+  ASSERT_EQ(cc::DrawQuad::TEXTURE_CONTENT, dq_iter->material);
+  const cc::TextureDrawQuad* round_trip_texture_quad =
+      cc::TextureDrawQuad::MaterialCast(&*dq_iter);
+  EXPECT_EQ(resource_id, round_trip_texture_quad->resource_id);
+  EXPECT_EQ(premultiplied_alpha, round_trip_texture_quad->premultiplied_alpha);
+  EXPECT_EQ(uv_top_left, round_trip_texture_quad->uv_top_left);
+  EXPECT_EQ(uv_bottom_right, round_trip_texture_quad->uv_bottom_right);
+  EXPECT_EQ(background_color, round_trip_texture_quad->background_color);
+  for (size_t i = 0; i < 4; ++i) {
+    EXPECT_EQ(vertex_opacity[i], round_trip_texture_quad->vertex_opacity[i])
+        << i;
+  }
+  EXPECT_EQ(flipped, round_trip_texture_quad->flipped);
+}
+
+TEST(SurfaceLibTest, Mailbox) {
+  gpu::Mailbox mailbox;
+  mailbox.Generate();
+
+  MailboxPtr mojo_mailbox = Mailbox::From(mailbox);
+  EXPECT_EQ(0, memcmp(mailbox.name, &mojo_mailbox->name.storage()[0], 64));
+
+  gpu::Mailbox round_trip_mailbox = mojo_mailbox.To<gpu::Mailbox>();
+  EXPECT_EQ(mailbox, round_trip_mailbox);
+}
+
+TEST(SurfaceLibTest, MailboxEmptyName) {
+  MailboxPtr mojo_mailbox = Mailbox::New();
+
+  gpu::Mailbox converted_mailbox = mojo_mailbox.To<gpu::Mailbox>();
+  EXPECT_TRUE(converted_mailbox.IsZero());
+}
+
+TEST(SurfaceLibTest, MailboxHolder) {
+  gpu::Mailbox mailbox;
+  mailbox.Generate();
+  uint32_t texture_target = GL_TEXTURE_2D;
+  uint32_t sync_point = 7u;
+  gpu::MailboxHolder holder(mailbox, texture_target, sync_point);
+
+  MailboxHolderPtr mojo_holder = MailboxHolder::From(holder);
+  EXPECT_EQ(texture_target, mojo_holder->texture_target);
+  EXPECT_EQ(sync_point, mojo_holder->sync_point);
+
+  gpu::MailboxHolder round_trip_holder = mojo_holder.To<gpu::MailboxHolder>();
+  EXPECT_EQ(mailbox, round_trip_holder.mailbox);
+  EXPECT_EQ(texture_target, round_trip_holder.texture_target);
+  EXPECT_EQ(sync_point, round_trip_holder.sync_point);
+}
+
+TEST(SurfaceLibTest, TransferableResource) {
+  uint32_t id = 7u;
+  cc::ResourceFormat format = cc::BGRA_8888;
+  uint32_t filter = 123u;
+  gfx::Size size(17, 18);
+  gpu::MailboxHolder mailbox_holder;
+  bool is_repeated = false;
+  ;
+  bool is_software = false;
+  cc::TransferableResource resource;
+  resource.id = id;
+  resource.format = format;
+  resource.filter = filter;
+  resource.size = size;
+  resource.mailbox_holder = mailbox_holder;
+  resource.is_repeated = is_repeated;
+  resource.is_software = is_software;
+
+  TransferableResourcePtr mojo_resource = TransferableResource::From(resource);
+  EXPECT_EQ(id, mojo_resource->id);
+  EXPECT_EQ(static_cast<ResourceFormat>(format),
+            mojo_resource->format);
+  EXPECT_EQ(filter, mojo_resource->filter);
+  EXPECT_EQ(Size::From(size), mojo_resource->size);
+  EXPECT_EQ(is_repeated, mojo_resource->is_repeated);
+  EXPECT_EQ(is_software, mojo_resource->is_software);
+
+  cc::TransferableResource round_trip_resource =
+      mojo_resource.To<cc::TransferableResource>();
+  EXPECT_EQ(id, round_trip_resource.id);
+  EXPECT_EQ(format, round_trip_resource.format);
+  EXPECT_EQ(filter, round_trip_resource.filter);
+  EXPECT_EQ(size, round_trip_resource.size);
+  EXPECT_EQ(mailbox_holder.mailbox, round_trip_resource.mailbox_holder.mailbox);
+  EXPECT_EQ(mailbox_holder.texture_target,
+            round_trip_resource.mailbox_holder.texture_target);
+  EXPECT_EQ(mailbox_holder.sync_point,
+            round_trip_resource.mailbox_holder.sync_point);
+  EXPECT_EQ(is_repeated, round_trip_resource.is_repeated);
+  EXPECT_EQ(is_software, round_trip_resource.is_software);
+}
+
+TEST(SurfaceLibTest, ReturnedResource) {
+  uint32_t id = 5u;
+  uint32_t sync_point = 24u;
+  int count = 2;
+  bool lost = false;
+  cc::ReturnedResource resource;
+  resource.id = id;
+  resource.sync_point = sync_point;
+  resource.count = count;
+  resource.lost = lost;
+
+  ReturnedResourcePtr mojo_resource = ReturnedResource::From(resource);
+  EXPECT_EQ(id, mojo_resource->id);
+  EXPECT_EQ(sync_point, mojo_resource->sync_point);
+  EXPECT_EQ(count, mojo_resource->count);
+  EXPECT_EQ(lost, mojo_resource->lost);
+
+  cc::ReturnedResource round_trip_resource =
+      mojo_resource.To<cc::ReturnedResource>();
+  EXPECT_EQ(id, round_trip_resource.id);
+  EXPECT_EQ(sync_point, round_trip_resource.sync_point);
+  EXPECT_EQ(count, round_trip_resource.count);
+  EXPECT_EQ(lost, round_trip_resource.lost);
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/BUILD.gn b/mojo/services/public/cpp/view_manager/BUILD.gn
new file mode 100644
index 0000000..cab9b73
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_lib
+source_set("view_manager") {
+  sources = [
+    "lib/bitmap_uploader.cc",
+    "lib/bitmap_uploader.h",
+    "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.h",
+    "view_observer.h",
+    "window_manager_delegate.h",
+  ]
+
+  public_deps = [
+    ":common",
+    "//skia",
+  ]
+  deps = [
+    "//base",
+    "//cc/surfaces",
+    "//gpu",
+    "//mojo/application",
+    "//mojo/public/c/gles2",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/gpu",
+    "//mojo/services/public/interfaces/input_events:input_events",
+    "//mojo/services/public/interfaces/native_viewport",
+    "//mojo/services/public/interfaces/surfaces:surface_id",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/services/public/interfaces/window_manager",
+    "//skia",
+    "//ui/events",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+}
+
+source_set("common") {
+  sources = [
+    "ids.h",
+    "types.h",
+  ]
+  public_deps = [
+    "//base",
+  ]
+}
diff --git a/mojo/services/public/cpp/view_manager/DEPS b/mojo/services/public/cpp/view_manager/DEPS
new file mode 100644
index 0000000..4460c19
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/skia/include/core",
+  "+ui/gfx/geometry"
+]
diff --git a/mojo/services/public/cpp/view_manager/lib/BUILD.gn b/mojo/services/public/cpp/view_manager/lib/BUILD.gn
new file mode 100644
index 0000000..df44a34
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/BUILD.gn
@@ -0,0 +1,29 @@
+# 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")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_run_unittests
+source_set("run_unittests") {
+  testonly = true
+  sources = [
+    "view_manager_test_suite.cc",
+    "view_manager_test_suite.h",
+    "view_manager_unittests.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+  ]
+
+  if (use_x11) {
+    deps += ["//ui/gfx/x"]
+  }
+
+  if (is_component_build) {
+    deps += ["//ui/gl"]
+  }
+
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/DEPS b/mojo/services/public/cpp/view_manager/lib/DEPS
new file mode 100644
index 0000000..0af5fad
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+  "+cc/surfaces/surface_id.h",
+  "+cc/surfaces/surface_id_allocator.h",
+  "+gpu/GLES2",
+  "+gpu/command_buffer/common/mailbox.h",
+  "+mojo/geometry",
+  "+third_party/khronos/GLES2",
+  "+third_party/skia",
+  "+ui/gfx",
+]
+
+specific_include_rules = {
+  "view_manager_test_suite.cc": [
+    "+mojo/services/native_viewport/native_viewport.h",
+    "+ui/gl",
+  ],
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc
new file mode 100644
index 0000000..80447ed
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc
@@ -0,0 +1,232 @@
+// 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/public/cpp/view_manager/lib/bitmap_uploader.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif  // GL_GLEXT_PROTOTYPES
+
+#include "base/bind.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+
+namespace {
+void LostContext(void*) {
+  DCHECK(false);
+}
+
+uint32_t TextureFormat() {
+  return SK_B32_SHIFT ? GL_RGBA : GL_BGRA_EXT;
+}
+}
+
+BitmapUploader::BitmapUploader(ViewManagerClientImpl* client,
+                               Id view_id,
+                               SurfacesServicePtr surfaces_service,
+                               GpuPtr gpu_service)
+    : client_(client),
+      view_id_(view_id),
+      surfaces_service_(surfaces_service.Pass()),
+      gpu_service_(gpu_service.Pass()),
+      color_(SK_ColorTRANSPARENT),
+      next_resource_id_(1u),
+      weak_factory_(this) {
+  surfaces_service_->CreateSurfaceConnection(base::Bind(
+      &BitmapUploader::OnSurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+  CommandBufferPtr gles2_client;
+  gpu_service_->CreateOffscreenGLES2Context(Get(&gles2_client));
+  gles2_context_ =
+      MojoGLES2CreateContext(gles2_client.PassMessagePipe().release().value(),
+                             &LostContext,
+                             NULL,
+                             Environment::GetDefaultAsyncWaiter());
+  MojoGLES2MakeCurrent(gles2_context_);
+}
+
+BitmapUploader::~BitmapUploader() {
+  MojoGLES2DestroyContext(gles2_context_);
+}
+
+void BitmapUploader::SetSize(const gfx::Size& size) {
+  if (size_ == size)
+    return;
+  size_ = size;
+}
+
+void BitmapUploader::SetColor(SkColor color) {
+  if (color_ == color)
+    return;
+  color_ = color;
+  if (surface_)
+    Upload();
+}
+
+void BitmapUploader::SetBitmap(SkBitmap bitmap) {
+  bitmap_ = bitmap;
+  if (surface_)
+    Upload();
+}
+
+void BitmapUploader::Upload() {
+  if (size_.width() == 0 || size_.height() == 0) {
+    client_->SetSurfaceId(view_id_, SurfaceId::New());
+    return;
+  }
+  if (!surface_) {  // Can't upload yet, store for later.
+    done_callback_ = done_callback_;
+    return;
+  }
+  if (id_.is_null() || size_ != surface_size_) {
+    if (!id_.is_null())
+      surface_->DestroySurface(SurfaceId::From(id_));
+    id_ = id_allocator_->GenerateId();
+    surface_->CreateSurface(SurfaceId::From(id_), Size::From(size_));
+    client_->SetSurfaceId(view_id_, SurfaceId::From(id_));
+    surface_size_ = size_;
+  }
+
+  gfx::Rect bounds(size_);
+  PassPtr pass = CreateDefaultPass(1, bounds);
+  FramePtr frame = Frame::New();
+  frame->resources.resize(0u);
+
+  pass->quads.resize(0u);
+  pass->shared_quad_states.push_back(CreateDefaultSQS(size_));
+
+  MojoGLES2MakeCurrent(gles2_context_);
+  if (!bitmap_.isNull()) {
+    gfx::Size bitmap_size(bitmap_.width(), bitmap_.height());
+    GLuint texture_id = BindTextureForSize(bitmap_size);
+    bitmap_.lockPixels();
+    glTexSubImage2D(GL_TEXTURE_2D,
+                    0,
+                    0,
+                    0,
+                    bitmap_size.width(),
+                    bitmap_size.height(),
+                    TextureFormat(),
+                    GL_UNSIGNED_BYTE,
+                    bitmap_.getPixels());
+    bitmap_.unlockPixels();
+
+    gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+    glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+    GLuint sync_point = glInsertSyncPointCHROMIUM();
+
+    TransferableResourcePtr resource = TransferableResource::New();
+    resource->id = next_resource_id_++;
+    resource_to_texture_id_map_[resource->id] = texture_id;
+    resource->format = mojo::RESOURCE_FORMAT_RGBA_8888;
+    resource->filter = GL_LINEAR;
+    resource->size = Size::From(bitmap_size);
+    MailboxHolderPtr mailbox_holder = MailboxHolder::New();
+    mailbox_holder->mailbox = Mailbox::From(mailbox);
+    mailbox_holder->texture_target = GL_TEXTURE_2D;
+    mailbox_holder->sync_point = sync_point;
+    resource->mailbox_holder = mailbox_holder.Pass();
+    resource->is_repeated = false;
+    resource->is_software = false;
+
+    QuadPtr quad = Quad::New();
+    quad->material = MATERIAL_TEXTURE_CONTENT;
+    quad->rect = Rect::From(bounds);
+    quad->opaque_rect = Rect::From(bounds);
+    quad->visible_rect = Rect::From(bounds);
+    quad->needs_blending = true;
+    quad->shared_quad_state_index = 0u;
+
+    TextureQuadStatePtr texture_state = TextureQuadState::New();
+    texture_state->resource_id = resource->id;
+    texture_state->premultiplied_alpha = true;
+    texture_state->uv_top_left = PointF::From(gfx::PointF(0.f, 0.f));
+    texture_state->uv_bottom_right = PointF::From(gfx::PointF(1.f, 1.f));
+    texture_state->background_color = Color::From(SkColor(SK_ColorTRANSPARENT));
+    for (int i = 0; i < 4; ++i)
+      texture_state->vertex_opacity.push_back(1.f);
+    texture_state->flipped = false;
+
+    frame->resources.push_back(resource.Pass());
+    quad->texture_quad_state = texture_state.Pass();
+    pass->quads.push_back(quad.Pass());
+  }
+
+  if (color_ != SK_ColorTRANSPARENT) {
+    QuadPtr quad = Quad::New();
+    quad->material = MATERIAL_SOLID_COLOR;
+    quad->rect = Rect::From(bounds);
+    quad->opaque_rect = Rect::From(gfx::Rect());
+    quad->visible_rect = Rect::From(bounds);
+    quad->needs_blending = true;
+    quad->shared_quad_state_index = 0u;
+
+    SolidColorQuadStatePtr color_state = SolidColorQuadState::New();
+    color_state->color = Color::From(color_);
+    color_state->force_anti_aliasing_off = false;
+
+    quad->solid_color_quad_state = color_state.Pass();
+    pass->quads.push_back(quad.Pass());
+  }
+
+  frame->passes.push_back(pass.Pass());
+
+  surface_->SubmitFrame(SurfaceId::From(id_), frame.Pass());
+}
+
+void BitmapUploader::ReturnResources(Array<ReturnedResourcePtr> resources) {
+  if (!resources.size())
+    return;
+  MojoGLES2MakeCurrent(gles2_context_);
+  // TODO(jamesr): Recycle.
+  for (size_t i = 0; i < resources.size(); ++i) {
+    ReturnedResourcePtr resource = resources[i].Pass();
+    DCHECK_EQ(1, resource->count);
+    glWaitSyncPointCHROMIUM(resource->sync_point);
+    uint32_t texture_id = resource_to_texture_id_map_[resource->id];
+    DCHECK_NE(0u, texture_id);
+    resource_to_texture_id_map_.erase(resource->id);
+    glDeleteTextures(1, &texture_id);
+  }
+}
+
+void BitmapUploader::OnSurfaceConnectionCreated(SurfacePtr surface,
+                                                uint32_t id_namespace) {
+  surface_ = surface.Pass();
+  surface_.set_client(this);
+  id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+  if (color_ != SK_ColorTRANSPARENT || !bitmap_.isNull())
+    Upload();
+}
+
+uint32_t BitmapUploader::BindTextureForSize(const gfx::Size size) {
+  // TODO(jamesr): Recycle textures.
+  GLuint texture = 0u;
+  glGenTextures(1, &texture);
+  glBindTexture(GL_TEXTURE_2D, texture);
+  glTexImage2D(GL_TEXTURE_2D,
+               0,
+               TextureFormat(),
+               size.width(),
+               size.height(),
+               0,
+               TextureFormat(),
+               GL_UNSIGNED_BYTE,
+               0);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  return texture;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h
new file mode 100644
index 0000000..f049409
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h
@@ -0,0 +1,72 @@
+// 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_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+class ViewManagerClientImpl;
+
+class BitmapUploader : public SurfaceClient {
+ public:
+  BitmapUploader(ViewManagerClientImpl* client,
+                 Id view_id,
+                 SurfacesServicePtr surfaces_service,
+                 GpuPtr gpu_service);
+  virtual ~BitmapUploader();
+
+  void SetSize(const gfx::Size& size);
+  void SetColor(SkColor color);
+  void SetBitmap(SkBitmap bitmap);
+  void SetDoneCallback(const base::Callback<void(SurfaceIdPtr)>& done_callback);
+
+ private:
+  void Upload();
+  void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  uint32_t BindTextureForSize(const gfx::Size size);
+
+  // SurfaceClient implementation.
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+  ViewManagerClientImpl* client_;
+  Id view_id_;
+  SurfacesServicePtr surfaces_service_;
+  GpuPtr gpu_service_;
+  MojoGLES2Context gles2_context_;
+
+  gfx::Size size_;
+  SkColor color_;
+  SkBitmap bitmap_;
+  base::Callback<void(SurfaceIdPtr)> done_callback_;
+  SurfacePtr surface_;
+  cc::SurfaceId id_;
+  scoped_ptr<cc::SurfaceIdAllocator> id_allocator_;
+  gfx::Size surface_size_;
+  uint32_t next_resource_id_;
+  base::hash_map<uint32_t, uint32_t> resource_to_texture_id_map_;
+
+  base::WeakPtrFactory<BitmapUploader> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BitmapUploader);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view.cc b/mojo/services/public/cpp/view_manager/lib/view.cc
new file mode 100644
index 0000000..fe45ffc
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view.cc
@@ -0,0 +1,456 @@
+// 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/public/cpp/view_manager/view.h"
+
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/canvas.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_;
+
+  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_;
+
+  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 == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
+      (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
+    return false;
+  }
+
+  ScopedOrderChangedNotifier notifier(view, relative, direction);
+
+  const size_t dest_i = direction == ORDER_DIRECTION_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 gfx::Rect& old_bounds,
+                          const gfx::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 gfx::Rect old_bounds_;
+  const gfx::Rect new_bounds_;
+
+  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:
+
+// static
+View* View::Create(ViewManager* view_manager) {
+  View* view = new View(view_manager);
+  static_cast<ViewManagerClientImpl*>(view_manager)->AddView(view);
+  return view;
+}
+
+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 gfx::Rect& bounds) {
+  if (!OwnsView(manager_, this))
+    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);
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this));
+  visible_ = value;
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(this));
+}
+
+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);
+}
+
+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(ViewPrivate(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(ViewPrivate(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(), ORDER_DIRECTION_ABOVE);
+}
+
+void View::MoveToBack() {
+  if (!parent_ || parent_->children_.front() == this)
+    return;
+  Reorder(parent_->children_.front(), ORDER_DIRECTION_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 (manager_)
+    CHECK_EQ(ViewPrivate(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::SetContents(const SkBitmap& contents) {
+  if (manager_) {
+    if (!bitmap_uploader_)
+      CreateBitmapUploader();
+    bitmap_uploader_->SetSize(bounds_.size());
+    bitmap_uploader_->SetBitmap(contents);
+  }
+}
+
+void View::SetColor(SkColor color) {
+  if (manager_) {
+    if (!bitmap_uploader_)
+      CreateBitmapUploader();
+    bitmap_uploader_->SetSize(bounds_.size());
+    bitmap_uploader_->SetColor(color);
+  }
+}
+
+void View::SetFocus() {
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_);
+}
+
+void View::Embed(const String& url) {
+  static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_);
+}
+
+scoped_ptr<ServiceProvider>
+    View::Embed(const String& url,
+                scoped_ptr<ServiceProviderImpl> exported_services) {
+  scoped_ptr<ServiceProvider> imported_services;
+  // BindToProxy() takes ownership of |exported_services|.
+  ServiceProviderImpl* registry = exported_services.release();
+  ServiceProviderPtr sp;
+  if (registry) {
+    BindToProxy(registry, &sp);
+    imported_services.reset(registry->CreateRemoteServiceProvider());
+  }
+  static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_, sp.Pass());
+  return imported_services.Pass();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, protected:
+
+View::View()
+    : manager_(NULL),
+      id_(static_cast<Id>(-1)),
+      parent_(NULL),
+      visible_(true),
+      drawn_(false) {
+}
+
+View::~View() {
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
+  if (parent_)
+    parent_->LocalRemoveChild(this);
+  // TODO(beng): It'd be better to do this via a destruction observer in the
+  //             ViewManagerClientImpl.
+  if (manager_)
+    static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
+  FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, private:
+
+View::View(ViewManager* manager)
+    : manager_(manager),
+      id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateView()),
+      parent_(NULL),
+      visible_(true),
+      drawn_(false) {
+}
+
+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 gfx::Rect& old_bounds,
+                          const gfx::Rect& new_bounds) {
+  DCHECK(old_bounds == bounds_);
+  ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
+  bounds_ = new_bounds;
+}
+
+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::CreateBitmapUploader() {
+  ViewManagerClientImpl* vmci = static_cast<ViewManagerClientImpl*>(manager_);
+  SurfacesServicePtr surfaces_service;
+  InterfacePtr<ServiceProvider> surfaces_service_provider;
+  vmci->shell()->ConnectToApplication("mojo:mojo_surfaces_service",
+                                      Get(&surfaces_service_provider));
+  ConnectToService(surfaces_service_provider.get(), &surfaces_service);
+  GpuPtr gpu_service;
+  InterfacePtr<ServiceProvider> gpu_service_provider;
+  vmci->shell()->ConnectToApplication("mojo:mojo_native_viewport_service",
+                                      Get(&gpu_service_provider));
+  ConnectToService(gpu_service_provider.get(), &gpu_service);
+  bitmap_uploader_.reset(new BitmapUploader(
+      vmci, id_, surfaces_service.Pass(), gpu_service.Pass()));
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc
new file mode 100644
index 0000000..8b8ada1
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc
@@ -0,0 +1,28 @@
+// 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/public/cpp/view_manager/view_manager_client_factory.h"
+
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+
+namespace mojo {
+
+ViewManagerClientFactory::ViewManagerClientFactory(
+    Shell* shell,
+    ViewManagerDelegate* delegate)
+    : shell_(shell), delegate_(delegate) {
+}
+
+ViewManagerClientFactory::~ViewManagerClientFactory() {
+}
+
+// InterfaceFactory<ViewManagerClient> implementation.
+void ViewManagerClientFactory::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<ViewManagerClient> request) {
+  BindToRequest(new ViewManagerClientImpl(delegate_, shell_), &request);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
new file mode 100644
index 0000000..19955b2
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
@@ -0,0 +1,428 @@
+// 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/public/cpp/view_manager/lib/view_manager_client_impl.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.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 "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.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);
+  client->AddView(view);
+  private_view.LocalSetBounds(gfx::Rect(), view_data->bounds.To<gfx::Rect>());
+  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) {}
+  virtual ~RootObserver() {}
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewDestroyed(View* view) OVERRIDE {
+    DCHECK_EQ(view, root_);
+    static_cast<ViewManagerClientImpl*>(
+        ViewPrivate(root_).view_manager())->RemoveRoot(root_);
+    view->RemoveObserver(this);
+    delete this;
+  }
+
+  View* root_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootObserver);
+};
+
+ViewManagerClientImpl::ViewManagerClientImpl(ViewManagerDelegate* delegate,
+                                             Shell* shell)
+    : connected_(false),
+      connection_id_(0),
+      next_id_(1),
+      delegate_(delegate),
+      window_manager_delegate_(NULL),
+      shell_(shell) {
+  // TODO(beng): Come up with a better way of establishing a configuration for
+  //             what the active window manager is.
+  std::string window_manager_url = "mojo:mojo_window_manager";
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch("window-manager")) {
+    window_manager_url =
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            "window-manager");
+  }
+  InterfacePtr<ServiceProvider> sp;
+  shell->ConnectToApplication(window_manager_url, Get(&sp));
+  ConnectToService(sp.get(), &window_manager_);
+  window_manager_.set_client(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);
+}
+
+Id ViewManagerClientImpl::CreateView() {
+  DCHECK(connected_);
+  const Id view_id = MakeTransportId(connection_id_, ++next_id_);
+  service_->CreateView(view_id, ActionCompletedCallbackWithErrorCode());
+  return view_id;
+}
+
+void ViewManagerClientImpl::DestroyView(Id view_id) {
+  DCHECK(connected_);
+  service_->DeleteView(view_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::AddChild(Id child_id, Id parent_id) {
+  DCHECK(connected_);
+  service_->AddView(parent_id, child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) {
+  DCHECK(connected_);
+  service_->RemoveViewFromParent(child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Reorder(
+    Id view_id,
+    Id relative_view_id,
+    OrderDirection direction) {
+  DCHECK(connected_);
+  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 gfx::Rect& bounds) {
+  DCHECK(connected_);
+  service_->SetViewBounds(view_id, Rect::From(bounds),
+                          ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetSurfaceId(Id view_id, SurfaceIdPtr surface_id) {
+  DCHECK(connected_);
+  if (surface_id.is_null())
+    return;
+  service_->SetViewSurfaceId(
+      view_id, surface_id.Pass(), ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetFocus(Id view_id) {
+  window_manager_->FocusWindow(view_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetVisible(Id view_id, bool visible) {
+  DCHECK(connected_);
+  service_->SetViewVisibility(view_id, visible, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Embed(const String& url, Id view_id) {
+  ServiceProviderPtr sp;
+  BindToProxy(new ServiceProviderImpl, &sp);
+  Embed(url, view_id, sp.Pass());
+}
+
+void ViewManagerClientImpl::Embed(
+    const String& url,
+    Id view_id,
+    ServiceProviderPtr service_provider) {
+  DCHECK(connected_);
+  service_->Embed(url, view_id, service_provider.Pass(),
+                  ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::AddView(View* view) {
+  DCHECK(views_.find(view->id()) == views_.end());
+  views_[view->id()] = view;
+}
+
+void ViewManagerClientImpl::RemoveView(Id view_id) {
+  IdToViewMap::iterator it = views_.find(view_id);
+  if (it != views_.end())
+    views_.erase(it);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManager implementation:
+
+void ViewManagerClientImpl::SetWindowManagerDelegate(
+    WindowManagerDelegate* window_manager_delegate) {
+  CHECK(NULL != GetViewById(1));
+  CHECK(!window_manager_delegate_);
+  window_manager_delegate_ = window_manager_delegate;
+}
+
+void ViewManagerClientImpl::DispatchEvent(View* target, EventPtr event) {
+  CHECK(window_manager_delegate_);
+  service_->DispatchOnViewInputEvent(target->id(), event.Pass());
+}
+
+const std::string& ViewManagerClientImpl::GetEmbedderURL() const {
+  return creator_url_;
+}
+
+const std::vector<View*>& ViewManagerClientImpl::GetRoots() const {
+  return roots_;
+}
+
+View* ViewManagerClientImpl::GetViewById(Id id) {
+  IdToViewMap::const_iterator it = views_.find(id);
+  return it != views_.end() ? it->second : NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, InterfaceImpl overrides:
+
+void ViewManagerClientImpl::OnConnectionEstablished() {
+  service_ = client();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManagerClient implementation:
+
+void ViewManagerClientImpl::OnEmbed(
+    ConnectionSpecificId connection_id,
+    const String& creator_url,
+    ViewDataPtr root_data,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  if (!connected_) {
+    connected_ = true;
+    connection_id_ = connection_id;
+    creator_url_ = String::From(creator_url);
+  } else {
+    DCHECK_EQ(connection_id_, connection_id);
+    DCHECK_EQ(creator_url_, creator_url);
+  }
+
+  // A new root must not already exist as a root or be contained by an existing
+  // hierarchy visible to this view manager.
+  View* root = AddViewToViewManager(this, NULL, root_data);
+  roots_.push_back(root);
+  root->AddObserver(new RootObserver(root));
+
+  // BindToRequest() binds the lifetime of |exported_services| to the pipe.
+  ServiceProviderImpl* exported_services = new ServiceProviderImpl;
+  BindToRequest(exported_services, &service_provider);
+  scoped_ptr<ServiceProvider> remote(
+      exported_services->CreateRemoteServiceProvider());
+  delegate_->OnEmbed(this, root, exported_services, remote.Pass());
+}
+
+void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id,
+                                                RectPtr old_bounds,
+                                                RectPtr new_bounds) {
+  View* view = GetViewById(view_id);
+  ViewPrivate(view).LocalSetBounds(old_bounds.To<gfx::Rect>(),
+                                   new_bounds.To<gfx::Rect>());
+}
+
+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)
+    view->SetVisible(visible);
+}
+
+void ViewManagerClientImpl::OnViewDrawnStateChanged(Id view_id, bool drawn) {
+  View* view = GetViewById(view_id);
+  if (view)
+    ViewPrivate(view).LocalSetDrawn(drawn);
+}
+
+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::Embed(
+    const String& url,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  if (window_manager_delegate_)
+    window_manager_delegate_->Embed(url, service_provider.Pass());
+}
+
+void ViewManagerClientImpl::DispatchOnViewInputEvent(EventPtr event) {
+  if (window_manager_delegate_)
+    window_manager_delegate_->DispatchEvent(event.Pass());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, WindowManagerClient implementation:
+
+void ViewManagerClientImpl::OnWindowManagerReady() {}
+
+void ViewManagerClientImpl::OnCaptureChanged(Id old_capture_view_id,
+                                             Id new_capture_view_id) {}
+
+void ViewManagerClientImpl::OnFocusChanged(Id old_focused_view_id,
+                                           Id new_focused_view_id) {
+  View* focused = GetViewById(new_focused_view_id);
+  View* blurred = GetViewById(old_focused_view_id);
+  if (blurred) {
+    FOR_EACH_OBSERVER(ViewObserver,
+                      *ViewPrivate(blurred).observers(),
+                      OnViewFocusChanged(focused, blurred));
+  }
+  if (focused) {
+    FOR_EACH_OBSERVER(ViewObserver,
+                      *ViewPrivate(focused).observers(),
+                      OnViewFocusChanged(focused, blurred));
+  }
+}
+
+void ViewManagerClientImpl::OnActiveWindowChanged(Id old_focused_window,
+                                                  Id new_focused_window) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, private:
+
+void ViewManagerClientImpl::RemoveRoot(View* root) {
+  std::vector<View*>::iterator it =
+      std::find(roots_.begin(), roots_.end(), root);
+  if (it != roots_.end())
+    roots_.erase(it);
+}
+
+void ViewManagerClientImpl::OnActionCompleted(bool success) {
+  if (!change_acked_callback_.is_null())
+    change_acked_callback_.Run();
+}
+
+void ViewManagerClientImpl::OnActionCompletedWithErrorCode(ErrorCode code) {
+  OnActionCompleted(code == ERROR_CODE_NONE);
+}
+
+base::Callback<void(bool)> ViewManagerClientImpl::ActionCompletedCallback() {
+  return base::Bind(&ViewManagerClientImpl::OnActionCompleted,
+                    base::Unretained(this));
+}
+
+base::Callback<void(ErrorCode)>
+    ViewManagerClientImpl::ActionCompletedCallbackWithErrorCode() {
+  return base::Bind(&ViewManagerClientImpl::OnActionCompletedWithErrorCode,
+                    base::Unretained(this));
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
new file mode 100644
index 0000000..bd09a58
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+
+class SkBitmap;
+
+namespace mojo {
+class ViewManager;
+class ViewManagerDelegate;
+class ViewManagerTransaction;
+class Shell;
+
+// Manages the connection with the View Manager service.
+class ViewManagerClientImpl : public ViewManager,
+                              public InterfaceImpl<ViewManagerClient>,
+                              public WindowManagerClient {
+ public:
+  ViewManagerClientImpl(ViewManagerDelegate* delegate, Shell* shell);
+  virtual ~ViewManagerClientImpl();
+
+  bool connected() const { return connected_; }
+  ConnectionSpecificId connection_id() const { return connection_id_; }
+
+  // API exposed to the view implementations that pushes local changes to the
+  // service.
+  Id CreateView();
+  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 gfx::Rect& bounds);
+  void SetSurfaceId(Id view_id, SurfaceIdPtr surface_id);
+  void SetFocus(Id view_id);
+  void SetVisible(Id view_id, bool visible);
+
+  void Embed(const String& url, Id view_id);
+  void Embed(const String& url,
+             Id view_id,
+             ServiceProviderPtr service_provider);
+
+  void set_change_acked_callback(const base::Callback<void(void)>& callback) {
+    change_acked_callback_ = callback;
+  }
+  void ClearChangeAckedCallback() {
+    change_acked_callback_ = base::Callback<void(void)>();
+  }
+
+  // Start/stop tracking views. While tracked, they can be retrieved via
+  // ViewManager::GetViewById.
+  void AddView(View* view);
+  void RemoveView(Id view_id);
+
+  Shell* shell() { return shell_; }
+
+ private:
+  friend class RootObserver;
+
+  typedef std::map<Id, View*> IdToViewMap;
+
+  // Overridden from ViewManager:
+  virtual void SetWindowManagerDelegate(
+      WindowManagerDelegate* delegate) OVERRIDE;
+  virtual void DispatchEvent(View* target, EventPtr event) OVERRIDE;
+  virtual const std::string& GetEmbedderURL() const OVERRIDE;
+  virtual const std::vector<View*>& GetRoots() const OVERRIDE;
+  virtual View* GetViewById(Id id) OVERRIDE;
+
+  // Overridden from InterfaceImpl:
+  virtual void OnConnectionEstablished() OVERRIDE;
+
+  // Overridden from ViewManagerClient:
+  virtual void OnEmbed(ConnectionSpecificId connection_id,
+                       const String& creator_url,
+                       ViewDataPtr root,
+                       InterfaceRequest<ServiceProvider> services) OVERRIDE;
+  virtual void OnViewBoundsChanged(Id view_id,
+                                   RectPtr old_bounds,
+                                   RectPtr new_bounds) OVERRIDE;
+  virtual void OnViewHierarchyChanged(Id view_id,
+                                      Id new_parent_id,
+                                      Id old_parent_id,
+                                      Array<ViewDataPtr> views) OVERRIDE;
+  virtual void OnViewReordered(Id view_id,
+                               Id relative_view_id,
+                               OrderDirection direction) OVERRIDE;
+  virtual void OnViewDeleted(Id view_id) OVERRIDE;
+  virtual void OnViewVisibilityChanged(Id view_id, bool visible) OVERRIDE;
+  virtual void OnViewDrawnStateChanged(Id view_id, bool drawn) OVERRIDE;
+  virtual void OnViewInputEvent(Id view_id,
+                                EventPtr event,
+                                const Callback<void()>& callback) OVERRIDE;
+  virtual void Embed(
+      const String& url,
+      InterfaceRequest<ServiceProvider> service_provider) OVERRIDE;
+  virtual void DispatchOnViewInputEvent(EventPtr event) OVERRIDE;
+
+    // Overridden from WindowManagerClient:
+  virtual void OnWindowManagerReady() OVERRIDE;
+  virtual void OnCaptureChanged(Id old_capture_view_id,
+                                Id new_capture_view_id) OVERRIDE;
+  virtual void OnFocusChanged(Id old_focused_view_id,
+                              Id new_focused_view_id) OVERRIDE;
+  virtual void OnActiveWindowChanged(Id old_focused_window,
+                                     Id new_focused_window) OVERRIDE;
+
+  void RemoveRoot(View* root);
+
+  void OnActionCompleted(bool success);
+  void OnActionCompletedWithErrorCode(ErrorCode code);
+
+  BitmapUploader* BitmapUploaderForView(Id view_id);
+
+  base::Callback<void(bool)> ActionCompletedCallback();
+  base::Callback<void(ErrorCode)> ActionCompletedCallbackWithErrorCode();
+
+  bool connected_;
+  ConnectionSpecificId connection_id_;
+  ConnectionSpecificId next_id_;
+
+  std::string creator_url_;
+
+  base::Callback<void(void)> change_acked_callback_;
+
+  ViewManagerDelegate* delegate_;
+  WindowManagerDelegate* window_manager_delegate_;
+
+  std::vector<View*> roots_;
+
+  IdToViewMap views_;
+
+  ViewManagerService* service_;
+
+  WindowManagerServicePtr window_manager_;
+
+  // TODO(jamesr): Remove once all callers switch from SetContents to
+  // SetSurfaceId.
+  Shell* shell_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_context.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_context.cc
new file mode 100644
index 0000000..284286e
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_context.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 "mojo/services/public/cpp/view_manager/view_manager_context.h"
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+namespace mojo {
+class ApplicationImpl;
+
+void ConnectCallback(bool success) {}
+
+class ViewManagerContext::InternalState {
+ public:
+  InternalState(ApplicationImpl* application_impl) {
+    application_impl->ConnectToService("mojo:mojo_view_manager",
+                                       &init_service_);
+  }
+  ~InternalState() {}
+
+  ViewManagerInitService* init_service() { return init_service_.get(); }
+
+ private:
+  ViewManagerInitServicePtr init_service_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InternalState);
+};
+
+ViewManagerContext::ViewManagerContext(ApplicationImpl* application_impl)
+    : state_(new InternalState(application_impl)) {}
+ViewManagerContext::~ViewManagerContext() {}
+
+void ViewManagerContext::Embed(const String& url) {
+  scoped_ptr<ServiceProviderImpl> spi(new ServiceProviderImpl);
+  Embed(url, spi.Pass());
+}
+
+scoped_ptr<ServiceProvider> ViewManagerContext::Embed(
+    const String& url,
+    scoped_ptr<ServiceProviderImpl> exported_services) {
+  scoped_ptr<ServiceProvider> imported_services;
+  // BindToProxy() takes ownership of |exported_services|.
+  ServiceProviderImpl* registry = exported_services.release();
+  ServiceProviderPtr sp;
+  if (registry) {
+    BindToProxy(registry, &sp);
+    imported_services.reset(registry->CreateRemoteServiceProvider());
+  }
+  state_->init_service()->Embed(url, sp.Pass(), base::Bind(&ConnectCallback));
+  return imported_services.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc
new file mode 100644
index 0000000..abe0906
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.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 "mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h"
+
+#include "base/i18n/icu_util.h"
+#include "ui/gl/gl_surface.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
+
+#if defined(COMPONENT_BUILD)
+  gfx::GLSurface::InitializeOneOffForTests();
+#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/public/cpp/view_manager/lib/view_manager_test_suite.h b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
new file mode 100644
index 0000000..e073bbd
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
@@ -0,0 +1,26 @@
+// 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_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace mojo {
+
+class ViewManagerTestSuite : public base::TestSuite {
+ public:
+  ViewManagerTestSuite(int argc, char** argv);
+  virtual ~ViewManagerTestSuite();
+
+ protected:
+  virtual void Initialize() OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc
new file mode 100644
index 0000000..9f4f109
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc
@@ -0,0 +1,14 @@
+// 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/public/cpp/view_manager/lib/view_manager_test_suite.h"
+
+int main(int argc, char** argv) {
+  mojo::ViewManagerTestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(
+      argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/view_observer.cc b/mojo/services/public/cpp/view_manager/lib/view_observer.cc
new file mode 100644
index 0000000..812c028
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_observer.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 "mojo/services/public/cpp/view_manager/view_observer.h"
+
+#include "base/basictypes.h"
+
+namespace mojo {
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewObserver, public:
+
+ViewObserver::TreeChangeParams::TreeChangeParams()
+    : target(NULL),
+      old_parent(NULL),
+      new_parent(NULL),
+      receiver(NULL) {}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_private.cc b/mojo/services/public/cpp/view_manager/lib/view_private.cc
new file mode 100644
index 0000000..5125c7d
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_private.cc
@@ -0,0 +1,21 @@
+// 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/public/cpp/view_manager/lib/view_private.h"
+
+namespace mojo {
+
+ViewPrivate::ViewPrivate(View* view)
+    : view_(view) {
+}
+
+ViewPrivate::~ViewPrivate() {
+}
+
+// static
+View* ViewPrivate::LocalCreate() {
+  return new View;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_private.h b/mojo/services/public/cpp/view_manager/lib/view_private.h
new file mode 100644
index 0000000..73138e2
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_private.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 MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/cpp/view_manager/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();
+
+  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; }
+
+  ViewManager* view_manager() { return view_->manager_; }
+  void set_view_manager(ViewManager* manager) {
+    view_->manager_ = manager;
+  }
+
+  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 gfx::Rect& old_bounds,
+                      const gfx::Rect& new_bounds) {
+    view_->LocalSetBounds(old_bounds, new_bounds);
+  }
+  void LocalSetDrawn(bool drawn) { view_->LocalSetDrawn(drawn); }
+
+ private:
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewPrivate);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
diff --git a/mojo/services/public/cpp/view_manager/tests/BUILD.gn b/mojo/services/public/cpp/view_manager/tests/BUILD.gn
new file mode 100644
index 0000000..a468a92
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_lib_unittests
+test("mojo_view_manager_lib_unittests") {
+  sources = [
+    "view_unittest.cc",
+    "view_manager_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//ui/gfx",
+    "//ui/gfx:test_support",
+    "//mojo/application_manager",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/shell:test_support",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/services/public/cpp/view_manager",
+  ]
+  if (use_aura) {
+    deps += [ "//mojo/services/public/cpp/view_manager/lib:run_unittests" ]
+  } else {
+    deps += [ "//mojo/common/test:run_all_unittests" ]
+  }
+}
diff --git a/mojo/services/public/cpp/view_manager/tests/DEPS b/mojo/services/public/cpp/view_manager/tests/DEPS
new file mode 100644
index 0000000..2ecf3db
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/application_manager",
+]
diff --git a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc
new file mode 100644
index 0000000..693e91a
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc
@@ -0,0 +1,650 @@
+// 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/public/cpp/view_manager/view_manager.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/application_manager/application_manager.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/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kWindowManagerURL[] = "mojo:window_manager";
+const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
+
+base::RunLoop* current_run_loop = NULL;
+
+void DoRunLoop() {
+  base::RunLoop run_loop;
+  current_run_loop = &run_loop;
+  current_run_loop->Run();
+  current_run_loop = NULL;
+}
+
+void QuitRunLoop() {
+  current_run_loop->Quit();
+}
+
+class ConnectApplicationLoader : public ApplicationLoader,
+                                 public ApplicationDelegate,
+                                 public ViewManagerDelegate {
+ public:
+  typedef base::Callback<void(ViewManager*, View*)> LoadedCallback;
+
+  explicit ConnectApplicationLoader(const LoadedCallback& callback)
+      : callback_(callback) {}
+  virtual ~ConnectApplicationLoader() {}
+
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  // Overridden from ApplicationLoader:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
+    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+    if (!shell_handle.is_valid())
+      return;
+    scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
+                                                        shell_handle.Pass()));
+    apps_.push_back(app.release());
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) OVERRIDE {}
+
+  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+      OVERRIDE {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
+    callback_.Run(view_manager, root);
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
+
+  ScopedVector<ApplicationImpl> apps_;
+  LoadedCallback callback_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
+};
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+  explicit BoundsChangeObserver(View* view) : view_(view) {}
+  virtual ~BoundsChangeObserver() {}
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) OVERRIDE {
+    DCHECK_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+// Wait until the bounds of the supplied view change.
+void WaitForBoundsToChange(View* view) {
+  BoundsChangeObserver observer(view);
+  view->AddObserver(&observer);
+  DoRunLoop();
+  view->RemoveObserver(&observer);
+}
+
+// Spins a runloop 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) {}
+  virtual ~TreeSizeMatchesObserver() {}
+
+  bool IsTreeCorrectSize() {
+    return CountViews(tree_) == tree_size_;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual 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_;
+
+  DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
+};
+
+void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
+  TreeSizeMatchesObserver observer(view, tree_size);
+  if (observer.IsTreeCorrectSize())
+    return;
+  view->AddObserver(&observer);
+  DoRunLoop();
+  view->RemoveObserver(&observer);
+}
+
+// Utility class that waits for the destruction of some number of views and
+// views.
+class DestructionObserver : public ViewObserver {
+ public:
+  // |views| or |views| can be NULL.
+  explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewDestroyed(View* view) OVERRIDE {
+    std::set<Id>::iterator it = views_->find(view->id());
+    if (it != views_->end())
+      views_->erase(it);
+    if (CanQuit())
+      QuitRunLoop();
+  }
+
+  bool CanQuit() {
+    return !views_ || views_->empty();
+  }
+
+  std::set<Id>* views_;
+
+  DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
+};
+
+void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
+  DestructionObserver observer(views);
+  DCHECK(views);
+  if (views) {
+    for (std::set<Id>::const_iterator it = views->begin();
+          it != views->end(); ++it) {
+      view_manager->GetViewById(*it)->AddObserver(&observer);
+    }
+  }
+  DoRunLoop();
+}
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+  OrderChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  virtual ~OrderChangeObserver() {
+    view_->RemoveObserver(this);
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewReordered(View* view,
+                               View* relative_view,
+                               OrderDirection direction) OVERRIDE {
+    DCHECK_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+void WaitForOrderChange(ViewManager* view_manager, View* view) {
+  OrderChangeObserver observer(view);
+  DoRunLoop();
+}
+
+// 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);
+  }
+  virtual ~ViewTracker() {
+    if (view_)
+      view_->RemoveObserver(this);
+  }
+
+  bool is_valid() const { return !!view_; }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewDestroyed(View* view) OVERRIDE {
+    DCHECK_EQ(view, view_);
+    view_ = NULL;
+  }
+
+  int id_;
+  View* view_;
+
+  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 testing::Test {
+ public:
+  ViewManagerTest()
+      : connect_loop_(NULL),
+        loaded_view_manager_(NULL),
+        window_manager_(NULL),
+        commit_count_(0) {}
+
+ protected:
+  ViewManager* window_manager() { return window_manager_; }
+
+  View* CreateViewInParent(View* parent) {
+    ViewManager* parent_manager = ViewPrivate(parent).view_manager();
+    View* view = View::Create(parent_manager);
+    parent->AddChild(view);
+    return view;
+  }
+
+  // Embeds another version of the test app @ view.
+  ViewManager* Embed(ViewManager* view_manager, View* view) {
+    DCHECK_EQ(view_manager, ViewPrivate(view).view_manager());
+    view->Embed(kEmbeddedApp1URL);
+    RunRunLoop();
+    return GetLoadedViewManager();
+  }
+
+  ViewManager* GetLoadedViewManager() {
+    ViewManager* view_manager = loaded_view_manager_;
+    loaded_view_manager_ = NULL;
+    return view_manager;
+  }
+
+  void UnloadApplication(const GURL& url) {
+    test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
+  }
+
+ private:
+  // Overridden from testing::Test:
+  virtual void SetUp() OVERRIDE {
+    ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
+        &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
+    test_helper_.Init();
+    test_helper_.SetLoaderForURL(
+        scoped_ptr<ApplicationLoader>(
+            new ConnectApplicationLoader(ready_callback)),
+        GURL(kWindowManagerURL));
+    test_helper_.SetLoaderForURL(
+        scoped_ptr<ApplicationLoader>(
+            new ConnectApplicationLoader(ready_callback)),
+        GURL(kEmbeddedApp1URL));
+
+    test_helper_.application_manager()->ConnectToService(
+        GURL("mojo:mojo_view_manager"), &view_manager_init_);
+    ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL));
+  }
+
+  void EmbedRootCallback(bool* result_cache, bool result) {
+    *result_cache = result;
+  }
+
+  bool EmbedRoot(ViewManagerInitService* view_manager_init,
+                 const std::string& url) {
+    bool result = false;
+    ServiceProviderPtr sp;
+    BindToProxy(new ServiceProviderImpl, &sp);
+    view_manager_init->Embed(
+        url, sp.Pass(),
+        base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this),
+                   &result));
+    RunRunLoop();
+    window_manager_ = GetLoadedViewManager();
+    return result;
+  }
+
+  void OnViewManagerLoaded(ViewManager* view_manager, View* root) {
+    loaded_view_manager_ = view_manager;
+    connect_loop_->Quit();
+  }
+
+  void RunRunLoop() {
+    base::RunLoop run_loop;
+    connect_loop_ = &run_loop;
+    connect_loop_->Run();
+    connect_loop_ = NULL;
+  }
+
+  base::RunLoop* connect_loop_;
+  shell::ShellTestHelper test_helper_;
+  ViewManagerInitServicePtr view_manager_init_;
+  // Used to receive the most recent view manager loaded by an embed action.
+  ViewManager* loaded_view_manager_;
+  // The View Manager connection held by the window manager (app running at the
+  // root view).
+  ViewManager* window_manager_;
+  int commit_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+// TODO(sky): all of these tests are disabled as each test triggers running
+// ViewsInit, which tries to register the same set of paths with the
+// PathService, triggering a DCHECK.
+TEST_F(ViewManagerTest, DISABLED_SetUp) {}
+
+TEST_F(ViewManagerTest, DISABLED_Embed) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  EXPECT_TRUE(NULL != embedded);
+
+  View* view_in_embedded = embedded->GetRoots().front();
+  EXPECT_EQ(view->parent(), window_manager()->GetRoots().front());
+  EXPECT_EQ(NULL, view_in_embedded->parent());
+}
+
+// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
+// N11.
+// TODO(sky): Update client lib to match server.
+TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  View* nested = View::Create(window_manager());
+  view->AddChild(nested);
+
+  ViewManager* embedded = Embed(window_manager(), view);
+  EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(),
+            nested->id());
+  EXPECT_TRUE(embedded->GetRoots().front()->children().empty());
+  EXPECT_TRUE(nested->parent() == NULL);
+}
+
+// http://crbug.com/396300
+TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+
+  Id view_id = view->id();
+
+  UnloadApplication(GURL(kWindowManagerURL));
+
+  std::set<Id> views;
+  views.insert(view_id);
+  WaitForDestruction(embedded, &views);
+
+  EXPECT_TRUE(embedded->GetRoots().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, DISABLED_SetBounds) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+
+  View* view_in_embedded = embedded->GetViewById(view->id());
+  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+
+  view->SetBounds(gfx::Rect(100, 100));
+  EXPECT_NE(view->bounds(), view_in_embedded->bounds());
+  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, DISABLED_SetBoundsSecurity) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+
+  View* view_in_embedded = embedded->GetViewById(view->id());
+  view->SetBounds(gfx::Rect(800, 600));
+  WaitForBoundsToChange(view_in_embedded);
+
+  view_in_embedded->SetBounds(gfx::Rect(1024, 768));
+  // 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, DISABLED_DestroySecurity) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+
+  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, DISABLED_MultiRoots) {
+  View* view1 = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view1);
+  View* view2 = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view2);
+  ViewManager* embedded1 = Embed(window_manager(), view1);
+  ViewManager* embedded2 = Embed(window_manager(), view2);
+  EXPECT_EQ(embedded1, embedded2);
+}
+
+TEST_F(ViewManagerTest, DISABLED_EmbeddingIdentity) {
+  View* view = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view);
+  ViewManager* embedded = Embed(window_manager(), view);
+  EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
+}
+
+TEST_F(ViewManagerTest, DISABLED_Reorder) {
+  View* view1 = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view1);
+
+  ViewManager* embedded = Embed(window_manager(), view1);
+
+  View* view11 = View::Create(embedded);
+  embedded->GetRoots().front()->AddChild(view11);
+  View* view12 = View::Create(embedded);
+  embedded->GetRoots().front()->AddChild(view12);
+
+  View* view1_in_wm = window_manager()->GetViewById(view1->id());
+
+  {
+    WaitForTreeSizeToMatch(view1, 2u);
+    view11->MoveToFront();
+    WaitForOrderChange(window_manager(),
+                       window_manager()->GetViewById(view11->id()));
+
+    EXPECT_EQ(view1_in_wm->children().front(),
+              window_manager()->GetViewById(view12->id()));
+    EXPECT_EQ(view1_in_wm->children().back(),
+              window_manager()->GetViewById(view11->id()));
+  }
+
+  {
+    view11->MoveToBack();
+    WaitForOrderChange(window_manager(),
+                       window_manager()->GetViewById(view11->id()));
+
+    EXPECT_EQ(view1_in_wm->children().front(),
+              window_manager()->GetViewById(view11->id()));
+    EXPECT_EQ(view1_in_wm->children().back(),
+              window_manager()->GetViewById(view12->id()));
+  }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+  explicit VisibilityChangeObserver(View* view) : view_(view) {
+    view_->AddObserver(this);
+  }
+  virtual ~VisibilityChangeObserver() { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewVisibilityChanged(View* view) OVERRIDE {
+    EXPECT_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewManagerTest, DISABLED_Visible) {
+  View* view1 = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view1);
+
+  // Embed another app and verify initial state.
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_EQ(1u, embedded->GetRoots().size());
+  View* embedded_root = embedded->GetRoots().front();
+  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);
+    DoRunLoop();
+  }
+
+  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);
+    DoRunLoop();
+  }
+
+  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);
+  }
+  virtual ~DrawnChangeObserver() { view_->RemoveObserver(this); }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewDrawnChanged(View* view) OVERRIDE {
+    EXPECT_EQ(view, view_);
+    QuitRunLoop();
+  }
+
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewManagerTest, DISABLED_Drawn) {
+  View* view1 = View::Create(window_manager());
+  window_manager()->GetRoots().front()->AddChild(view1);
+
+  // Embed another app and verify initial state.
+  ViewManager* embedded = Embed(window_manager(), view1);
+  ASSERT_EQ(1u, embedded->GetRoots().size());
+  View* embedded_root = embedded->GetRoots().front();
+  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()->GetRoots().front()->SetVisible(false);
+    DoRunLoop();
+  }
+
+  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.
+
+// TODO(beng): tests for focus:
+// - focus between two views known to a connection
+// - focus between views unknown to one of the connections.
+// - focus between views unknown to either connection.
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
new file mode 100644
index 0000000..3f08075
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
@@ -0,0 +1,619 @@
+// 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/public/cpp/view_manager/view.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.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:
+  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());
+}
+
+// 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);
+  }
+  virtual ~TreeChangeObserver() {
+    observee_->RemoveObserver(this);
+  }
+
+  void Reset() {
+    received_params_.clear();
+  }
+
+  const std::vector<TreeChangeParams>& received_params() {
+    return received_params_;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+   virtual void OnTreeChanging(const TreeChangeParams& params) OVERRIDE {
+     received_params_.push_back(params);
+   }
+  virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
+    received_params_.push_back(params);
+  }
+
+  View* observee_;
+  std::vector<TreeChangeParams> received_params_;
+
+  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);
+  }
+  virtual ~OrderChangeObserver() {
+    observee_->RemoveObserver(this);
+  }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes_.swap(changes);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewReordering(View* view,
+                                View* relative_view,
+                                OrderDirection direction) OVERRIDE {
+    OnViewReordered(view, relative_view, direction);
+  }
+
+  virtual 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_;
+
+  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(ORDER_DIRECTION_ABOVE, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v13, changes[1].relative_view);
+    EXPECT_EQ(ORDER_DIRECTION_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(ORDER_DIRECTION_BELOW, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction);
+  }
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 above v12.
+    // Resulting order: v12. v11, v13
+    v11.Reorder(&v12, ORDER_DIRECTION_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(ORDER_DIRECTION_ABOVE, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction);
+  }
+
+  {
+    OrderChangeObserver observer(&v11);
+
+    // Move v11 below v12.
+    // Resulting order: v11, v12, v13
+    v11.Reorder(&v12, ORDER_DIRECTION_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(ORDER_DIRECTION_BELOW, changes[0].direction);
+
+    EXPECT_EQ(&v11, changes[1].view);
+    EXPECT_EQ(&v12, changes[1].relative_view);
+    EXPECT_EQ(ORDER_DIRECTION_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 gfx::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);
+  }
+  virtual ~BoundsChangeObserver() {
+    view_->RemoveObserver(this);
+  }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes.swap(changes_);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewBoundsChanging(View* view,
+                                    const gfx::Rect& old_bounds,
+                                    const gfx::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()));
+  }
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::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_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, SetBounds) {
+  TestView v1;
+  {
+    BoundsChangeObserver observer(&v1);
+    v1.SetBounds(gfx::Rect(0, 0, 100, 100));
+
+    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);
+  }
+  virtual ~VisibilityChangeObserver() { view_->RemoveObserver(this); }
+
+  Changes GetAndClearChanges() {
+    Changes changes;
+    changes.swap(changes_);
+    return changes;
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual 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"));
+  }
+  virtual 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_;
+
+  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());
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/types.h b/mojo/services/public/cpp/view_manager/types.h
new file mode 100644
index 0000000..d72236f
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/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_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
+
+#include "base/basictypes.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_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
diff --git a/mojo/services/public/cpp/view_manager/util.h b/mojo/services/public/cpp/view_manager/util.h
new file mode 100644
index 0000000..72c904d
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/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_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
+
+#include "mojo/services/public/cpp/view_manager/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_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
diff --git a/mojo/services/public/cpp/view_manager/view.h b/mojo/services/public/cpp/view_manager/view.h
new file mode 100644
index 0000000..f7afa49
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view.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_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkBitmap;
+
+namespace mojo {
+
+class BitmapUploader;
+class ServiceProviderImpl;
+class View;
+class ViewManager;
+class ViewObserver;
+
+// 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:
+  typedef std::vector<View*> Children;
+
+  static View* Create(ViewManager* view_manager);
+
+  // Destroys this view and all its children.
+  void Destroy();
+
+  ViewManager* view_manager() { return manager_; }
+
+  // Configuration.
+  Id id() const { return id_; }
+
+  // Geometric disposition.
+  const gfx::Rect& bounds() const { return bounds_; }
+  void SetBounds(const gfx::Rect& bounds);
+
+  // Visibility (also see IsDrawn()).
+  bool visible() const { return visible_; }
+  void SetVisible(bool 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_; }
+
+  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);
+
+  // TODO(beng): temporary only.
+  void SetContents(const SkBitmap& contents);
+  void SetColor(SkColor color);
+
+  // Focus.
+  void SetFocus();
+
+  // Embedding.
+  void Embed(const String& url);
+  scoped_ptr<ServiceProvider> Embed(
+      const String& url,
+      scoped_ptr<ServiceProviderImpl> exported_services);
+
+ protected:
+  // This class is subclassed only by test classes that provide a public ctor.
+  View();
+  ~View();
+
+ private:
+  friend class ViewPrivate;
+  friend class ViewManagerClientImpl;
+
+  explicit View(ViewManager* manager);
+
+  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 gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
+  void LocalSetDrawn(bool drawn);
+  void CreateBitmapUploader();
+
+  ViewManager* manager_;
+  Id id_;
+  View* parent_;
+  Children children_;
+
+  ObserverList<ViewObserver> observers_;
+
+  gfx::Rect bounds_;
+
+  bool visible_;
+
+  // 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_;
+
+  // TODO(jamesr): Temporary, remove when all clients are using surfaces
+  // directly.
+  scoped_ptr<BitmapUploader> bitmap_uploader_;
+
+  DISALLOW_COPY_AND_ASSIGN(View);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager.h b/mojo/services/public/cpp/view_manager/view_manager.h
new file mode 100644
index 0000000..d153283
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager.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 MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace mojo {
+class View;
+class ViewManagerDelegate;
+class WindowManagerDelegate;
+
+// Encapsulates a connection to the view manager service.
+// A unique connection is made for every unique embed path for an app. e.g. for
+// app B embed by the following paths: A->B, A->C->B - there are two connections
+// and thus two instances of this class.
+class ViewManager {
+ public:
+  // Sets the window manager delegate. Can only be called by the app embedded at
+  // the service root view. Can only be called once.
+  virtual void SetWindowManagerDelegate(
+      WindowManagerDelegate* window_manager_delegate) = 0;
+
+  // Dispatches the supplied event to the specified View. Can be called only
+  // by the application that called SetWindowManagerDelegate().
+  virtual void DispatchEvent(View* target, EventPtr event) = 0;
+
+  // Returns the URL of the application that embedded this application.
+  virtual const std::string& GetEmbedderURL() const = 0;
+
+  // Returns all root views known to this connection.
+  virtual const std::vector<View*>& GetRoots() const = 0;
+
+  // Returns a View known to this connection.
+  virtual View* GetViewById(Id id) = 0;
+
+ protected:
+  virtual ~ViewManager() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_client_factory.h b/mojo/services/public/cpp/view_manager/view_manager_client_factory.h
new file mode 100644
index 0000000..d2ed548
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_client_factory.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 MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/public/interfaces/view_manager/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);
+  virtual ~ViewManagerClientFactory();
+
+  // InterfaceFactory<ViewManagerClient> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<ViewManagerClient> request) override;
+
+ private:
+  Shell* shell_;
+  ViewManagerDelegate* delegate_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_context.h b/mojo/services/public/cpp/view_manager/view_manager_context.h
new file mode 100644
index 0000000..0bc51a4
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_context.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_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CONTEXT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CONTEXT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/service_provider_impl.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. |exported_services| encapsulates services offered by the
+  // application calling Embed() to the application being embedded. Returns
+  // an object implementing ServiceProvider encapsulating services offered by
+  // the embedded application to the embedder.
+  void Embed(const String& url);
+  scoped_ptr<ServiceProvider> Embed(
+      const String& url,
+      scoped_ptr<ServiceProviderImpl> exported_services);
+
+ private:
+  class InternalState;
+  scoped_ptr<InternalState> state_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerContext);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CONTEXT_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_delegate.h b/mojo/services/public/cpp/view_manager/view_manager_delegate.h
new file mode 100644
index 0000000..bbb9867
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_delegate.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 MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+class ServiceProviderImpl;
+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|. |view_manager| is an implementation of an object bound to a
+  // connection to the view manager service. The object is valid until
+  // OnViewManagerDisconnected() is called with the same object.
+  // This function is called for every embed, but there will be a unique
+  // instance of |view_manager| only for every unique connection. See
+  // the class description of ViewManager for some rules about when a new
+  // connection is made.
+  // |exported_services| is an object that the delegate can add services to to
+  // expose to the embedder. |imported_services| exposes the services offered by
+  // the embedder to the delegate. Note that if a different application is
+  // subsequently embedded at |root|, the pipe(s) connecting |imported_services|
+  // to the embedder and any services obtained from it are not broken and will
+  // continue to be valid.
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_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;
+
+ protected:
+  virtual ~ViewManagerDelegate() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/public/cpp/view_manager/view_observer.h b/mojo/services/public/cpp/view_manager/view_observer.h
new file mode 100644
index 0000000..1a2ab7e
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_observer.h
@@ -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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace gfx {
+class Rect;
+}
+
+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 gfx::Rect& old_bounds,
+                                    const gfx::Rect& new_bounds) {}
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) {}
+
+  virtual void OnViewFocusChanged(View* gained_focus, View* lost_focus) {}
+
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) {}
+
+  virtual void OnViewVisibilityChanging(View* view) {}
+  virtual void OnViewVisibilityChanged(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_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
diff --git a/mojo/services/public/cpp/view_manager/window_manager_delegate.h b/mojo/services/public/cpp/view_manager/window_manager_delegate.h
new file mode 100644
index 0000000..0ab271c
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/window_manager_delegate.h
@@ -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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace mojo {
+
+class View;
+
+// A WindowManagerDelegate is provided by the application embedded at the
+// service root view.
+class WindowManagerDelegate {
+ public:
+  // Create an appropriate view to embed |url|.
+  virtual void Embed(const String& url,
+                     InterfaceRequest<ServiceProvider> service_provider) {}
+
+  // Dispatch the supplied input event to the appropriate view (taking into
+  // account focus, activation, modality, etc.).
+  virtual void DispatchEvent(EventPtr event) = 0;
+
+ protected:
+  virtual ~WindowManagerDelegate() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/public/interfaces/clipboard/BUILD.gn b/mojo/services/public/interfaces/clipboard/BUILD.gn
new file mode 100644
index 0000000..65dd7d3
--- /dev/null
+++ b/mojo/services/public/interfaces/clipboard/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard_bindings
+mojom("clipboard") {
+  sources = [
+    "clipboard.mojom",
+  ]
+}
diff --git a/mojo/services/public/interfaces/clipboard/clipboard.mojom b/mojo/services/public/interfaces/clipboard/clipboard.mojom
new file mode 100644
index 0000000..eca47b9
--- /dev/null
+++ b/mojo/services/public/interfaces/clipboard/clipboard.mojom
@@ -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.
+
+module mojo {
+
+// A wrapper type which is just a Key/Value pair. Workaround until we get
+// proper maps in mojom.
+struct MimeTypePair {
+  string mime_type;
+  array<uint8> data;
+};
+
+interface Clipboard {
+  enum Type {
+    COPY_PASTE = 0,
+    SELECTION = 1,
+    DRAG = 2
+  };
+
+  // Mime type constants
+  const string MIME_TYPE_TEXT = "text/plain";
+  const string MIME_TYPE_HTML = "text/html";
+  const string MIME_TYPE_URL = "text/url";
+
+  // Returns a sequence number which uniquely identifies clipboard state.
+  // Clients are able to assume that the clipboard contents are unchanged as
+  // long as this number has not changed. This number is monotonically
+  // increasing, is increased when the clipboard state changes, and is
+  // provided by Windows, Linux, and Mac.
+  GetSequenceNumber(Type clipboard_type) => (uint64 sequence);
+
+  // Returns the available mime types. (Note: the chrome interface has a
+  // |contains_filenames| parameter here, but it appears to always be set
+  // to false.)
+  GetAvailableMimeTypes(Type clipboard_types) => (array<string> types);
+
+  // Returns the data associated with a Mime type, returning NULL if that data
+  // doesn't exist. Note: because of the inherit raciness of clipboard access,
+  // this may return NULL even if you just verified that it exists with
+  // GetAvailableFormatMimeTypes(). We don't want to provide one API to return
+  // the entire clipboard state because the combined size of the clipboard can
+  // be megabytes, especially when image data is involved.
+  ReadMimeType(Type clipboard_type, string mime_type) => (array<uint8>? data);
+
+  // Writes a set of mime types to the clipboard. This will increment the
+  // sequence number. In the case of an empty or NULL list, this will just
+  // clear the clipboard.
+  WriteClipboardData(Type clipboard_type, array<MimeTypePair>? data);
+};
+
+}  // module mojo
diff --git a/mojo/services/public/interfaces/content_handler/BUILD.gn b/mojo/services/public/interfaces/content_handler/BUILD.gn
new file mode 100644
index 0000000..c72cc9a
--- /dev/null
+++ b/mojo/services/public/interfaces/content_handler/BUILD.gn
@@ -0,0 +1,17 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_content_handler_bindings
+mojom("content_handler") {
+  sources = [
+    "content_handler.mojom",
+  ]
+
+  deps = [
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/interfaces/network",
+  ]
+}
diff --git a/mojo/services/public/interfaces/content_handler/content_handler.mojom b/mojo/services/public/interfaces/content_handler/content_handler.mojom
new file mode 100644
index 0000000..4383401
--- /dev/null
+++ b/mojo/services/public/interfaces/content_handler/content_handler.mojom
@@ -0,0 +1,16 @@
+// 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/interfaces/application/service_provider.mojom"
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+
+module mojo {
+
+interface ContentHandler {
+  OnConnect(string url,
+            URLResponse response,
+            ServiceProvider&? service_provider);
+};
+
+}
diff --git a/mojo/services/public/interfaces/geometry/BUILD.gn b/mojo/services/public/interfaces/geometry/BUILD.gn
new file mode 100644
index 0000000..3cd9bcd
--- /dev/null
+++ b/mojo/services/public/interfaces/geometry/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_geometry_bindings
+mojom("geometry") {
+  sources = [
+    "geometry.mojom",
+  ]
+}
diff --git a/mojo/services/public/interfaces/geometry/geometry.mojom b/mojo/services/public/interfaces/geometry/geometry.mojom
new file mode 100644
index 0000000..72e4246
--- /dev/null
+++ b/mojo/services/public/interfaces/geometry/geometry.mojom
@@ -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.
+
+module mojo {
+
+struct Point {
+  int32 x;
+  int32 y;
+};
+
+struct PointF {
+  float x;
+  float y;
+};
+
+struct Size {
+  int32 width;
+  int32 height;
+};
+
+struct Rect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+};
+
+struct RectF {
+  float x;
+  float y;
+  float width;
+  float height;
+};
+
+struct Transform {
+  // Row major order.
+  array<float, 16>? matrix;
+};
+
+}
diff --git a/mojo/services/public/interfaces/gpu/BUILD.gn b/mojo/services/public/interfaces/gpu/BUILD.gn
new file mode 100644
index 0000000..1e56abc
--- /dev/null
+++ b/mojo/services/public/interfaces/gpu/BUILD.gn
@@ -0,0 +1,17 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_gpu_bindings
+mojom("gpu") {
+  sources = [
+    "gpu.mojom",
+  ]
+
+  deps = [
+    "//mojo/services/gles2:interfaces",
+    "//mojo/services/public/interfaces/geometry",
+  ]
+}
diff --git a/mojo/services/public/interfaces/gpu/gpu.mojom b/mojo/services/public/interfaces/gpu/gpu.mojom
new file mode 100644
index 0000000..881c6be
--- /dev/null
+++ b/mojo/services/public/interfaces/gpu/gpu.mojom
@@ -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.
+
+import "mojo/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+
+module mojo {
+
+interface Gpu {
+  CreateOnscreenGLES2Context(uint64 native_viewport_id, Size? size, CommandBuffer&? gles2_client);
+  CreateOffscreenGLES2Context(CommandBuffer&? gles2_client);
+};
+
+}
diff --git a/mojo/services/public/interfaces/input_events/BUILD.gn b/mojo/services/public/interfaces/input_events/BUILD.gn
new file mode 100644
index 0000000..6c179c6
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_input_events_bindings
+mojom("input_events") {
+  sources = [
+    "input_events.mojom",
+    "input_event_constants.mojom",
+    "input_key_codes.mojom",
+  ]
+
+  deps = [
+    "//mojo/services/public/interfaces/geometry",
+  ]
+}
diff --git a/mojo/services/public/interfaces/input_events/input_event_constants.mojom b/mojo/services/public/interfaces/input_events/input_event_constants.mojom
new file mode 100644
index 0000000..0de69aa
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_event_constants.mojom
@@ -0,0 +1,79 @@
+// 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.
+
+module mojo {
+
+// This mirrors ui::EventType
+enum EventType {
+  UNKNOWN ,
+  MOUSE_PRESSED,
+  MOUSE_DRAGGED,
+  MOUSE_RELEASED,
+  MOUSE_MOVED,
+  MOUSE_ENTERED,
+  MOUSE_EXITED,
+  KEY_PRESSED,
+  KEY_RELEASED,
+  MOUSEWHEEL,
+  MOUSE_CAPTURE_CHANGED,
+  TOUCH_RELEASED,
+  TOUCH_PRESSED,
+  TOUCH_MOVED,
+  TOUCH_CANCELLED,
+  DROP_TARGET_EVENT,
+  TRANSLATED_KEY_PRESS,
+  TRANSLATED_KEY_RELEASE,
+  GESTURE_SCROLL_BEGIN,
+  GESTURE_SCROLL_END,
+  GESTURE_SCROLL_UPDATE,
+  GESTURE_TAP,
+  GESTURE_TAP_DOWN,
+  GESTURE_TAP_CANCEL,
+  GESTURE_TAP_UNCONFIRMED,
+  GESTURE_DOUBLE_TAP,
+  GESTURE_BEGIN,
+  GESTURE_END,
+  GESTURE_TWO_FINGER_TAP,
+  GESTURE_PINCH_BEGIN,
+  GESTURE_PINCH_END,
+  GESTURE_PINCH_UPDATE,
+  GESTURE_LONG_PRESS,
+  GESTURE_LONG_TAP,
+  GESTURE_SWIPE,
+  GESTURE_SHOW_PRESS,
+  GESTURE_WIN8_EDGE_SWIPE,
+  SCROLL,
+  SCROLL_FLING_START,
+  SCROLL_FLING_CANCEL,
+  CANCEL_MODE,
+  UMA_DATA
+};
+
+// This mirrors ui::EventFlags
+// TODO(morrita): Use shift operator once it is available.
+enum EventFlags {
+  NONE                =      0,
+  CAPS_LOCK_DOWN      =      1,
+  SHIFT_DOWN          =      2,
+  CONTROL_DOWN        =      4,
+  ALT_DOWN            =      8,
+  LEFT_MOUSE_BUTTON   =     16,
+  MIDDLE_MOUSE_BUTTON =     32,
+  RIGHT_MOUSE_BUTTON  =     64,
+  COMMAND_DOWN        =    128,
+  EXTENDED            =    256,
+  IS_SYNTHESIZED      =    512,
+  ALTGR_DOWN          =   1024,
+  MOD3_DOWN           =   2048
+};
+
+enum MouseEventFlags {
+  IS_DOUBLE_CLICK     =  65536,
+  IS_TRIPLE_CLICK     = 131072,
+  IS_NON_CLIENT       = 262144,
+
+  // TODO(erg): Move accessibility flags and maybe synthetic touch events here.
+};
+
+}  // module mojo
diff --git a/mojo/services/public/interfaces/input_events/input_events.mojom b/mojo/services/public/interfaces/input_events/input_events.mojom
new file mode 100644
index 0000000..4067f68
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_events.mojom
@@ -0,0 +1,74 @@
+// 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/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_event_constants.mojom"
+import "mojo/services/public/interfaces/input_events/input_key_codes.mojom"
+
+module mojo {
+
+struct LocationData {
+  Point? in_view_location;
+  Point? screen_location;
+};
+
+struct KeyData {
+  // The chromium event key code; these values are from the ui/ KeyCode enum,
+  // which has the fun property of being neither consistently the Windows key
+  // code, nor the X11 keycodes. (This value is consistent across platforms
+  // for basic ASCII characters; it will differ for modifiers. We don't define
+  // this as a mojo enum because mojom doesn't appear to have a platform
+  // dependent preprocessor yet.)
+  //
+  // TODO(erg): Remove this, and declare Win32 keycodes correct by fiat. We can
+  // not do this until we remove ui::Event usage from within mojo.
+  int32 key_code;
+
+  // Whether this is a character event, and the character value if it is. Note
+  // that this is different than |text|, which holds a value even when there
+  // isn't actually a character to insert. (For example, |text| will be set and
+  // have a value on backspace, and |character| won't.)
+  bool is_char;
+  uint16 character;
+
+  // The Win32 key code. Because of the web, this is the closest thing that we
+  // have to a cross platform key state.
+  KeyboardCode windows_key_code;
+
+  // The platform specific key code.
+  //
+  // TODO(erg): This exists only for NPAPI support, pepper USB keyboard support
+  // and IME on android support. Theoretically, we should be able to remove this
+  // in the medium to long term.
+  int32 native_key_code;
+
+  // The text generated by this keystroke. Corresponds to
+  // blink::WebKeyboardEvent::text.
+  uint16 text;
+
+  // Like |text|, but unmodified by concurrently held modifier keys (except
+  // shift). Corresponds to blink::WebKeyboardEvent::unmodifiedText.
+  uint16 unmodified_text;
+};
+
+struct TouchData {
+  int32 pointer_id;
+};
+
+struct MouseWheelData {
+  int32 x_offset;
+  int32 y_offset;
+};
+
+struct Event {
+  EventType action;
+  EventFlags flags;
+  int64 time_stamp;
+  LocationData? location_data;
+  KeyData? key_data;
+  TouchData? touch_data;
+  MouseWheelData? wheel_data;
+};
+
+}
diff --git a/mojo/services/public/interfaces/input_events/input_key_codes.mojom b/mojo/services/public/interfaces/input_events/input_key_codes.mojom
new file mode 100644
index 0000000..bd955aa
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_key_codes.mojom
@@ -0,0 +1,188 @@
+// 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.
+
+module mojo {
+
+// Cross platform keyboard codes.
+//
+// Because the web has standardized on Win32 keyboard codes, so does mojo.
+enum KeyboardCode {
+  BACK                      = 0x08,
+  TAB                       = 0x09,
+  CLEAR                     = 0x0C,
+  RETURN                    = 0x0D,
+  SHIFT                     = 0x10,
+  CONTROL                   = 0x11,
+  MENU                      = 0x12,  // a.k.a. ALT
+  PAUSE                     = 0x13,
+  CAPITAL                   = 0x14,
+  KANA                      = 0x15,
+  HANGUL                    = 0x15,
+  JUNJA                     = 0x17,
+  FINAL                     = 0x18,
+  HANJA                     = 0x19,
+  KANJI                     = 0x19,
+  ESCAPE                    = 0x1B,
+  CONVERT                   = 0x1C,
+  NONCONVERT                = 0x1D,
+  ACCEPT                    = 0x1E,
+  MODECHANGE                = 0x1F,
+  SPACE                     = 0x20,
+  PRIOR                     = 0x21,
+  NEXT                      = 0x22,
+  END                       = 0x23,
+  HOME                      = 0x24,
+  LEFT                      = 0x25,
+  UP                        = 0x26,
+  RIGHT                     = 0x27,
+  DOWN                      = 0x28,
+  SELECT                    = 0x29,
+  PRINT                     = 0x2A,
+  EXECUTE                   = 0x2B,
+  SNAPSHOT                  = 0x2C,
+  INSERT                    = 0x2D,
+  DELETE                    = 0x2E,
+  HELP                      = 0x2F,
+  NUM_0                     = 0x30,
+  NUM_1                     = 0x31,
+  NUM_2                     = 0x32,
+  NUM_3                     = 0x33,
+  NUM_4                     = 0x34,
+  NUM_5                     = 0x35,
+  NUM_6                     = 0x36,
+  NUM_7                     = 0x37,
+  NUM_8                     = 0x38,
+  NUM_9                     = 0x39,
+  A                         = 0x41,
+  B                         = 0x42,
+  C                         = 0x43,
+  D                         = 0x44,
+  E                         = 0x45,
+  F                         = 0x46,
+  G                         = 0x47,
+  H                         = 0x48,
+  I                         = 0x49,
+  J                         = 0x4A,
+  K                         = 0x4B,
+  L                         = 0x4C,
+  M                         = 0x4D,
+  N                         = 0x4E,
+  O                         = 0x4F,
+  P                         = 0x50,
+  Q                         = 0x51,
+  R                         = 0x52,
+  S                         = 0x53,
+  T                         = 0x54,
+  U                         = 0x55,
+  V                         = 0x56,
+  W                         = 0x57,
+  X                         = 0x58,
+  Y                         = 0x59,
+  Z                         = 0x5A,
+  LWIN                      = 0x5B,
+  COMMAND                   = 0x5B,  // Provide the Mac name for convenience.
+  RWIN                      = 0x5C,
+  APPS                      = 0x5D,
+  SLEEP                     = 0x5F,
+  NUMPAD0                   = 0x60,
+  NUMPAD1                   = 0x61,
+  NUMPAD2                   = 0x62,
+  NUMPAD3                   = 0x63,
+  NUMPAD4                   = 0x64,
+  NUMPAD5                   = 0x65,
+  NUMPAD6                   = 0x66,
+  NUMPAD7                   = 0x67,
+  NUMPAD8                   = 0x68,
+  NUMPAD9                   = 0x69,
+  MULTIPLY                  = 0x6A,
+  ADD                       = 0x6B,
+  SEPARATOR                 = 0x6C,
+  SUBTRACT                  = 0x6D,
+  DECIMAL                   = 0x6E,
+  DIVIDE                    = 0x6F,
+  F1                        = 0x70,
+  F2                        = 0x71,
+  F3                        = 0x72,
+  F4                        = 0x73,
+  F5                        = 0x74,
+  F6                        = 0x75,
+  F7                        = 0x76,
+  F8                        = 0x77,
+  F9                        = 0x78,
+  F10                       = 0x79,
+  F11                       = 0x7A,
+  F12                       = 0x7B,
+  F13                       = 0x7C,
+  F14                       = 0x7D,
+  F15                       = 0x7E,
+  F16                       = 0x7F,
+  F17                       = 0x80,
+  F18                       = 0x81,
+  F19                       = 0x82,
+  F20                       = 0x83,
+  F21                       = 0x84,
+  F22                       = 0x85,
+  F23                       = 0x86,
+  F24                       = 0x87,
+  NUMLOCK                   = 0x90,
+  SCROLL                    = 0x91,
+  LSHIFT                    = 0xA0,
+  RSHIFT                    = 0xA1,
+  LCONTROL                  = 0xA2,
+  RCONTROL                  = 0xA3,
+  LMENU                     = 0xA4,
+  RMENU                     = 0xA5,
+  BROWSER_BACK              = 0xA6,
+  BROWSER_FORWARD           = 0xA7,
+  BROWSER_REFRESH           = 0xA8,
+  BROWSER_STOP              = 0xA9,
+  BROWSER_SEARCH            = 0xAA,
+  BROWSER_FAVORITES         = 0xAB,
+  BROWSER_HOME              = 0xAC,
+  VOLUME_MUTE               = 0xAD,
+  VOLUME_DOWN               = 0xAE,
+  VOLUME_UP                 = 0xAF,
+  MEDIA_NEXT_TRACK          = 0xB0,
+  MEDIA_PREV_TRACK          = 0xB1,
+  MEDIA_STOP                = 0xB2,
+  MEDIA_PLAY_PAUSE          = 0xB3,
+  MEDIA_LAUNCH_MAIL         = 0xB4,
+  MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
+  MEDIA_LAUNCH_APP1         = 0xB6,
+  MEDIA_LAUNCH_APP2         = 0xB7,
+
+  OEM_1                     = 0xBA,
+  OEM_PLUS                  = 0xBB,
+  OEM_COMMA                 = 0xBC,
+  OEM_MINUS                 = 0xBD,
+  OEM_PERIOD                = 0xBE,
+  OEM_2                     = 0xBF,
+  OEM_3                     = 0xC0,
+  OEM_4                     = 0xDB,
+  OEM_5                     = 0xDC,
+  OEM_6                     = 0xDD,
+  OEM_7                     = 0xDE,
+  OEM_8                     = 0xDF,
+  OEM_102                   = 0xE2,
+  PROCESSKEY                = 0xE5,
+  PACKET                    = 0xE7,
+  DBE_SBCSCHAR              = 0xF3,
+  DBE_DBCSCHAR              = 0xF4,
+  ATTN                      = 0xF6,
+  CRSEL                     = 0xF7,
+  EXSEL                     = 0xF8,
+  EREOF                     = 0xF9,
+  PLAY                      = 0xFA,
+  ZOOM                      = 0xFB,
+  NONAME                    = 0xFC,
+  PA1                       = 0xFD,
+  OEM_CLEAR                 = 0xFE,
+  UNKNOWN                   = 0,
+
+  // Windows does not have a specific key code for AltGr. We use the unused
+  // VK_OEM_AX to represent AltGr, matching the behaviour of Firefox on Linux.
+  ALTGR                     = 0xE1,
+};
+
+}  // module mojo
diff --git a/mojo/services/public/interfaces/native_viewport/BUILD.gn b/mojo/services/public/interfaces/native_viewport/BUILD.gn
new file mode 100644
index 0000000..015fc89
--- /dev/null
+++ b/mojo/services/public/interfaces/native_viewport/BUILD.gn
@@ -0,0 +1,19 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_native_viewport_bindings
+mojom("native_viewport") {
+  sources = [
+    "native_viewport.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/gpu",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/surfaces:surface_id",
+  ]
+}
diff --git a/mojo/services/public/interfaces/native_viewport/native_viewport.mojom b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
new file mode 100644
index 0000000..6f05b72
--- /dev/null
+++ b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
@@ -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.
+
+import "mojo/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_events.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+[Client=NativeViewportClient]
+interface NativeViewport {
+  // TODO(sky): having a create function is awkward. Should there be a factory
+  // to create the NativeViewport that takes the size?
+  Create(Size size) => (uint64 native_viewport_id);
+  Show();
+  Hide();
+  Close();
+  SetSize(Size size);
+  SubmittedFrame(SurfaceId surface_id);
+};
+
+interface NativeViewportClient {
+  // OnSizeChanged() is sent at least once after the callback from Create() is
+  // called.
+  OnSizeChanged(Size size);
+  OnDestroyed();
+  OnEvent(Event event) => ();
+};
+
+// Connect to this interface before any other connections are made to configure
+// the NativeViewport service.
+interface NativeViewportConfig {
+  // Call UseTestConfig() and block on the reply. This will ensure that the
+  // correct global initialization is done before subsequent connections.
+  UseTestConfig() => ();
+
+  // Call UseHeadlessConfig() and block on the reply. This will ensure that
+  // the native viewport is later created in headless mode.
+  UseHeadlessConfig() => ();
+};
+
+}
diff --git a/mojo/services/public/interfaces/navigation/BUILD.gn b/mojo/services/public/interfaces/navigation/BUILD.gn
new file mode 100644
index 0000000..73e0578
--- /dev/null
+++ b/mojo/services/public/interfaces/navigation/BUILD.gn
@@ -0,0 +1,16 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_navigation_bindings
+mojom("navigation") {
+  sources = [
+    "navigation.mojom",
+  ]
+
+  deps = [
+    "//mojo/services/public/interfaces/network",
+  ]
+}
diff --git a/mojo/services/public/interfaces/navigation/navigation.mojom b/mojo/services/public/interfaces/navigation/navigation.mojom
new file mode 100644
index 0000000..6a9e94f
--- /dev/null
+++ b/mojo/services/public/interfaces/navigation/navigation.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+
+module mojo {
+
+// Expresses a preference for where a navigation will be performed.
+enum Target {
+  // No preference
+  DEFAULT,
+
+  // In the same ViewManager node that the navigation was initiated
+  SOURCE_NODE,
+
+  // In a new ViewManager node
+  NEW_NODE
+};
+
+// Embedders that support navigation of implement this interface.
+interface NavigatorHost {
+  RequestNavigate(Target target, URLRequest request);
+
+  // Applications call this to inform hosts of navigations they performed
+  // locally. For example, pushState() navigations in an HTML application.
+  DidNavigateLocally(string url);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/BUILD.gn b/mojo/services/public/interfaces/network/BUILD.gn
new file mode 100644
index 0000000..e55c6aa
--- /dev/null
+++ b/mojo/services/public/interfaces/network/BUILD.gn
@@ -0,0 +1,21 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_bindings
+mojom("network") {
+  sources = [
+    "cookie_store.mojom",
+    "net_address.mojom",
+    "network_error.mojom",
+    "network_service.mojom",
+    "tcp_bound_socket.mojom",
+    "tcp_client_socket.mojom",
+    "tcp_server_socket.mojom",
+    "udp_socket.mojom",
+    "url_loader.mojom",
+    "web_socket.mojom",
+  ]
+}
diff --git a/mojo/services/public/interfaces/network/cookie_store.mojom b/mojo/services/public/interfaces/network/cookie_store.mojom
new file mode 100644
index 0000000..76b374a
--- /dev/null
+++ b/mojo/services/public/interfaces/network/cookie_store.mojom
@@ -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.
+
+module mojo {
+
+interface CookieStore {
+  Get(string? url) => (string? cookies);
+  Set(string? url, string? cookie) => (bool success);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/net_address.mojom b/mojo/services/public/interfaces/network/net_address.mojom
new file mode 100644
index 0000000..337f251
--- /dev/null
+++ b/mojo/services/public/interfaces/network/net_address.mojom
@@ -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.
+
+module mojo {
+
+enum NetAddressFamily {
+  UNSPECIFIED,
+  IPV4,
+  IPV6
+};
+
+// All members are expressed in network byte order, i.e., the most significant
+// byte is stored in the smallest address. For example, if we store 127.0.0.1 in
+// |addr|, 127 should be at index 0.
+struct NetAddressIPv4 {
+  uint16 port;
+  array<uint8, 4> addr;
+};
+
+// All members are expressed in network byte order.
+struct NetAddressIPv6 {
+  uint16 port;
+  array<uint8, 16> addr;
+};
+
+struct NetAddress {
+  NetAddressFamily family = UNSPECIFIED;
+  // At most one of the following fields is non-NULL depending on the value of
+  // |family|.
+  NetAddressIPv4? ipv4;
+  NetAddressIPv6? ipv6;
+};
+
+}
+
diff --git a/mojo/services/public/interfaces/network/network_error.mojom b/mojo/services/public/interfaces/network/network_error.mojom
new file mode 100644
index 0000000..4af9935
--- /dev/null
+++ b/mojo/services/public/interfaces/network/network_error.mojom
@@ -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.
+
+module mojo {
+
+struct NetworkError {
+  int32 code;
+  string? description;
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/network_service.mojom b/mojo/services/public/interfaces/network/network_service.mojom
new file mode 100644
index 0000000..7884144
--- /dev/null
+++ b/mojo/services/public/interfaces/network/network_service.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/network/cookie_store.mojom"
+import "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_bound_socket.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+import "mojo/services/public/interfaces/network/web_socket.mojom"
+
+module mojo {
+
+// TODO Darin suggfests that this should probably be two classes. One for
+// high-level origin-build requests like WebSockets and HTTP, and the other for
+// non-origin-bound low-level stuff like DNS, UDP, and TCP.
+interface NetworkService {
+  CreateURLLoader(URLLoader&? loader);
+
+  GetCookieStore(CookieStore&? cookie_store);
+
+  CreateWebSocket(WebSocket& socket);
+
+  // Creates a TCP socket bound to a given local address. This bound socket
+  // can be used for creating a client or server socket on that local address.
+  //
+  // If you want to create a client socket to connect to a server and are in
+  // the common case where you don't care about the local address it's bound
+  // to, use CreateTCPClientSocket.
+  //
+  // The local address can specify 0 for the port to specify that the OS should
+  // pick an available port for the given address, or it can pass 0 for the
+  // address and port for the OS to pick both the local address and port. In
+  // all success cases, the resulting local address will be passed to the
+  // callback as bound_to.
+  CreateTCPBoundSocket(NetAddress local_address,
+                       TCPBoundSocket& bound_socket)
+      => (NetworkError result, NetAddress? bound_to);
+
+  // Creates a client socket connected to the given remote address. A local
+  // address and port will be allocated for the connection and passed to the
+  // callback on success.
+  //
+  // If you want control over the local address and port, instead use
+  // CreateTCPBoundSocket.
+  CreateTCPClientSocket(NetAddress remote_address,
+                        handle<data_pipe_consumer> send_stream,
+                        handle<data_pipe_producer> receive_stream,
+                        TCPClientSocket& client_socket)
+      => (NetworkError result,
+          NetAddress? local_address);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_bound_socket.mojom b/mojo/services/public/interfaces/network/tcp_bound_socket.mojom
new file mode 100644
index 0000000..0772846
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_bound_socket.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+import "mojo/services/public/interfaces/network/tcp_server_socket.mojom"
+
+module mojo {
+
+// Represents a TCP socket that is bound to a local address and port, but
+// is not yet in a listening or connected state.
+//
+// A bound socket can be used to create a server socket listening on the
+// local address, or it can be used to create a client socket by connecting to
+// a remote host.
+interface TCPBoundSocket {
+  // Puts the socket into server mode, awaiting incoming connections.
+  //
+  // Once this function is called, neither StartListening nor Connect can be
+  // used on this socket again.
+  StartListening(TCPServerSocket& server);
+
+  // Puts this socket into client mode by connecting to a remote host. If you
+  // do not care about the local address or port, you can call
+  // NetworkService.CreateTCPClientSocket to connect directly and skip the
+  // "bound" state.
+  //
+  // Once this function is called, neither StartListening nor Connect can be
+  // used on this socket again.
+  Connect(NetAddress remote_address,
+          handle<data_pipe_consumer> send_stream,
+          handle<data_pipe_producer> receive_stream,
+          TCPClientSocket& client_socket)
+      => (NetworkError result);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_client_socket.mojom b/mojo/services/public/interfaces/network/tcp_client_socket.mojom
new file mode 100644
index 0000000..2dd3544
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_client_socket.mojom
@@ -0,0 +1,16 @@
+// 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.
+
+module mojo {
+
+// Represents a TCP socket connected to a report system.
+//
+// Reading and writing over the connection are done via the data pipe supplied
+// by the caller when creating the socket.
+interface TCPClientSocket {
+  // TODO(brettw) here we put options and whatnot for controlling the
+  // connection.
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_server_socket.mojom b/mojo/services/public/interfaces/network/tcp_server_socket.mojom
new file mode 100644
index 0000000..4b2e596
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_server_socket.mojom
@@ -0,0 +1,34 @@
+// 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/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+
+module mojo {
+
+// Represents a TCP server socket listening for incoming requests.
+[Client=TCPServerSocketClient]
+interface TCPServerSocket {
+  // Accepts an incoming connection request and hooks up a TCPClientSocket for
+  // connecting with the remote host. This function is called in reponse to
+  // OnConnectionAvailable().
+  //
+  // On success, the address of the remote host will be provided.
+  AcceptConnection(handle<data_pipe_consumer> send_stream,
+                   handle<data_pipe_producer> receive_stream,
+                   TCPClientSocket& client_socket)
+      => (NetworkError result, NetAddress? remote_address);
+};
+
+interface TCPServerSocketClient {
+  // Notifies the client that an incoming connection is available.
+  //
+  // The client should call AcceptConnection() to accept the request.
+  OnConnectionAvailable();
+
+  // TODO(brettw) probably need some error reporting function here.
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/udp_socket.mojom b/mojo/services/public/interfaces/network/udp_socket.mojom
new file mode 100644
index 0000000..b5b848d
--- /dev/null
+++ b/mojo/services/public/interfaces/network/udp_socket.mojom
@@ -0,0 +1,74 @@
+// 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/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+
+module mojo {
+
+// UDPSocket and UDPSocketClient represent a UDP socket and its client. The
+// typical flow of using the interfaces is:
+// - Acquire a UDPSocket interface pointer and set a UDPSocketClient instance.
+// - (optional) Set options which are allowed prior to Bind().
+// - Bind the socket.
+// - (optional) Set options which are allowed after Bind().
+// - Send / request to receive packets. Received packets will be delivered to
+//   UDPSocketClient.OnReceived().
+[Client=UDPSocketClient]
+interface UDPSocket {
+  // Allows the socket to share the local address to which it will be bound with
+  // other processes. Should be called before Bind().
+  // (This is equivalent to SO_REUSEADDR of the POSIX socket API.)
+  AllowAddressReuse() => (NetworkError result);
+
+  // Binds the socket to the given address.
+  // |bound_addr| is non-NULL on success. It might not be the same as |addr|.
+  // For example, if port 0 is used in |addr|, a random port is picked and
+  // returned in |bound_addr|.
+  Bind(NetAddress addr) => (NetworkError result, NetAddress? bound_addr);
+
+  // Sets the send buffer size (in bytes) for the socket. The socket must be
+  // bound.
+  //
+  // Note: This is only treated as a hint. Even if it succeeds, the service
+  // doesn't guarantee it will conform to the size.
+  SetSendBufferSize(uint32 size) => (NetworkError result);
+
+  // Sets the receive buffer size (in bytes) for the socket. The socket must be
+  // bound.
+  //
+  // Note: This is only treated as a hint. Even if it succeeds, the service
+  // doesn't guarantee it will conform to the size.
+  SetReceiveBufferSize(uint32 size) => (NetworkError result);
+
+  // Notifies that the client is ready to accept |number| of packets.
+  // Correspondingly, OnReceived() of the UDPSocketClient interface will be
+  // called |number| times (errors also count), unless the connection is closed
+  // before that. The socket must be bound.
+  //
+  // It is allowed to call this method again before the previous request is
+  // completely satisfied. For example:
+  //   service->ReceiveMorePackets(3);
+  //   ...
+  //   // OnReceived() is called.
+  //   // OnReceived() is called.
+  //   ...
+  //   service->ReceiveMorePackets(3);
+  //   // The client expects 4 more calls to OnReceived().
+  ReceiveMorePackets(uint32 number);
+
+  // Sends data to the specified destination. The socket must be bound.
+  // The method doesn't report the result of the operation.
+  SendToAndForget(NetAddress addr, array<uint8> data);
+
+  // Sends data to the specified destination. The socket must be bound.
+  SendTo(NetAddress addr, array<uint8> data) => (NetworkError result);
+};
+
+interface UDPSocketClient {
+  // |addr| and |data| are non-NULL on success.
+  OnReceived(NetworkError result, NetAddress? addr, array<uint8>? data);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/url_loader.mojom b/mojo/services/public/interfaces/network/url_loader.mojom
new file mode 100644
index 0000000..61cdc8f
--- /dev/null
+++ b/mojo/services/public/interfaces/network/url_loader.mojom
@@ -0,0 +1,106 @@
+// 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/services/public/interfaces/network/network_error.mojom"
+
+module mojo {
+
+struct URLRequest {
+  // The URL to load.
+  string? url;
+
+  // The HTTP method if applicable.
+  string? method = "GET";
+
+  // Additional HTTP request headers.
+  array<string?>? headers;
+
+  // The payload for the request body, represented as a concatenation of data
+  // streams. For HTTP requests, the method must be set to "POST" or "PUT".
+  array<handle<data_pipe_consumer>?>? body;
+
+  // The number of bytes to be read from |body|. A Content-Length header of
+  // this value will be sent. Set to -1 if length is unknown, which will cause
+  // |body| to be uploaded using a chunked encoding.
+  int64 body_length = 0;
+
+  // The buffer size of the data pipe returned in URLResponse's |body| member.
+  // A value of 0 indicates that the default buffer size should be used.  This
+  // value is just a suggestion. The URLLoader may choose to ignore this value.
+  uint32 response_body_buffer_size = 0;
+
+  // If set to true, then redirects will be automatically followed. Otherwise,
+  // when a redirect is encounterd, FollowRedirect must be called to proceed.
+  bool auto_follow_redirects = false;
+
+  // If set to true, then the HTTP request will bypass the local cache and will
+  // have a 'Cache-Control: nocache' header added in that causes any proxy
+  // servers to also not satisfy the request from their cache.  This has the
+  // effect of forcing a full end-to-end fetch.
+  bool bypass_cache = false;
+};
+
+struct URLResponse {
+  // If the response resulted in a network level error, this field will be set.
+  NetworkError? error;
+
+  // The response body stream. Read from this data pipe to receive the bytes of
+  // response body.
+  handle<data_pipe_consumer>? body;
+
+  // The final URL of the response, after redirects have been followed.
+  string? url;
+
+  // The HTTP status code. 0 if not applicable.
+  uint32 status_code;
+
+  // The HTTP status line.
+  string? status_line;
+
+  // The HTTP response headers.
+  array<string?>? headers;
+
+  // The MIME type of the response body.
+  string? mime_type;
+
+  // The character set of the response body.
+  string? charset;
+
+  // These fields are set to non-NULL if this response corresponds to a
+  // redirect.  Call the |FollowRedirect| method on the URLLoader instance to
+  // follow this redirect.
+  string? redirect_method;
+  string? redirect_url;
+};
+
+struct URLLoaderStatus {
+  // If the loader has failed due to a network level error, this field will be
+  // set.
+  NetworkError? error;
+
+  // Set to true if the URLLoader is still working. Set to false once an error
+  // is encountered or the response body is completely copied to the response
+  // body stream.
+  bool is_loading;
+
+  // TODO(darin): Add further details about the stages of loading (e.g.,
+  // "resolving host") that happen prior to receiving bytes.
+};
+
+interface URLLoader {
+  // Loads the given |request|, asynchronously producing |response|. Consult
+  // |response| to determine if the request resulted in an error, was
+  // redirected, or has a response body to be consumed.
+  Start(URLRequest? request) => (URLResponse? response);
+
+  // If the request passed to |Start| had |auto_follow_redirects| set to false,
+  // then upon receiving an URLResponse with a non-NULL |redirect_url| field,
+  // |FollowRedirect| may be called to load the URL indicated by the redirect.
+  FollowRedirect() => (URLResponse? response);
+
+  // Query status about the URLLoader.
+  QueryStatus() => (URLLoaderStatus? status);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/web_socket.mojom b/mojo/services/public/interfaces/network/web_socket.mojom
new file mode 100644
index 0000000..d96fb26
--- /dev/null
+++ b/mojo/services/public/interfaces/network/web_socket.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.
+
+import "mojo/services/public/interfaces/network/network_error.mojom"
+
+module mojo {
+
+interface WebSocket {
+  enum MessageType {
+    CONTINUATION,
+    TEXT,
+    BINARY
+  };
+  const uint16 kAbnormalCloseCode = 1006;  // stolen from websocket_bridge
+
+  // Initiates a WebSocket connection to the given url. |send_stream| is a data
+  // pipe which should remain open for the lifetime of the WebSocket. Data
+  // to send over the WebSocket should be written to the producer end of the
+  // |send_stream|.
+  Connect(string url,
+          array<string> protocols,
+          string origin,
+          handle<data_pipe_consumer> send_stream,
+          WebSocketClient client);
+
+  // Called after writing |num_bytes| worth of data to the WebSocket's
+  // |send_stream|.
+  Send(bool fin, MessageType type, uint32 num_bytes);
+
+  FlowControl(int64 quota);
+
+  Close(uint16 code, string reason);
+};
+
+interface WebSocketClient {
+  // Called in response to a WebSocket.Connect call to indicate success or
+  // failure. |receive_stream| is a data pipe which where incoming data from
+  // the server is written.
+  DidConnect(bool fail,
+             string selected_subprotocol,
+             string extensions,
+             handle<data_pipe_consumer> receive_stream);
+
+  // Called when there is |num_bytes| worth of incoming data available on the
+  // |receive_stream|.
+  DidReceiveData(bool fin, WebSocket.MessageType type, uint32 num_bytes);
+
+  DidReceiveFlowControl(int64 quota);
+
+  DidFail(string message);
+
+  DidClose(bool was_clean, uint16 code, string reason);
+
+  // Blink has 3 extra methods that we don't implement, because they are used
+  // for the inspector:
+  // didStartOpeningHandshake
+  // didFinishOpeningHandshake
+  // didStartClosingHandshake
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/BUILD.gn b/mojo/services/public/interfaces/surfaces/BUILD.gn
new file mode 100644
index 0000000..cd00cd5
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/BUILD.gn
@@ -0,0 +1,28 @@
+# 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/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_bindings
+mojom("surfaces") {
+  sources = [
+    "quads.mojom",
+    "surfaces.mojom",
+    "surfaces_service.mojom",
+  ]
+
+  deps = [
+    ":surface_id",
+    "//mojo/services/gles2:interfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/native_viewport",
+  ]
+}
+
+
+mojom("surface_id") {
+  sources = [
+    "surface_id.mojom",
+  ]
+}
diff --git a/mojo/services/public/interfaces/surfaces/quads.mojom b/mojo/services/public/interfaces/surfaces/quads.mojom
new file mode 100644
index 0000000..4f51be4
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/quads.mojom
@@ -0,0 +1,210 @@
+// 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/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+struct Color {
+  uint32 rgba;
+};
+
+// TODO(jamesr): Populate subtype fields.
+struct CheckerboardQuadState {};
+
+struct DebugBorderQuadState {};
+
+struct IoSurfaceContentQuadState {};
+
+struct RenderPassId {
+  int32 layer_id;
+  int32 index;
+};
+
+struct RenderPassQuadState {
+  RenderPassId render_pass_id;
+
+  // If nonzero, resource id of mask to use when drawing this pass.
+  uint32 mask_resource_id;
+  RectF mask_uv_rect;
+
+  // Post-processing filters, applied to the pixels in the render pass' texture.
+  // TODO(jamesr): Support
+  // FilterOperations filters;
+
+  // The scale from layer space of the root layer of the render pass to
+  // the render pass physical pixels. This scale is applied to the filter
+  // parameters for pixel-moving filters. This scale should include
+  // content-to-target-space scale, and device pixel ratio.
+  PointF filters_scale;
+
+  // Post-processing filters, applied to the pixels showing through the
+  // background of the render pass, from behind it.
+  // TODO(jamesr): Support
+  // FilterOperations background_filters;
+};
+
+struct SolidColorQuadState {
+  Color color;
+  bool force_anti_aliasing_off;
+};
+
+struct SurfaceQuadState {
+  SurfaceId surface;
+};
+
+struct TextureQuadState {
+  uint32 resource_id;
+  bool premultiplied_alpha;
+  PointF uv_top_left;
+  PointF uv_bottom_right;
+  Color background_color;
+  array<float, 4> vertex_opacity;
+  bool flipped;
+};
+
+struct TileQuadState {
+  RectF tex_coord_rect;
+  Size texture_size;
+  bool swizzle_contents;
+  uint32 resource_id;
+};
+
+struct StreamVideoQuadState {};
+
+enum YUVColorSpace {
+  REC_601,       // SDTV standard with restricted "studio swing" color range.
+  REC_601_JPEG,  // Full color range [0, 255] variant of the above.
+};
+
+struct YUVVideoQuadState {
+  RectF tex_coord_rect;
+  uint32 y_plane_resource_id;
+  uint32 u_plane_resource_id;
+  uint32 v_plane_resource_id;
+  uint32 a_plane_resource_id;
+  YUVColorSpace color_space;
+};
+
+enum Material {
+  CHECKERBOARD = 1,
+  DEBUG_BORDER,
+  IO_SURFACE_CONTENT,
+  PICTURE_CONTENT,
+  RENDER_PASS,
+  SOLID_COLOR,
+  STREAM_VIDEO_CONTENT,
+  SURFACE_CONTENT,
+  TEXTURE_CONTENT,
+  TILED_CONTENT,
+  YUV_VIDEO_CONTENT,
+};
+
+struct Quad {
+  Material material;
+
+  // This rect, after applying the quad_transform(), gives the geometry that
+  // this quad should draw to. This rect lives in content space.
+  Rect rect;
+
+  // This specifies the region of the quad that is opaque. This rect lives in
+  // content space.
+  Rect opaque_rect;
+
+  // Allows changing the rect that gets drawn to make it smaller. This value
+  // should be clipped to |rect|. This rect lives in content space.
+  Rect visible_rect;
+
+  // Allows changing the rect that gets drawn to make it smaller. This value
+  // should be clipped to |rect|. This rect lives in content space.
+  bool needs_blending;
+
+  // Index into the containing pass' shared quad state array which has state
+  // (transforms etc) shared by multiple quads.
+  int32 shared_quad_state_index;
+
+  // Only one of the following will be set, depending on the material.
+  CheckerboardQuadState? checkerboard_quad_state;
+  DebugBorderQuadState? debug_border_quad_state;
+  IoSurfaceContentQuadState? io_surface_quad_state;
+  RenderPassQuadState? render_pass_quad_state;
+  SolidColorQuadState? solid_color_quad_state;
+  SurfaceQuadState? surface_quad_state;
+  TextureQuadState? texture_quad_state;
+  TileQuadState? tile_quad_state;
+  StreamVideoQuadState? stream_video_quad_state;
+  YUVVideoQuadState? yuv_video_quad_state;
+};
+
+enum SkXfermode {
+  kClear_Mode = 0,    //!< [0, 0]
+  kSrc_Mode,      //!< [Sa, Sc]
+  kDst_Mode,      //!< [Da, Dc]
+  kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]
+  kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]
+  kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+  kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+  kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+  kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+  kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+  kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+  kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+  kPlus_Mode,     //!< [Sa + Da, Sc + Dc]
+  kModulate_Mode, // multiplies all components (= alpha and color)
+
+  // Following blend modes are defined in the CSS Compositing standard:
+  // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending
+  kScreen_Mode,
+  kLastCoeffMode = kScreen_Mode,
+
+  kOverlay_Mode,
+  kDarken_Mode,
+  kLighten_Mode,
+  kColorDodge_Mode,
+  kColorBurn_Mode,
+  kHardLight_Mode,
+  kSoftLight_Mode,
+  kDifference_Mode,
+  kExclusion_Mode,
+  kMultiply_Mode,
+  kLastSeparableMode = kMultiply_Mode,
+
+  kHue_Mode,
+  kSaturation_Mode,
+  kColor_Mode,
+  kLuminosity_Mode,
+  kLastMode = kLuminosity_Mode
+};
+
+struct SharedQuadState {
+  // Transforms from quad's original content space to its target content space.
+  Transform content_to_target_transform;
+
+  // This size lives in the content space for the quad's originating layer.
+  Size content_bounds;
+
+  // This rect lives in the content space for the quad's originating layer.
+  Rect visible_content_rect;
+
+  // This rect lives in the target content space.
+  Rect clip_rect;
+
+  bool is_clipped;
+  float opacity;
+  SkXfermode blend_mode;
+  int32 sorting_context_id;
+};
+
+struct Pass {
+  int32 id;
+  Rect output_rect;
+  Rect damage_rect;
+  Transform transform_to_root_target;
+  bool has_transparent_background;
+  array<Quad> quads;
+  array<SharedQuadState> shared_quad_states;
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surface_id.mojom b/mojo/services/public/interfaces/surfaces/surface_id.mojom
new file mode 100644
index 0000000..5a7686d
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/surface_id.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.
+
+module mojo {
+
+struct SurfaceId {
+  uint64 id;
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surfaces.mojom b/mojo/services/public/interfaces/surfaces/surfaces.mojom
new file mode 100644
index 0000000..1c5b0b9
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/surfaces.mojom
@@ -0,0 +1,73 @@
+// 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/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/surfaces/quads.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+enum ResourceFormat {
+  RGBA_8888,
+  RGBA_4444,
+  BGRA_8888,
+  ALPHA_8,
+  LUMINANCE_8,
+  RGB_565,
+  ETC1,
+};
+
+struct Mailbox {
+  array<int8, 64> name;
+};
+
+struct MailboxHolder {
+  Mailbox mailbox;
+  uint32 texture_target;
+  uint32 sync_point;
+};
+
+struct TransferableResource {
+  uint32 id;
+  ResourceFormat format;
+  uint32 filter;
+  Size size;
+  MailboxHolder mailbox_holder;
+  bool is_repeated;
+  bool is_software;
+};
+
+struct ReturnedResource {
+  uint32 id;
+  uint32 sync_point;
+  int32 count;
+  bool lost;
+};
+
+struct Frame {
+  array<TransferableResource> resources;
+  array<Pass> passes;
+};
+
+interface SurfaceClient {
+  ReturnResources(array<ReturnedResource> resources);
+};
+
+[Client=SurfaceClient]
+interface Surface {
+  // The id is created by the client and must be unique and contain the
+  // connection's namespace in the upper 32 bits.
+  CreateSurface(SurfaceId id, Size size);
+
+  // The client can only submit frames to surfaces created with this connection.
+  SubmitFrame(SurfaceId id, Frame frame);
+  DestroySurface(SurfaceId id);
+
+  CreateGLES2BoundSurface(CommandBuffer gles2_client,
+                          SurfaceId id,
+                          Size size);
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surfaces_service.mojom b/mojo/services/public/interfaces/surfaces/surfaces_service.mojom
new file mode 100644
index 0000000..76faeec
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/surfaces_service.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/surfaces/surfaces.mojom"
+
+module mojo {
+
+// Use this interface to request connections to the surfaces service. Each
+// connection is associated with an id namespace
+interface SurfacesService {
+  CreateSurfaceConnection() => (Surface surface, uint32 id_namespace);
+};
+
+}
diff --git a/mojo/services/public/interfaces/view_manager/BUILD.gn b/mojo/services/public/interfaces/view_manager/BUILD.gn
new file mode 100644
index 0000000..0bc0920
--- /dev/null
+++ b/mojo/services/public/interfaces/view_manager/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_bindings
+mojom("view_manager") {
+  sources = [
+    "view_manager.mojom",
+    "view_manager_constants.mojom",
+  ]
+
+  deps = [
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/surfaces:surface_id",
+  ]
+}
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
new file mode 100644
index 0000000..094f5d3
--- /dev/null
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -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.
+
+import "mojo/public/interfaces/application/service_provider.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_events.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+import "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom"
+
+module mojo {
+
+struct ViewData {
+  uint32 parent_id;
+  uint32 view_id;
+  mojo.Rect bounds;
+  // 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;
+};
+
+enum ErrorCode {
+  NONE,
+  VALUE_IN_USE,
+  ILLEGAL_ARGUMENT,
+};
+
+// ViewManagerInitService is used to grant an application a connection to the
+// ViewManager by embedding it at an approriate View.
+interface ViewManagerInitService {
+  // Embed the application @ |url| at an appropriate View.
+  // The first time this method is called in the lifetime of a View Manager
+  // application instance, the "appropriate View" is defined as being the
+  // service root View.
+  // Subsequent times, implementation of this method is delegated to the
+  // application embedded at the service root View. This application is
+  // typically referred to as the "window manager", and will have a specific
+  // definition of where within its View hierarchy to embed an unparented URL.
+  // See ViewManagerService below for more details about |service_provider|.
+  Embed(string url, ServiceProvider? service_provider) => (bool success);
+};
+
+// 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.
+[Client=ViewManagerClient]
+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.
+  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);
+
+  // 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);
+
+  // Embeds the app for |url| in the specified view. More specifically this
+  // creates a new connection to the specified url, expecting to get a
+  // ViewManagerClient and configures it with the root view |view|. Fails
+  // if |view| was not created by this connection.
+  //
+  // 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
+  // current connection. The current connection is told this by way of
+  // OnViewDeleted().
+  //
+  // 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.
+  //
+  // If |view_id| is 0, the View Manager delegates determination of what view to
+  // embed |url| at to the app embedded at the service root view (i.e. the
+  // window manager).
+  // 
+  // |service_provider| encapsulates services offered by the embedder to the
+  // embedded app alongside this Embed() call. It also provides a means for
+  // the embedder to connect to services symmetrically exposed by the embedded
+  // app. Note that if a different app is subsequently embedded at |view_id|
+  // the |service_provider|'s connection to its client in the embedded app and
+  // any services it provided are not broken and continue to be valid.
+  Embed(string url,
+        uint32 view_id,
+        ServiceProvider? service_provider) => (bool success);
+
+  // TODO(sky): move these to a separate interface when FIFO works.
+
+  // Sends OnViewInputEvent() to the owner of the specified view.
+  DispatchOnViewInputEvent(uint32 view_id, mojo.Event event);
+};
+
+// 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().
+[Client=ViewManagerService]
+interface ViewManagerClient {
+  // Invoked when the client application has been embedded at |root|.
+  // See Embed() on ViewManagerService for more details.
+  OnEmbed(uint16 connection_id,
+          string embedder_url,
+          ViewData root,
+          ServiceProvider&? service_provider);
+
+  // Invoked when a view's bounds have changed.
+  OnViewBoundsChanged(uint32 view,
+                      mojo.Rect old_bounds,
+                      mojo.Rect new_bounds);
+
+  // 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 an event is targeted at the specified view.
+  OnViewInputEvent(uint32 view, mojo.Event event) => ();
+
+  // TODO(sky): The following methods represent an interface between the view
+  //            manager and the application embedded at the service root view
+  //            (i.e. the window manager). These methods are not called on
+  //            any other clients. They should be moved to a separate interface
+  //            once support for derived FIFOs is landed.
+
+  // Requests the window manager create a "top level" view embedding |url|.
+  Embed(string url, ServiceProvider&? service_provider);
+
+  // Requests the view manager dispatch the event.
+  DispatchOnViewInputEvent(mojo.Event event);
+};
+
+}
diff --git a/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom b/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom
new file mode 100644
index 0000000..5d13805
--- /dev/null
+++ b/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom
@@ -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.
+
+module mojo {
+
+enum OrderDirection {
+  ABOVE = 1,
+  BELOW,
+};
+
+}
diff --git a/mojo/services/public/interfaces/window_manager/BUILD.gn b/mojo/services/public/interfaces/window_manager/BUILD.gn
new file mode 100644
index 0000000..ee50bc5
--- /dev/null
+++ b/mojo/services/public/interfaces/window_manager/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_bindings
+mojom("window_manager") {
+  sources = [
+    "window_manager.mojom",
+  ]
+}
diff --git a/mojo/services/public/interfaces/window_manager/window_manager.mojom b/mojo/services/public/interfaces/window_manager/window_manager.mojom
new file mode 100644
index 0000000..41deb44
--- /dev/null
+++ b/mojo/services/public/interfaces/window_manager/window_manager.mojom
@@ -0,0 +1,28 @@
+// 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.
+
+module mojo {
+
+[Client=WindowManagerClient]
+interface WindowManagerService {
+  SetCapture(uint32 view_id) => (bool success);
+  FocusWindow(uint32 view_id) => (bool success);
+  ActivateWindow(uint32 view_id) => (bool success);
+};
+
+[Client=WindowManagerService]
+interface WindowManagerClient {
+  // Called when the window manager is ready to use (in the event a client
+  // connects to it before it has been initialized).
+  OnWindowManagerReady();
+
+  // TODO(beng): how is the WM supposed to know if a view is known to a client
+  //             or not?
+  OnCaptureChanged(uint32 old_capture_view_id, uint32 new_capture_view_id);
+
+  OnFocusChanged(uint32 old_focused_node_id, uint32 new_focused_node_id);
+  OnActiveWindowChanged(uint32 old_focused_window, uint32 new_focused_window);
+};
+
+}
diff --git a/mojo/services/surfaces/BUILD.gn b/mojo/services/surfaces/BUILD.gn
new file mode 100644
index 0000000..8c43ca5
--- /dev/null
+++ b/mojo/services/surfaces/BUILD.gn
@@ -0,0 +1,34 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_service
+shared_library("surfaces") {
+  output_name = "mojo_surfaces_service"
+
+  deps = [
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//ui/gfx/geometry",
+    "//mojo/application",
+    "//mojo/cc",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/gles2:interfaces",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/surfaces",
+  ]
+
+  sources = [
+    "surfaces_impl.cc",
+    "surfaces_impl.h",
+    "surfaces_service_application.cc",
+    "surfaces_service_application.h",
+    "surfaces_service_impl.cc",
+    "surfaces_service_impl.h",
+  ]
+}
diff --git a/mojo/services/surfaces/DEPS b/mojo/services/surfaces/DEPS
new file mode 100644
index 0000000..3c07200
--- /dev/null
+++ b/mojo/services/surfaces/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+mojo/application",
+  "+mojo/cc",
+  "+mojo/services/gles2",
+  "+mojo/services/public",
+]
diff --git a/mojo/services/surfaces/surfaces_impl.cc b/mojo/services/surfaces/surfaces_impl.cc
new file mode 100644
index 0000000..9d79a7a
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_impl.cc
@@ -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.
+
+#include "mojo/services/surfaces/surfaces_impl.h"
+
+#include "cc/output/compositor_frame.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/cc/context_provider_mojo.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+
+namespace mojo {
+
+SurfacesImpl::SurfacesImpl(cc::SurfaceManager* manager,
+                           uint32_t id_namespace,
+                           Client* client)
+    : manager_(manager),
+      factory_(manager, this),
+      id_namespace_(id_namespace),
+      client_(client) {
+}
+
+SurfacesImpl::~SurfacesImpl() {
+}
+
+void SurfacesImpl::CreateSurface(SurfaceIdPtr id, mojo::SizePtr size) {
+  cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+  if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+    // Bad message, do something bad to the caller?
+    NOTREACHED();
+    return;
+  }
+  factory_.Create(id.To<cc::SurfaceId>(), size.To<gfx::Size>());
+}
+
+void SurfacesImpl::SubmitFrame(SurfaceIdPtr id, FramePtr frame_ptr) {
+  cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+  if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+    // Bad message, do something bad to the caller?
+    LOG(FATAL) << "Received frame for id " << cc_id.id << " namespace "
+               << cc::SurfaceIdAllocator::NamespaceForId(cc_id)
+               << " should be namespace " << id_namespace_;
+    return;
+  }
+  factory_.SubmitFrame(id.To<cc::SurfaceId>(),
+                       frame_ptr.To<scoped_ptr<cc::CompositorFrame> >(),
+                       base::Closure());
+  client_->FrameSubmitted();
+}
+
+void SurfacesImpl::DestroySurface(SurfaceIdPtr id) {
+  cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+  if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+    // Bad message, do something bad to the caller?
+    NOTREACHED();
+    return;
+  }
+  factory_.Destroy(id.To<cc::SurfaceId>());
+}
+
+void SurfacesImpl::CreateGLES2BoundSurface(CommandBufferPtr gles2_client,
+                                           SurfaceIdPtr id,
+                                           mojo::SizePtr size) {
+  command_buffer_handle_ = gles2_client.PassMessagePipe();
+
+  cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+  if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+    // Bad message, do something bad to the caller?
+    LOG(FATAL) << "Received request for id " << cc_id.id << " namespace "
+               << cc::SurfaceIdAllocator::NamespaceForId(cc_id)
+               << " should be namespace " << id_namespace_;
+    return;
+  }
+  if (!display_) {
+    display_.reset(new cc::Display(this, manager_, NULL));
+    client_->SetDisplay(display_.get());
+    display_->Initialize(make_scoped_ptr(new cc::OutputSurface(
+        new ContextProviderMojo(command_buffer_handle_.Pass()))));
+  }
+  factory_.Create(cc_id, size.To<gfx::Size>());
+  display_->Resize(cc_id, size.To<gfx::Size>());
+}
+
+void SurfacesImpl::ReturnResources(const cc::ReturnedResourceArray& resources) {
+  Array<ReturnedResourcePtr> ret(resources.size());
+  for (size_t i = 0; i < resources.size(); ++i) {
+    ret[i] = ReturnedResource::From(resources[i]);
+  }
+  client()->ReturnResources(ret.Pass());
+}
+
+void SurfacesImpl::DisplayDamaged() {
+}
+
+void SurfacesImpl::DidSwapBuffers() {
+}
+
+void SurfacesImpl::DidSwapBuffersComplete() {
+}
+
+void SurfacesImpl::CommitVSyncParameters(base::TimeTicks timebase,
+                                         base::TimeDelta interval) {
+}
+
+void SurfacesImpl::OutputSurfaceLost() {
+}
+
+void SurfacesImpl::SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) {
+}
+
+}  // namespace mojo
diff --git a/mojo/services/surfaces/surfaces_impl.h b/mojo/services/surfaces/surfaces_impl.h
new file mode 100644
index 0000000..fb6ce53
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_impl.h
@@ -0,0 +1,76 @@
+// 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_SURFACES_SURFACES_IMPL_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "cc/surfaces/display_client.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+
+namespace cc {
+class Display;
+}
+
+namespace mojo {
+class ApplicationManager;
+
+class SurfaceNativeViewportClient;
+
+class SurfacesImpl : public InterfaceImpl<Surface>,
+                     public cc::SurfaceFactoryClient,
+                     public cc::DisplayClient {
+ public:
+  class Client {
+   public:
+    virtual void FrameSubmitted() = 0;
+    virtual void SetDisplay(cc::Display*) = 0;
+  };
+
+  SurfacesImpl(cc::SurfaceManager* manager,
+               uint32_t id_namespace,
+               Client* client);
+  virtual ~SurfacesImpl();
+
+  // Surface implementation.
+  virtual void CreateSurface(SurfaceIdPtr id, mojo::SizePtr size) OVERRIDE;
+  virtual void SubmitFrame(SurfaceIdPtr id, FramePtr frame) OVERRIDE;
+  virtual void DestroySurface(SurfaceIdPtr id) OVERRIDE;
+  virtual void CreateGLES2BoundSurface(CommandBufferPtr gles2_client,
+                                       SurfaceIdPtr id,
+                                       mojo::SizePtr size) OVERRIDE;
+
+  // SurfaceFactoryClient implementation.
+  virtual void ReturnResources(
+      const cc::ReturnedResourceArray& resources) OVERRIDE;
+
+  // DisplayClient implementation.
+  virtual void DisplayDamaged() OVERRIDE;
+  virtual void DidSwapBuffers() OVERRIDE;
+  virtual void DidSwapBuffersComplete() OVERRIDE;
+  virtual void CommitVSyncParameters(base::TimeTicks timebase,
+                                     base::TimeDelta interval) OVERRIDE;
+  virtual void OutputSurfaceLost() OVERRIDE;
+  virtual void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) OVERRIDE;
+
+  cc::SurfaceFactory* factory() { return &factory_; }
+
+ private:
+  cc::SurfaceManager* manager_;
+  cc::SurfaceFactory factory_;
+  uint32_t id_namespace_;
+  Client* client_;
+  scoped_ptr<cc::Display> display_;
+  ScopedMessagePipeHandle command_buffer_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfacesImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_SURFACES_SURFACES_IMPL_H_
diff --git a/mojo/services/surfaces/surfaces_service_application.cc b/mojo/services/surfaces/surfaces_service_application.cc
new file mode 100644
index 0000000..8677be4
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_application.cc
@@ -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.
+
+#include "mojo/services/surfaces/surfaces_service_application.h"
+
+#include "cc/surfaces/display.h"
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/services/surfaces/surfaces_service_impl.h"
+
+namespace mojo {
+
+SurfacesServiceApplication::SurfacesServiceApplication()
+    : next_id_namespace_(1u), display_(NULL), draw_timer_(false, false) {
+}
+
+SurfacesServiceApplication::~SurfacesServiceApplication() {
+}
+
+bool SurfacesServiceApplication::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(this);
+  return true;
+}
+
+void SurfacesServiceApplication::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<SurfacesService> request) {
+  BindToRequest(new SurfacesServiceImpl(&manager_, &next_id_namespace_, this),
+                &request);
+}
+
+void SurfacesServiceApplication::FrameSubmitted() {
+  if (!draw_timer_.IsRunning() && display_) {
+    draw_timer_.Start(FROM_HERE,
+                      base::TimeDelta::FromMilliseconds(17),
+                      base::Bind(base::IgnoreResult(&cc::Display::Draw),
+                                 base::Unretained(display_)));
+  }
+}
+
+void SurfacesServiceApplication::SetDisplay(cc::Display* display) {
+  display_ = display;
+}
+
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::SurfacesServiceApplication);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/surfaces/surfaces_service_application.h b/mojo/services/surfaces/surfaces_service_application.h
new file mode 100644
index 0000000..27fd621
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_application.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 MOJO_SERVICES_SURFACES_SURFACES_SERVICE_APPLICATION_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_SERVICE_APPLICATION_H_
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "cc/surfaces/surface_manager.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/surfaces/surfaces_impl.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+class SurfacesServiceApplication : public ApplicationDelegate,
+                                   public InterfaceFactory<SurfacesService>,
+                                   public SurfacesImpl::Client {
+ public:
+  SurfacesServiceApplication();
+  virtual ~SurfacesServiceApplication();
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) OVERRIDE;
+
+  // InterfaceFactory<SurfacsServicee> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<SurfacesService> request) OVERRIDE;
+
+  // SurfacesImpl::Client implementation.
+  virtual void FrameSubmitted() OVERRIDE;
+  virtual void SetDisplay(cc::Display*) OVERRIDE;
+
+ private:
+  cc::SurfaceManager manager_;
+  uint32_t next_id_namespace_;
+  cc::Display* display_;
+  // TODO(jamesr): Integrate with real scheduler.
+  base::Timer draw_timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfacesServiceApplication);
+};
+
+}  // namespace mojo
+
+#endif  //  MOJO_SERVICES_SURFACES_SURFACES_SERVICE_APPLICATION_H_
diff --git a/mojo/services/surfaces/surfaces_service_impl.cc b/mojo/services/surfaces/surfaces_service_impl.cc
new file mode 100644
index 0000000..919f8a5
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_impl.cc
@@ -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.
+
+#include "mojo/services/surfaces/surfaces_service_impl.h"
+
+namespace mojo {
+
+SurfacesServiceImpl::SurfacesServiceImpl(cc::SurfaceManager* manager,
+                                         uint32_t* next_id_namespace,
+                                         SurfacesImpl::Client* client)
+    : manager_(manager),
+      next_id_namespace_(next_id_namespace),
+      client_(client) {
+}
+SurfacesServiceImpl::~SurfacesServiceImpl() {
+}
+
+void SurfacesServiceImpl::CreateSurfaceConnection(
+    const Callback<void(SurfacePtr, uint32_t)>& callback) {
+  uint32_t id_namespace = (*next_id_namespace_)++;
+  SurfacePtr surface;
+  BindToProxy(new SurfacesImpl(manager_, id_namespace, client_), &surface);
+  callback.Run(surface.Pass(), id_namespace);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/surfaces/surfaces_service_impl.h b/mojo/services/surfaces/surfaces_service_impl.h
new file mode 100644
index 0000000..21dd185
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_impl.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 MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/surfaces/surfaces_impl.h"
+
+namespace cc {
+class SurfaceManager;
+}
+
+namespace mojo {
+
+class SurfacesServiceImpl : public InterfaceImpl<SurfacesService> {
+ public:
+  // The instances pointed to by |manager|, |next_id_namespace| and |client| are
+  // owned by the caller and must outlive the SurfacesServiceImpl instance.
+  SurfacesServiceImpl(cc::SurfaceManager* manager,
+                      uint32_t* next_id_namespace,
+                      SurfacesImpl::Client* client);
+  virtual ~SurfacesServiceImpl();
+
+  // InterfaceImpl<SurfacesService> implementation.
+  virtual void CreateSurfaceConnection(const mojo::Callback<
+      void(mojo::SurfacePtr, uint32_t)>& callback) OVERRIDE;
+
+ private:
+  cc::SurfaceManager* manager_;
+  uint32_t* next_id_namespace_;
+  SurfacesImpl::Client* client_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfacesServiceImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
diff --git a/mojo/services/test_service/BUILD.gn b/mojo/services/test_service/BUILD.gn
new file mode 100644
index 0000000..3c50ae6
--- /dev/null
+++ b/mojo/services/test_service/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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_test_service_bindings
+mojom("bindings") {
+  sources = [
+    "test_request_tracker.mojom",
+    "test_service.mojom",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_test_app
+shared_library("mojo_test_app") {
+  deps = [
+    ":bindings",
+    "//base",
+    "//base:i18n",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/utility",
+  ]
+
+  sources = [
+    "test_request_tracker_client_impl.cc",
+    "test_request_tracker_client_impl.h",
+    "test_service_application.cc",
+    "test_service_application.h",
+    "test_service_impl.cc",
+    "test_service_impl.h",
+    "test_time_service_impl.cc",
+    "test_time_service_impl.h",
+  ]
+}
+
+# GYP version: //mojo/mojo_services.gypi:mojo_test_request_tracker_app
+shared_library("mojo_test_request_tracker_app") {
+  deps = [
+    ":bindings",
+    "//base",
+    "//base:i18n",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/utility",
+  ]
+
+  sources = [
+    "test_request_tracker_client_impl.cc",
+    "test_request_tracker_client_impl.h",
+    "test_request_tracker_application.cc",
+    "test_request_tracker_application.h",
+    "test_time_service_impl.cc",
+    "test_time_service_impl.h",
+    "test_request_tracker_impl.cc",
+    "test_request_tracker_impl.h",
+  ]
+}
diff --git a/mojo/services/test_service/test_request_tracker.mojom b/mojo/services/test_service/test_request_tracker.mojom
new file mode 100644
index 0000000..a750031
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker.mojom
@@ -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.
+
+module mojo.test {
+
+// Various counters that services can periodically send to a
+// TestTrackedRequestService for recording.
+struct ServiceStats {
+  uint64 num_new_requests;
+  double health;
+};
+
+// A per-service summary of all the ServiceStats the
+// TestTrackedRequestService has observed.
+struct ServiceReport {
+  string? service_name;
+  uint64 total_requests;
+  double mean_health;
+};
+
+// A simple interface to obtain a "report" from all services that have
+// opted to connect themselves to for request tracking.
+interface TestTrackedRequestService {
+  GetReport() => (array<ServiceReport?>? report);
+};
+
+// TestRequestTracker records ServiceStats for an individual service
+// connection for aggregation in a TestTrackedRequestService.
+[Client=TestRequestTrackerClient]
+interface TestRequestTracker {
+  // Upload a ServiceStats for tracking.
+  RecordStats(uint64 client_id, ServiceStats? stats);
+};
+
+// The client-side contract for uploading ServiceStats to TestRequestTracker.
+interface TestRequestTrackerClient {
+  const uint64 kInvalidId = 0;
+  // Handshake to tell the client its global identifier and get the name.
+  SetIdAndReturnName(uint64 id) => (string? service_name);
+};
+
+}  // module mojo.test
diff --git a/mojo/services/test_service/test_request_tracker_application.cc b/mojo/services/test_service/test_request_tracker_application.cc
new file mode 100644
index 0000000..b533377
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_application.cc
@@ -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.
+
+#include "mojo/services/test_service/test_request_tracker_application.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestRequestTrackerApplication::TestRequestTrackerApplication()
+    : test_tracked_request_factory_(&context_),
+      test_request_tracker_factory_(&context_) {
+}
+
+TestRequestTrackerApplication::~TestRequestTrackerApplication() {
+}
+
+bool TestRequestTrackerApplication::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  // Every instance of the service and recorder shares the context.
+  // Note, this app is single-threaded, so this is thread safe.
+  connection->AddService(&test_tracked_request_factory_);
+  connection->AddService(&test_request_tracker_factory_);
+  connection->AddService(this);
+  return true;
+}
+
+void TestRequestTrackerApplication::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<TestTimeService> request) {
+  BindToRequest(new TestTimeServiceImpl(connection), &request);
+}
+
+}  // namespace test
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(
+      new mojo::test::TestRequestTrackerApplication);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/test_service/test_request_tracker_application.h b/mojo/services/test_service/test_request_tracker_application.h
new file mode 100644
index 0000000..b519a91
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_application.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 MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_APPLICATION_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_APPLICATION_H_
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker_impl.h"
+
+namespace mojo {
+namespace test {
+class TestTimeService;
+
+// Embeds TestRequestTracker mojo services into an application.
+class TestRequestTrackerApplication : public ApplicationDelegate,
+                                      public InterfaceFactory<TestTimeService> {
+ public:
+  TestRequestTrackerApplication();
+  virtual ~TestRequestTrackerApplication();
+
+  // ApplicationDelegate methods:
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+  // InterfaceFactory<TestTimeService> methods:
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestTimeService> request) override;
+
+ private:
+  TrackingContext context_;
+  typedef InterfaceFactoryImplWithContext<TestTrackedRequestServiceImpl,
+                                          TrackingContext>
+      TestTrackedRequestFactory;
+  TestTrackedRequestFactory test_tracked_request_factory_;
+  typedef InterfaceFactoryImplWithContext<TestRequestTrackerImpl,
+                                          TrackingContext>
+      TestRequestTrackerFactory;
+  TestRequestTrackerFactory test_request_tracker_factory_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestRequestTrackerApplication);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_APPLICATION_H_
diff --git a/mojo/services/test_service/test_request_tracker_client_impl.cc b/mojo/services/test_service/test_request_tracker_client_impl.cc
new file mode 100644
index 0000000..c9e66c3
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_client_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 "mojo/services/test_service/test_request_tracker_client_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestRequestTrackerClientImpl::TestRequestTrackerClientImpl(
+    TestRequestTrackerPtr tracker,
+    const std::string& service_name,
+    const mojo::Callback<void()>& callback)
+    : id_(0),
+      requests_since_upload_(0),
+      service_name_(service_name),
+      tracker_(tracker.Pass()),
+      tracking_connected_callback_(callback) {
+  tracker_.set_client(this);
+}
+
+TestRequestTrackerClientImpl::~TestRequestTrackerClientImpl() {
+}
+
+void TestRequestTrackerClientImpl::RecordNewRequest() {
+  requests_since_upload_++;
+  if (id_ == kInvalidId)
+   return;
+  SendStats();
+}
+
+void TestRequestTrackerClientImpl::SendStats() {
+  ServiceStatsPtr stats(ServiceStats::New());
+  stats->num_new_requests = requests_since_upload_;
+  stats->health = 0.7;
+  tracker_->RecordStats(id_, stats.Pass());
+  requests_since_upload_ = 0;
+}
+
+void TestRequestTrackerClientImpl::SetIdAndReturnName(
+    uint64_t id,
+    const mojo::Callback<void(mojo::String)>& callback) {
+  assert(id != kInvalidId);
+  assert(id_ == kInvalidId);
+  id_ = id;
+  callback.Run(service_name_);
+  tracking_connected_callback_.Run();
+  if (requests_since_upload_ == 0)
+    return;
+  SendStats();
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/services/test_service/test_request_tracker_client_impl.h b/mojo/services/test_service/test_request_tracker_client_impl.h
new file mode 100644
index 0000000..91340cb
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_client_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 MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+
+namespace mojo {
+namespace test {
+
+class TestRequestTrackerClientImpl : public TestRequestTrackerClient {
+ public:
+  TestRequestTrackerClientImpl(
+      TestRequestTrackerPtr tracker,
+      const std::string& service_name,
+      const mojo::Callback<void()>& tracking_connected_callback);
+  virtual ~TestRequestTrackerClientImpl();
+
+  // Call whenever an event happens that you want to be recorded.
+  void RecordNewRequest();
+
+  // TestRequestTrackerClient impl.
+  virtual void SetIdAndReturnName(
+      uint64_t id,
+      const mojo::Callback<void(mojo::String)>& callback) override;
+
+ private:
+  void SendStats();
+  uint64_t id_;
+  uint64_t requests_since_upload_;
+  const std::string service_name_;
+  TestRequestTrackerPtr tracker_;
+  mojo::Callback<void()> tracking_connected_callback_;
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
diff --git a/mojo/services/test_service/test_request_tracker_impl.cc b/mojo/services/test_service/test_request_tracker_impl.cc
new file mode 100644
index 0000000..9af6266
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_impl.cc
@@ -0,0 +1,75 @@
+// 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 "mojo/services/test_service/test_request_tracker_impl.h"
+
+namespace mojo {
+namespace test {
+
+ TrackingContext::TrackingContext() : next_id(1) {}
+ TrackingContext::~TrackingContext() {}
+
+ TestRequestTrackerImpl::TestRequestTrackerImpl(TrackingContext* context)
+     : context_(context), weak_factory_(this) {
+ }
+
+ TestRequestTrackerImpl::~TestRequestTrackerImpl() {
+ }
+
+void TestRequestTrackerImpl::RecordStats(
+      uint64_t client_id,
+      ServiceStatsPtr stats) {
+  assert(context_->ids_to_names.find(client_id) !=
+         context_->ids_to_names.end());
+  context_->records[client_id].push_back(*stats);
+}
+
+void TestRequestTrackerImpl::OnConnectionEstablished() {
+  uint64_t id = context_->next_id++;
+  client()->SetIdAndReturnName(id,
+      base::Bind(&TestRequestTrackerImpl::UploaderNameCallback,
+                 weak_factory_.GetWeakPtr(),
+                 id));
+}
+
+void TestRequestTrackerImpl::UploaderNameCallback(
+    uint64_t id, const mojo::String& name) {
+  DCHECK(context_->ids_to_names.find(id) == context_->ids_to_names.end());
+  context_->ids_to_names[id] = name;
+}
+
+TestTrackedRequestServiceImpl::TestTrackedRequestServiceImpl(
+    TrackingContext* context)
+    : context_(context) {
+}
+
+TestTrackedRequestServiceImpl::~TestTrackedRequestServiceImpl() {
+}
+
+void TestTrackedRequestServiceImpl::GetReport(
+    const mojo::Callback<void(mojo::Array<ServiceReportPtr>)>& callback) {
+  mojo::Array<ServiceReportPtr> reports;
+  for (AllRecordsMap::const_iterator it1 = context_->records.begin();
+       it1 != context_->records.end(); ++it1) {
+    ServiceReportPtr report(ServiceReport::New());
+    report->service_name = context_->ids_to_names[it1->first];
+    double mean_health_numerator = 0;
+    size_t num_samples = it1->second.size();
+    if (num_samples == 0)
+      continue;
+
+    for (std::vector<ServiceStats>::const_iterator it2 = it1->second.begin();
+         it2 != it1->second.end(); ++it2) {
+      report->total_requests += it2->num_new_requests;
+      mean_health_numerator += it2->health;
+    }
+    report->mean_health = mean_health_numerator / num_samples;
+    reports.push_back(report.Pass());
+  }
+  callback.Run(reports.Pass());
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/services/test_service/test_request_tracker_impl.h b/mojo/services/test_service/test_request_tracker_impl.h
new file mode 100644
index 0000000..736cc70
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_impl.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 MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+
+namespace mojo {
+class ApplicationConnection;
+namespace test {
+
+typedef std::map<uint64_t, std::vector<ServiceStats> > AllRecordsMap;
+
+// Shared state between all instances of TestRequestTrackerImpl
+// and the master TrackedRequestService.
+struct TrackingContext {
+  TrackingContext();
+  ~TrackingContext();
+  AllRecordsMap records;
+  std::map<uint64_t, std::string> ids_to_names;
+  uint64_t next_id;
+};
+
+class TestRequestTrackerImpl : public InterfaceImpl<TestRequestTracker> {
+ public:
+  explicit TestRequestTrackerImpl(TrackingContext* context);
+  virtual ~TestRequestTrackerImpl();
+
+  // TestRequestTracker.
+  virtual void RecordStats(uint64_t client_id, ServiceStatsPtr stats) override;
+
+  // InterfaceImpl override.
+  virtual void OnConnectionEstablished() override;
+
+ private:
+  void UploaderNameCallback(uint64_t id, const mojo::String& name);
+  TrackingContext* context_;
+  base::WeakPtrFactory<TestRequestTrackerImpl> weak_factory_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestRequestTrackerImpl);
+};
+
+class TestTrackedRequestServiceImpl
+    : public InterfaceImpl<TestTrackedRequestService> {
+ public:
+  explicit TestTrackedRequestServiceImpl(TrackingContext* context);
+  virtual ~TestTrackedRequestServiceImpl();
+
+  // |TestTrackedRequestService| implementation.
+  virtual void GetReport(
+      const mojo::Callback<void(mojo::Array<ServiceReportPtr>)>& callback)
+      override;
+
+ private:
+  TrackingContext* context_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestTrackedRequestServiceImpl);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_
diff --git a/mojo/services/test_service/test_service.mojom b/mojo/services/test_service/test_service.mojom
new file mode 100644
index 0000000..bda3b68
--- /dev/null
+++ b/mojo/services/test_service/test_service.mojom
@@ -0,0 +1,21 @@
+// 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.
+
+module mojo.test {
+
+interface TestService {
+  Ping() => ();
+  // Connect to a TestTimeService at |app_url| and ferry the data back
+  // in |response|.
+  ConnectToAppAndGetTime(string? app_url) => (int64 time_usec);
+  StartTrackingRequests() => ();
+};
+
+interface TestTimeService {
+  // Provides a constant time value.
+  GetPartyTime() => (int64 time_usec);
+  StartTrackingRequests() => ();
+};
+
+}  // module mojo.test
diff --git a/mojo/services/test_service/test_service_application.cc b/mojo/services/test_service/test_service_application.cc
new file mode 100644
index 0000000..f08e183
--- /dev/null
+++ b/mojo/services/test_service/test_service_application.cc
@@ -0,0 +1,60 @@
+// 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/test_service/test_service_application.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/test_service/test_service_impl.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceApplication::TestServiceApplication() : ref_count_(0) {
+}
+
+TestServiceApplication::~TestServiceApplication() {
+}
+
+bool TestServiceApplication::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService<TestService>(this);
+  connection->AddService<TestTimeService>(this);
+  return true;
+}
+
+void TestServiceApplication::Create(ApplicationConnection* connection,
+                                    InterfaceRequest<TestService> request) {
+  BindToRequest(new TestServiceImpl(connection, this), &request);
+}
+
+void TestServiceApplication::Create(ApplicationConnection* connection,
+                                    InterfaceRequest<TestTimeService> request) {
+  BindToRequest(new TestTimeServiceImpl(connection), &request);
+}
+
+void TestServiceApplication::AddRef() {
+  assert(ref_count_ >= 0);
+  ref_count_++;
+}
+
+void TestServiceApplication::ReleaseRef() {
+  assert(ref_count_ > 0);
+  ref_count_--;
+  if (ref_count_ <= 0)
+    RunLoop::current()->Quit();
+}
+
+}  // namespace test
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new mojo::test::TestServiceApplication);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/test_service/test_service_application.h b/mojo/services/test_service/test_service_application.h
new file mode 100644
index 0000000..47743a1
--- /dev/null
+++ b/mojo/services/test_service/test_service_application.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 MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace test {
+class TestService;
+class TestTimeService;
+
+class TestServiceApplication : public ApplicationDelegate,
+                               public InterfaceFactory<TestService>,
+                               public InterfaceFactory<TestTimeService> {
+ public:
+  TestServiceApplication();
+  virtual ~TestServiceApplication();
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+  // InterfaceFactory<TestService> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestService> request) override;
+
+  // InterfaceFactory<TestTimeService> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestTimeService> request) override;
+
+  void AddRef();
+  void ReleaseRef();
+
+ private:
+  int ref_count_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceApplication);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
diff --git a/mojo/services/test_service/test_service_impl.cc b/mojo/services/test_service/test_service_impl.cc
new file mode 100644
index 0000000..9e589a5
--- /dev/null
+++ b/mojo/services/test_service/test_service_impl.cc
@@ -0,0 +1,67 @@
+// 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/test_service/test_service_impl.h"
+
+#include "base/bind.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/test_service/test_request_tracker_client_impl.h"
+#include "mojo/services/test_service/test_service_application.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceImpl::TestServiceImpl(ApplicationConnection* connection,
+                                 TestServiceApplication* application)
+    : application_(application), connection_(connection) {
+}
+
+TestServiceImpl::~TestServiceImpl() {
+}
+
+void TestServiceImpl::OnConnectionEstablished() {
+  application_->AddRef();
+}
+
+void TestServiceImpl::OnConnectionError() {
+  application_->ReleaseRef();
+}
+
+void TestServiceImpl::Ping(const mojo::Callback<void()>& callback) {
+  if (tracking_)
+    tracking_->RecordNewRequest();
+  callback.Run();
+}
+
+void SendTimeResponse(
+    const mojo::Callback<void(int64_t)>& requestor_callback,
+    int64_t time_usec) {
+  requestor_callback.Run(time_usec);
+}
+
+void TestServiceImpl::ConnectToAppAndGetTime(
+    const mojo::String& app_url,
+    const mojo::Callback<void(int64_t)>& callback) {
+  connection_->ConnectToService(app_url, &time_service_);
+  if (tracking_) {
+    tracking_->RecordNewRequest();
+    time_service_->StartTrackingRequests(mojo::Callback<void()>());
+  }
+  time_service_->GetPartyTime(base::Bind(&SendTimeResponse, callback));
+}
+
+void TestServiceImpl::StartTrackingRequests(
+    const mojo::Callback<void()>& callback) {
+  TestRequestTrackerPtr tracker;
+  connection_->ConnectToService(
+      "mojo:mojo_test_request_tracker_app", &tracker);
+  tracking_.reset(
+      new TestRequestTrackerClientImpl(tracker.Pass(), Name_, callback));
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/services/test_service/test_service_impl.h b/mojo/services/test_service/test_service_impl.h
new file mode 100644
index 0000000..379f02e
--- /dev/null
+++ b/mojo/services/test_service/test_service_impl.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_TEST_SERVICE_TEST_SERVICE_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+
+namespace mojo {
+class ApplicationConnection;
+namespace test {
+
+class TestRequestTrackerClientImpl;
+class TestServiceApplication;
+
+class TestServiceImpl : public InterfaceImpl<TestService> {
+ public:
+  TestServiceImpl(ApplicationConnection* connection,
+                  TestServiceApplication* application);
+  virtual ~TestServiceImpl();
+
+  // |TestService| methods:
+  virtual void OnConnectionEstablished() override;
+  virtual void OnConnectionError() override;
+  virtual void Ping(const mojo::Callback<void()>& callback) override;
+  virtual void ConnectToAppAndGetTime(
+      const mojo::String& app_url,
+      const mojo::Callback<void(int64_t)>& callback) override;
+  virtual void StartTrackingRequests(
+      const mojo::Callback<void()>& callback) override;
+
+ private:
+  TestServiceApplication* const application_;
+  ApplicationConnection* const connection_;
+  TestTimeServicePtr time_service_;
+  scoped_ptr<TestRequestTrackerClientImpl> tracking_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceImpl);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
diff --git a/mojo/services/test_service/test_time_service_impl.cc b/mojo/services/test_service/test_time_service_impl.cc
new file mode 100644
index 0000000..f4adbaa
--- /dev/null
+++ b/mojo/services/test_service/test_time_service_impl.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/time/time.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+#include "mojo/services/test_service/test_request_tracker_client_impl.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestTimeServiceImpl::TestTimeServiceImpl(ApplicationConnection* application)
+    : application_(application) {
+}
+
+TestTimeServiceImpl::~TestTimeServiceImpl() {
+}
+
+void TestTimeServiceImpl::StartTrackingRequests(
+    const mojo::Callback<void()>& callback) {
+  TestRequestTrackerPtr tracker;
+  application_->ConnectToService(
+      "mojo:mojo_test_request_tracker_app", &tracker);
+  tracking_.reset(new TestRequestTrackerClientImpl(
+      tracker.Pass(), Name_, callback));
+}
+
+void TestTimeServiceImpl::GetPartyTime(
+    const mojo::Callback<void(int64_t)>& callback) {
+  if (tracking_)
+    tracking_->RecordNewRequest();
+  base::Time frozen_time(base::Time::UnixEpoch()
+      + base::TimeDelta::FromDays(10957)
+      + base::TimeDelta::FromHours(7)
+      + base::TimeDelta::FromMinutes(59));
+  int64 time(frozen_time.ToInternalValue());
+  callback.Run(time);
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/services/test_service/test_time_service_impl.h b/mojo/services/test_service/test_time_service_impl.h
new file mode 100644
index 0000000..c9eaea2
--- /dev/null
+++ b/mojo/services/test_service/test_time_service_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace test {
+
+class TestRequestTrackerClientImpl;
+
+class TestTimeServiceImpl : public InterfaceImpl<TestTimeService> {
+ public:
+  explicit TestTimeServiceImpl(ApplicationConnection* application);
+  virtual ~TestTimeServiceImpl();
+
+  // |TestTimeService| methods:
+  virtual void GetPartyTime(
+      const mojo::Callback<void(int64_t time_usec)>& callback) override;
+  virtual void StartTrackingRequests(
+      const mojo::Callback<void()>& callback) override;
+
+ private:
+  ApplicationConnection* application_;
+  scoped_ptr<TestRequestTrackerClientImpl> tracking_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestTimeServiceImpl);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/BUILD.gn b/mojo/services/view_manager/BUILD.gn
new file mode 100644
index 0000000..ddb76e2
--- /dev/null
+++ b/mojo/services/view_manager/BUILD.gn
@@ -0,0 +1,96 @@
+# 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")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager
+shared_library("view_manager") {
+  output_name = "mojo_view_manager"
+
+  deps = [
+    "//base",
+    "//cc/surfaces",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/native_viewport",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/interfaces/view_manager",
+    "//ui/base",
+    "//ui/events",
+    "//ui/events:events_base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  defines = [
+    "MOJO_VIEW_MANAGER_IMPLEMENTATION",
+  ]
+
+  sources = [
+    "access_policy.h",
+    "access_policy_delegate.h",
+    "connection_manager.cc",
+    "connection_manager.h",
+    "default_access_policy.cc",
+    "default_access_policy.h",
+    "display_manager.cc",
+    "display_manager.h",
+    "main.cc",
+    "server_view.cc",
+    "server_view.h",
+    "server_view_delegate.h",
+    "view_manager_export.h",
+    "view_manager_init_service_context.cc",
+    "view_manager_init_service_context.h",
+    "view_manager_init_service_impl.cc",
+    "view_manager_init_service_impl.h",
+    "view_manager_service_impl.cc",
+    "view_manager_service_impl.h",
+    "window_manager_access_policy.cc",
+    "window_manager_access_policy.h",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_unittests
+test("mojo_view_manager_unittests") {
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/application",
+    "//mojo/application_manager",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/cpp/view_manager/lib:run_unittests",
+    "//mojo/shell:test_support",
+    "//testing/gtest",
+    "//ui/gfx/geometry",
+  ]
+
+  if (use_x11) {
+    deps += ["//ui/gfx/x"]
+  }
+
+  if (is_component_build) {
+    deps += ["//ui/gl"]
+  }
+
+  sources = [
+    "test_change_tracker.cc",
+    "test_change_tracker.h",
+    "view_manager_unittest.cc",
+  ]
+}
diff --git a/mojo/services/view_manager/DEPS b/mojo/services/view_manager/DEPS
new file mode 100644
index 0000000..d91b8be
--- /dev/null
+++ b/mojo/services/view_manager/DEPS
@@ -0,0 +1,23 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+gpu/command_buffer/service/mailbox_manager.h",
+  "+mojo/application",
+  "+mojo/cc",
+  "+mojo/geometry",
+  "+mojo/services",
+  "+third_party/skia",
+  "+ui/base/cursor/cursor.h",
+  "+ui/base/hit_test.h",
+  "+ui/compositor",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/gl",
+  "+webkit/common/gpu",
+]
+
+specific_include_rules = {
+  "view_manager_unittest.cc": [
+    "+mojo/application_manager/application_manager.h",
+  ],
+}
diff --git a/mojo/services/view_manager/access_policy.h b/mojo/services/view_manager/access_policy.h
new file mode 100644
index 0000000..cac5579
--- /dev/null
+++ b/mojo/services/view_manager/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 MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
+
+#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+
+namespace mojo {
+namespace service {
+
+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,
+                              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;
+
+  // 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 service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
diff --git a/mojo/services/view_manager/access_policy_delegate.h b/mojo/services/view_manager/access_policy_delegate.h
new file mode 100644
index 0000000..12c7479
--- /dev/null
+++ b/mojo/services/view_manager/access_policy_delegate.h
@@ -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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "mojo/services/view_manager/ids.h"
+
+namespace mojo {
+namespace service {
+
+class ServerView;
+
+// Delegate used by the AccessPolicy implementations to get state.
+class AccessPolicyDelegate {
+ public:
+  // Returns the ids of the roots views for this connection. That is, this is
+  // the set of views the connection was embedded at.
+  virtual const base::hash_set<Id>& GetRootsForAccessPolicy() 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 service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
diff --git a/mojo/services/view_manager/connection_manager.cc b/mojo/services/view_manager/connection_manager.cc
new file mode 100644
index 0000000..6d9fe2a
--- /dev/null
+++ b/mojo/services/view_manager/connection_manager.cc
@@ -0,0 +1,297 @@
+// 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/connection_manager.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+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(
+    ApplicationConnection* app_connection,
+    const Callback<void()>& native_viewport_closed_callback)
+    : app_connection_(app_connection),
+      next_connection_id_(1),
+      display_manager_(app_connection,
+                       this,
+                       native_viewport_closed_callback),
+      root_(new ServerView(this, RootViewId())),
+      current_change_(NULL) {
+  root_->SetBounds(gfx::Rect(800, 600));
+}
+
+ConnectionManager::~ConnectionManager() {
+  while (!connections_created_by_connect_.empty())
+    delete *(connections_created_by_connect_.begin());
+  // All the connections should have been destroyed.
+  DCHECK(connection_map_.empty());
+  root_.reset();
+}
+
+ConnectionSpecificId ConnectionManager::GetAndAdvanceNextConnectionId() {
+  const ConnectionSpecificId id = next_connection_id_++;
+  DCHECK_LT(id, next_connection_id_);
+  return id;
+}
+
+void ConnectionManager::AddConnection(ViewManagerServiceImpl* connection) {
+  DCHECK_EQ(0u, connection_map_.count(connection->id()));
+  connection_map_[connection->id()] = connection;
+}
+
+void ConnectionManager::RemoveConnection(ViewManagerServiceImpl* connection) {
+  connection_map_.erase(connection->id());
+  connections_created_by_connect_.erase(connection);
+
+  // Notify remaining connections so that they can cleanup.
+  for (ConnectionMap::const_iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->OnViewManagerServiceImplDestroyed(connection->id());
+  }
+}
+
+void ConnectionManager::EmbedRoot(
+    const std::string& url,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  if (connection_map_.empty()) {
+    EmbedImpl(kInvalidConnectionId,
+              String::From(url),
+              RootViewId(),
+              service_provider.Pass());
+    return;
+  }
+  ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection);
+  connection->client()->Embed(url, service_provider.Pass());
+}
+
+void ConnectionManager::Embed(
+    ConnectionSpecificId creator_id,
+    const String& url,
+    Id transport_view_id,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  EmbedImpl(creator_id,
+            url,
+            ViewIdFromTransportId(transport_view_id),
+            service_provider.Pass())->set_delete_on_connection_error();
+}
+
+ViewManagerServiceImpl* ConnectionManager::GetConnection(
+    ConnectionSpecificId connection_id) {
+  ConnectionMap::iterator i = connection_map_.find(connection_id);
+  return i == connection_map_.end() ? NULL : i->second;
+}
+
+ServerView* ConnectionManager::GetView(const ViewId& id) {
+  if (id == root_->id())
+    return root_.get();
+  ConnectionMap::iterator i = connection_map_.find(id.connection_id);
+  return i == connection_map_.end() ? NULL : i->second->GetView(id);
+}
+
+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 (ConnectionMap::const_iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    if (i->second->HasRoot(id))
+      return i->second;
+  }
+  return NULL;
+}
+
+void ConnectionManager::DispatchViewInputEventToWindowManager(EventPtr event) {
+  // Input events are forwarded to the WindowManager. The WindowManager
+  // eventually calls back to us with DispatchOnViewInputEvent().
+  ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection);
+  if (!connection)
+    return;
+  connection->client()->DispatchOnViewInputEvent(event.Pass());
+}
+
+void ConnectionManager::ProcessViewBoundsChanged(const ServerView* view,
+                                                 const gfx::Rect& old_bounds,
+                                                 const gfx::Rect& new_bounds) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessViewBoundsChanged(
+        view, old_bounds, new_bounds, IsChangeSource(i->first));
+  }
+}
+
+void ConnectionManager::ProcessWillChangeViewHierarchy(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessWillChangeViewHierarchy(
+        view, new_parent, old_parent, IsChangeSource(i->first));
+  }
+}
+
+void ConnectionManager::ProcessViewHierarchyChanged(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessViewHierarchyChanged(
+        view, new_parent, old_parent, IsChangeSource(i->first));
+  }
+}
+
+void ConnectionManager::ProcessViewReorder(const ServerView* view,
+                                           const ServerView* relative_view,
+                                           const OrderDirection direction) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessViewReorder(
+        view, relative_view, direction, IsChangeSource(i->first));
+  }
+}
+
+void ConnectionManager::ProcessViewDeleted(const ViewId& view) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessViewDeleted(view, IsChangeSource(i->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;
+}
+
+ViewManagerServiceImpl* ConnectionManager::EmbedImpl(
+    const ConnectionSpecificId creator_id,
+    const String& url,
+    const ViewId& root_id,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  MessagePipe pipe;
+
+  ServiceProvider* view_manager_service_provider =
+      app_connection_->ConnectToApplication(url)->GetServiceProvider();
+  view_manager_service_provider->ConnectToService(
+      ViewManagerServiceImpl::Client::Name_, pipe.handle1.Pass());
+
+  std::string creator_url;
+  ConnectionMap::const_iterator it = connection_map_.find(creator_id);
+  if (it != connection_map_.end())
+    creator_url = it->second->url();
+
+  ViewManagerServiceImpl* connection =
+      new ViewManagerServiceImpl(this,
+                                 creator_id,
+                                 creator_url,
+                                 url.To<std::string>(),
+                                 root_id,
+                                 service_provider.Pass());
+  WeakBindToPipe(connection, pipe.handle0.Pass());
+  connections_created_by_connect_.insert(connection);
+  OnConnectionMessagedClient(connection->id());
+  return connection;
+}
+
+void ConnectionManager::OnViewDestroyed(const ServerView* view) {
+  ProcessViewDeleted(view->id());
+}
+
+void ConnectionManager::OnWillChangeViewHierarchy(
+    const ServerView* view,
+    const ServerView* new_parent,
+    const ServerView* old_parent) {
+  if (!display_manager_.in_setup())
+    ProcessWillChangeViewHierarchy(view, new_parent, old_parent);
+}
+
+void ConnectionManager::OnViewHierarchyChanged(const ServerView* view,
+                                               const ServerView* new_parent,
+                                               const ServerView* old_parent) {
+  if (!display_manager_.in_setup())
+    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(const ServerView* view,
+                                            const gfx::Rect& old_bounds,
+                                            const gfx::Rect& new_bounds) {
+  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::OnViewSurfaceIdChanged(const ServerView* view) {
+  display_manager_.SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnViewReordered(const ServerView* view,
+                                        const ServerView* relative,
+                                        OrderDirection direction) {
+  display_manager_.SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnWillChangeViewVisibility(const ServerView* view) {
+  for (ConnectionMap::iterator i = connection_map_.begin();
+       i != connection_map_.end();
+       ++i) {
+    i->second->ProcessWillChangeViewVisibility(view, IsChangeSource(i->first));
+  }
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/connection_manager.h b/mojo/services/view_manager/connection_manager.h
new file mode 100644
index 0000000..c92fd69
--- /dev/null
+++ b/mojo/services/view_manager/connection_manager.h
@@ -0,0 +1,206 @@
+// 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_CONNECTION_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/view_manager/display_manager.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/server_view.h"
+#include "mojo/services/view_manager/server_view_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ViewManagerServiceImpl;
+
+// ConnectionManager manages the set of connections to the ViewManager (all the
+// ViewManagerServiceImpls) as well as providing the root of the hierarchy.
+class MOJO_VIEW_MANAGER_EXPORT ConnectionManager : public ServerViewDelegate {
+ 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();
+
+    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(ConnectionSpecificId connection_id) {
+      message_ids_.insert(connection_id);
+    }
+
+    // Returns true if MarkConnectionAsMessaged(connection_id) was invoked.
+    bool DidMessageConnection(ConnectionSpecificId connection_id) const {
+      return message_ids_.count(connection_id) > 0;
+    }
+
+   private:
+    ConnectionManager* connection_manager_;
+    const ConnectionSpecificId connection_id_;
+    const bool is_delete_view_;
+
+    // See description of MarkConnectionAsMessaged/DidMessageConnection.
+    std::set<ConnectionSpecificId> message_ids_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedChange);
+  };
+
+  ConnectionManager(ApplicationConnection* app_connection,
+                    const Callback<void()>& native_viewport_closed_callback);
+  virtual ~ConnectionManager();
+
+  // Returns the id for the next ViewManagerServiceImpl.
+  ConnectionSpecificId GetAndAdvanceNextConnectionId();
+
+  void AddConnection(ViewManagerServiceImpl* connection);
+  void RemoveConnection(ViewManagerServiceImpl* connection);
+
+  // Establishes the initial client. Similar to Connect(), but the resulting
+  // client is allowed to do anything.
+  void EmbedRoot(const std::string& url,
+                 InterfaceRequest<ServiceProvider> service_provider);
+
+  // See description of ViewManagerService::Embed() for details. This assumes
+  // |transport_view_id| is valid.
+  void Embed(ConnectionSpecificId creator_id,
+             const String& url,
+             Id transport_view_id,
+             InterfaceRequest<ServiceProvider> service_provider);
+
+  // Returns the connection by id.
+  ViewManagerServiceImpl* GetConnection(ConnectionSpecificId connection_id);
+
+  // Returns the View identified by |id|.
+  ServerView* GetView(const ViewId& id);
+
+  ServerView* root() { return root_.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(ConnectionSpecificId id);
+
+  // Returns true if OnConnectionMessagedClient() was invoked for id.
+  bool DidConnectionMessageClient(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;
+
+  void DispatchViewInputEventToWindowManager(EventPtr event);
+
+  // 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 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 OrderDirection direction);
+  void ProcessViewDeleted(const ViewId& view);
+
+ private:
+  typedef std::map<ConnectionSpecificId, ViewManagerServiceImpl*> 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(ConnectionSpecificId connection_id) const {
+    return current_change_ && current_change_->connection_id() == connection_id;
+  }
+
+  // Implementation of the two embed variants.
+  ViewManagerServiceImpl* EmbedImpl(
+      ConnectionSpecificId creator_id,
+      const String& url,
+      const ViewId& root_id,
+      InterfaceRequest<ServiceProvider> service_provider);
+
+  // Overridden from ServerViewDelegate:
+  virtual void OnViewDestroyed(const ServerView* view) OVERRIDE;
+  virtual void OnWillChangeViewHierarchy(const ServerView* view,
+                                         const ServerView* new_parent,
+                                         const ServerView* old_parent) OVERRIDE;
+  virtual void OnViewHierarchyChanged(const ServerView* view,
+                                      const ServerView* new_parent,
+                                      const ServerView* old_parent) OVERRIDE;
+  virtual void OnViewBoundsChanged(const ServerView* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) OVERRIDE;
+  virtual void OnViewSurfaceIdChanged(const ServerView* view) OVERRIDE;
+  virtual void OnViewReordered(const ServerView* view,
+                               const ServerView* relative,
+                               OrderDirection direction) OVERRIDE;
+  virtual void OnWillChangeViewVisibility(const ServerView* view) OVERRIDE;
+
+  ApplicationConnection* app_connection_;
+
+  // ID to use for next ViewManagerServiceImpl.
+  ConnectionSpecificId next_connection_id_;
+
+  // Set of ViewManagerServiceImpls.
+  ConnectionMap connection_map_;
+
+  DisplayManager display_manager_;
+
+  scoped_ptr<ServerView> root_;
+
+  // Set of ViewManagerServiceImpls created by way of Connect(). These have to
+  // be explicitly destroyed.
+  std::set<ViewManagerServiceImpl*> connections_created_by_connect_;
+
+  // 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_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
diff --git a/mojo/services/view_manager/default_access_policy.cc b/mojo/services/view_manager/default_access_policy.cc
new file mode 100644
index 0000000..a2443c3
--- /dev/null
+++ b/mojo/services/view_manager/default_access_policy.cc
@@ -0,0 +1,106 @@
+// 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/default_access_policy.h"
+
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/server_view.h"
+
+namespace mojo {
+namespace service {
+
+DefaultAccessPolicy::DefaultAccessPolicy(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 IsViewInRoots(view->parent()) ||
+         WasCreatedByThisConnection(view->parent());
+}
+
+bool DefaultAccessPolicy::CanAddView(const ServerView* parent,
+                                     const ServerView* child) const {
+  return WasCreatedByThisConnection(child) &&
+         (IsViewInRoots(parent) ||
+          (WasCreatedByThisConnection(parent) &&
+           !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(parent)));
+}
+
+bool DefaultAccessPolicy::CanReorderView(const ServerView* view,
+                                         const ServerView* relative_view,
+                                         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) || IsViewInRoots(view);
+}
+
+bool DefaultAccessPolicy::CanDescendIntoViewForViewTree(
+    const ServerView* view) const {
+  return WasCreatedByThisConnection(view) &&
+         !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view);
+}
+
+bool DefaultAccessPolicy::CanEmbed(const ServerView* view) const {
+  return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanChangeViewVisibility(
+    const ServerView* view) const {
+  return WasCreatedByThisConnection(view) || IsViewInRoots(view);
+}
+
+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) || IsViewInRoots(view);
+}
+
+bool DefaultAccessPolicy::CanSetViewBounds(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) &&
+      !IsViewInRoots(*new_parent)) {
+    *new_parent = NULL;
+  }
+
+  if (*old_parent && !WasCreatedByThisConnection(*old_parent) &&
+      !IsViewInRoots(*old_parent)) {
+    *old_parent = NULL;
+  }
+  return true;
+}
+
+bool DefaultAccessPolicy::IsViewInRoots(const ServerView* view) const {
+  return delegate_->GetRootsForAccessPolicy().count(
+             ViewIdToTransportId(view->id())) > 0;
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/default_access_policy.h b/mojo/services/view_manager/default_access_policy.h
new file mode 100644
index 0000000..197736a
--- /dev/null
+++ b/mojo/services/view_manager/default_access_policy.h
@@ -0,0 +1,60 @@
+// 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_DEFAULT_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/view_manager/access_policy.h"
+
+namespace mojo {
+namespace service {
+
+class AccessPolicyDelegate;
+
+// AccessPolicy for all connections, except the window manager.
+class DefaultAccessPolicy : public AccessPolicy {
+ public:
+  DefaultAccessPolicy(ConnectionSpecificId connection_id,
+                      AccessPolicyDelegate* delegate);
+  virtual ~DefaultAccessPolicy();
+
+  // AccessPolicy:
+  virtual bool CanRemoveViewFromParent(const ServerView* view) const OVERRIDE;
+  virtual bool CanAddView(const ServerView* parent,
+                          const ServerView* child) const OVERRIDE;
+  virtual bool CanReorderView(const ServerView* view,
+                              const ServerView* relative_view,
+                              OrderDirection direction) const OVERRIDE;
+  virtual bool CanDeleteView(const ServerView* view) const OVERRIDE;
+  virtual bool CanGetViewTree(const ServerView* view) const OVERRIDE;
+  virtual bool CanDescendIntoViewForViewTree(
+      const ServerView* view) const OVERRIDE;
+  virtual bool CanEmbed(const ServerView* view) const OVERRIDE;
+  virtual bool CanChangeViewVisibility(const ServerView* view) const OVERRIDE;
+  virtual bool CanSetViewSurfaceId(const ServerView* view) const OVERRIDE;
+  virtual bool CanSetViewBounds(const ServerView* view) const OVERRIDE;
+  virtual bool ShouldNotifyOnHierarchyChange(
+      const ServerView* view,
+      const ServerView** new_parent,
+      const ServerView** old_parent) const OVERRIDE;
+
+ private:
+  bool IsViewInRoots(const ServerView* view) const;
+
+  template <typename T>
+  bool WasCreatedByThisConnection(const T* t) const {
+    return t->id().connection_id == connection_id_;
+  }
+
+  const ConnectionSpecificId connection_id_;
+  AccessPolicyDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultAccessPolicy);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
diff --git a/mojo/services/view_manager/display_manager.cc b/mojo/services/view_manager/display_manager.cc
new file mode 100644
index 0000000..435951b
--- /dev/null
+++ b/mojo/services/view_manager/display_manager.cc
@@ -0,0 +1,172 @@
+// 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/display_manager.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+#include "mojo/services/view_manager/connection_manager.h"
+
+namespace mojo {
+namespace service {
+namespace {
+
+gfx::Rect ConvertRectToRoot(const ServerView* view, const gfx::Rect& bounds) {
+  gfx::Point origin(bounds.origin());
+  while (view->parent()) {
+    origin += view->bounds().OffsetFromOrigin();
+    view = view->parent();
+    if (!view->visible())
+      return gfx::Rect();
+  }
+  return gfx::Rect(origin, bounds.size());
+}
+
+void DrawViewTree(Pass* pass, const ServerView* view, gfx::Vector2d offset) {
+  if (!view->visible())
+    return;
+
+  gfx::Rect node_bounds = view->bounds() + offset;
+  std::vector<const ServerView*> children(view->GetChildren());
+  for (std::vector<const ServerView*>::reverse_iterator it = children.rbegin();
+       it != children.rend();
+       ++it) {
+    DrawViewTree(pass, *it, offset + view->bounds().OffsetFromOrigin());
+  }
+
+  cc::SurfaceId node_id = view->surface_id();
+
+  SurfaceQuadStatePtr surface_quad_state = SurfaceQuadState::New();
+  surface_quad_state->surface = SurfaceId::From(node_id);
+
+  gfx::Transform node_transform;
+  node_transform.Translate(node_bounds.x(), node_bounds.y());
+
+  QuadPtr surface_quad = Quad::New();
+  surface_quad->material = Material::MATERIAL_SURFACE_CONTENT;
+  surface_quad->rect = Rect::From(node_bounds);
+  surface_quad->opaque_rect = Rect::From(node_bounds);
+  surface_quad->visible_rect = Rect::From(node_bounds);
+  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();
+
+  SharedQuadStatePtr sqs = CreateDefaultSQS(node_bounds.size());
+  sqs->content_to_target_transform = Transform::From(node_transform);
+
+  pass->quads.push_back(surface_quad.Pass());
+  pass->shared_quad_states.push_back(sqs.Pass());
+}
+
+}  // namespace
+
+DisplayManager::DisplayManager(
+    ApplicationConnection* app_connection,
+    ConnectionManager* connection_manager,
+    const Callback<void()>& native_viewport_closed_callback)
+    : connection_manager_(connection_manager),
+      in_setup_(false),
+      size_(800, 600),
+      draw_timer_(false, false),
+      weak_factory_(this) {
+  app_connection->ConnectToService("mojo:mojo_native_viewport_service",
+                                   &native_viewport_);
+  native_viewport_.set_client(this);
+  native_viewport_->Create(
+      Size::From(size_),
+      base::Bind(&DisplayManager::OnCreatedNativeViewport,
+                 weak_factory_.GetWeakPtr()));
+  native_viewport_->Show();
+  app_connection->ConnectToService("mojo:mojo_surfaces_service",
+                                   &surfaces_service_);
+  surfaces_service_->CreateSurfaceConnection(base::Bind(
+      &DisplayManager::OnSurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+}
+
+DisplayManager::~DisplayManager() {
+}
+
+void DisplayManager::SchedulePaint(const ServerView* view,
+                                   const gfx::Rect& bounds) {
+  if (!view->visible())
+    return;
+  gfx::Rect root_relative_rect = ConvertRectToRoot(view, bounds);
+  if (root_relative_rect.IsEmpty())
+    return;
+  dirty_rect_.Union(root_relative_rect);
+  if (!draw_timer_.IsRunning()) {
+    draw_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta(),
+        base::Bind(&DisplayManager::Draw, base::Unretained(this)));
+  }
+}
+
+void DisplayManager::OnCreatedNativeViewport(uint64_t native_viewport_id) {
+}
+
+void DisplayManager::OnSurfaceConnectionCreated(SurfacePtr surface,
+                                                uint32_t id_namespace) {
+  surface_ = surface.Pass();
+  surface_.set_client(this);
+  surface_id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+  Draw();
+}
+
+void DisplayManager::Draw() {
+  if (!surface_)
+    return;
+  if (surface_id_.is_null()) {
+    surface_id_ = surface_id_allocator_->GenerateId();
+    surface_->CreateSurface(SurfaceId::From(surface_id_), Size::From(size_));
+  }
+
+  PassPtr pass = CreateDefaultPass(1, gfx::Rect(size_));
+  pass->damage_rect = Rect::From(dirty_rect_);
+
+  DrawViewTree(pass.get(), connection_manager_->root(), gfx::Vector2d());
+
+  FramePtr frame = Frame::New();
+  frame->passes.push_back(pass.Pass());
+  frame->resources.resize(0u);
+  surface_->SubmitFrame(SurfaceId::From(surface_id_), frame.Pass());
+
+  native_viewport_->SubmittedFrame(SurfaceId::From(surface_id_));
+
+  dirty_rect_ = gfx::Rect();
+}
+
+void DisplayManager::OnDestroyed() {
+  native_viewport_closed_callback_.Run();
+}
+
+void DisplayManager::OnSizeChanged(SizePtr size) {
+  size_ = size.To<gfx::Size>();
+  connection_manager_->root()->SetBounds(gfx::Rect(size_));
+  if (surface_id_.is_null())
+    return;
+  surface_->DestroySurface(SurfaceId::From(surface_id_));
+  surface_id_ = cc::SurfaceId();
+  SchedulePaint(connection_manager_->root(), gfx::Rect(size_));
+}
+
+void DisplayManager::OnEvent(EventPtr event,
+                             const mojo::Callback<void()>& callback) {
+  connection_manager_->DispatchViewInputEventToWindowManager(event.Pass());
+  callback.Run();
+}
+
+void DisplayManager::ReturnResources(Array<ReturnedResourcePtr> resources) {
+  DCHECK_EQ(0u, resources.size());
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/display_manager.h b/mojo/services/view_manager/display_manager.h
new file mode 100644
index 0000000..21009f3
--- /dev/null
+++ b/mojo/services/view_manager/display_manager.h
@@ -0,0 +1,88 @@
+// 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_DISPLAY_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ConnectionManager;
+class ServerView;
+
+// DisplayManager binds the root node to an actual display.
+class MOJO_VIEW_MANAGER_EXPORT DisplayManager
+    : NON_EXPORTED_BASE(public NativeViewportClient),
+      NON_EXPORTED_BASE(public SurfaceClient) {
+ public:
+  DisplayManager(ApplicationConnection* app_connection,
+                 ConnectionManager* connection_manager,
+                 const Callback<void()>& native_viewport_closed_callback);
+  virtual ~DisplayManager();
+
+  // Schedules a paint for the specified region of the specified view.
+  void SchedulePaint(const ServerView* view, const gfx::Rect& bounds);
+
+  // See description above field for details.
+  bool in_setup() const { return in_setup_; }
+
+ private:
+  void OnCreatedNativeViewport(uint64_t native_viewport_id);
+  void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  void Draw();
+
+  // NativeViewportClient implementation.
+  virtual void OnDestroyed() OVERRIDE;
+  virtual void OnSizeChanged(SizePtr size) OVERRIDE;
+  virtual void OnEvent(EventPtr event,
+                       const mojo::Callback<void()>& callback) OVERRIDE;
+
+  // SurfaceClient implementation.
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+  ConnectionManager* connection_manager_;
+
+  // Returns true if adding the root view's window to |window_tree_host_|.
+  bool in_setup_;
+
+  gfx::Size size_;
+  gfx::Rect dirty_rect_;
+  base::Timer draw_timer_;
+
+  SurfacesServicePtr surfaces_service_;
+  SurfacePtr surface_;
+  scoped_ptr<cc::SurfaceIdAllocator> surface_id_allocator_;
+  cc::SurfaceId surface_id_;
+  NativeViewportPtr native_viewport_;
+  Callback<void()> native_viewport_closed_callback_;
+  base::WeakPtrFactory<DisplayManager> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayManager);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
diff --git a/mojo/services/view_manager/ids.h b/mojo/services/view_manager/ids.h
new file mode 100644
index 0000000..0d1c486
--- /dev/null
+++ b/mojo/services/view_manager/ids.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_IDS_H_
+#define MOJO_SERVICES_VIEW_MANAGER_IDS_H_
+
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+
+namespace mojo {
+namespace service {
+
+// Connection id is used to indicate no connection. That is, no
+// ViewManagerServiceImpl ever gets this id.
+const ConnectionSpecificId kInvalidConnectionId = 0;
+
+// TODO(sky): remove this, temporary while window manager API is in existing
+// api.
+const ConnectionSpecificId kWindowManagerConnection = 1;
+
+// Adds a bit of type safety to view ids.
+struct ViewId {
+  ViewId(ConnectionSpecificId connection_id, 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);
+  }
+
+  ConnectionSpecificId connection_id;
+  ConnectionSpecificId view_id;
+};
+
+inline ViewId ViewIdFromTransportId(Id id) {
+  return ViewId(HiWord(id), LoWord(id));
+}
+
+inline 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);
+}
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_IDS_H_
diff --git a/mojo/services/view_manager/main.cc b/mojo/services/view_manager/main.cc
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/mojo/services/view_manager/main.cc
@@ -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.
+
+#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/services/view_manager/view_manager_init_service_context.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+class ViewManagerApp : public ApplicationDelegate,
+                       public InterfaceFactory<ViewManagerInitService> {
+ public:
+  ViewManagerApp() {}
+  virtual ~ViewManagerApp() {}
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) OVERRIDE {
+    context_.ConfigureIncomingConnection(connection);
+    // TODO(sky): this needs some sort of authentication as well as making sure
+    // we only ever have one active at a time.
+    connection->AddService(this);
+    return true;
+  }
+
+  virtual void Create(
+      ApplicationConnection* connection,
+      InterfaceRequest<ViewManagerInitService> request) OVERRIDE {
+    BindToRequest(new ViewManagerInitServiceImpl(connection, &context_),
+                  &request);
+  }
+
+ private:
+  ViewManagerInitServiceContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerApp);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::service::ViewManagerApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/view_manager/server_view.cc b/mojo/services/view_manager/server_view.cc
new file mode 100644
index 0000000..8f5a559
--- /dev/null
+++ b/mojo/services/view_manager/server_view.cc
@@ -0,0 +1,144 @@
+// 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/server_view.h"
+
+#include "mojo/services/view_manager/server_view_delegate.h"
+
+namespace mojo {
+namespace service {
+
+ServerView::ServerView(ServerViewDelegate* delegate, const ViewId& id)
+    : delegate_(delegate), id_(id), parent_(NULL), visible_(true) {
+  DCHECK(delegate);  // Must provide a delegate.
+}
+
+ServerView::~ServerView() {
+  while (!children_.empty())
+    children_.front()->parent()->Remove(children_.front());
+
+  if (parent_)
+    parent_->Remove(this);
+
+  delegate_->OnViewDestroyed(this);
+}
+
+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(), ORDER_DIRECTION_ABOVE);
+    return;
+  }
+
+  const ServerView* old_parent = child->parent();
+  child->delegate_->OnWillChangeViewHierarchy(child, this, old_parent);
+  if (child->parent())
+    child->parent()->RemoveImpl(child);
+
+  child->parent_ = this;
+  children_.push_back(child);
+  child->delegate_->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_->OnWillChangeViewHierarchy(child, NULL, this);
+  RemoveImpl(child);
+  child->delegate_->OnViewHierarchyChanged(child, NULL, this);
+}
+
+void ServerView::Reorder(ServerView* child,
+                         ServerView* relative,
+                         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 == ORDER_DIRECTION_ABOVE) {
+    DCHECK(i != children_.end());
+    children_.insert(++i, child);
+  } else if (direction == ORDER_DIRECTION_BELOW) {
+    DCHECK(i != children_.end());
+    children_.insert(i, child);
+  }
+  delegate_->OnViewReordered(this, relative, direction);
+}
+
+void ServerView::SetBounds(const gfx::Rect& bounds) {
+  if (bounds_ == bounds)
+    return;
+
+  const gfx::Rect old_bounds = bounds_;
+  bounds_ = bounds;
+  delegate_->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_->OnWillChangeViewVisibility(this);
+  visible_ = 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_->OnViewSurfaceIdChanged(this);
+}
+
+void ServerView::RemoveImpl(ServerView* view) {
+  view->parent_ = NULL;
+  children_.erase(std::find(children_.begin(), children_.end(), view));
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/server_view.h b/mojo/services/view_manager/server_view.h
new file mode 100644
index 0000000..af7b37d
--- /dev/null
+++ b/mojo/services/view_manager/server_view.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 MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
+#define MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
+
+#include <vector>
+
+#include "base/logging.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+namespace service {
+
+class ServerViewDelegate;
+
+// 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.
+class MOJO_VIEW_MANAGER_EXPORT ServerView {
+ public:
+  ServerView(ServerViewDelegate* delegate, const ViewId& id);
+  virtual ~ServerView();
+
+  const ViewId& id() const { return id_; }
+
+  void Add(ServerView* child);
+  void Remove(ServerView* child);
+  void Reorder(ServerView* child,
+               ServerView* relative,
+               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);
+
+  // 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_; }
+
+ 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_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerView);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
diff --git a/mojo/services/view_manager/server_view_delegate.h b/mojo/services/view_manager/server_view_delegate.h
new file mode 100644
index 0000000..24e5f8f
--- /dev/null
+++ b/mojo/services/view_manager/server_view_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_SERVER_VIEW_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
+
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace service {
+
+class ServerView;
+
+class MOJO_VIEW_MANAGER_EXPORT ServerViewDelegate {
+ public:
+  // Invoked at the end of the View's destructor (after it has been removed from
+  // the hierarchy).
+  virtual void OnViewDestroyed(const ServerView* view) = 0;
+
+  virtual void OnWillChangeViewHierarchy(const ServerView* view,
+                                         const ServerView* new_parent,
+                                         const ServerView* old_parent) = 0;
+
+  virtual void OnViewHierarchyChanged(const ServerView* view,
+                                      const ServerView* new_parent,
+                                      const ServerView* old_parent) = 0;
+
+  virtual void OnViewBoundsChanged(const ServerView* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) = 0;
+
+  virtual void OnViewSurfaceIdChanged(const ServerView* view) = 0;
+
+  virtual void OnViewReordered(const ServerView* view,
+                               const ServerView* relative,
+                               OrderDirection direction) = 0;
+
+  virtual void OnWillChangeViewVisibility(const ServerView* view) = 0;
+
+ protected:
+  virtual ~ServerViewDelegate() {}
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc
new file mode 100644
index 0000000..bf95ac5
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.cc
@@ -0,0 +1,244 @@
+// 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/test_change_tracker.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+
+namespace mojo {
+namespace service {
+
+std::string ViewIdToString(Id id) {
+  return (id == 0) ? "null" :
+      base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+namespace {
+
+std::string RectToString(const gfx::Rect& rect) {
+  return base::StringPrintf("%d,%d %dx%d", rect.x(), rect.y(), rect.width(),
+                            rect.height());
+}
+
+std::string DirectionToString(OrderDirection direction) {
+  return direction == ORDER_DIRECTION_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_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_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(),
+                                change.event_action);
+
+    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 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;
+  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(0),
+      direction(ORDER_DIRECTION_ABOVE),
+      bool_value(false) {
+}
+
+Change::~Change() {
+}
+
+TestChangeTracker::TestChangeTracker()
+    : delegate_(NULL) {
+}
+
+TestChangeTracker::~TestChangeTracker() {
+}
+
+void TestChangeTracker::OnEmbed(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::OnViewBoundsChanged(Id view_id,
+                                            RectPtr old_bounds,
+                                            RectPtr new_bounds) {
+  Change change;
+  change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+  change.view_id = view_id;
+  change.bounds = old_bounds.To<gfx::Rect>();
+  change.bounds2 = new_bounds.To<gfx::Rect>();
+  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,
+                                        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, EventPtr event) {
+  Change change;
+  change.type = CHANGE_TYPE_INPUT_EVENT;
+  change.view_id = view_id;
+  change.event_action = event->action;
+  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();
+}
+
+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 service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h
new file mode 100644
index 0000000..791c8ad
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.h
@@ -0,0 +1,133 @@
+// 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_TEST_CHANGE_TRACKER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace service {
+
+enum ChangeType {
+  CHANGE_TYPE_EMBED,
+  // TODO(sky): NODE->VIEW.
+  CHANGE_TYPE_NODE_BOUNDS_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_DELEGATE_EMBED,
+};
+
+// TODO(sky): consider nuking and converting directly to ViewData.
+struct TestView {
+  // Returns a string description of this.
+  std::string ToString() const;
+
+  // Returns a string description that includes visible and drawn.
+  std::string ToString2() const;
+
+  Id parent_id;
+  Id view_id;
+  bool visible;
+  bool drawn;
+};
+
+// Tracks a call to ViewManagerClient. See the individual functions for the
+// fields that are used.
+struct Change {
+  Change();
+  ~Change();
+
+  ChangeType type;
+  ConnectionSpecificId connection_id;
+  std::vector<TestView> views;
+  Id view_id;
+  Id view_id2;
+  Id view_id3;
+  gfx::Rect bounds;
+  gfx::Rect bounds2;
+  int32 event_action;
+  String creator_url;
+  String embed_url;
+  OrderDirection direction;
+  bool bool_value;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+    const std::vector<Change>& changes);
+
+// 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 Array<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(ConnectionSpecificId connection_id,
+               const String& creator_url,
+               ViewDataPtr root);
+  void OnViewBoundsChanged(Id view_id, RectPtr old_bounds, RectPtr new_bounds);
+  void OnViewHierarchyChanged(Id view_id,
+                              Id new_parent_id,
+                              Id old_parent_id,
+                              Array<ViewDataPtr> views);
+  void OnViewReordered(Id view_id,
+                       Id relative_view_id,
+                       OrderDirection direction);
+  void OnViewDeleted(Id view_id);
+  void OnViewVisibilityChanged(Id view_id, bool visible);
+  void OnViewDrawnStateChanged(Id view_id, bool drawn);
+  void OnViewInputEvent(Id view_id, EventPtr event);
+  void DelegateEmbed(const String& url);
+
+ private:
+  void AddChange(const Change& change);
+
+  Delegate* delegate_;
+  std::vector<Change> changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
diff --git a/mojo/services/view_manager/view_manager_export.h b/mojo/services/view_manager/view_manager_export.h
new file mode 100644
index 0000000..29b0e62
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_export.h
@@ -0,0 +1,31 @@
+// 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_VIEW_MANAGER_EXPORT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_VIEW_MANAGER_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // defined(COMPONENT_BUILD)
+#define MOJO_VIEW_MANAGER_EXPORT
+#endif
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
diff --git a/mojo/services/view_manager/view_manager_init_service_context.cc b/mojo/services/view_manager/view_manager_init_service_context.cc
new file mode 100644
index 0000000..79a7067
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_context.cc
@@ -0,0 +1,78 @@
+// 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/view_manager_init_service_context.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "mojo/services/view_manager/connection_manager.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerInitServiceContext::ConnectParams::ConnectParams() {}
+
+ViewManagerInitServiceContext::ConnectParams::~ConnectParams() {}
+
+ViewManagerInitServiceContext::ViewManagerInitServiceContext()
+    : deleting_connection_(false) {
+}
+ViewManagerInitServiceContext::~ViewManagerInitServiceContext() {}
+
+void ViewManagerInitServiceContext::AddConnection(
+    ViewManagerInitServiceImpl* connection) {
+  DCHECK(std::find(connections_.begin(), connections_.end(), connection) ==
+                   connections_.end());
+  connections_.push_back(connection);
+}
+
+void ViewManagerInitServiceContext::RemoveConnection(
+    ViewManagerInitServiceImpl* connection) {
+  if (!deleting_connection_) {
+    Connections::iterator it =
+        std::find(connections_.begin(), connections_.end(), connection);
+    DCHECK(it != connections_.end());
+    connections_.erase(it);
+  }
+
+  // This object is owned by an object that outlives the current thread's
+  // message loop, so we need to destroy the ConnectionManager earlier, as it
+  // may attempt to post tasks during its destruction.
+  if (connections_.empty())
+    connection_manager_.reset();
+}
+
+void ViewManagerInitServiceContext::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  if (!connection_manager_.get()) {
+    connection_manager_.reset(new ConnectionManager(
+        connection,
+        base::Bind(&ViewManagerInitServiceContext::OnNativeViewportDeleted,
+                   base::Unretained(this))));
+  }
+}
+
+void ViewManagerInitServiceContext::Embed(
+    const String& url,
+    ServiceProviderPtr service_provider,
+    const Callback<void(bool)>& callback) {
+  connection_manager_->EmbedRoot(url, Get(&service_provider));
+  callback.Run(true);
+}
+
+void ViewManagerInitServiceContext::OnNativeViewportDeleted() {
+  // Prevent the connection from modifying the connection list during manual
+  // teardown.
+  base::AutoReset<bool> deleting_connection(&deleting_connection_, true);
+  for (Connections::const_iterator it = connections_.begin();
+       it != connections_.end(); ++it) {
+    delete *it;
+  }
+  connections_.clear();
+  connection_manager_.reset();
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_init_service_context.h b/mojo/services/view_manager/view_manager_init_service_context.h
new file mode 100644
index 0000000..5019c68
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_context.h
@@ -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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_CONTEXT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_CONTEXT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace service {
+
+class ConnectionManager;
+class ViewManagerInitServiceImpl;
+
+// State shared between all ViewManagerInitService impls.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceContext {
+ public:
+  ViewManagerInitServiceContext();
+  virtual ~ViewManagerInitServiceContext();
+
+  void AddConnection(ViewManagerInitServiceImpl* connection);
+  void RemoveConnection(ViewManagerInitServiceImpl* connection);
+
+  void ConfigureIncomingConnection(ApplicationConnection* connection);
+
+  void Embed(const String& url,
+             ServiceProviderPtr service_provider,
+             const Callback<void(bool)>& callback);
+
+  ConnectionManager* connection_manager() { return connection_manager_.get(); }
+
+ private:
+  typedef std::vector<ViewManagerInitServiceImpl*> Connections;
+
+  struct ConnectParams {
+    ConnectParams();
+    ~ConnectParams();
+
+    std::string url;
+    InterfaceRequest<ServiceProvider> service_provider;
+    Callback<void(bool)> callback;
+  };
+
+  void OnNativeViewportDeleted();
+
+  scoped_ptr<ConnectionManager> connection_manager_;
+  Connections connections_;
+
+  bool deleting_connection_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceContext);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_CONTEXT_H_
diff --git a/mojo/services/view_manager/view_manager_init_service_impl.cc b/mojo/services/view_manager/view_manager_init_service_impl.cc
new file mode 100644
index 0000000..ab30718
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_impl.cc
@@ -0,0 +1,34 @@
+// 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/view_manager_init_service_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_init_service_context.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerInitServiceImpl::ViewManagerInitServiceImpl(
+    ApplicationConnection* connection,
+    ViewManagerInitServiceContext* context)
+    : context_(context) {
+  context_->AddConnection(this);
+}
+
+ViewManagerInitServiceImpl::~ViewManagerInitServiceImpl() {
+  context_->RemoveConnection(this);
+}
+
+void ViewManagerInitServiceImpl::Embed(
+    const String& url,
+    ServiceProviderPtr service_provider,
+    const Callback<void(bool)>& callback) {
+  context_->Embed(url, service_provider.Pass(), callback);
+}
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_init_service_impl.h b/mojo/services/view_manager/view_manager_init_service_impl.h
new file mode 100644
index 0000000..49cc628
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/connection_manager.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ViewManagerInitServiceContext;
+
+#if defined(OS_WIN)
+// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu
+// below.
+#pragma warning(push)
+#pragma warning(disable : 4275)
+#endif
+
+// Used to create the initial ViewManagerClient. Doesn't initiate the Connect()
+// until the WindowTreeHost has been created.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl
+    : public InterfaceImpl<ViewManagerInitService> {
+ public:
+  ViewManagerInitServiceImpl(ApplicationConnection* connection,
+                             ViewManagerInitServiceContext* context);
+  virtual ~ViewManagerInitServiceImpl();
+
+ private:
+  // ViewManagerInitService overrides:
+  virtual void Embed(const String& url,
+                     ServiceProviderPtr service_provider,
+                     const Callback<void(bool)>& callback) OVERRIDE;
+
+  ViewManagerInitServiceContext* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc
new file mode 100644
index 0000000..ad0df79
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_service_impl.cc
@@ -0,0 +1,564 @@
+// 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/view_manager_service_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/view_manager/connection_manager.h"
+#include "mojo/services/view_manager/default_access_policy.h"
+#include "mojo/services/view_manager/server_view.h"
+#include "mojo/services/view_manager/window_manager_access_policy.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerServiceImpl::ViewManagerServiceImpl(
+    ConnectionManager* connection_manager,
+    ConnectionSpecificId creator_id,
+    const std::string& creator_url,
+    const std::string& url,
+    const ViewId& root_id,
+    InterfaceRequest<ServiceProvider> service_provider)
+    : connection_manager_(connection_manager),
+      id_(connection_manager_->GetAndAdvanceNextConnectionId()),
+      url_(url),
+      creator_id_(creator_id),
+      creator_url_(creator_url),
+      delete_on_connection_error_(false),
+      service_provider_(service_provider.Pass()) {
+  CHECK(GetView(root_id));
+  roots_.insert(ViewIdToTransportId(root_id));
+  if (root_id == RootViewId())
+    access_policy_.reset(new WindowManagerAccessPolicy(id_, this));
+  else
+    access_policy_.reset(new DefaultAccessPolicy(id_, this));
+}
+
+ViewManagerServiceImpl::~ViewManagerServiceImpl() {
+  // Delete any views we created.
+  if (!view_map_.empty()) {
+    ConnectionManager::ScopedChange change(this, connection_manager_, true);
+    while (!view_map_.empty())
+      delete view_map_.begin()->second;
+  }
+
+  connection_manager_->RemoveConnection(this);
+}
+
+const ServerView* ViewManagerServiceImpl::GetView(const ViewId& id) const {
+  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::HasRoot(const ViewId& id) const {
+  return roots_.find(ViewIdToTransportId(id)) != roots_.end();
+}
+
+void ViewManagerServiceImpl::OnViewManagerServiceImplDestroyed(
+    ConnectionSpecificId id) {
+  if (creator_id_ == id)
+    creator_id_ = kInvalidConnectionId;
+}
+
+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()),
+                                Rect::From(old_bounds),
+                                Rect::From(new_bounds));
+}
+
+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::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) {
+  view_map_.erase(view.view_id);
+
+  const bool in_known = known_views_.erase(ViewIdToTransportId(view)) > 0;
+  roots_.erase(ViewIdToTransportId(view));
+
+  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);
+}
+
+void ViewManagerServiceImpl::OnConnectionError() {
+  if (delete_on_connection_error_)
+    delete this;
+}
+
+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 == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
+      (direction == ORDER_DIRECTION_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(const ViewId& view_id) {
+  const Id transport_view_id(ViewIdToTransportId(view_id));
+  CHECK(roots_.count(transport_view_id) > 0);
+
+  roots_.erase(transport_view_id);
+
+  // No need to do anything if we created the view.
+  if (view_id.connection_id == id_)
+    return;
+
+  client()->OnViewDeleted(transport_view_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(view_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) {
+  Array<ViewDataPtr> array(views.size());
+  for (size_t i = 0; i < views.size(); ++i)
+    array[i] = ViewToViewData(views[i]).Pass();
+  return array.Pass();
+}
+
+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(ViewData::New());
+  view_data->parent_id = ViewIdToTransportId(parent ? parent->id() : ViewId());
+  view_data->view_id = ViewIdToTransportId(view->id());
+  view_data->bounds = Rect::From(view->bounds());
+  view_data->visible = view->visible();
+  view_data->drawn = view->IsDrawn(connection_manager_->root());
+  return view_data.Pass();
+}
+
+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 one of our
+  // roots, in which case the change may effect our roots drawn state.
+  for (ViewIdSet::iterator i = roots_.begin(); i != roots_.end(); ++i) {
+    const ServerView* root = GetView(ViewIdFromTransportId(*i));
+    DCHECK(root);
+    if (view->Contains(root) &&
+        (new_drawn_value != root->IsDrawn(connection_manager_->root()))) {
+      client()->OnViewDrawnStateChanged(ViewIdToTransportId(root->id()),
+                                        new_drawn_value);
+    }
+  }
+}
+
+void ViewManagerServiceImpl::CreateView(
+    Id transport_view_id,
+    const Callback<void(ErrorCode)>& callback) {
+  const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+  ErrorCode error_code = ERROR_CODE_NONE;
+  if (view_id.connection_id != id_) {
+    error_code = ERROR_CODE_ILLEGAL_ARGUMENT;
+  } else if (view_map_.find(view_id.view_id) != view_map_.end()) {
+    error_code = ERROR_CODE_VALUE_IN_USE;
+  } else {
+    view_map_[view_id.view_id] = new ServerView(connection_manager_, view_id);
+    known_views_.insert(transport_view_id);
+  }
+  callback.Run(error_code);
+}
+
+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) {
+  bool success = false;
+  ServerView* parent = GetView(ViewIdFromTransportId(parent_id));
+  ServerView* child = GetView(ViewIdFromTransportId(child_id));
+  if (parent && child && child->parent() != parent &&
+      !child->Contains(parent) && access_policy_->CanAddView(parent, child)) {
+    success = true;
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    parent->Add(child);
+  }
+  callback.Run(success);
+}
+
+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) {
+  ServerView* view = GetView(ViewIdFromTransportId(view_id));
+  std::vector<const ServerView*> views;
+  if (view) {
+    GetViewTreeImpl(view, &views);
+    // TODO(sky): this should map in views that weren't none.
+  }
+  callback.Run(ViewsToViewDatas(views));
+}
+
+void ViewManagerServiceImpl::SetViewSurfaceId(
+    Id view_id,
+    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,
+    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) {
+  ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+  if (!view || view->visible() == visible ||
+      !access_policy_->CanChangeViewVisibility(view)) {
+    callback.Run(false);
+    return;
+  }
+  {
+    ConnectionManager::ScopedChange change(this, connection_manager_, false);
+    view->SetVisible(visible);
+  }
+  callback.Run(true);
+}
+
+void ViewManagerServiceImpl::Embed(
+    const String& url,
+    Id transport_view_id,
+    ServiceProviderPtr service_provider,
+    const Callback<void(bool)>& callback) {
+  InterfaceRequest<ServiceProvider> spir;
+  spir.Bind(service_provider.PassMessagePipe());
+
+  if (ViewIdFromTransportId(transport_view_id) == InvalidViewId()) {
+    connection_manager_->EmbedRoot(url, spir.Pass());
+    callback.Run(true);
+    return;
+  }
+  const ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+  if (!view || !access_policy_->CanEmbed(view)) {
+    callback.Run(false);
+    return;
+  }
+
+  // Only allow a node to be the root for one connection.
+  const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+  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(view_id);
+  }
+  connection_manager_->Embed(id_, url, transport_view_id, spir.Pass());
+  callback.Run(true);
+}
+
+void ViewManagerServiceImpl::DispatchOnViewInputEvent(Id transport_view_id,
+                                                      EventPtr event) {
+  // We only allow the WM to dispatch events. At some point this function will
+  // move to a separate interface and the check can go away.
+  if (id_ != kWindowManagerConnection)
+    return;
+
+  const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+
+  // If another app is embedded at this view, we forward the input event to the
+  // embedded app, rather than the app that created the view.
+  ViewManagerServiceImpl* connection =
+      connection_manager_->GetConnectionWithRoot(view_id);
+  if (!connection)
+    connection = connection_manager_->GetConnection(view_id.connection_id);
+  if (connection) {
+    connection->client()->OnViewInputEvent(
+        transport_view_id,
+        event.Pass(),
+        base::Bind(&base::DoNothing));
+  }
+}
+
+void ViewManagerServiceImpl::OnConnectionEstablished() {
+  connection_manager_->AddConnection(this);
+
+  std::vector<const ServerView*> to_send;
+  for (ViewIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i)
+    GetUnknownViewsFrom(GetView(ViewIdFromTransportId(*i)), &to_send);
+
+  client()->OnEmbed(id_,
+                    creator_url_,
+                    ViewToViewData(to_send.front()),
+                    service_provider_.Pass());
+}
+
+const base::hash_set<Id>&
+ViewManagerServiceImpl::GetRootsForAccessPolicy() const {
+  return roots_;
+}
+
+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 service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_service_impl.h b/mojo/services/view_manager/view_manager_service_impl.h
new file mode 100644
index 0000000..0295119
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_service_impl.h
@@ -0,0 +1,240 @@
+// 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_VIEW_MANAGER_SERVICE_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace service {
+
+class AccessPolicy;
+class ConnectionManager;
+class ServerView;
+
+#if defined(OS_WIN)
+// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu
+// below.
+#pragma warning(push)
+#pragma warning(disable : 4275)
+#endif
+
+// Manages a connection from the client.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl
+    : public InterfaceImpl<ViewManagerService>,
+      public AccessPolicyDelegate {
+ public:
+  ViewManagerServiceImpl(ConnectionManager* connection_manager,
+                         ConnectionSpecificId creator_id,
+                         const std::string& creator_url,
+                         const std::string& url,
+                         const ViewId& root_id,
+                         InterfaceRequest<ServiceProvider> service_provider);
+  virtual ~ViewManagerServiceImpl();
+
+  // Used to mark this connection as originating from a call to
+  // ViewManagerService::Connect(). When set OnConnectionError() deletes |this|.
+  void set_delete_on_connection_error() { delete_on_connection_error_ = true; }
+
+  ConnectionSpecificId id() const { return id_; }
+  ConnectionSpecificId creator_id() const { return creator_id_; }
+  const std::string& url() const { return url_; }
+
+  // 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 has |id| as a root.
+  bool HasRoot(const ViewId& id) const;
+
+  // Invoked when a connection is destroyed.
+  void OnViewManagerServiceImplDestroyed(ConnectionSpecificId id);
+
+  // 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 ProcessWillChangeViewHierarchy(const ServerView* view,
+                                      const ServerView* new_parent,
+                                      const ServerView* old_parent,
+                                      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,
+                          OrderDirection direction,
+                          bool originated_change);
+  void ProcessViewDeleted(const ViewId& view, bool originated_change);
+  void ProcessWillChangeViewVisibility(const ServerView* view,
+                                       bool originated_change);
+
+  // TODO(sky): move this to private section (currently can't because of
+  // bindings).
+  // InterfaceImp overrides:
+  virtual void OnConnectionError() override;
+
+ private:
+  typedef std::map<ConnectionSpecificId, ServerView*> ViewMap;
+  typedef base::hash_set<Id> ViewIdSet;
+
+  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,
+                      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);
+
+  // Removes |view_id| from the set of roots this connection knows about.
+  void RemoveRoot(const ViewId& view_id);
+
+  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)).
+  Array<ViewDataPtr> ViewsToViewDatas(
+      const std::vector<const ServerView*>& views);
+  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);
+
+  // ViewManagerService:
+  virtual void CreateView(Id transport_view_id,
+                          const Callback<void(ErrorCode)>& callback) OVERRIDE;
+  virtual void DeleteView(Id transport_view_id,
+                          const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void AddView(Id parent_id,
+                       Id child_id,
+                       const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void RemoveViewFromParent(
+      Id view_id,
+      const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void ReorderView(Id view_id,
+                           Id relative_view_id,
+                           OrderDirection direction,
+                           const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void GetViewTree(
+      Id view_id,
+      const Callback<void(Array<ViewDataPtr>)>& callback) OVERRIDE;
+  virtual void SetViewSurfaceId(Id view_id,
+                                SurfaceIdPtr surface_id,
+                                const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void SetViewBounds(Id view_id,
+                             RectPtr bounds,
+                             const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void SetViewVisibility(Id view_id,
+                                 bool visible,
+                                 const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void Embed(const String& url,
+                     Id view_id,
+                     ServiceProviderPtr service_provider,
+                     const Callback<void(bool)>& callback) OVERRIDE;
+  virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE;
+
+  // InterfaceImpl:
+  virtual void OnConnectionEstablished() override;
+
+  // AccessPolicyDelegate:
+  virtual const base::hash_set<Id>& GetRootsForAccessPolicy() const OVERRIDE;
+  virtual bool IsViewKnownForAccessPolicy(
+      const ServerView* view) const OVERRIDE;
+  virtual bool IsViewRootOfAnotherConnectionForAccessPolicy(
+      const ServerView* view) const OVERRIDE;
+
+  ConnectionManager* connection_manager_;
+
+  // Id of this connection as assigned by ConnectionManager.
+  const 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.
+  ConnectionSpecificId creator_id_;
+
+  // The URL of the app that embedded the app this connection was created for.
+  const std::string creator_url_;
+
+  scoped_ptr<AccessPolicy> access_policy_;
+
+  // The views and 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_;
+
+  // Set of root views from other connections. More specifically any time
+  // Embed() is invoked the id of the view is added to this set for the child
+  // connection. The connection Embed() was invoked on (the parent) doesn't
+  // directly track which connections are attached to which of its views. That
+  // information can be found by looking through the |roots_| of all
+  // connections.
+  ViewIdSet roots_;
+
+  // See description above setter.
+  bool delete_on_connection_error_;
+
+  InterfaceRequest<ServiceProvider> service_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/view_manager_unittest.cc b/mojo/services/view_manager/view_manager_unittest.cc
new file mode 100644
index 0000000..cc972d4
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_unittest.cc
@@ -0,0 +1,1434 @@
+// 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/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/common/common_type_converters.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/bindings/lib/router.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/test_change_tracker.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+#if defined(OS_WIN)
+#include "ui/gfx/win/window_impl.h"
+#endif
+
+namespace mojo {
+namespace service {
+
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+const char kTestServiceURL2[] = "mojo:test_url2";
+
+// ViewManagerProxy is a proxy to an ViewManagerService. It handles invoking
+// ViewManagerService functions on the right thread in a synchronous manner
+// (each ViewManagerService cover function blocks until the response from the
+// ViewManagerService is returned). In addition it tracks the set of
+// ViewManagerClient messages received by way of a vector of Changes. Use
+// DoRunLoopUntilChangesCount() to wait for a certain number of messages to be
+// received.
+class ViewManagerProxy : public TestChangeTracker::Delegate {
+ public:
+  explicit ViewManagerProxy(TestChangeTracker* tracker)
+      : tracker_(tracker),
+        main_loop_(NULL),
+        view_manager_(NULL),
+        quit_count_(0),
+        router_(NULL) {
+    SetInstance(this);
+  }
+
+  virtual ~ViewManagerProxy() {
+  }
+
+  // Returns true if in an initial state. If this returns false it means the
+  // last test didn't clean up properly, or most likely didn't invoke
+  // WaitForInstance() when it needed to.
+  static bool IsInInitialState() { return instance_ == NULL; }
+
+  // Runs a message loop until the single instance has been created.
+  static ViewManagerProxy* WaitForInstance() {
+    if (!instance_)
+      RunMainLoop();
+    ViewManagerProxy* instance = instance_;
+    instance_ = NULL;
+    return instance;
+  }
+
+  ViewManagerService* view_manager() { return view_manager_; }
+
+  // Runs the main loop until |count| changes have been received.
+  std::vector<Change> DoRunLoopUntilChangesCount(size_t count) {
+    DCHECK_EQ(0u, quit_count_);
+    if (tracker_->changes()->size() >= count) {
+      CopyChangesFromTracker();
+      return changes_;
+    }
+    quit_count_ = count - tracker_->changes()->size();
+    // Run the current message loop. When |count| Changes have been received,
+    // we'll quit.
+    RunMainLoop();
+    return changes_;
+  }
+
+  const std::vector<Change>& changes() const { return changes_; }
+
+  // Destroys the connection, blocking until done.
+  void Destroy() {
+    router_->CloseMessagePipe();
+  }
+
+  void ClearChanges() {
+    changes_.clear();
+    tracker_->changes()->clear();
+  }
+
+  void CopyChangesFromTracker() {
+    std::vector<Change> changes;
+    tracker_->changes()->swap(changes);
+    changes_.swap(changes);
+  }
+
+  // The following functions are cover methods for ViewManagerService. They
+  // block until the result is received.
+  bool CreateView(Id view_id) {
+    changes_.clear();
+    ErrorCode result = ERROR_CODE_NONE;
+    view_manager_->CreateView(
+        view_id,
+        base::Bind(&ViewManagerProxy::GotResultWithErrorCode,
+                   base::Unretained(this),
+                   &result));
+    RunMainLoop();
+    return result == ERROR_CODE_NONE;
+  }
+  ErrorCode CreateViewWithErrorCode(Id view_id) {
+    changes_.clear();
+    ErrorCode result = ERROR_CODE_NONE;
+    view_manager_->CreateView(
+        view_id,
+        base::Bind(&ViewManagerProxy::GotResultWithErrorCode,
+                   base::Unretained(this),
+                   &result));
+    RunMainLoop();
+    return result;
+  }
+  bool AddView(Id parent, Id child) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->AddView(parent, child,
+                           base::Bind(&ViewManagerProxy::GotResult,
+                                      base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  bool RemoveViewFromParent(Id view_id) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->RemoveViewFromParent(
+        view_id,
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  bool ReorderView(Id view_id, Id relative_view_id, OrderDirection direction) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->ReorderView(
+        view_id,
+        relative_view_id,
+        direction,
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  void GetViewTree(Id view_id, std::vector<TestView>* views) {
+    changes_.clear();
+    view_manager_->GetViewTree(
+        view_id,
+        base::Bind(
+            &ViewManagerProxy::GotViewTree, base::Unretained(this), views));
+    RunMainLoop();
+  }
+  bool Embed(const Id view_id, const char* url) {
+    changes_.clear();
+    base::AutoReset<bool> auto_reset(&in_embed_, true);
+    bool result = false;
+    ServiceProviderPtr services;
+    view_manager_->Embed(
+        url,
+        view_id,
+        services.Pass(),
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  bool DeleteView(Id view_id) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->DeleteView(
+        view_id,
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  bool SetViewBounds(Id view_id, const gfx::Rect& bounds) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->SetViewBounds(
+        view_id,
+        Rect::From(bounds),
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+  bool SetViewVisibility(Id view_id, bool visible) {
+    changes_.clear();
+    bool result = false;
+    view_manager_->SetViewVisibility(
+        view_id,
+        visible,
+        base::Bind(
+            &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+    RunMainLoop();
+    return result;
+  }
+
+ private:
+  friend class TestViewManagerClientConnection;
+
+  void set_router(mojo::internal::Router* router) { router_ = router; }
+
+  void set_view_manager(ViewManagerService* view_manager) {
+    view_manager_ = view_manager;
+  }
+
+  static void RunMainLoop() {
+    DCHECK(!main_run_loop_);
+    main_run_loop_ = new base::RunLoop;
+    main_run_loop_->Run();
+    delete main_run_loop_;
+    main_run_loop_ = NULL;
+  }
+
+  void QuitCountReached() {
+    CopyChangesFromTracker();
+    main_run_loop_->Quit();
+  }
+
+  static void SetInstance(ViewManagerProxy* instance) {
+    DCHECK(!instance_);
+    instance_ = instance;
+    // Embed() runs its own run loop that is quit when the result is
+    // received. Embed() also results in a new instance. If we quit here while
+    // waiting for a Embed() we would prematurely return before we got the
+    // result from Embed().
+    if (!in_embed_ && main_run_loop_)
+      main_run_loop_->Quit();
+  }
+
+  // Callbacks from the various ViewManagerService functions.
+  void GotResult(bool* result_cache, bool result) {
+    *result_cache = result;
+    DCHECK(main_run_loop_);
+    main_run_loop_->Quit();
+  }
+
+  void GotResultWithErrorCode(ErrorCode* error_code_cache,
+                              ErrorCode error_code) {
+    *error_code_cache = error_code;
+    DCHECK(main_run_loop_);
+    main_run_loop_->Quit();
+  }
+
+  void GotViewTree(std::vector<TestView>* views, Array<ViewDataPtr> results) {
+    ViewDatasToTestViews(results, views);
+    DCHECK(main_run_loop_);
+    main_run_loop_->Quit();
+  }
+
+  // TestChangeTracker::Delegate:
+  virtual void OnChangeAdded() OVERRIDE {
+    if (quit_count_ > 0 && --quit_count_ == 0)
+      QuitCountReached();
+  }
+
+  static ViewManagerProxy* instance_;
+  static base::RunLoop* main_run_loop_;
+  static bool in_embed_;
+
+  TestChangeTracker* tracker_;
+
+  // MessageLoop of the test.
+  base::MessageLoop* main_loop_;
+
+  ViewManagerService* view_manager_;
+
+  // Number of changes we're waiting on until we quit the current loop.
+  size_t quit_count_;
+
+  std::vector<Change> changes_;
+
+  mojo::internal::Router* router_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerProxy);
+};
+
+// static
+ViewManagerProxy* ViewManagerProxy::instance_ = NULL;
+
+// static
+base::RunLoop* ViewManagerProxy::main_run_loop_ = NULL;
+
+// static
+bool ViewManagerProxy::in_embed_ = false;
+
+class TestViewManagerClientConnection
+    : public InterfaceImpl<ViewManagerClient> {
+ public:
+  TestViewManagerClientConnection() : connection_(&tracker_) {
+    tracker_.set_delegate(&connection_);
+  }
+
+  // InterfaceImpl:
+  virtual void OnConnectionEstablished() OVERRIDE {
+    connection_.set_router(internal_state()->router());
+    connection_.set_view_manager(client());
+  }
+
+  // ViewManagerClient:
+  virtual void OnEmbed(
+      ConnectionSpecificId connection_id,
+      const String& creator_url,
+      ViewDataPtr root,
+      InterfaceRequest<ServiceProvider> services) OVERRIDE {
+    tracker_.OnEmbed(connection_id, creator_url, root.Pass());
+  }
+  virtual void OnViewBoundsChanged(Id view_id,
+                                   RectPtr old_bounds,
+                                   RectPtr new_bounds) OVERRIDE {
+    tracker_.OnViewBoundsChanged(view_id, old_bounds.Pass(), new_bounds.Pass());
+  }
+  virtual void OnViewHierarchyChanged(Id view,
+                                      Id new_parent,
+                                      Id old_parent,
+                                      Array<ViewDataPtr> views) OVERRIDE {
+    tracker_.OnViewHierarchyChanged(view, new_parent, old_parent, views.Pass());
+  }
+  virtual void OnViewReordered(Id view_id,
+                               Id relative_view_id,
+                               OrderDirection direction) OVERRIDE {
+    tracker_.OnViewReordered(view_id, relative_view_id, direction);
+  }
+  virtual void OnViewDeleted(Id view) OVERRIDE { tracker_.OnViewDeleted(view); }
+  virtual void OnViewVisibilityChanged(uint32_t view, bool visible) OVERRIDE {
+    tracker_.OnViewVisibilityChanged(view, visible);
+  }
+  virtual void OnViewDrawnStateChanged(uint32_t view, bool drawn) OVERRIDE {
+    tracker_.OnViewDrawnStateChanged(view, drawn);
+  }
+  virtual void OnViewInputEvent(Id view_id,
+                                EventPtr event,
+                                const Callback<void()>& callback) OVERRIDE {
+    tracker_.OnViewInputEvent(view_id, event.Pass());
+  }
+  virtual void Embed(
+      const String& url,
+      InterfaceRequest<ServiceProvider> service_provider) OVERRIDE {
+    tracker_.DelegateEmbed(url);
+  }
+  virtual void DispatchOnViewInputEvent(mojo::EventPtr event) OVERRIDE {
+  }
+
+ private:
+  TestChangeTracker tracker_;
+  ViewManagerProxy connection_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestViewManagerClientConnection);
+};
+
+// Used with ViewManagerService::Embed(). Creates a
+// TestViewManagerClientConnection, which creates and owns the ViewManagerProxy.
+class EmbedApplicationLoader : public ApplicationLoader,
+                               ApplicationDelegate,
+                               public InterfaceFactory<ViewManagerClient> {
+ public:
+  EmbedApplicationLoader() {}
+  virtual ~EmbedApplicationLoader() {}
+
+  // ApplicationLoader implementation:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
+    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+    if (!shell_handle.is_valid())
+      return;
+    scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
+                                                        shell_handle.Pass()));
+    apps_.push_back(app.release());
+  }
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) OVERRIDE {}
+
+  // ApplicationDelegate implementation:
+  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+      OVERRIDE {
+    connection->AddService(this);
+    return true;
+  }
+
+  // InterfaceFactory<ViewManagerClient> implementation:
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<ViewManagerClient> request) OVERRIDE {
+    BindToRequest(new TestViewManagerClientConnection, &request);
+  }
+
+ private:
+  ScopedVector<ApplicationImpl> apps_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbedApplicationLoader);
+};
+
+// 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 from Embed(). |result| is the result of the
+// Embed() call and |run_loop| the nested RunLoop.
+void EmbedCallback(bool* result_cache, base::RunLoop* run_loop, bool result) {
+  *result_cache = result;
+  run_loop->Quit();
+}
+
+// Embed from an application that does not yet have a view manager connection.
+// Blocks until result is determined.
+bool InitEmbed(ViewManagerInitService* view_manager_init,
+               const std::string& url,
+               size_t number_of_calls) {
+  bool result = false;
+  base::RunLoop run_loop;
+  for (size_t i = 0; i < number_of_calls; ++i) {
+    ServiceProviderPtr sp;
+    view_manager_init->Embed(url, sp.Pass(),
+                             base::Bind(&EmbedCallback, &result, &run_loop));
+  }
+  run_loop.Run();
+  return result;
+}
+
+}  // namespace
+
+typedef std::vector<std::string> Changes;
+
+class ViewManagerTest : public testing::Test {
+ public:
+  ViewManagerTest()
+      : connection_(NULL),
+        connection2_(NULL),
+        connection3_(NULL) {}
+
+  virtual void SetUp() OVERRIDE {
+    ASSERT_TRUE(ViewManagerProxy::IsInInitialState());
+    test_helper_.Init();
+
+#if defined(OS_WIN)
+    // As we unload the wndproc of window classes we need to be sure to
+    // unregister them.
+    gfx::WindowImpl::UnregisterClassesAtExit();
+#endif
+
+    test_helper_.SetLoaderForURL(
+        scoped_ptr<ApplicationLoader>(new EmbedApplicationLoader()),
+        GURL(kTestServiceURL));
+
+    test_helper_.SetLoaderForURL(
+        scoped_ptr<ApplicationLoader>(new EmbedApplicationLoader()),
+        GURL(kTestServiceURL2));
+
+    test_helper_.application_manager()->ConnectToService(
+        GURL("mojo:mojo_view_manager"), &view_manager_init_);
+    ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 1));
+
+    connection_ = ViewManagerProxy::WaitForInstance();
+    ASSERT_TRUE(connection_ != NULL);
+    connection_->DoRunLoopUntilChangesCount(1);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    if (connection3_)
+      connection3_->Destroy();
+    if (connection2_)
+      connection2_->Destroy();
+    if (connection_)
+      connection_->Destroy();
+  }
+
+ protected:
+  void EstablishSecondConnectionWithRoot(Id root_id) {
+    ASSERT_TRUE(connection_->Embed(root_id, kTestServiceURL));
+    connection2_ = ViewManagerProxy::WaitForInstance();
+    ASSERT_TRUE(connection2_ != NULL);
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+  }
+
+  // Creates a second connection to the viewmanager.
+  void EstablishSecondConnection(bool create_initial_view) {
+    if (create_initial_view)
+      ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+    ASSERT_NO_FATAL_FAILURE(
+        EstablishSecondConnectionWithRoot(BuildViewId(1, 1)));
+    const std::vector<Change>& changes(connection2_->changes());
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("OnEmbed creator=mojo:test_url",
+              ChangesToDescription1(changes)[0]);
+    if (create_initial_view)
+      EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(changes));
+  }
+
+  void EstablishThirdConnection(ViewManagerProxy* owner, Id root_id) {
+    ASSERT_TRUE(connection3_ == NULL);
+    ASSERT_TRUE(owner->Embed(root_id, kTestServiceURL2));
+    connection3_ = ViewManagerProxy::WaitForInstance();
+    ASSERT_TRUE(connection3_ != NULL);
+    connection3_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection3_->changes().size());
+    EXPECT_EQ("OnEmbed creator=mojo:test_url",
+              ChangesToDescription1(connection3_->changes())[0]);
+  }
+
+  void DestroySecondConnection() {
+    connection2_->Destroy();
+    connection2_ = NULL;
+  }
+
+  base::ShadowingAtExitManager at_exit_;
+  shell::ShellTestHelper test_helper_;
+
+  ViewManagerInitServicePtr view_manager_init_;
+
+  // NOTE: this connection is the root. As such, it has special permissions.
+  ViewManagerProxy* connection_;
+  ViewManagerProxy* connection2_;
+  ViewManagerProxy* connection3_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+TEST_F(ViewManagerTest, SecondEmbedRoot_InitService) {
+  ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 1));
+  connection_->DoRunLoopUntilChangesCount(1);
+  EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+}
+
+TEST_F(ViewManagerTest, SecondEmbedRoot_Service) {
+  ASSERT_TRUE(connection_->Embed(BuildViewId(0, 0), kTestServiceURL));
+  connection_->DoRunLoopUntilChangesCount(1);
+  EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+}
+
+TEST_F(ViewManagerTest, MultipleEmbedRootsBeforeWTHReady) {
+  ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 2));
+  connection_->DoRunLoopUntilChangesCount(2);
+  EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+  EXPECT_EQ(kTestServiceURL, connection_->changes()[1].embed_url);
+}
+
+// Verifies client gets a valid id.
+// http://crbug.com/396492
+TEST_F(ViewManagerTest, DISABLED_ValidId) {
+  // TODO(beng): this should really have the URL of the application that
+  //             connected to ViewManagerInit.
+  EXPECT_EQ("OnEmbed creator=",
+            ChangesToDescription1(connection_->changes())[0]);
+
+  // 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.
+  EXPECT_EQ(1, connection_->changes()[0].connection_id);
+}
+
+// Verifies two clients/connections get different ids.
+TEST_F(ViewManagerTest, TwoClientsGetDifferentConnectionIds) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  EXPECT_EQ("OnEmbed creator=mojo:test_url",
+            ChangesToDescription1(connection2_->changes())[0]);
+
+  // 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.
+  EXPECT_EQ(2, connection2_->changes()[0].connection_id);
+}
+
+// Verifies when Embed() is invoked any child views are removed.
+TEST_F(ViewManagerTest, ViewsRemovedWhenEmbedding) {
+  // Two views 1 and 2. 2 is parented to 1.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+  EXPECT_EQ("[view=1,1 parent=null]",
+            ChangeViewDescription(connection2_->changes()));
+
+  // Embed() removed view 2.
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(BuildViewId(1, 2), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,2 parent=null", views[0].ToString());
+  }
+
+  // |connection2_| should not see view 2.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+  }
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(1, 2), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Views 3 and 4 in connection 2.
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 4)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 3), BuildViewId(2, 4)));
+
+  // Connection 3 rooted at 2.
+  ASSERT_NO_FATAL_FAILURE(
+      EstablishThirdConnection(connection2_, BuildViewId(2, 3)));
+
+  // View 4 should no longer have a parent.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(2, 3), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=2,3 parent=null", views[0].ToString());
+
+    views.clear();
+    connection2_->GetViewTree(BuildViewId(2, 4), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=2,4 parent=null", views[0].ToString());
+  }
+
+  // And view 4 should not be visible to connection 3.
+  {
+    std::vector<TestView> views;
+    connection3_->GetViewTree(BuildViewId(2, 3), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=2,3 parent=null", views[0].ToString());
+  }
+}
+
+// Verifies once Embed() has been invoked the parent connection can't see any
+// children.
+TEST_F(ViewManagerTest, CantAccessChildrenOfEmbeddedView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(
+      EstablishThirdConnection(connection2_, BuildViewId(2, 2)));
+
+  ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 3)));
+  ASSERT_TRUE(connection3_->AddView(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;
+    connection2_->GetViewTree(BuildViewId(2, 2), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=2,2 parent=1,1", views[0].ToString());
+  }
+
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(3, 3), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Connection 2 shouldn't be able to get view 3 at all.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(3, 3), &views);
+    EXPECT_TRUE(views.empty());
+  }
+
+  // Connection 1 should be able to see it all (its the root).
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(ViewManagerTest, CantModifyChildrenOfEmbeddedView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(
+      EstablishThirdConnection(connection2_, BuildViewId(2, 2)));
+
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+  // Connection 2 shouldn't be able to add anything to the view anymore.
+  ASSERT_FALSE(connection2_->AddView(BuildViewId(2, 2), BuildViewId(2, 3)));
+
+  // Create view 3 in connection 3 and add it to view 3.
+  ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 3)));
+  ASSERT_TRUE(connection3_->AddView(BuildViewId(2, 2), BuildViewId(3, 3)));
+
+  // Connection 2 shouldn't be able to remove view 3.
+  ASSERT_FALSE(connection2_->RemoveViewFromParent(BuildViewId(3, 3)));
+}
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerTest, CreateView) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  EXPECT_TRUE(connection_->changes().empty());
+
+  // Can't create a view with the same id.
+  ASSERT_EQ(ERROR_CODE_VALUE_IN_USE,
+            connection_->CreateViewWithErrorCode(BuildViewId(1, 1)));
+  EXPECT_TRUE(connection_->changes().empty());
+
+  // Can't create a view with a bogus connection id.
+  EXPECT_EQ(ERROR_CODE_ILLEGAL_ARGUMENT,
+            connection_->CreateViewWithErrorCode(BuildViewId(2, 1)));
+  EXPECT_TRUE(connection_->changes().empty());
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerTest, AddViewWithNoChange) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Make 3 a child of 2.
+  ASSERT_TRUE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 3)));
+
+  // Try again, this should fail.
+  EXPECT_FALSE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 3)));
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerTest, AddAncestorFails) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Make 3 a child of 2.
+  ASSERT_TRUE(connection_->AddView(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(connection_->AddView(BuildViewId(1, 3), BuildViewId(1, 2)));
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(ViewManagerTest, AddToRoot) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 21)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Make 3 a child of 21.
+  ASSERT_TRUE(connection_->AddView(BuildViewId(1, 21), BuildViewId(1, 3)));
+
+  // Make 21 a child of 1.
+  ASSERT_TRUE(connection_->AddView(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(connection2_->CreateView(BuildViewId(2, 100)));
+  connection2_->CopyChangesFromTracker();
+  EXPECT_TRUE(connection2_->changes().empty());
+}
+
+// Verifies HierarchyChanged is correctly sent for various adds/removes.
+TEST_F(ViewManagerTest, ViewHierarchyChangedViews) {
+  // 1,2->1,11.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 11)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // 1,1->1,2->1,11
+  {
+    // Client 2 should not get anything (1,2 is from another connection).
+    connection2_->ClearChanges();
+    ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+    ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 100)));
+    connection2_->CopyChangesFromTracker();
+    EXPECT_TRUE(connection2_->changes().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(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // 1,1->1,2->1,11.
+  {
+    // Client 2 is no longer connected to the root, should get drawn state
+    // changed.
+    ASSERT_TRUE(connection_->RemoveViewFromParent(BuildViewId(1, 1)));
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=false",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // 1,1->1,2->1,11->1,111.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 111)));
+  {
+    connection2_->ClearChanges();
+    ASSERT_TRUE(connection_->AddView(BuildViewId(1, 11), BuildViewId(1, 111)));
+    ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 103)));
+    connection2_->CopyChangesFromTracker();
+    EXPECT_TRUE(connection2_->changes().empty());
+  }
+
+  // 0,1->1,1->1,2->1,11->1,111
+  {
+    connection2_->ClearChanges();
+    ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+}
+
+TEST_F(ViewManagerTest, ViewHierarchyChangedAddingKnownToUnknown) {
+  // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
+  // parent).
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11)));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 21)));
+
+  // Set up the hierarchy.
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 11)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 2), BuildViewId(2, 21)));
+
+  // Remove 11, should result in a hierarchy change for the root.
+  {
+    connection_->ClearChanges();
+    ASSERT_TRUE(connection2_->RemoveViewFromParent(BuildViewId(2, 11)));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("HierarchyChanged view=2,11 new_parent=null old_parent=1,1",
+              changes[0]);
+  }
+
+  // Add 2 to 1.
+  {
+    ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              changes[0]);
+    EXPECT_EQ(
+        "[view=2,2 parent=1,1],"
+        "[view=2,21 parent=2,2]",
+        ChangeViewDescription(connection_->changes()));
+  }
+}
+
+TEST_F(ViewManagerTest, 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(connection2_->CreateView(view1_id));
+  ASSERT_TRUE(connection2_->CreateView(view2_id));
+  ASSERT_TRUE(connection2_->CreateView(view3_id));
+  ASSERT_TRUE(connection_->CreateView(view4_id));
+  ASSERT_TRUE(connection_->CreateView(view5_id));
+  ASSERT_TRUE(connection2_->CreateView(view6_id));
+  ASSERT_TRUE(connection2_->CreateView(view7_id));
+  ASSERT_TRUE(connection2_->CreateView(view8_id));
+  ASSERT_TRUE(connection2_->AddView(view1_id, view2_id));
+  ASSERT_TRUE(connection2_->AddView(view2_id, view6_id));
+  ASSERT_TRUE(connection2_->AddView(view1_id, view3_id));
+  ASSERT_TRUE(
+      connection_->AddView(ViewIdToTransportId(RootViewId()), view4_id));
+  ASSERT_TRUE(
+      connection_->AddView(ViewIdToTransportId(RootViewId()), view5_id));
+
+  ASSERT_TRUE(
+      connection_->AddView(ViewIdToTransportId(RootViewId()), view1_id));
+
+  {
+    ASSERT_TRUE(
+        connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_ABOVE));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=above", changes[0]);
+  }
+
+  {
+    ASSERT_TRUE(
+        connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_BELOW));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=below", changes[0]);
+  }
+
+  // view2 is already below view3.
+  EXPECT_FALSE(
+      connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_BELOW));
+
+  // view4 & 5 are unknown to connection2_.
+  EXPECT_FALSE(
+      connection2_->ReorderView(view4_id, view5_id, ORDER_DIRECTION_ABOVE));
+
+  // view6 & view3 have different parents.
+  EXPECT_FALSE(
+      connection_->ReorderView(view3_id, view6_id, ORDER_DIRECTION_ABOVE));
+
+  // Non-existent view-ids
+  EXPECT_FALSE(connection_->ReorderView(
+      BuildViewId(1, 27), BuildViewId(1, 28), ORDER_DIRECTION_ABOVE));
+
+  // view7 & view8 are un-parented.
+  EXPECT_FALSE(
+      connection_->ReorderView(view7_id, view8_id, ORDER_DIRECTION_ABOVE));
+}
+
+// Verifies DeleteView works.
+TEST_F(ViewManagerTest, DeleteView) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+
+  // Make 2 a child of 1.
+  {
+    ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              changes[0]);
+  }
+
+  // Delete 2.
+  {
+    ASSERT_TRUE(connection2_->DeleteView(BuildViewId(2, 2)));
+    EXPECT_TRUE(connection2_->changes().empty());
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("ViewDeleted view=2,2", changes[0]);
+  }
+}
+
+// Verifies DeleteView isn't allowed from a separate connection.
+TEST_F(ViewManagerTest, DeleteViewFromAnotherConnectionDisallowed) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  EXPECT_FALSE(connection2_->DeleteView(BuildViewId(1, 1)));
+}
+
+// Verifies if a view was deleted and then reused that other clients are
+// properly notified.
+TEST_F(ViewManagerTest, ReuseDeletedViewId) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+
+  // Add 2 to 1.
+  {
+    ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              changes[0]);
+    EXPECT_EQ("[view=2,2 parent=1,1]",
+              ChangeViewDescription(connection_->changes()));
+  }
+
+  // Delete 2.
+  {
+    ASSERT_TRUE(connection2_->DeleteView(BuildViewId(2, 2)));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("ViewDeleted view=2,2", changes[0]);
+  }
+
+  // Create 2 again, and add it back to 1. Should get the same notification.
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+  {
+    ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+              changes[0]);
+    EXPECT_EQ("[view=2,2 parent=1,1]",
+              ChangeViewDescription(connection_->changes()));
+  }
+}
+
+// Assertions for GetViewTree.
+TEST_F(ViewManagerTest, GetViewTree) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  // Create 11 in first connection and make it a child of 1.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 11)));
+
+  // Create two views in second connection, 2 and 3, both children of 1.
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 3)));
+
+  // Verifies GetViewTree() on the root. The root connection sees all.
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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. This does not include any children
+  // as they are not from this connection.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+  }
+
+  // Connection 2 shouldn't be able to get the root tree.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(0, 1), &views);
+    ASSERT_EQ(0u, views.size());
+  }
+}
+
+TEST_F(ViewManagerTest, SetViewBounds) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  ASSERT_TRUE(
+      connection_->SetViewBounds(BuildViewId(1, 1), gfx::Rect(0, 0, 100, 100)));
+
+  connection2_->DoRunLoopUntilChangesCount(1);
+  const Changes changes(ChangesToDescription1(connection2_->changes()));
+  ASSERT_EQ(1u, changes.size());
+  EXPECT_EQ("BoundsChanged view=1,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100",
+            changes[0]);
+
+  // Should not be possible to change the bounds of a view created by another
+  // connection.
+  ASSERT_FALSE(
+      connection2_->SetViewBounds(BuildViewId(1, 1), gfx::Rect(0, 0, 0, 0)));
+}
+
+// Verify AddView fails when trying to manipulate views in other roots.
+TEST_F(ViewManagerTest, CantMoveViewsFromOtherRoot) {
+  // Create 1 and 2 in the first connection.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(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(connection2_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+
+  // Try to reparent 1 to the root. A connection is not allowed to reparent its
+  // roots.
+  ASSERT_FALSE(connection2_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+}
+
+// Verify RemoveViewFromParent fails for views that are descendants of the
+// roots.
+TEST_F(ViewManagerTest, CantRemoveViewsInOtherRoots) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->AddView(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(connection2_->RemoveViewFromParent(BuildViewId(1, 2)));
+  ASSERT_FALSE(connection2_->RemoveViewFromParent(BuildViewId(1, 1)));
+
+  // Create views 10 and 11 in 2.
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 10)));
+  ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11)));
+
+  // Parent 11 to 10.
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 10), BuildViewId(2, 11)));
+  // Remove 11 from 10.
+  ASSERT_TRUE(connection2_->RemoveViewFromParent(BuildViewId(2, 11)));
+
+  // Verify nothing was actually removed.
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(ViewManagerTest, CantGetViewTreeOfOtherRoots) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 2)));
+
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  std::vector<TestView> views;
+
+  // Should get nothing for the root.
+  connection2_->GetViewTree(BuildViewId(0, 1), &views);
+  ASSERT_TRUE(views.empty());
+
+  // Should get nothing for view 2.
+  connection2_->GetViewTree(BuildViewId(1, 2), &views);
+  ASSERT_TRUE(views.empty());
+
+  // Should get view 1 if asked for.
+  connection2_->GetViewTree(BuildViewId(1, 1), &views);
+  ASSERT_EQ(1u, views.size());
+  EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+}
+
+TEST_F(ViewManagerTest, OnViewInput) {
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+  // Dispatch an event to the view and verify its received.
+  {
+    EventPtr event(Event::New());
+    event->action = static_cast<EventType>(1);
+    connection_->view_manager()->DispatchOnViewInputEvent(BuildViewId(1, 1),
+                                                          event.Pass());
+    connection2_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection2_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("InputEvent view=1,1 event_action=1", changes[0]);
+  }
+}
+
+TEST_F(ViewManagerTest, EmbedWithSameViewId) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_NO_FATAL_FAILURE(
+      EstablishThirdConnection(connection_, BuildViewId(1, 1)));
+
+  // Connection2 should have been told the view was deleted.
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection2_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("ViewDeleted view=1,1", changes[0]);
+  }
+
+  // Connection2 has no root. Verify it can't see view 1,1 anymore.
+  {
+    std::vector<TestView> views;
+    connection2_->GetViewTree(BuildViewId(1, 1), &views);
+    EXPECT_TRUE(views.empty());
+  }
+}
+
+TEST_F(ViewManagerTest, EmbedWithSameViewId2) {
+  ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+  ASSERT_NO_FATAL_FAILURE(
+      EstablishThirdConnection(connection_, BuildViewId(1, 1)));
+
+  // Connection2 should have been told the view was deleted.
+  connection2_->DoRunLoopUntilChangesCount(1);
+  connection2_->ClearChanges();
+
+  // Create a view in the third connection and parent it to the root.
+  ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 1)));
+  ASSERT_TRUE(connection3_->AddView(BuildViewId(1, 1), BuildViewId(3, 1)));
+
+  // Connection 1 should have been told about the add (it owns the view).
+  {
+    connection_->DoRunLoopUntilChangesCount(1);
+    const Changes changes(ChangesToDescription1(connection_->changes()));
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("HierarchyChanged view=3,1 new_parent=1,1 old_parent=null",
+              changes[0]);
+  }
+
+  // Embed 1,1 again.
+  {
+    // We should get a new connection for the new embedding.
+    ASSERT_TRUE(connection_->Embed(BuildViewId(1, 1), kTestServiceURL));
+    ViewManagerProxy* connection4 = ViewManagerProxy::WaitForInstance();
+    connection4->DoRunLoopUntilChangesCount(1);
+    const std::vector<Change>& changes(connection4->changes());
+    ASSERT_EQ(1u, changes.size());
+    EXPECT_EQ("OnEmbed creator=mojo:test_url",
+              ChangesToDescription1(changes)[0]);
+    EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(changes));
+
+    // And 3 should get a delete.
+    connection3_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection3_->changes().size());
+    EXPECT_EQ("ViewDeleted view=1,1",
+              ChangesToDescription1(connection3_->changes())[0]);
+  }
+
+  // Connection3_ has no root. Verify it can't see view 1,1 anymore.
+  {
+    std::vector<TestView> views;
+    connection3_->GetViewTree(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
+  // connection3_ can no longer see 1,1.
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(BuildViewId(1, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+  }
+
+  // Verify connection3_ can still see the view it created 3,1.
+  {
+    std::vector<TestView> views;
+    connection3_->GetViewTree(BuildViewId(3, 1), &views);
+    ASSERT_EQ(1u, views.size());
+    EXPECT_EQ("view=3,1 parent=null", views[0].ToString());
+  }
+}
+
+// Assertions for SetViewVisibility.
+TEST_F(ViewManagerTest, SetViewVisibility) {
+  // Create 1 and 2 in the first connection and parent both to the root.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(connection_->SetViewVisibility(BuildViewId(1, 1), false));
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(connection_->SetViewVisibility(BuildViewId(1, 1), true));
+  {
+    std::vector<TestView> views;
+    connection_->GetViewTree(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(ViewManagerTest, SetViewVisibilityNotifications) {
+  // Create 1,1 and 1,2, 1,2 and child of 1,1 and 1,1 a child of the root.
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  ASSERT_TRUE(connection_->AddView(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(connection2_->CreateView(BuildViewId(2, 3)));
+  connection_->ClearChanges();
+  ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 2), BuildViewId(2, 3)));
+  connection_->DoRunLoopUntilChangesCount(1);
+
+  // Hide 1,2 from connection 1. Connection 2 should see this.
+  ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 2), false));
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("VisibilityChanged view=1,2 visible=false",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // Show 1,2 from connection 2, connection 1 should be notified.
+  ASSERT_TRUE(connection2_->SetViewVisibility(BuildViewId(1, 2), true));
+  {
+    connection_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection_->changes().size());
+    EXPECT_EQ("VisibilityChanged view=1,2 visible=true",
+              ChangesToDescription1(connection_->changes())[0]);
+  }
+
+  // Hide 1,1, connection 2 should be told the draw state changed.
+  ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), false));
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // Show 1,1 from connection 1. Connection 2 should see this.
+  ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), true));
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // Change visibility of 2,3, connection 1 should see this.
+  connection_->ClearChanges();
+  ASSERT_TRUE(connection2_->SetViewVisibility(BuildViewId(2, 3), false));
+  {
+    connection_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection_->changes().size());
+    EXPECT_EQ("VisibilityChanged view=2,3 visible=false",
+              ChangesToDescription1(connection_->changes())[0]);
+  }
+
+  // Remove 1,1 from the root, connection 2 should see drawn state changed.
+  ASSERT_TRUE(connection_->RemoveViewFromParent(BuildViewId(1, 1)));
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+
+  // Add 1,1 back to the root, connection 2 should see drawn state changed.
+  ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+  {
+    connection2_->DoRunLoopUntilChangesCount(1);
+    ASSERT_EQ(1u, connection2_->changes().size());
+    EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+              ChangesToDescription1(connection2_->changes())[0]);
+  }
+}
+
+// TODO(sky): add coverage of test that destroys connections and ensures other
+// connections get deletion notification.
+
+// 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.
+
+}  // namespace service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/window_manager_access_policy.cc b/mojo/services/view_manager/window_manager_access_policy.cc
new file mode 100644
index 0000000..f781534
--- /dev/null
+++ b/mojo/services/view_manager/window_manager_access_policy.cc
@@ -0,0 +1,92 @@
+// 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/window_manager_access_policy.h"
+
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/server_view.h"
+
+namespace mojo {
+namespace service {
+
+// TODO(sky): document why this differs from default for each case. Maybe want
+// to subclass DefaultAccessPolicy.
+
+WindowManagerAccessPolicy::WindowManagerAccessPolicy(
+    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,
+                                               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 true;
+}
+
+bool WindowManagerAccessPolicy::CanDescendIntoViewForViewTree(
+    const ServerView* view) const {
+  return true;
+}
+
+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_->GetRootsForAccessPolicy().count(
+              ViewIdToTransportId(view->id())) > 0);
+}
+
+bool WindowManagerAccessPolicy::CanSetViewBounds(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 {
+  // 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 service
+}  // namespace mojo
diff --git a/mojo/services/view_manager/window_manager_access_policy.h b/mojo/services/view_manager/window_manager_access_policy.h
new file mode 100644
index 0000000..5730a18
--- /dev/null
+++ b/mojo/services/view_manager/window_manager_access_policy.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 MOJO_SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/view_manager/access_policy.h"
+
+namespace mojo {
+namespace service {
+
+class AccessPolicyDelegate;
+
+class WindowManagerAccessPolicy : public AccessPolicy {
+ public:
+  WindowManagerAccessPolicy(ConnectionSpecificId connection_id,
+                            AccessPolicyDelegate* delegate);
+  virtual ~WindowManagerAccessPolicy();
+
+  // AccessPolicy:
+  virtual bool CanRemoveViewFromParent(const ServerView* view) const OVERRIDE;
+  virtual bool CanAddView(const ServerView* parent,
+                          const ServerView* child) const OVERRIDE;
+  virtual bool CanReorderView(const ServerView* view,
+                              const ServerView* relative_view,
+                              OrderDirection direction) const OVERRIDE;
+  virtual bool CanDeleteView(const ServerView* view) const OVERRIDE;
+  virtual bool CanGetViewTree(const ServerView* view) const OVERRIDE;
+  virtual bool CanDescendIntoViewForViewTree(
+      const ServerView* view) const OVERRIDE;
+  virtual bool CanEmbed(const ServerView* view) const OVERRIDE;
+  virtual bool CanChangeViewVisibility(const ServerView* view) const OVERRIDE;
+  virtual bool CanSetViewSurfaceId(const ServerView* view) const OVERRIDE;
+  virtual bool CanSetViewBounds(const ServerView* view) const OVERRIDE;
+  virtual bool ShouldNotifyOnHierarchyChange(
+      const ServerView* view,
+      const ServerView** new_parent,
+      const ServerView** old_parent) const OVERRIDE;
+
+ private:
+  bool IsViewKnown(const ServerView* view) const;
+
+  const ConnectionSpecificId connection_id_;
+  AccessPolicyDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerAccessPolicy);
+};
+
+}  // namespace service
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
diff --git a/mojo/services/window_manager/BUILD.gn b/mojo/services/window_manager/BUILD.gn
new file mode 100644
index 0000000..b439775
--- /dev/null
+++ b/mojo/services/window_manager/BUILD.gn
@@ -0,0 +1,80 @@
+# 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")
+
+if (use_aura) {
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager
+shared_library("window_manager") {
+  output_name = "mojo_core_window_manager"
+
+  sources = [ "main.cc" ]
+
+  public_deps = [
+    ":lib",
+  ]
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_lib
+source_set("lib") {
+  sources = [
+    "window_manager_app.cc",
+    "window_manager_app.h",
+    "window_manager_service_impl.cc",
+    "window_manager_service_impl.h",
+  ]
+
+  public_deps = [
+    "//mojo/aura",
+  ]
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/window_manager",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/events",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/wm",
+  ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_unittests
+test("mojo_core_window_manager_unittests") {
+  sources = [
+    "window_manager_api_unittest.cc",
+    "window_manager_unittests.cc",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//mojo/application_manager",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/services/public/interfaces/window_manager",
+    "//mojo/shell:test_support",
+    "//testing/gtest",
+    "//ui/gl",
+  ]
+  if (use_x11) {
+    deps += [ "//ui/gfx/x" ]
+  }
+}
+
+}  # use_aura
diff --git a/mojo/services/window_manager/DEPS b/mojo/services/window_manager/DEPS
new file mode 100644
index 0000000..3421f3b
--- /dev/null
+++ b/mojo/services/window_manager/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+  "+mojo/aura",
+  "+mojo/application",
+  "+mojo/application_manager",
+  "+mojo/services/native_viewport",
+  "+mojo/services/public",
+  "+ui/aura",
+  "+ui/base",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/gl",
+  "+ui/wm",
+]
diff --git a/mojo/services/window_manager/main.cc b/mojo/services/window_manager/main.cc
new file mode 100644
index 0000000..ea0edb1
--- /dev/null
+++ b/mojo/services/window_manager/main.cc
@@ -0,0 +1,77 @@
+// 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/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/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+
+// ApplicationDelegate implementation file for WindowManager users (e.g.
+// core window manager tests) that do not want to provide their own
+// ApplicationDelegate::Create().
+
+namespace mojo {
+
+class DefaultWindowManager : public ApplicationDelegate,
+                             public ViewManagerDelegate,
+                             public WindowManagerDelegate {
+ public:
+  DefaultWindowManager()
+      : window_manager_app_(new WindowManagerApp(this, this)),
+        view_manager_(NULL),
+        root_(NULL) {}
+  virtual ~DefaultWindowManager() {}
+
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* impl) override {
+    window_manager_app_->Initialize(impl);
+  }
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    window_manager_app_->ConfigureIncomingConnection(connection);
+    return true;
+  }
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    view_manager_ = view_manager;
+    root_ = root;
+    view_manager_->SetWindowManagerDelegate(this);
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+  // Overridden from WindowManagerDelegate:
+  virtual void Embed(
+      const String& url,
+      InterfaceRequest<ServiceProvider> service_provider) override {
+    View* view = View::Create(view_manager_);
+    root_->AddChild(view);
+    view->Embed(url, scoped_ptr<mojo::ServiceProviderImpl>(
+        new mojo::ServiceProviderImpl).Pass());
+  }
+  virtual void DispatchEvent(EventPtr event) override {}
+
+  scoped_ptr<WindowManagerApp> window_manager_app_;
+
+  ViewManager* view_manager_;
+  View* root_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManager);
+};
+
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::DefaultWindowManager);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/window_manager/window_manager_api_unittest.cc b/mojo/services/window_manager/window_manager_api_unittest.cc
new file mode 100644
index 0000000..49bd98e
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_api_unittest.cc
@@ -0,0 +1,278 @@
+// 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/memory/scoped_vector.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+
+void EmptyResultCallback(bool result) {}
+
+// Callback from Embed(). |result| is the result of the Embed() call and
+// |run_loop| the nested RunLoop.
+void ResultCallback(bool* result_cache, base::RunLoop* run_loop, bool result) {
+  *result_cache = result;
+  run_loop->Quit();
+}
+
+// Responsible for establishing the initial ViewManagerService connection.
+// Blocks until result is determined.
+bool InitEmbed(ViewManagerInitService* view_manager_init,
+               const std::string& url) {
+  bool result = false;
+  base::RunLoop run_loop;
+  ServiceProviderPtr sp;
+  BindToProxy(new ServiceProviderImpl, &sp);
+  view_manager_init->Embed(url, sp.Pass(),
+                           base::Bind(&ResultCallback, &result, &run_loop));
+  run_loop.Run();
+  return result;
+}
+
+class TestWindowManagerClient : public WindowManagerClient {
+ public:
+  typedef base::Callback<void(Id, Id)>
+      TwoNodeCallback;
+
+  explicit TestWindowManagerClient(base::RunLoop* run_loop)
+      : run_loop_(run_loop) {}
+  virtual ~TestWindowManagerClient() {}
+
+  void set_focus_changed_callback(const TwoNodeCallback& callback) {
+    focus_changed_callback_ = callback;
+  }
+  void set_active_window_changed_callback(const TwoNodeCallback& callback) {
+    active_window_changed_callback_ = callback;
+  }
+
+ private:
+  // Overridden from WindowManagerClient:
+  virtual void OnWindowManagerReady() override { run_loop_->Quit(); }
+  virtual void OnCaptureChanged(Id old_capture_node_id,
+                                Id new_capture_node_id) override {}
+  virtual void OnFocusChanged(Id old_focused_node_id,
+                              Id new_focused_node_id) override {
+    if (!focus_changed_callback_.is_null())
+      focus_changed_callback_.Run(old_focused_node_id, new_focused_node_id);
+  }
+  virtual void OnActiveWindowChanged(Id old_active_window,
+                                     Id new_active_window) override {
+    if (!active_window_changed_callback_.is_null())
+      active_window_changed_callback_.Run(old_active_window, new_active_window);
+  }
+
+  base::RunLoop* run_loop_;
+  TwoNodeCallback focus_changed_callback_;
+  TwoNodeCallback active_window_changed_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindowManagerClient);
+};
+
+class TestApplicationLoader : public ApplicationLoader,
+                              public ApplicationDelegate,
+                              public ViewManagerDelegate {
+ public:
+  typedef base::Callback<void(View*)> RootAddedCallback;
+
+  explicit TestApplicationLoader(const RootAddedCallback& root_added_callback)
+      : root_added_callback_(root_added_callback) {}
+  virtual ~TestApplicationLoader() {}
+
+ private:
+  // Overridden from ApplicationLoader:
+  virtual void Load(ApplicationManager* application_manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override {
+    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+    if (!shell_handle.is_valid())
+      return;
+    scoped_ptr<ApplicationImpl> app(
+        new ApplicationImpl(this, shell_handle.Pass()));
+    apps_.push_back(app.release());
+  }
+  virtual void OnApplicationError(ApplicationManager* application_manager,
+                                  const GURL& url) override {}
+
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    root_added_callback_.Run(root);
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+  RootAddedCallback root_added_callback_;
+
+  ScopedVector<ApplicationImpl> apps_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+}  // namespace
+
+class WindowManagerApiTest : public testing::Test {
+ public:
+  WindowManagerApiTest() {}
+  virtual ~WindowManagerApiTest() {}
+
+ protected:
+  typedef std::pair<Id, Id> TwoIds;
+
+  Id WaitForEmbed() {
+    Id id;
+    base::RunLoop run_loop;
+    root_added_callback_ = base::Bind(&WindowManagerApiTest::OnEmbed,
+                                      base::Unretained(this), &id, &run_loop);
+    run_loop.Run();
+    return id;
+  }
+
+  TwoIds WaitForFocusChange() {
+    TwoIds old_and_new;
+    base::RunLoop run_loop;
+    window_manager_client()->set_focus_changed_callback(
+        base::Bind(&WindowManagerApiTest::OnFocusChanged,
+                   base::Unretained(this), &old_and_new, &run_loop));
+    run_loop.Run();
+    return old_and_new;
+  }
+
+  TwoIds WaitForActiveWindowChange() {
+    TwoIds old_and_new;
+    base::RunLoop run_loop;
+    window_manager_client()->set_active_window_changed_callback(
+        base::Bind(&WindowManagerApiTest::OnActiveWindowChanged,
+                   base::Unretained(this), &old_and_new, &run_loop));
+    run_loop.Run();
+    return old_and_new;
+  }
+
+  Id OpenWindow() {
+    return OpenWindowWithURL(kTestServiceURL);
+  }
+
+  Id OpenWindowWithURL(const std::string& url) {
+    InitEmbed(view_manager_init_.get(), url);
+    return WaitForEmbed();
+  }
+
+  TestWindowManagerClient* window_manager_client() {
+    return window_manager_client_.get();
+  }
+
+  WindowManagerServicePtr window_manager_;
+
+ private:
+  // Overridden from testing::Test:
+  virtual void SetUp() override {
+    test_helper_.Init();
+    test_helper_.SetLoaderForURL(
+        scoped_ptr<ApplicationLoader>(new TestApplicationLoader(base::Bind(
+            &WindowManagerApiTest::OnRootAdded, base::Unretained(this)))),
+        GURL(kTestServiceURL));
+    test_helper_.application_manager()->ConnectToService(
+        GURL("mojo:mojo_view_manager"), &view_manager_init_);
+    ASSERT_TRUE(InitEmbed(view_manager_init_.get(),
+                          "mojo:mojo_core_window_manager"));
+    ConnectToWindowManager();
+  }
+  virtual void TearDown() override {}
+
+  void ConnectToWindowManager() {
+    test_helper_.application_manager()->ConnectToService(
+        GURL("mojo:mojo_core_window_manager"), &window_manager_);
+    base::RunLoop connect_loop;
+    window_manager_client_.reset(new TestWindowManagerClient(&connect_loop));
+    window_manager_.set_client(window_manager_client());
+    connect_loop.Run();
+  }
+
+  void OnRootAdded(View* root) {
+    if (!root_added_callback_.is_null())
+      root_added_callback_.Run(root);
+  }
+
+  void OnEmbed(Id* root_id,
+               base::RunLoop* loop,
+               View* root) {
+    *root_id = root->id();
+    loop->Quit();
+  }
+
+  void OnFocusChanged(TwoIds* old_and_new,
+                      base::RunLoop* run_loop,
+                      Id old_focused_node_id,
+                      Id new_focused_node_id) {
+    DCHECK(old_and_new);
+    old_and_new->first = old_focused_node_id;
+    old_and_new->second = new_focused_node_id;
+    run_loop->Quit();
+  }
+
+  void OnActiveWindowChanged(TwoIds* old_and_new,
+                             base::RunLoop* run_loop,
+                             Id old_focused_node_id,
+                             Id new_focused_node_id) {
+    DCHECK(old_and_new);
+    old_and_new->first = old_focused_node_id;
+    old_and_new->second = new_focused_node_id;
+    run_loop->Quit();
+  }
+
+  shell::ShellTestHelper test_helper_;
+  ViewManagerInitServicePtr view_manager_init_;
+  scoped_ptr<TestWindowManagerClient> window_manager_client_;
+  TestApplicationLoader::RootAddedCallback root_added_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerApiTest);
+};
+
+TEST_F(WindowManagerApiTest, FocusAndActivateWindow) {
+  Id first_window = OpenWindow();
+  window_manager_->FocusWindow(first_window,
+                               base::Bind(&EmptyResultCallback));
+  TwoIds ids = WaitForFocusChange();
+  EXPECT_TRUE(ids.first == 0);
+  EXPECT_EQ(ids.second, first_window);
+
+  Id second_window = OpenWindow();
+  window_manager_->ActivateWindow(second_window,
+                                  base::Bind(&EmptyResultCallback));
+  ids = WaitForActiveWindowChange();
+  EXPECT_EQ(ids.first, first_window);
+  EXPECT_EQ(ids.second, second_window);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_app.cc b/mojo/services/window_manager/window_manager_app.cc
new file mode 100644
index 0000000..a22acba
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.cc
@@ -0,0 +1,342 @@
+// 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/window_manager/window_manager_app.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/aura/aura_init.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/hit_test.h"
+#include "ui/wm/core/capture_controller.h"
+#include "ui/wm/core/focus_controller.h"
+#include "ui/wm/public/activation_client.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(mojo::View*);
+
+namespace mojo {
+
+// The aura::Windows we use to track Views don't render, so we don't actually
+// need to supply a fully functional WindowDelegate. We do need to provide _a_
+// delegate however, otherwise the event dispatcher won't dispatch events to
+// these windows. (The aura WindowTargeter won't allow a delegate-less window
+// to be the target of an event, since the window delegate is considered the
+// "target handler").
+class DummyDelegate : public aura::WindowDelegate {
+ public:
+  DummyDelegate() {}
+  virtual ~DummyDelegate() {}
+
+ private:
+  // WindowDelegate overrides:
+  virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); }
+  virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); }
+  virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+                               const gfx::Rect& new_bounds) OVERRIDE {}
+  virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
+    return gfx::kNullCursor;
+  }
+  virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
+    return HTCAPTION;
+  }
+  virtual bool ShouldDescendIntoChildForEventHandling(
+      aura::Window* child,
+      const gfx::Point& location) OVERRIDE { return true; }
+  virtual bool CanFocus() OVERRIDE { return true; }
+  virtual void OnCaptureLost() OVERRIDE {}
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {}
+  virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
+  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {}
+  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {}
+  virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {}
+  virtual bool HasHitTestMask() const OVERRIDE { return false; }
+  virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {}
+
+  DISALLOW_COPY_AND_ASSIGN(DummyDelegate);
+};
+
+namespace {
+
+DEFINE_WINDOW_PROPERTY_KEY(View*, kViewKey, NULL);
+
+Id GetIdForWindow(aura::Window* window) {
+  return window ? WindowManagerApp::GetViewForWindow(window)->id() : 0;
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, public:
+
+WindowManagerApp::WindowManagerApp(
+    ViewManagerDelegate* view_manager_delegate,
+    WindowManagerDelegate* window_manager_delegate)
+    : window_manager_service_factory_(this),
+      wrapped_view_manager_delegate_(view_manager_delegate),
+      wrapped_window_manager_delegate_(window_manager_delegate),
+      view_manager_(NULL),
+      root_(NULL),
+      dummy_delegate_(new DummyDelegate) {
+}
+
+WindowManagerApp::~WindowManagerApp() {}
+
+// static
+View* WindowManagerApp::GetViewForWindow(aura::Window* window) {
+  return window->GetProperty(kViewKey);
+}
+
+aura::Window* WindowManagerApp::GetWindowForViewId(Id view) {
+  ViewIdToWindowMap::const_iterator it = view_id_to_window_map_.find(view);
+  return it != view_id_to_window_map_.end() ? it->second : NULL;
+}
+
+void WindowManagerApp::AddConnection(WindowManagerServiceImpl* connection) {
+  DCHECK(connections_.find(connection) == connections_.end());
+  connections_.insert(connection);
+}
+
+void WindowManagerApp::RemoveConnection(WindowManagerServiceImpl* connection) {
+  DCHECK(connections_.find(connection) != connections_.end());
+  connections_.erase(connection);
+}
+
+void WindowManagerApp::SetCapture(Id view) {
+  capture_client_->capture_client()->SetCapture(GetWindowForViewId(view));
+  // TODO(beng): notify connected clients that capture has changed, probably
+  //             by implementing some capture-client observer.
+}
+
+void WindowManagerApp::FocusWindow(Id view) {
+  aura::Window* window = GetWindowForViewId(view);
+  DCHECK(window);
+  focus_client_->FocusWindow(window);
+}
+
+void WindowManagerApp::ActivateWindow(Id view) {
+  aura::Window* window = GetWindowForViewId(view);
+  DCHECK(window);
+  activation_client_->ActivateWindow(window);
+}
+
+bool WindowManagerApp::IsReady() const {
+  return view_manager_ && root_;
+}
+
+void WindowManagerApp::InitFocus(wm::FocusRules* rules) {
+  wm::FocusController* focus_controller = new wm::FocusController(rules);
+  activation_client_ = focus_controller;
+  focus_client_.reset(focus_controller);
+  aura::client::SetFocusClient(window_tree_host_->window(), focus_controller);
+  aura::client::SetActivationClient(window_tree_host_->window(),
+                                    focus_controller);
+
+  focus_client_->AddObserver(this);
+  activation_client_->AddObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ApplicationDelegate implementation:
+
+void WindowManagerApp::Initialize(ApplicationImpl* impl) {
+  aura_init_.reset(new AuraInit);
+  view_manager_client_factory_.reset(
+      new ViewManagerClientFactory(impl->shell(), this));
+}
+
+bool WindowManagerApp::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(&window_manager_service_factory_);
+  connection->AddService(view_manager_client_factory_.get());
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewManagerDelegate implementation:
+
+void WindowManagerApp::OnEmbed(ViewManager* view_manager,
+                               View* root,
+                               ServiceProviderImpl* exported_services,
+                               scoped_ptr<ServiceProvider> imported_services) {
+  DCHECK(!view_manager_ && !root_);
+  view_manager_ = view_manager;
+  view_manager_->SetWindowManagerDelegate(this);
+  root_ = root;
+
+  window_tree_host_.reset(new WindowTreeHostMojo(root_, this));
+  window_tree_host_->window()->SetBounds(root->bounds());
+  window_tree_host_->window()->Show();
+
+  RegisterSubtree(root_, window_tree_host_->window());
+
+  capture_client_.reset(
+      new wm::ScopedCaptureClient(window_tree_host_->window()));
+
+  if (wrapped_view_manager_delegate_) {
+    wrapped_view_manager_delegate_->OnEmbed(
+        view_manager, root, exported_services, imported_services.Pass());
+  }
+
+  for (Connections::const_iterator it = connections_.begin();
+       it != connections_.end(); ++it) {
+    (*it)->NotifyReady();
+  }
+}
+
+void WindowManagerApp::OnViewManagerDisconnected(
+    ViewManager* view_manager) {
+  DCHECK_EQ(view_manager_, view_manager);
+  if (wrapped_view_manager_delegate_)
+    wrapped_view_manager_delegate_->OnViewManagerDisconnected(view_manager);
+  view_manager_ = NULL;
+  base::MessageLoop::current()->Quit();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, WindowManagerDelegate implementation:
+
+void WindowManagerApp::Embed(
+    const String& url,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  if (wrapped_window_manager_delegate_)
+    wrapped_window_manager_delegate_->Embed(url, service_provider.Pass());
+}
+
+void WindowManagerApp::DispatchEvent(EventPtr event) {
+  scoped_ptr<ui::Event> ui_event = event.To<scoped_ptr<ui::Event> >();
+  if (ui_event)
+    window_tree_host_->SendEventToProcessor(ui_event.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewObserver implementation:
+
+void WindowManagerApp::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 (view_id_to_window_map_.find(params.target->id()) ==
+        view_id_to_window_map_.end()) {
+      ViewIdToWindowMap::const_iterator it =
+          view_id_to_window_map_.find(params.new_parent->id());
+      DCHECK(it != view_id_to_window_map_.end());
+      RegisterSubtree(params.target, it->second);
+    }
+  } else if (params.old_parent) {
+    UnregisterSubtree(params.target);
+  }
+}
+
+void WindowManagerApp::OnViewDestroyed(View* view) {
+  if (view != root_)
+    return;
+  aura::Window* window = GetWindowForViewId(view->id());
+  window->RemovePreTargetHandler(this);
+  root_ = NULL;
+  STLDeleteValues(&view_id_to_window_map_);
+  if (focus_client_.get())
+    focus_client_->RemoveObserver(this);
+  if (activation_client_)
+    activation_client_->RemoveObserver(this);
+  window_tree_host_.reset();
+}
+
+void WindowManagerApp::OnViewBoundsChanged(View* view,
+                                           const gfx::Rect& old_bounds,
+                                           const gfx::Rect& new_bounds) {
+  aura::Window* window = GetWindowForViewId(view->id());
+  window->SetBounds(new_bounds);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, WindowTreeHostMojoDelegate implementation:
+
+void WindowManagerApp::CompositorContentsChanged(const SkBitmap& bitmap) {
+  // We draw nothing.
+  NOTREACHED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ui::EventHandler implementation:
+
+void WindowManagerApp::OnEvent(ui::Event* event) {
+  aura::Window* window = static_cast<aura::Window*>(event->target());
+  view_manager_->DispatchEvent(GetViewForWindow(window), Event::From(*event));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, aura::client::FocusChangeObserver implementation:
+
+void WindowManagerApp::OnWindowFocused(aura::Window* gained_focus,
+                                       aura::Window* lost_focus) {
+  for (Connections::const_iterator it = connections_.begin();
+       it != connections_.end(); ++it) {
+    (*it)->NotifyViewFocused(GetIdForWindow(gained_focus),
+                             GetIdForWindow(lost_focus));
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, aura::client::ActivationChangeObserver implementation:
+
+void WindowManagerApp::OnWindowActivated(aura::Window* gained_active,
+                                         aura::Window* lost_active) {
+  for (Connections::const_iterator it = connections_.begin();
+       it != connections_.end(); ++it) {
+    (*it)->NotifyWindowActivated(GetIdForWindow(gained_active),
+                                 GetIdForWindow(lost_active));
+  }
+  if (gained_active) {
+    View* view = GetViewForWindow(gained_active);
+    view->MoveToFront();
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, private:
+
+void WindowManagerApp::RegisterSubtree(View* view, aura::Window* parent) {
+  view->AddObserver(this);
+  DCHECK(view_id_to_window_map_.find(view->id()) ==
+         view_id_to_window_map_.end());
+  aura::Window* window = new aura::Window(dummy_delegate_.get());
+  window->set_id(view->id());
+  window->SetProperty(kViewKey, view);
+  // All events pass through the root during dispatch, so we only need a handler
+  // installed there.
+  if (view == root_)
+    window->AddPreTargetHandler(this);
+  parent->AddChild(window);
+  window->SetBounds(view->bounds());
+  window->Show();
+  view_id_to_window_map_[view->id()] = window;
+  View::Children::const_iterator it = view->children().begin();
+  for (; it != view->children().end(); ++it)
+    RegisterSubtree(*it, window);
+}
+
+void WindowManagerApp::UnregisterSubtree(View* view) {
+  view->RemoveObserver(this);
+  ViewIdToWindowMap::iterator it = view_id_to_window_map_.find(view->id());
+  DCHECK(it != view_id_to_window_map_.end());
+  scoped_ptr<aura::Window> window(it->second);
+  view_id_to_window_map_.erase(it);
+  View::Children::const_iterator child = view->children().begin();
+  for (; child != view->children().end(); ++child)
+    UnregisterSubtree(*child);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_app.h b/mojo/services/window_manager/window_manager_app.h
new file mode 100644
index 0000000..01221d6
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.h
@@ -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.
+
+#ifndef MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+#define MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/window_manager/window_manager_service_impl.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/public/activation_change_observer.h"
+
+namespace aura {
+namespace client {
+class ActivationClient;
+class FocusClient;
+}
+class Window;
+}
+
+namespace wm {
+class FocusRules;
+class ScopedCaptureClient;
+}
+
+namespace mojo {
+
+class AuraInit;
+class DummyDelegate;
+class WindowManagerServiceImpl;
+class WindowTreeHostMojo;
+
+// Implements core window manager functionality that could conceivably be shared
+// across multiple window managers implementing superficially different user
+// experiences. Establishes communication with the view manager.
+// 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.
+// This object maintains an aura::WindowTreeHost containing a hierarchy of
+// aura::Windows. Window manager functionality (e.g. focus, activation,
+// modality, etc.) are implemented using aura core window manager components.
+class WindowManagerApp
+    : public ApplicationDelegate,
+      public ViewManagerDelegate,
+      public WindowManagerDelegate,
+      public ViewObserver,
+      public WindowTreeHostMojoDelegate,
+      public ui::EventHandler,
+      public aura::client::FocusChangeObserver,
+      public aura::client::ActivationChangeObserver {
+ public:
+  WindowManagerApp(ViewManagerDelegate* view_manager_delegate,
+                   WindowManagerDelegate* window_manager_delegate);
+  virtual ~WindowManagerApp();
+
+  static View* GetViewForWindow(aura::Window* window);
+  aura::Window* GetWindowForViewId(Id view);
+
+  // Register/deregister new connections to the window manager service.
+  void AddConnection(WindowManagerServiceImpl* connection);
+  void RemoveConnection(WindowManagerServiceImpl* connection);
+
+  // These are canonical implementations of the window manager API methods.
+  void SetCapture(Id view);
+  void FocusWindow(Id view);
+  void ActivateWindow(Id view);
+
+  bool IsReady() const;
+
+  // A client of this object will use this accessor to gain access to the
+  // aura::Window hierarchy and attach event handlers.
+  aura::WindowTreeHost* host() { return window_tree_host_.get(); }
+
+  void InitFocus(wm::FocusRules* rules);
+
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* impl) override;
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+ private:
+  typedef std::set<WindowManagerServiceImpl*> Connections;
+  typedef std::map<Id, aura::Window*> ViewIdToWindowMap;
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override;
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override;
+
+  // Overridden from WindowManagerDelegate:
+  virtual void Embed(
+      const String& url,
+      InterfaceRequest<ServiceProvider> service_provider) OVERRIDE;
+  virtual void DispatchEvent(EventPtr event) OVERRIDE;
+
+  // Overridden from ViewObserver:
+  virtual void OnTreeChanged(
+      const ViewObserver::TreeChangeParams& params) override;
+  virtual void OnViewDestroyed(View* view) override;
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override;
+
+  // Overridden from WindowTreeHostMojoDelegate:
+  virtual void CompositorContentsChanged(const SkBitmap& bitmap) override;
+
+  // Overridden from ui::EventHandler:
+  virtual void OnEvent(ui::Event* event) override;
+
+  // Overridden from aura::client::FocusChangeObserver:
+  virtual void OnWindowFocused(aura::Window* gained_focus,
+                               aura::Window* lost_focus) override;
+
+  // Overridden from aura::client::ActivationChangeObserver:
+  virtual void OnWindowActivated(aura::Window* gained_active,
+                                 aura::Window* lost_active) override;
+
+  // Creates an aura::Window for every view in the hierarchy beneath |view|,
+  // and adds to the registry so that it can be retrieved later via
+  // GetWindowForViewId().
+  // TODO(beng): perhaps View should have a property bag.
+  void RegisterSubtree(View* view, aura::Window* parent);
+  // Deletes the aura::Windows associated with the hierarchy beneath |id|,
+  // and removes from the registry.
+  void UnregisterSubtree(View* view);
+
+  InterfaceFactoryImplWithContext<WindowManagerServiceImpl, WindowManagerApp>
+      window_manager_service_factory_;
+
+  ViewManagerDelegate* wrapped_view_manager_delegate_;
+  WindowManagerDelegate* wrapped_window_manager_delegate_;
+
+  ViewManager* view_manager_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+  View* root_;
+
+  scoped_ptr<AuraInit> aura_init_;
+  scoped_ptr<WindowTreeHostMojo> window_tree_host_;
+
+  scoped_ptr<wm::ScopedCaptureClient> capture_client_;
+  scoped_ptr<aura::client::FocusClient> focus_client_;
+  aura::client::ActivationClient* activation_client_;
+
+  Connections connections_;
+  ViewIdToWindowMap view_id_to_window_map_;
+
+  scoped_ptr<DummyDelegate> dummy_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerApp);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
diff --git a/mojo/services/window_manager/window_manager_service_impl.cc b/mojo/services/window_manager/window_manager_service_impl.cc
new file mode 100644
index 0000000..aa1be19
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_service_impl.cc
@@ -0,0 +1,80 @@
+
+// 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/window_manager/window_manager_service_impl.h"
+
+#include "mojo/services/window_manager/window_manager_app.h"
+
+namespace mojo {
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, public:
+
+WindowManagerServiceImpl::WindowManagerServiceImpl(
+    WindowManagerApp* window_manager)
+    : window_manager_(window_manager) {
+  window_manager_->AddConnection(this);
+}
+
+WindowManagerServiceImpl::~WindowManagerServiceImpl() {
+  window_manager_->RemoveConnection(this);
+}
+
+void WindowManagerServiceImpl::NotifyReady() {
+  client()->OnWindowManagerReady();
+}
+
+void WindowManagerServiceImpl::NotifyViewFocused(Id new_focused_id,
+                                                 Id old_focused_id) {
+  client()->OnFocusChanged(old_focused_id, new_focused_id);
+}
+
+void WindowManagerServiceImpl::NotifyWindowActivated(Id new_active_id,
+                                                     Id old_active_id) {
+  client()->OnActiveWindowChanged(old_active_id, new_active_id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, WindowManager implementation:
+
+void WindowManagerServiceImpl::SetCapture(
+    Id view,
+    const Callback<void(bool)>& callback) {
+  bool success = window_manager_->IsReady();
+  if (success)
+    window_manager_->SetCapture(view);
+  callback.Run(success);
+}
+
+void WindowManagerServiceImpl::FocusWindow(
+    Id view,
+    const Callback<void(bool)>& callback) {
+  bool success = window_manager_->IsReady();
+  if (success)
+    window_manager_->FocusWindow(view);
+  callback.Run(success);
+}
+
+void WindowManagerServiceImpl::ActivateWindow(
+    Id view,
+    const Callback<void(bool)>& callback) {
+  bool success = window_manager_->IsReady();
+  if (success)
+    window_manager_->ActivateWindow(view);
+  callback.Run(success);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, InterfaceImpl overrides:
+
+void WindowManagerServiceImpl::OnConnectionEstablished() {
+  // If the connection was established prior to the window manager being
+  // embedded by the view manager, |window_manager_|'s ViewManagerDelegate
+  // impl will call NotifyReady() when it is.
+  if (window_manager_->IsReady())
+    NotifyReady();
+}
+
+}  // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_service_impl.h b/mojo/services/window_manager/window_manager_service_impl.h
new file mode 100644
index 0000000..2d662e4
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_service_impl.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 MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
+#define MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+
+namespace mojo {
+
+class WindowManagerApp;
+
+class WindowManagerServiceImpl : public InterfaceImpl<WindowManagerService> {
+ public:
+  explicit WindowManagerServiceImpl(WindowManagerApp* manager);
+  virtual ~WindowManagerServiceImpl();
+
+  void NotifyReady();
+  void NotifyViewFocused(Id new_focused_id, Id old_focused_id);
+  void NotifyWindowActivated(Id new_active_id, Id old_active_id);
+
+ private:
+  // Overridden from WindowManagerService:
+  virtual void SetCapture(Id view,
+                          const Callback<void(bool)>& callback) override;
+  virtual void FocusWindow(Id view,
+                           const Callback<void(bool)>& callback) override;
+  virtual void ActivateWindow(Id view,
+                              const Callback<void(bool)>& callback) override;
+
+  // Overridden from InterfaceImpl:
+  virtual void OnConnectionEstablished() override;
+
+  WindowManagerApp* window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerServiceImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
diff --git a/mojo/services/window_manager/window_manager_unittests.cc b/mojo/services/window_manager/window_manager_unittests.cc
new file mode 100644
index 0000000..2ddceb8
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_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 mojo {
+
+class WindowManagerTestSuite : public base::TestSuite {
+ public:
+  WindowManagerTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+  virtual ~WindowManagerTestSuite() {}
+
+ protected:
+  virtual 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 mojo
+
+int main(int argc, char** argv) {
+  mojo::WindowManagerTestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(
+      argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}