Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
new file mode 100644
index 0000000..9341abb
--- /dev/null
+++ b/mojo/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")
+
+group("mojo") {
+  # Meta-target, don't link into production code.
+  testonly = true
+  declare_args() {
+    mojo_use_go = false
+  }
+  deps = [
+    ":tests",
+    "//mojo/apps/js",
+    "//mojo/common",
+    "//mojo/examples",
+    "//mojo/public",
+    "//mojo/services",
+    "//mojo/shell:mojo_shell",
+    "//mojo/tools/package_manager",
+  ]
+
+  if (is_linux) {
+    deps += [
+      "//mojo/python",
+    ]
+    if (mojo_use_go) {
+      deps += [
+        "//mojo/go",
+      ]
+    }
+  }
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    "//mojo/application_manager:mojo_application_manager_unittests",
+    "//mojo/apps/js/test:mojo_apps_js_unittests",
+    "//mojo/bindings/js/tests:mojo_js_unittests",
+    "//mojo/common:mojo_common_unittests",
+    "//mojo/edk/system:mojo_message_pipe_perftests",
+    "//mojo/edk/system:mojo_system_unittests",
+    "//mojo/public/c/system/tests:perftests",
+    "//mojo/public/cpp/application/tests:mojo_public_application_unittests",
+    "//mojo/public/cpp/bindings/tests:mojo_public_bindings_unittests",
+    "//mojo/public/cpp/environment/tests:mojo_public_environment_unittests",
+    "//mojo/public/cpp/system/tests:mojo_public_system_unittests",
+    "//mojo/public/cpp/utility/tests:mojo_public_utility_unittests",
+    "//mojo/services/clipboard:mojo_clipboard_unittests",
+    "//mojo/services/public/cpp/surfaces/tests:mojo_surfaces_lib_unittests",
+    "//mojo/shell:mojo_external_application_tests",
+    "//mojo/shell:mojo_shell_tests",
+    "//mojo/tools:message_generator",
+  ]
+
+  if (use_aura) {
+    deps += [
+      "//mojo/services/public/cpp/view_manager/tests:mojo_view_manager_lib_unittests",
+      "//mojo/services/view_manager:mojo_view_manager_unittests",
+      "//mojo/services/window_manager:mojo_core_window_manager_unittests",
+    ]
+  }
+}
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+
+  generate_jni("jni_headers") {
+    sources = [
+      "android/javatests/src/org/chromium/mojo/MojoTestCase.java",
+      "android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
+      "android/system/src/org/chromium/mojo/system/impl/CoreImpl.java",
+      "services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java",
+    ]
+
+    jni_package = "mojo"
+  }
+}
diff --git a/mojo/DEPS b/mojo/DEPS
new file mode 100644
index 0000000..c37b8de
--- /dev/null
+++ b/mojo/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+base",
+  "+build",
+  "+mojo",
+  "-mojo/edk/system",
+  "+testing",
+]
diff --git a/mojo/OWNERS b/mojo/OWNERS
new file mode 100644
index 0000000..b864087
--- /dev/null
+++ b/mojo/OWNERS
@@ -0,0 +1,11 @@
+aa@chromium.org
+abarth@chromium.org
+ben@chromium.org
+darin@chromium.org
+davemoore@chromium.org
+jamesr@chromium.org
+mpcomplete@chromium.org
+qsr@chromium.org
+sky@chromium.org
+viettrungluu@chromium.org
+yzshen@chromium.org
diff --git a/mojo/PRESUBMIT.py b/mojo/PRESUBMIT.py
new file mode 100644
index 0000000..a7dc055
--- /dev/null
+++ b/mojo/PRESUBMIT.py
@@ -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.
+
+"""Presubmit script for mojo
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+import os.path
+
+def CheckChangeOnUpload(input_api, output_api):
+  # Additional python module paths (we're in src/mojo/); not everyone needs
+  # them, but it's easiest to add them to everyone's path.
+  # For ply and jinja2:
+  third_party_path = os.path.join(
+      input_api.PresubmitLocalPath(), "..", "third_party")
+  # For the bindings generator:
+  mojo_public_bindings_pylib_path = os.path.join(
+      input_api.PresubmitLocalPath(), "public", "tools", "bindings", "pylib")
+  # TODO(vtl): Don't lint these files until the (many) problems are fixed
+  # (possibly by deleting/rewriting some files).
+  temporary_black_list = input_api.DEFAULT_BLACK_LIST + \
+      (r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]pylib[\\\/]mojom[\\\/]"
+           r"generate[\\\/].+\.py$",
+       r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]generators[\\\/].+\.py$",
+       r".*\bspy[\\\/]ui[\\\/].+\.py$",
+       r".*\btools[\\\/]pylib[\\\/]transitive_hash\.py$",
+       r".*\btools[\\\/]test_runner\.py$")
+
+  results = []
+  pylint_extra_paths = [third_party_path, mojo_public_bindings_pylib_path]
+  results += input_api.canned_checks.RunPylint(
+      input_api, output_api, extra_paths_list=pylint_extra_paths,
+      black_list=temporary_black_list)
+  return results
diff --git a/mojo/README.md b/mojo/README.md
new file mode 100644
index 0000000..5af1dcb
--- /dev/null
+++ b/mojo/README.md
@@ -0,0 +1,6 @@
+Mojo
+====
+
+Mojo is an effort to extract a common platform out of Chrome's renderer and
+plugin processes that can support multiple types of sandboxed content, such as
+HTML, Pepper, or NaCl.
diff --git a/mojo/android/DEPS b/mojo/android/DEPS
new file mode 100644
index 0000000..c80012b
--- /dev/null
+++ b/mojo/android/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+jni",
+]
diff --git a/mojo/android/javatests/AndroidManifest.xml b/mojo/android/javatests/AndroidManifest.xml
new file mode 100644
index 0000000..290a1de
--- /dev/null
+++ b/mojo/android/javatests/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+  <!-- Copyright (c) 2012 The Chromium Authors. All rights reserved. Use of
+       this source code is governed by a BSD-style license that can be found
+       in the LICENSE file. -->
+  <!-- package name must be unique so suffix with "tests" so package loader
+       doesn't ignore this. -->
+  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.chromium.mojo.tests">
+    <!-- We add an application tag here just so that we can indicate that this
+         package needs to link against the android.test library, which is
+         needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="20" />
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="org.chromium.mojo.tests"
+        android:label="Tests for org.chromium.mojo"/>
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
+</manifest>
diff --git a/mojo/android/javatests/DEPS b/mojo/android/javatests/DEPS
new file mode 100644
index 0000000..78cf465
--- /dev/null
+++ b/mojo/android/javatests/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # out should be allowed by default, but bots are failing on this.
+  "+out",
+]
diff --git a/mojo/android/javatests/apk/.empty b/mojo/android/javatests/apk/.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/android/javatests/apk/.empty
diff --git a/mojo/android/javatests/init_library.cc b/mojo/android/javatests/init_library.cc
new file mode 100644
index 0000000..c738981
--- /dev/null
+++ b/mojo/android/javatests/init_library.cc
@@ -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.
+
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "mojo/android/javatests/mojo_test_case.h"
+#include "mojo/android/javatests/validation_test_util.h"
+#include "mojo/android/system/core_impl.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+  { "CoreImpl", mojo::android::RegisterCoreImpl },
+  { "MojoTestCase", mojo::android::RegisterMojoTestCase },
+  { "ValidationTestUtil", mojo::android::RegisterValidationTestUtil },
+};
+
+bool RegisterMojoJni(JNIEnv* env) {
+  return RegisterNativeMethods(env, kMojoRegisteredMethods,
+                               arraysize(kMojoRegisteredMethods));
+}
+
+}  // namespace
+
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!base::android::RegisterLibraryLoaderEntryHook(env))
+    return -1;
+
+  if (!base::android::RegisterJni(env))
+    return -1;
+
+  if (!RegisterMojoJni(env))
+    return -1;
+
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+
+  return JNI_VERSION_1_4;
+}
diff --git a/mojo/android/javatests/mojo_test_case.cc b/mojo/android/javatests/mojo_test_case.cc
new file mode 100644
index 0000000..f14dfd8
--- /dev/null
+++ b/mojo/android/javatests/mojo_test_case.cc
@@ -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.
+
+#include "mojo/android/javatests/mojo_test_case.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_support_android.h"
+#include "jni/MojoTestCase_jni.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace {
+
+struct TestEnvironment {
+  base::ShadowingAtExitManager at_exit;
+  base::MessageLoopForUI message_loop;
+};
+
+}  // namespace
+
+namespace mojo {
+namespace android {
+
+static void InitApplicationContext(JNIEnv* env,
+                                   jobject jcaller,
+                                   jobject context) {
+  base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
+  base::android::InitApplicationContext(env, scoped_context);
+  base::InitAndroidTestMessageLoop();
+}
+
+static jlong SetupTestEnvironment(JNIEnv* env, jobject jcaller) {
+  return reinterpret_cast<intptr_t>(new TestEnvironment());
+}
+
+static void TearDownTestEnvironment(JNIEnv* env,
+                                    jobject jcaller,
+                                    jlong test_environment) {
+  delete reinterpret_cast<TestEnvironment*>(test_environment);
+}
+
+static void RunLoop(JNIEnv* env, jobject jcaller, jlong timeout_ms) {
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::MessageLoop::QuitClosure(),
+      base::TimeDelta::FromMilliseconds(timeout_ms));
+  base::RunLoop run_loop;
+  run_loop.Run();
+}
+
+bool RegisterMojoTestCase(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android
+}  // namespace mojo
diff --git a/mojo/android/javatests/mojo_test_case.h b/mojo/android/javatests/mojo_test_case.h
new file mode 100644
index 0000000..2ce3428
--- /dev/null
+++ b/mojo/android/javatests/mojo_test_case.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_ANDROID_JAVATESTS_CORE_TEST_H_
+#define MOJO_ANDROID_JAVATESTS_CORE_TEST_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterMojoTestCase(JNIEnv* env);
+
+}  // namespace android
+}  // namespace mojo
+
+#endif  // MOJO_SYSTEM_ANDROID_JAVATESTS_CORE_TEST_H_
diff --git a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
new file mode 100644
index 0000000..9a7b473
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
@@ -0,0 +1,227 @@
+// Copyright 2014 The Chromium 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 org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A mock handle, that does nothing.
+ */
+public class HandleMock implements UntypedHandle, MessagePipeHandle,
+        ProducerHandle, ConsumerHandle, SharedBufferHandle {
+
+    /**
+     * @see Handle#close()
+     */
+    @Override
+    public void close() {
+        // Do nothing.
+    }
+
+    /**
+     * @see Handle#wait(Core.HandleSignals, long)
+     */
+    @Override
+    public int wait(Core.HandleSignals signals, long deadline) {
+        // Do nothing.
+        return MojoResult.OK;
+    }
+
+    /**
+     * @see Handle#isValid()
+     */
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    /**
+     * @see Handle#toUntypedHandle()
+     */
+    @Override
+    public UntypedHandle toUntypedHandle() {
+        return this;
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#getCore()
+     */
+    @Override
+    public Core getCore() {
+        return CoreImpl.getInstance();
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#pass()
+     */
+    @Override
+    public HandleMock pass() {
+        return this;
+    }
+
+    /**
+     * @see Handle#releaseNativeHandle()
+     */
+    @Override
+    public int releaseNativeHandle() {
+        return 0;
+    }
+
+    /**
+     * @see ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+     */
+    @Override
+    public int discardData(int numBytes, DataPipe.ReadFlags flags) {
+        // Do nothing.
+        return 0;
+    }
+
+    /**
+     * @see ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags)
+     */
+    @Override
+    public int readData(ByteBuffer elements,
+            DataPipe.ReadFlags flags) {
+        // Do nothing.
+        return 0;
+    }
+
+    /**
+     * @see ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+     */
+    @Override
+    public ByteBuffer beginReadData(int numBytes,
+            DataPipe.ReadFlags flags) {
+        // Do nothing.
+        return null;
+    }
+
+    /**
+     * @see ConsumerHandle#endReadData(int)
+     */
+    @Override
+    public void endReadData(int numBytesRead) {
+        // Do nothing.
+    }
+
+    /**
+     * @see ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags)
+     */
+    @Override
+    public int writeData(ByteBuffer elements,
+            DataPipe.WriteFlags flags) {
+        // Do nothing.
+        return 0;
+    }
+
+    /**
+     * @see ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
+     */
+    @Override
+    public ByteBuffer beginWriteData(int numBytes,
+            DataPipe.WriteFlags flags) {
+        // Do nothing.
+        return null;
+    }
+
+    /**
+     * @see ProducerHandle#endWriteData(int)
+     */
+    @Override
+    public void endWriteData(int numBytesWritten) {
+        // Do nothing.
+    }
+
+    /**
+     * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List,
+     *      MessagePipeHandle.WriteFlags)
+     */
+    @Override
+    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles,
+            WriteFlags flags) {
+        // Do nothing.
+    }
+
+    /**
+     * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags)
+     */
+    @Override
+    public ReadMessageResult readMessage(ByteBuffer bytes, int maxNumberOfHandles,
+            ReadFlags flags) {
+        // Do nothing.
+        return new ReadMessageResult();
+    }
+
+    /**
+     * @see UntypedHandle#toMessagePipeHandle()
+     */
+    @Override
+    public MessagePipeHandle toMessagePipeHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toDataPipeConsumerHandle()
+     */
+    @Override
+    public ConsumerHandle toDataPipeConsumerHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toDataPipeProducerHandle()
+     */
+    @Override
+    public ProducerHandle toDataPipeProducerHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toSharedBufferHandle()
+     */
+    @Override
+    public SharedBufferHandle toSharedBufferHandle() {
+        return this;
+    }
+
+    /**
+     * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions)
+     */
+    @Override
+    public SharedBufferHandle duplicate(DuplicateOptions options) {
+        // Do nothing.
+        return null;
+    }
+
+    /**
+     * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags)
+     */
+    @Override
+    public ByteBuffer map(long offset, long numBytes, MapFlags flags) {
+        // Do nothing.
+        return null;
+    }
+
+    /**
+     * @see SharedBufferHandle#unmap(java.nio.ByteBuffer)
+     */
+    @Override
+    public void unmap(ByteBuffer buffer) {
+        // Do nothing.
+    }
+
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
new file mode 100644
index 0000000..3adb2fb
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
@@ -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.
+
+package org.chromium.mojo;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+import org.chromium.base.JNINamespace;
+import org.chromium.base.library_loader.LibraryLoader;
+
+/**
+ * Base class to test mojo. Setup the environment.
+ */
+@JNINamespace("mojo::android")
+public class MojoTestCase extends InstrumentationTestCase {
+
+    private long mTestEnvironmentPointer;
+
+    /**
+     * @see junit.framework.TestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        LibraryLoader.ensureInitialized();
+        nativeInitApplicationContext(getInstrumentation().getTargetContext());
+        mTestEnvironmentPointer = nativeSetupTestEnvironment();
+    }
+
+    /**
+     * @see android.test.InstrumentationTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        nativeTearDownTestEnvironment(mTestEnvironmentPointer);
+        super.tearDown();
+    }
+
+    private native void nativeInitApplicationContext(Context context);
+
+    private native long nativeSetupTestEnvironment();
+
+    private native void nativeTearDownTestEnvironment(long testEnvironment);
+
+    protected native void nativeRunLoop(long timeoutMS);
+
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java b/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java
new file mode 100644
index 0000000..2134a9f
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java
@@ -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.
+
+package org.chromium.mojo;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Random;
+
+/**
+ * Utilities methods for tests.
+ */
+public final class TestUtils {
+
+    private static final Random RANDOM = new Random();
+
+    /**
+     * Returns a new direct ByteBuffer of the given size with random (but reproducible) data.
+     */
+    public static ByteBuffer newRandomBuffer(int size) {
+        byte bytes[] = new byte[size];
+        RANDOM.setSeed(size);
+        RANDOM.nextBytes(bytes);
+        ByteBuffer data = ByteBuffer.allocateDirect(size);
+        data.order(ByteOrder.nativeOrder());
+        data.put(bytes);
+        data.flip();
+        return data;
+    }
+
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
new file mode 100644
index 0000000..a46a157
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.Charset;
+
+/**
+ * Testing {@link BindingsHelper}.
+ */
+public class BindingsHelperTest extends TestCase {
+
+    /**
+     * Testing {@link BindingsHelper#utf8StringSizeInBytes(String)}.
+     */
+    @SmallTest
+    public void testUTF8StringLength() {
+        String[] stringsToTest = {
+            "",
+            "a",
+            "hello world",
+            "éléphant",
+            "𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕",
+            "你午饭想吃什么",
+            "你午饭想吃什么\0éléphant",
+        };
+        for (String s : stringsToTest) {
+            assertEquals(s.getBytes(Charset.forName("utf8")).length,
+                    BindingsHelper.utf8StringSizeInBytes(s));
+        }
+        assertEquals(1, BindingsHelper.utf8StringSizeInBytes("\0"));
+        String s = new StringBuilder().appendCodePoint(0x0).appendCodePoint(0x80).
+                appendCodePoint(0x800).appendCodePoint(0x10000).toString();
+        assertEquals(10, BindingsHelper.utf8StringSizeInBytes(s));
+        assertEquals(10, s.getBytes(Charset.forName("utf8")).length);
+    }
+
+    /**
+     * Testing {@link BindingsHelper#align(int)}.
+     */
+    @SmallTest
+    public void testAlign() {
+        for (int i = 0; i < 3 * BindingsHelper.ALIGNMENT; ++i) {
+            int j = BindingsHelper.align(i);
+            assertTrue(j >= i);
+            assertTrue(j % BindingsHelper.ALIGNMENT == 0);
+            assertTrue(j - i < BindingsHelper.ALIGNMENT);
+        }
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
new file mode 100644
index 0000000..e957bfe
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
@@ -0,0 +1,252 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.bindings.test.mojom.imported.Color;
+import org.chromium.mojo.bindings.test.mojom.imported.Point;
+import org.chromium.mojo.bindings.test.mojom.imported.Shape;
+import org.chromium.mojo.bindings.test.mojom.imported.Thing;
+import org.chromium.mojo.bindings.test.mojom.sample.Bar;
+import org.chromium.mojo.bindings.test.mojom.sample.Bar.Type;
+import org.chromium.mojo.bindings.test.mojom.sample.DefaultsTest;
+import org.chromium.mojo.bindings.test.mojom.sample.Enum;
+import org.chromium.mojo.bindings.test.mojom.sample.Foo;
+import org.chromium.mojo.bindings.test.mojom.sample.InterfaceConstants;
+import org.chromium.mojo.bindings.test.mojom.sample.SampleServiceConstants;
+import org.chromium.mojo.bindings.test.mojom.test_structs.EmptyStruct;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+/**
+ * Testing generated classes and associated features.
+ */
+public class BindingsTest extends TestCase {
+
+    /**
+     * Create a new typical Bar instance.
+     */
+    private static Bar newBar() {
+        Bar bar = new Bar();
+        bar.alpha = (byte) 0x01;
+        bar.beta = (byte) 0x02;
+        bar.gamma = (byte) 0x03;
+        bar.type = Type.BOTH;
+        return bar;
+    }
+
+    /**
+     * Check that 2 Bar instances are equals.
+     */
+    private static void assertBarEquals(Bar bar, Bar bar2) {
+        if (bar == bar2) {
+            return;
+        }
+        assertTrue(bar != null && bar2 != null);
+        assertEquals(bar.alpha, bar2.alpha);
+        assertEquals(bar.beta, bar2.beta);
+        assertEquals(bar.gamma, bar2.gamma);
+        assertEquals(bar.type, bar2.type);
+    }
+
+    /**
+     * Create a new typical Foo instance.
+     */
+    private static Foo createFoo() {
+        Foo foo = new Foo();
+        foo.name = "HELLO WORLD";
+        foo.arrayOfArrayOfBools = new boolean[][] {
+            { true, false, true }, { }, { }, { false }, { true } };
+        foo.bar = newBar();
+        foo.a = true;
+        foo.c = true;
+        foo.data = new byte[] { 0x01, 0x02, 0x03 };
+        foo.extraBars = new Bar[] { newBar(), newBar() };
+        String[][][] strings = new String[3][2][1];
+        for (int i0 = 0; i0 < strings.length; ++i0) {
+            for (int i1 = 0; i1 < strings[i0].length; ++i1) {
+                for (int i2 = 0; i2 < strings[i0][i1].length; ++i2) {
+                    strings[i0][i1][i2] = "Hello(" + i0 + ", " + i1 + ", " + i2 + ")";
+                }
+            }
+        }
+        foo.multiArrayOfStrings = strings;
+        ConsumerHandle[] inputStreams = new ConsumerHandle[5];
+        for (int i = 0; i < inputStreams.length; ++i) {
+            inputStreams[i] = new HandleMock();
+        }
+        foo.inputStreams = inputStreams;
+        ProducerHandle[] outputStreams = new ProducerHandle[3];
+        for (int i = 0; i < outputStreams.length; ++i) {
+            outputStreams[i] = new HandleMock();
+        }
+        foo.outputStreams = outputStreams;
+        foo.source = new HandleMock();
+        return foo;
+    }
+
+    /**
+     * Check that 2 Foo instances are equals.
+     */
+    private static void assertFooEquals(Foo foo1, Foo foo2) {
+        assertEquals(foo1.a, foo2.a);
+        assertEquals(foo1.b, foo2.b);
+        assertEquals(foo1.c, foo2.c);
+        assertEquals(foo1.name, foo2.name);
+        assertEquals(foo1.x, foo2.x);
+        assertEquals(foo1.y, foo2.y);
+        TestCase.assertTrue(Arrays.deepEquals(foo1.arrayOfArrayOfBools, foo2.arrayOfArrayOfBools));
+        assertBarEquals(foo1.bar, foo2.bar);
+        assertTrue(Arrays.equals(foo1.data, foo2.data));
+        TestCase.assertTrue(Arrays.deepEquals(foo1.multiArrayOfStrings, foo2.multiArrayOfStrings));
+        assertEquals(foo1.source, foo2.source);
+        TestCase.assertTrue(Arrays.deepEquals(foo1.inputStreams, foo2.inputStreams));
+        TestCase.assertTrue(Arrays.deepEquals(foo1.outputStreams, foo2.outputStreams));
+        if (foo1.extraBars != foo2.extraBars) {
+            assertEquals(foo1.extraBars.length, foo2.extraBars.length);
+            for (int i = 0; i < foo1.extraBars.length; ++i) {
+                assertBarEquals(foo1.extraBars[i], foo2.extraBars[i]);
+            }
+        }
+    }
+
+    private static <T> void checkConstantField(
+            Field field, Class<T> expectedClass, T value) throws IllegalAccessException {
+        assertEquals(expectedClass, field.getType());
+        assertEquals(Modifier.FINAL, field.getModifiers() & Modifier.FINAL);
+        assertEquals(Modifier.STATIC, field.getModifiers() & Modifier.STATIC);
+        assertEquals(value, field.get(null));
+    }
+
+    private static <T> void checkField(Field field, Class<T> expectedClass,
+            Object object, T value) throws IllegalArgumentException, IllegalAccessException {
+        assertEquals(expectedClass, field.getType());
+        assertEquals(0, field.getModifiers() & Modifier.FINAL);
+        assertEquals(0, field.getModifiers() & Modifier.STATIC);
+        assertEquals(value, field.get(object));
+    }
+
+    /**
+     * Testing constants are correctly generated.
+     */
+    @SmallTest
+    public void testConstants() throws NoSuchFieldException, SecurityException,
+            IllegalAccessException {
+        checkConstantField(SampleServiceConstants.class.getField("TWELVE"), byte.class, (byte) 12);
+        checkConstantField(InterfaceConstants.class.getField("LONG"), long.class, 4405L);
+    }
+
+    /**
+     * Testing enums are correctly generated.
+     */
+    @SmallTest
+    public void testEnums() throws NoSuchFieldException, SecurityException,
+            IllegalAccessException {
+        checkConstantField(Color.class.getField("RED"), int.class, 0);
+        checkConstantField(Color.class.getField("BLACK"), int.class, 1);
+
+        checkConstantField(Enum.class.getField("VALUE"), int.class, 0);
+
+        checkConstantField(Shape.class.getField("RECTANGLE"), int.class, 1);
+        checkConstantField(Shape.class.getField("CIRCLE"), int.class, 2);
+        checkConstantField(Shape.class.getField("TRIANGLE"), int.class, 3);
+    }
+
+    /**
+     * Testing default values on structs.
+     *
+     * @throws IllegalAccessException
+     * @throws IllegalArgumentException
+     */
+    @SmallTest
+    public void testStructDefaults() throws NoSuchFieldException, SecurityException,
+            IllegalArgumentException, IllegalAccessException {
+        // Check default values.
+        DefaultsTest test = new DefaultsTest();
+
+        checkField(DefaultsTest.class.getField("a0"), byte.class, test, (byte) -12);
+        checkField(DefaultsTest.class.getField("a1"), byte.class, test, (byte) 12);
+        checkField(DefaultsTest.class.getField("a2"), short.class, test, (short) 1234);
+        checkField(DefaultsTest.class.getField("a3"), short.class, test, (short) 34567);
+        checkField(DefaultsTest.class.getField("a4"), int.class, test, 123456);
+        checkField(DefaultsTest.class.getField("a5"), int.class, test, (int) 3456789012L);
+        checkField(DefaultsTest.class.getField("a6"), long.class, test, -111111111111L);
+        // -8446744073709551617 == 9999999999999999999 - 2 ^ 64.
+        checkField(DefaultsTest.class.getField("a7"), long.class, test, -8446744073709551617L);
+        checkField(DefaultsTest.class.getField("a8"), int.class, test, 0x12345);
+        checkField(DefaultsTest.class.getField("a9"), int.class, test, -0x12345);
+        checkField(DefaultsTest.class.getField("a10"), int.class, test, 1234);
+        checkField(DefaultsTest.class.getField("a11"), boolean.class, test, true);
+        checkField(DefaultsTest.class.getField("a12"), boolean.class, test, false);
+        checkField(DefaultsTest.class.getField("a13"), float.class, test, (float) 123.25);
+        checkField(DefaultsTest.class.getField("a14"), double.class, test, 1234567890.123);
+        checkField(DefaultsTest.class.getField("a15"), double.class, test, 1E10);
+        checkField(DefaultsTest.class.getField("a16"), double.class, test, -1.2E+20);
+        checkField(DefaultsTest.class.getField("a17"), double.class, test, +1.23E-20);
+        checkField(DefaultsTest.class.getField("a18"), byte[].class, test, null);
+        checkField(DefaultsTest.class.getField("a19"), String.class, test, null);
+        checkField(DefaultsTest.class.getField("a20"), int.class, test, Bar.Type.BOTH);
+        checkField(DefaultsTest.class.getField("a21"), Point.class, test, null);
+
+        assertNotNull(test.a22);
+        checkField(DefaultsTest.class.getField("a22"), Thing.class, test, test.a22);
+        checkField(DefaultsTest.class.getField("a23"), long.class, test, -1L);
+        checkField(DefaultsTest.class.getField("a24"), long.class, test, 0x123456789L);
+        checkField(DefaultsTest.class.getField("a25"), long.class, test, -0x123456789L);
+    }
+
+    /**
+     * Testing generation of the Foo class.
+     *
+     * @throws IllegalAccessException
+     */
+    @SmallTest
+    public void testFooGeneration() throws NoSuchFieldException, SecurityException,
+            IllegalAccessException {
+        // Checking Foo constants.
+        checkConstantField(Foo.class.getField("FOOBY"), String.class, "Fooby");
+
+        // Checking Foo default values.
+        Foo foo = new Foo();
+        checkField(Foo.class.getField("name"), String.class, foo, Foo.FOOBY);
+
+        assertNotNull(foo.source);
+        assertFalse(foo.source.isValid());
+        checkField(Foo.class.getField("source"), MessagePipeHandle.class, foo, foo.source);
+    }
+
+    /**
+     * Testing serialization of the Foo class.
+     */
+    @SmallTest
+    public void testFooSerialization() {
+        // Checking serialization and deserialization of a Foo object.
+        Foo typicalFoo = createFoo();
+        Message serializedFoo = typicalFoo.serialize(null);
+        Foo deserializedFoo = Foo.deserialize(serializedFoo);
+        assertFooEquals(typicalFoo, deserializedFoo);
+    }
+
+    /**
+     * Testing serialization of the EmptyStruct class.
+     */
+    @SmallTest
+    public void testEmptyStructSerialization() {
+        // Checking serialization and deserialization of a EmptyStruct object.
+        Message serializedStruct = new EmptyStruct().serialize(null);
+        EmptyStruct emptyStruct = EmptyStruct.deserialize(serializedStruct);
+        assertNotNull(emptyStruct);
+    }
+
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java
new file mode 100644
index 0000000..880577a
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.TestUtils;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for bindings tests.
+ */
+public class BindingsTestUtils {
+
+    /**
+     * {@link MessageReceiver} that records any message it receives.
+     */
+    public static class RecordingMessageReceiver extends SideEffectFreeCloseable
+            implements MessageReceiver {
+
+        public final List<Message> messages = new ArrayList<Message>();
+
+        /**
+         * @see MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            messages.add(message);
+            return true;
+        }
+    }
+
+    /**
+     * {@link MessageReceiverWithResponder} that records any message it receives.
+     */
+    public static class RecordingMessageReceiverWithResponder extends RecordingMessageReceiver
+            implements MessageReceiverWithResponder {
+
+        public final List<Pair<Message, MessageReceiver>> messagesWithReceivers =
+                new ArrayList<Pair<Message, MessageReceiver>>();
+
+        /**
+         * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+         */
+        @Override
+        public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+            messagesWithReceivers.add(Pair.create(message, responder));
+            return true;
+        }
+    }
+
+    /**
+     * {@link ConnectionErrorHandler} that records any error it received.
+     */
+    public static class CapturingErrorHandler implements ConnectionErrorHandler {
+
+        private MojoException mLastMojoException = null;
+
+        /**
+         * @see ConnectionErrorHandler#onConnectionError(MojoException)
+         */
+        @Override
+        public void onConnectionError(MojoException e) {
+            mLastMojoException = e;
+        }
+
+        /**
+         * Returns the last recorded exception.
+         */
+        public MojoException getLastMojoException() {
+            return mLastMojoException;
+        }
+
+    }
+
+    /**
+     * Creates a new valid {@link Message}. The message will have a valid header.
+     */
+    public static Message newRandomMessage(int size) {
+        assert size > 16;
+        ByteBuffer message = TestUtils.newRandomBuffer(size);
+        int[] headerAsInts = { 16, 2, 0, 0 };
+        for (int i = 0; i < 4; ++i) {
+            message.putInt(4 * i, headerAsInts[i]);
+        }
+        message.position(0);
+        return new Message(message, new ArrayList<Handle>());
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
new file mode 100644
index 0000000..dfb8e21
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.bindings.Callbacks.Callback1;
+import org.chromium.mojo.bindings.Callbacks.Callback7;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testing generated callbacks
+ */
+public class CallbacksTest extends TestCase {
+
+    /**
+     * Testing {@link Callback1}.
+     */
+    @SmallTest
+    public void testCallback1() {
+        final List<Integer> parameters = new ArrayList<Integer>();
+        new Callback1<Integer>() {
+            @Override
+            public void call(Integer i1) {
+                parameters.add(i1);
+            }
+        }.call(1);
+        assertEquals(Arrays.asList(1), parameters);
+    }
+
+    /**
+     * Testing {@link Callback7}.
+     */
+    @SmallTest
+    public void testCallback7() {
+        final List<Integer> parameters = new ArrayList<Integer>();
+        new Callback7<Integer, Integer, Integer, Integer, Integer, Integer, Integer>() {
+            @Override
+            public void call(Integer i1, Integer i2, Integer i3, Integer i4, Integer i5, Integer i6,
+                    Integer i7) {
+                parameters.add(i1);
+                parameters.add(i2);
+                parameters.add(i3);
+                parameters.add(i4);
+                parameters.add(i5);
+                parameters.add(i6);
+                parameters.add(i7);
+            }
+        }.call(1, 2, 3, 4, 5, 6, 7);
+        assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7), parameters);
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
new file mode 100644
index 0000000..2bf733c
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Testing the {@link Connector} class.
+ */
+public class ConnectorTest extends MojoTestCase {
+
+    private static final long RUN_LOOP_TIMEOUT_MS = 25;
+
+    private static final int DATA_LENGTH = 1024;
+
+    private MessagePipeHandle mHandle;
+    private Connector mConnector;
+    private Message mTestMessage;
+    private RecordingMessageReceiver mReceiver;
+    private CapturingErrorHandler mErrorHandler;
+
+    /**
+     * @see MojoTestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
+                new MessagePipeHandle.CreateOptions());
+        mHandle = handles.first;
+        mConnector = new Connector(handles.second);
+        mReceiver = new RecordingMessageReceiver();
+        mConnector.setIncomingMessageReceiver(mReceiver);
+        mErrorHandler = new CapturingErrorHandler();
+        mConnector.setErrorHandler(mErrorHandler);
+        mConnector.start();
+        mTestMessage = BindingsTestUtils.newRandomMessage(DATA_LENGTH);
+        assertNull(mErrorHandler.getLastMojoException());
+        assertEquals(0, mReceiver.messages.size());
+    }
+
+    /**
+     * @see MojoTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        mConnector.close();
+        mHandle.close();
+        super.tearDown();
+    }
+
+    /**
+     * Test sending a message through a {@link Connector}.
+     */
+    @SmallTest
+    public void testSendingMessage() {
+        mConnector.accept(mTestMessage);
+        assertNull(mErrorHandler.getLastMojoException());
+        ByteBuffer received = ByteBuffer.allocateDirect(DATA_LENGTH);
+        MessagePipeHandle.ReadMessageResult result = mHandle.readMessage(received, 0,
+                MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(DATA_LENGTH, result.getMessageSize());
+        assertEquals(mTestMessage.getData(), received);
+    }
+
+    /**
+     * Test receiving a message through a {@link Connector}
+     */
+    @SmallTest
+    public void testReceivingMessage() {
+        mHandle.writeMessage(mTestMessage.getData(), new ArrayList<Handle>(),
+                MessagePipeHandle.WriteFlags.NONE);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertNull(mErrorHandler.getLastMojoException());
+        assertEquals(1, mReceiver.messages.size());
+        Message received = mReceiver.messages.get(0);
+        assertEquals(0, received.getHandles().size());
+        assertEquals(mTestMessage.getData(), received.getData());
+    }
+
+    /**
+     * Test receiving an error through a {@link Connector}.
+     */
+    @SmallTest
+    public void testErrors() {
+        mHandle.close();
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertNotNull(mErrorHandler.getLastMojoException());
+        assertEquals(MojoResult.FAILED_PRECONDITION,
+                mErrorHandler.getLastMojoException().getMojoResult());
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
new file mode 100644
index 0000000..40fa43b
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
@@ -0,0 +1,104 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Testing the executor factory.
+ */
+public class ExecutorFactoryTest extends MojoTestCase {
+
+    private static final long RUN_LOOP_TIMEOUT_MS = 50;
+    private static final int CONCURRENCY_LEVEL = 5;
+    private static final ExecutorService WORKERS = Executors.newFixedThreadPool(CONCURRENCY_LEVEL);
+
+    private Executor mExecutor;
+    private List<Thread> mThreadContainer;
+
+    /**
+     * @see MojoTestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mExecutor = ExecutorFactory.getExecutorForCurrentThread(CoreImpl.getInstance());
+        mThreadContainer = new ArrayList<Thread>();
+    }
+
+    /**
+     * Testing the {@link Executor} when called from the executor thread.
+     */
+    @SmallTest
+    public void testExecutorOnCurrentThread() {
+        Runnable action = new Runnable() {
+            @Override
+            public void run() {
+                mThreadContainer.add(Thread.currentThread());
+            }
+        };
+        mExecutor.execute(action);
+        mExecutor.execute(action);
+        assertEquals(0, mThreadContainer.size());
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(2, mThreadContainer.size());
+        for (Thread thread : mThreadContainer) {
+            assertEquals(Thread.currentThread(), thread);
+        }
+    }
+
+    /**
+     * Testing the {@link Executor} when called from another thread.
+     */
+    @SmallTest
+    public void testExecutorOnOtherThread() {
+        final CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY_LEVEL + 1);
+        for (int i = 0; i < CONCURRENCY_LEVEL; ++i) {
+            WORKERS.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mExecutor.execute(new Runnable() {
+
+                        @Override
+                        public void run() {
+                            mThreadContainer.add(Thread.currentThread());
+                        }
+                    });
+                    try {
+                        barrier.await();
+                    } catch (InterruptedException e) {
+                        fail("Unexpected exception: " + e.getMessage());
+                    } catch (BrokenBarrierException e) {
+                        fail("Unexpected exception: " + e.getMessage());
+                    }
+                }
+            });
+        }
+        try {
+            barrier.await();
+        } catch (InterruptedException e) {
+            fail("Unexpected exception: " + e.getMessage());
+        } catch (BrokenBarrierException e) {
+            fail("Unexpected exception: " + e.getMessage());
+        }
+        assertEquals(0, mThreadContainer.size());
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(CONCURRENCY_LEVEL, mThreadContainer.size());
+        for (Thread thread : mThreadContainer) {
+            assertEquals(Thread.currentThread(), thread);
+        }
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
new file mode 100644
index 0000000..253add9
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
@@ -0,0 +1,345 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.test.mojom.imported.ImportedInterface;
+import org.chromium.mojo.bindings.test.mojom.sample.Factory;
+import org.chromium.mojo.bindings.test.mojom.sample.FactoryClient;
+import org.chromium.mojo.bindings.test.mojom.sample.NamedObject;
+import org.chromium.mojo.bindings.test.mojom.sample.NamedObject.GetNameResponse;
+import org.chromium.mojo.bindings.test.mojom.sample.Request;
+import org.chromium.mojo.bindings.test.mojom.sample.Response;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for interfaces / proxies / stubs generated for sample_factory.mojom.
+ */
+public class InterfacesTest extends MojoTestCase {
+
+    private static final long RUN_LOOP_TIMEOUT_MS = 25;
+
+    private final List<Closeable> mCloseablesToClose = new ArrayList<Closeable>();
+
+    /**
+     * Basic implementation of {@link NamedObject}.
+     */
+    public static class MockNamedObjectImpl extends CapturingErrorHandler implements NamedObject {
+
+        private String mName = "";
+
+        /**
+         * @see org.chromium.mojo.bindings.Interface#close()
+         */
+        @Override
+        public void close() {
+        }
+
+        @Override
+        public void setName(String name) {
+            mName = name;
+        }
+
+        @Override
+        public void getName(GetNameResponse callback) {
+            callback.call(mName);
+        }
+
+        public String getNameSynchronously() {
+            return mName;
+        }
+    }
+
+    /**
+     * Implementation of {@link GetNameResponse} keeping track of usage.
+     */
+    public static class RecordingGetNameResponse implements GetNameResponse {
+        private String mName;
+        private boolean mCalled;
+
+        public RecordingGetNameResponse() {
+            reset();
+        }
+
+        @Override
+        public void call(String name) {
+            mName = name;
+            mCalled = true;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean wasCalled() {
+            return mCalled;
+        }
+
+        public void reset() {
+            mName = null;
+            mCalled = false;
+        }
+    }
+
+    /**
+     * Basic implementation of {@link Factory}.
+     */
+    public class MockFactoryImpl extends CapturingErrorHandler implements Factory {
+
+        private boolean mClosed = false;
+        private FactoryClient mFactoryClient;
+
+        public boolean isClosed() {
+            return mClosed;
+        }
+
+        @Override
+        public void setClient(FactoryClient client) {
+            mFactoryClient = client;
+            mCloseablesToClose.add(client);
+        }
+
+        /**
+         * @see org.chromium.mojo.bindings.Interface#close()
+         */
+        @Override
+        public void close() {
+            mClosed = true;
+        }
+
+        @Override
+        public void doStuff(Request request, MessagePipeHandle pipe) {
+            if (pipe != null) {
+                pipe.close();
+            }
+            Response response = new Response();
+            response.x = 42;
+            mFactoryClient.didStuff(response, "Hello");
+        }
+
+        @Override
+        public void doStuff2(ConsumerHandle pipe) {
+        }
+
+        @Override
+        public void createNamedObject(InterfaceRequest<NamedObject> obj) {
+            NamedObject.MANAGER.bind(new MockNamedObjectImpl(), obj);
+        }
+
+        @Override
+        public void requestImportedInterface(InterfaceRequest<ImportedInterface> obj,
+                RequestImportedInterfaceResponse callback) {
+            throw new UnsupportedOperationException("Not implemented.");
+        }
+
+        @Override
+        public void takeImportedInterface(ImportedInterface obj,
+                TakeImportedInterfaceResponse callback) {
+            throw new UnsupportedOperationException("Not implemented.");
+        }
+    }
+
+    /**
+     * Basic implementation of {@link FactoryClient}.
+     */
+    public static class MockFactoryClientImpl implements FactoryClient {
+
+        private boolean mClosed = false;
+        private boolean mDidStuffCalled = false;
+
+        public boolean isClosed() {
+            return mClosed;
+        }
+
+        public boolean wasDidStuffCalled() {
+            return mDidStuffCalled;
+        }
+
+        /**
+         * @see org.chromium.mojo.bindings.Interface#close()
+         */
+        @Override
+        public void close() {
+            mClosed = true;
+        }
+
+        /**
+         * @see ConnectionErrorHandler#onConnectionError(MojoException)
+         */
+        @Override
+        public void onConnectionError(MojoException e) {
+        }
+
+        /**
+         * @see FactoryClient#didStuff(Response, java.lang.String)
+         */
+        @Override
+        public void didStuff(Response response, String text) {
+            mDidStuffCalled = true;
+        }
+
+        /**
+         * @see FactoryClient#didStuff2(String)
+         */
+        @Override
+        public void didStuff2(String text) {
+        }
+
+    }
+
+    /**
+     * @see MojoTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        // Close the elements in the reverse order they were added. This is needed because it is an
+        // error to close the handle of a proxy without closing the proxy first.
+        Collections.reverse(mCloseablesToClose);
+        for (Closeable c : mCloseablesToClose) {
+            c.close();
+        }
+        super.tearDown();
+    }
+
+    private <I extends Interface, P extends Interface.Proxy> P newProxyOverPipe(
+            Interface.Manager<I, P> manager, I impl) {
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                CoreImpl.getInstance().createMessagePipe(null);
+        P proxy = manager.attachProxy(handles.first);
+        mCloseablesToClose.add(proxy);
+        manager.bind(impl, handles.second);
+        return proxy;
+    }
+
+    private <I extends InterfaceWithClient<C>, P extends InterfaceWithClient.Proxy<C>,
+            C extends Interface> P newProxyOverPipeWithClient(
+            InterfaceWithClient.Manager<I, P, C> manager, I impl, C client) {
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                CoreImpl.getInstance().createMessagePipe(null);
+        P proxy = manager.attachProxy(handles.first, client);
+        mCloseablesToClose.add(proxy);
+        manager.bind(impl, handles.second);
+        return proxy;
+    }
+
+    /**
+     * Check that the given proxy receives the calls. If |impl| is not null, also check that the
+     * calls are forwared to |impl|.
+     */
+    private void checkProxy(NamedObject.Proxy proxy, MockNamedObjectImpl impl) {
+        final String NAME = "hello world";
+        RecordingGetNameResponse callback = new RecordingGetNameResponse();
+        CapturingErrorHandler errorHandler = new CapturingErrorHandler();
+        proxy.setErrorHandler(errorHandler);
+
+        if (impl != null) {
+            assertNull(impl.getLastMojoException());
+            assertEquals("", impl.getNameSynchronously());
+        }
+
+        proxy.getName(callback);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertNull(errorHandler.getLastMojoException());
+        assertTrue(callback.wasCalled());
+        assertEquals("", callback.getName());
+
+        callback.reset();
+        proxy.setName(NAME);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertNull(errorHandler.getLastMojoException());
+        if (impl != null) {
+            assertNull(impl.getLastMojoException());
+            assertEquals(NAME, impl.getNameSynchronously());
+        }
+
+        proxy.getName(callback);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertNull(errorHandler.getLastMojoException());
+        assertTrue(callback.wasCalled());
+        assertEquals(NAME, callback.getName());
+    }
+
+    @SmallTest
+    public void testName() {
+        assertEquals("sample::NamedObject", NamedObject.MANAGER.getName());
+    }
+
+    @SmallTest
+    public void testProxyAndStub() {
+        MockNamedObjectImpl impl = new MockNamedObjectImpl();
+        NamedObject.Proxy proxy =
+                NamedObject.MANAGER.buildProxy(null, NamedObject.MANAGER.buildStub(null, impl));
+
+        checkProxy(proxy, impl);
+    }
+
+    @SmallTest
+    public void testProxyAndStubOverPipe() {
+        MockNamedObjectImpl impl = new MockNamedObjectImpl();
+        NamedObject.Proxy proxy = newProxyOverPipe(NamedObject.MANAGER, impl);
+
+        checkProxy(proxy, impl);
+    }
+
+    @SmallTest
+    public void testFactoryOverPipe() {
+        Factory.Proxy proxy = newProxyOverPipe(Factory.MANAGER, new MockFactoryImpl());
+        Pair<NamedObject.Proxy, InterfaceRequest<NamedObject>> request =
+                NamedObject.MANAGER.getInterfaceRequest(CoreImpl.getInstance());
+        mCloseablesToClose.add(request.first);
+        proxy.createNamedObject(request.second);
+
+        checkProxy(request.first, null);
+    }
+
+    @SmallTest
+    public void testInterfaceClosing() {
+        MockFactoryImpl impl = new MockFactoryImpl();
+        MockFactoryClientImpl client = new MockFactoryClientImpl();
+        Factory.Proxy proxy = newProxyOverPipeWithClient(
+                Factory.MANAGER, impl, client);
+
+        assertFalse(impl.isClosed());
+        assertFalse(client.isClosed());
+
+        proxy.close();
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertTrue(impl.isClosed());
+        assertTrue(client.isClosed());
+    }
+
+    @SmallTest
+    public void testClient() {
+        MockFactoryImpl impl = new MockFactoryImpl();
+        MockFactoryClientImpl client = new MockFactoryClientImpl();
+        Factory.Proxy proxy = newProxyOverPipeWithClient(
+                Factory.MANAGER, impl, client);
+        Request request = new Request();
+        request.x = 42;
+        proxy.doStuff(request, null);
+
+        assertFalse(client.wasDidStuffCalled());
+
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertTrue(client.wasDidStuffCalled());
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
new file mode 100644
index 0000000..8af8be1
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.bindings.test.mojom.imported.Point;
+
+/**
+ * Testing internal classes of interfaces.
+ */
+public class MessageHeaderTest extends TestCase {
+
+    /**
+     * Testing that headers are identical after being serialized/deserialized.
+     */
+    @SmallTest
+    public void testSimpleMessageHeader() {
+        final int xValue = 1;
+        final int yValue = 2;
+        final int type = 6;
+        Point p = new Point();
+        p.x = xValue;
+        p.y = yValue;
+        ServiceMessage message = p.serializeWithHeader(null, new MessageHeader(type));
+
+        MessageHeader header = message.getHeader();
+        assertTrue(header.validateHeader(type, 0));
+        assertEquals(type, header.getType());
+        assertEquals(0, header.getFlags());
+
+        Point p2 = Point.deserialize(message.getPayload());
+        assertNotNull(p2);
+        assertEquals(p.x, p2.x);
+        assertEquals(p.y, p2.y);
+    }
+
+    /**
+     * Testing that headers are identical after being serialized/deserialized.
+     */
+    @SmallTest
+    public void testMessageWithRequestIdHeader() {
+        final int xValue = 1;
+        final int yValue = 2;
+        final int type = 6;
+        final long requestId = 0x1deadbeafL;
+        Point p = new Point();
+        p.x = xValue;
+        p.y = yValue;
+        ServiceMessage message = p.serializeWithHeader(null,
+                new MessageHeader(type, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0));
+        message.setRequestId(requestId);
+
+        MessageHeader header = message.getHeader();
+        assertTrue(header.validateHeader(type, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG));
+        assertEquals(type, header.getType());
+        assertEquals(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, header.getFlags());
+        assertEquals(requestId, header.getRequestId());
+
+        Point p2 = Point.deserialize(message.getPayload());
+        assertNotNull(p2);
+        assertEquals(p.x, p2.x);
+        assertEquals(p.y, p2.y);
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
new file mode 100644
index 0000000..54f0155
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testing {@link Connector#readAndDispatchMessage}.
+ */
+public class ReadAndDispatchMessageTest extends MojoTestCase {
+
+    private static final int DATA_SIZE = 1024;
+
+    private ByteBuffer mData;
+    private Pair<MessagePipeHandle, MessagePipeHandle> mHandles;
+    private List<Handle> mHandlesToSend = new ArrayList<Handle>();
+    private List<Handle> mHandlesToClose = new ArrayList<Handle>();
+    private RecordingMessageReceiver mMessageReceiver;
+
+    /**
+     * @see org.chromium.mojo.MojoTestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Core core = CoreImpl.getInstance();
+        mData = BindingsTestUtils.newRandomMessage(DATA_SIZE).getData();
+        mMessageReceiver = new RecordingMessageReceiver();
+        mHandles = core.createMessagePipe(new MessagePipeHandle.CreateOptions());
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> datapipe = core.createDataPipe(null);
+        mHandlesToSend.addAll(Arrays.asList(datapipe.first, datapipe.second));
+        mHandlesToClose.addAll(Arrays.asList(mHandles.first, mHandles.second));
+        mHandlesToClose.addAll(mHandlesToSend);
+    }
+
+    /**
+     * @see org.chromium.mojo.MojoTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        for (Handle handle : mHandlesToClose) {
+            handle.close();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+     */
+    @SmallTest
+    public void testReadAndDispatchMessage() {
+        mHandles.first.writeMessage(mData, mHandlesToSend, MessagePipeHandle.WriteFlags.NONE);
+        assertEquals(MojoResult.OK,
+                Connector.readAndDispatchMessage(mHandles.second, mMessageReceiver));
+        assertEquals(1, mMessageReceiver.messages.size());
+        Message message = mMessageReceiver.messages.get(0);
+        mHandlesToClose.addAll(message.getHandles());
+        assertEquals(mData, message.getData());
+        assertEquals(2, message.getHandles().size());
+        for (Handle handle : message.getHandles()) {
+            assertTrue(handle.isValid());
+        }
+    }
+
+    /**
+     * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+     * with no message available.
+     */
+    @SmallTest
+    public void testReadAndDispatchMessageOnEmptyHandle() {
+        assertEquals(MojoResult.SHOULD_WAIT,
+                Connector.readAndDispatchMessage(mHandles.second, mMessageReceiver));
+        assertEquals(0, mMessageReceiver.messages.size());
+    }
+
+    /**
+     * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+     * on closed handle.
+     */
+    @SmallTest
+    public void testReadAndDispatchMessageOnClosedHandle() {
+        mHandles.first.close();
+        try {
+            Connector.readAndDispatchMessage(mHandles.second, mMessageReceiver);
+            fail("MojoException should have been thrown");
+        } catch (MojoException expected) {
+            assertEquals(MojoResult.FAILED_PRECONDITION, expected.getMojoResult());
+        }
+        assertEquals(0, mMessageReceiver.messages.size());
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
new file mode 100644
index 0000000..e90bcd2
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Testing {@link Router}
+ */
+public class RouterTest extends MojoTestCase {
+
+    private static final long RUN_LOOP_TIMEOUT_MS = 25;
+
+    private MessagePipeHandle mHandle;
+    private Router mRouter;
+    private RecordingMessageReceiverWithResponder mReceiver;
+    private CapturingErrorHandler mErrorHandler;
+
+    /**
+     * @see MojoTestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        mHandle = handles.first;
+        mRouter = new RouterImpl(handles.second);
+        mReceiver = new RecordingMessageReceiverWithResponder();
+        mRouter.setIncomingMessageReceiver(mReceiver);
+        mErrorHandler = new CapturingErrorHandler();
+        mRouter.setErrorHandler(mErrorHandler);
+        mRouter.start();
+    }
+
+    /**
+     * Testing sending a message via the router that expected a response.
+     */
+    @SmallTest
+    public void testSendingToRouterWithResponse() {
+        final int requestMessageType = 0xdead;
+        final int responseMessageType = 0xbeaf;
+
+        // Sending a message expecting a response.
+        MessageHeader header = new MessageHeader(requestMessageType,
+                MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0);
+        Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+        header.encode(encoder);
+        mRouter.acceptWithResponder(encoder.getMessage(), mReceiver);
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(header.getSize());
+        MessagePipeHandle.ReadMessageResult result = mHandle.readMessage(receiveBuffer, 0,
+                MessagePipeHandle.ReadFlags.NONE);
+
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        MessageHeader receivedHeader = new Message(
+                receiveBuffer, new ArrayList<Handle>()).asServiceMessage().getHeader();
+
+        assertEquals(header.getType(), receivedHeader.getType());
+        assertEquals(header.getFlags(), receivedHeader.getFlags());
+        assertTrue(receivedHeader.getRequestId() != 0);
+
+        // Sending the response.
+        MessageHeader responseHeader = new MessageHeader(responseMessageType,
+                MessageHeader.MESSAGE_IS_RESPONSE_FLAG, receivedHeader.getRequestId());
+        encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+        responseHeader.encode(encoder);
+        Message responseMessage = encoder.getMessage();
+        mHandle.writeMessage(responseMessage.getData(), new ArrayList<Handle>(),
+                MessagePipeHandle.WriteFlags.NONE);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertEquals(1, mReceiver.messages.size());
+        ServiceMessage receivedResponseMessage = mReceiver.messages.get(0).asServiceMessage();
+        assertEquals(MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
+                receivedResponseMessage.getHeader().getFlags());
+        assertEquals(responseMessage.getData(), receivedResponseMessage.getData());
+    }
+
+    /**
+     * Testing receiving a message via the router that expected a response.
+     */
+    @SmallTest
+    public void testReceivingViaRouterWithResponse() {
+        final int requestMessageType = 0xdead;
+        final int responseMessageType = 0xbeef;
+        final int requestId = 0xdeadbeaf;
+
+        // Sending a message expecting a response.
+        MessageHeader header = new MessageHeader(requestMessageType,
+                MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, requestId);
+        Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+        header.encode(encoder);
+        Message headerMessage = encoder.getMessage();
+        mHandle.writeMessage(headerMessage.getData(), new ArrayList<Handle>(),
+                MessagePipeHandle.WriteFlags.NONE);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+
+        assertEquals(1, mReceiver.messagesWithReceivers.size());
+        Pair<Message, MessageReceiver> receivedMessage =
+                mReceiver.messagesWithReceivers.get(0);
+        assertEquals(headerMessage.getData(), receivedMessage.first.getData());
+
+        // Sending the response.
+        MessageHeader responseHeader = new MessageHeader(responseMessageType,
+                MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, requestId);
+        encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+        responseHeader.encode(encoder);
+        Message message = encoder.getMessage();
+        receivedMessage.second.accept(message);
+        ByteBuffer receivedResponseMessage = ByteBuffer.allocateDirect(responseHeader.getSize());
+        MessagePipeHandle.ReadMessageResult result = mHandle.readMessage(receivedResponseMessage, 0,
+                MessagePipeHandle.ReadFlags.NONE);
+
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(message.getData(), receivedResponseMessage);
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
new file mode 100644
index 0000000..a7e213c
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct1;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct2;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct3;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct4;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct5;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct6;
+import org.chromium.mojo.bindings.test.mojom.mojo.StructOfNullables;
+
+/**
+ * Tests for the serialization logic of the generated structs, using structs defined in
+ * mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom .
+ */
+public class SerializationTest extends TestCase {
+
+    private static void assertThrowsSerializationException(Struct struct) {
+        try {
+            struct.serialize(null);
+            fail("Serialization of invalid struct should have thrown an exception.");
+        } catch (SerializationException ex) {
+            // Expected.
+        }
+    }
+
+    /**
+     * Verifies that serializing a struct with an invalid handle of a non-nullable type throws an
+     * exception.
+     */
+    @SmallTest
+    public void testHandle() {
+        Struct2 struct = new Struct2();
+        assertFalse(struct.hdl.isValid());
+        assertThrowsSerializationException(struct);
+
+        // Make the struct valid and verify that it serializes without an exception.
+        struct.hdl = new HandleMock();
+        struct.serialize(null);
+    }
+
+    /**
+     * Verifies that serializing a struct with a null struct pointer throws an exception.
+     */
+    @SmallTest
+    public void testStructPointer() {
+        Struct3 struct = new Struct3();
+        assertNull(struct.struct1);
+        assertThrowsSerializationException(struct);
+
+        // Make the struct valid and verify that it serializes without an exception.
+        struct.struct1 = new Struct1();
+        struct.serialize(null);
+    }
+
+    /**
+     * Verifies that serializing a struct with an array of structs throws an exception when the
+     * struct is invalid.
+     */
+    @SmallTest
+    public void testStructArray() {
+        Struct4 struct = new Struct4();
+        assertNull(struct.data);
+        assertThrowsSerializationException(struct);
+
+        // Create the (1-element) array but have the element null.
+        struct.data = new Struct1[1];
+        assertThrowsSerializationException(struct);
+
+        // Create the array element, struct should serialize now.
+        struct.data[0] = new Struct1();
+        struct.serialize(null);
+    }
+
+    /**
+     * Verifies that serializing a struct with a fixed-size array of incorrect length throws an
+     * exception.
+     */
+    @SmallTest
+    public void testFixedSizeArray() {
+        Struct5 struct = new Struct5();
+        assertNull(struct.pair);
+        assertThrowsSerializationException(struct);
+
+        // Create the (1-element) array, 2-element array is required.
+        struct.pair = new Struct1[1];
+        struct.pair[0] = new Struct1();
+        assertThrowsSerializationException(struct);
+
+        // Create the array of a correct size, struct should serialize now.
+        struct.pair = new Struct1[2];
+        struct.pair[0] = new Struct1();
+        struct.pair[1] = new Struct1();
+        struct.serialize(null);
+    }
+
+    /**
+     * Verifies that serializing a struct with a null string throws an exception.
+     */
+    @SmallTest
+    public void testString() {
+        Struct6 struct = new Struct6();
+        assertNull(struct.str);
+        assertThrowsSerializationException(struct);
+
+        // Make the struct valid and verify that it serializes without an exception.
+        struct.str = "";
+        struct.serialize(null);
+    }
+
+    /**
+     * Verifies that a struct with an invalid nullable handle, null nullable struct pointer and null
+     * nullable string serializes without an exception.
+     */
+    @SmallTest
+    public void testNullableFields() {
+        StructOfNullables struct = new StructOfNullables();
+        assertFalse(struct.hdl.isValid());
+        assertNull(struct.struct1);
+        assertNull(struct.str);
+        struct.serialize(null);
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
new file mode 100644
index 0000000..d0d63bb
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.test.mojom.mojo.ConformanceTestInterface;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface1;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2TestHelper;
+import org.chromium.mojo.system.Handle;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * Testing validation upon deserialization using the interfaces defined in the
+ * mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom file.
+ * <p>
+ * One needs to pass '--test_data=bindings:{path to mojo/public/interfaces/bindings/tests/data}' to
+ * the test_runner script for this test to find the validation data it needs.
+ */
+public class ValidationTest extends MojoTestCase {
+
+    /**
+     * The path where validation test data is.
+     */
+    private static final File VALIDATION_TEST_DATA_PATH =
+            new File(UrlUtils.getTestFilePath("bindings/validation"));
+
+    /**
+     * The data needed for a validation test.
+     */
+    private static class TestData {
+        public File dataFile;
+        public ValidationTestUtil.Data inputData;
+        public String expectedResult;
+    }
+
+    private static class DataFileFilter implements FileFilter {
+        private final String mPrefix;
+
+        public DataFileFilter(String prefix) {
+            this.mPrefix = prefix;
+        }
+
+        @Override
+        public boolean accept(File pathname) {
+            return pathname.isFile() && pathname.getName().startsWith(mPrefix)
+                    && pathname.getName().endsWith(".data");
+        }
+    }
+
+    private static String getStringContent(File f) throws FileNotFoundException {
+        Scanner scanner = new Scanner(f).useDelimiter("\\Z");
+        if (scanner.hasNext()) {
+            return scanner.next();
+        }
+        return "";
+    }
+
+    private static List<TestData> getTestData(String prefix)
+            throws FileNotFoundException {
+        List<TestData> results = new ArrayList<TestData>();
+
+        // Do not fail if the test data is not present.
+        if (!VALIDATION_TEST_DATA_PATH.isDirectory()) {
+            Log.w("ValidationTest", "No test found.");
+            return results;
+        }
+
+        for (File dataFile : VALIDATION_TEST_DATA_PATH.listFiles(new DataFileFilter(prefix))) {
+            File resultFile = new File(dataFile.getParent(),
+                    dataFile.getName().replaceFirst("\\.data$", ".expected"));
+            TestData testData = new TestData();
+            testData.dataFile = dataFile;
+            testData.inputData = ValidationTestUtil.parseData(getStringContent(dataFile));
+            testData.expectedResult = getStringContent(resultFile);
+            results.add(testData);
+        }
+        return results;
+    }
+
+    /**
+     * Runs all the test with the given prefix on the given {@link MessageReceiver}.
+     */
+    private static void runTest(String prefix, MessageReceiver messageReceiver)
+            throws FileNotFoundException {
+        List<TestData> testData = getTestData(prefix);
+        for (TestData test : testData) {
+            assertNull(test.inputData.getErrorMessage());
+            List<Handle> handles = new ArrayList<Handle>();
+            for (int i = 0; i < test.inputData.getHandlesCount(); ++i) {
+                handles.add(new HandleMock());
+            }
+            Message message = new Message(test.inputData.getData(), handles);
+            boolean passed = messageReceiver.accept(message);
+            if (passed && !test.expectedResult.equals("PASS")) {
+                fail("Input: " + test.dataFile.getName() +
+                        ": The message should have been refused. Expected error: " +
+                        test.expectedResult);
+            }
+            if (!passed && test.expectedResult.equals("PASS")) {
+                fail("Input: " + test.dataFile.getName() +
+                        ": The message should have been accepted.");
+            }
+        }
+    }
+
+    private static class RoutingMessageReceiver implements MessageReceiver {
+        private final MessageReceiver mRequest;
+        private final MessageReceiver mResponse;
+
+        private RoutingMessageReceiver(MessageReceiver request, MessageReceiver response) {
+            this.mRequest = request;
+            this.mResponse = response;
+        }
+
+        /**
+         * @see MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            try {
+                MessageHeader header = message.asServiceMessage().getHeader();
+                if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
+                    return mResponse.accept(message);
+                } else {
+                    return mRequest.accept(message);
+                }
+            } catch (DeserializationException e) {
+                return false;
+            }
+        }
+
+        /**
+         * @see MessageReceiver#close()
+         */
+        @Override
+        public void close() {
+        }
+
+    }
+
+    /**
+     * A trivial message receiver that refuses all messages it receives.
+     */
+    private static class SinkMessageReceiver implements MessageReceiverWithResponder {
+
+        @Override
+        public boolean accept(Message message) {
+            return false;
+        }
+
+        @Override
+        public void close() {
+        }
+
+        @Override
+        public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+            return false;
+        }
+    }
+
+    /**
+     * Testing the conformance suite.
+     */
+    @SmallTest
+    public void testConformance() throws FileNotFoundException {
+        runTest("conformance_", ConformanceTestInterface.MANAGER.buildStub(null,
+                ConformanceTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver())));
+    }
+
+    /**
+     * Testing the integration suite.
+     */
+    @SmallTest
+    public void testIntegration() throws FileNotFoundException {
+        runTest("integration_",
+                new RoutingMessageReceiver(IntegrationTestInterface1.MANAGER.buildStub(null,
+                        IntegrationTestInterface1.MANAGER.buildProxy(null,
+                                new SinkMessageReceiver())),
+                        IntegrationTestInterface2TestHelper.
+                                newIntegrationTestInterface2MethodCallback()));
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java
new file mode 100644
index 0000000..7d78c19
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Utility class for testing message validation. The file format used to describe a message is
+ * described in The format is described in
+ * mojo/public/cpp/bindings/tests/validation_test_input_parser.h
+ */
+@JNINamespace("mojo::android")
+public class ValidationTestUtil {
+
+    /**
+     * Content of a '.data' file.
+     */
+    public static class Data {
+        private final ByteBuffer mData;
+        private final int mHandlesCount;
+        private final String mErrorMessage;
+
+        public ByteBuffer getData() {
+            return mData;
+        }
+
+        public int getHandlesCount() {
+            return mHandlesCount;
+        }
+
+        public String getErrorMessage() {
+            return mErrorMessage;
+        }
+
+        private Data(ByteBuffer data, int handlesCount, String errorMessage) {
+            this.mData = data;
+            this.mHandlesCount = handlesCount;
+            this.mErrorMessage = errorMessage;
+        }
+    }
+
+    /**
+     * Parse a '.data' file.
+     */
+    public static Data parseData(String dataAsString) {
+        return nativeParseData(dataAsString);
+    }
+
+    private static native Data nativeParseData(String dataAsString);
+
+    @CalledByNative
+    private static Data buildData(ByteBuffer data, int handlesCount, String errorMessage) {
+        ByteBuffer copiedData = null;
+        if (data != null) {
+            copiedData = ByteBuffer.allocateDirect(data.limit());
+            copiedData.order(ByteOrder.nativeOrder());
+            copiedData.put(data);
+            copiedData.flip();
+        }
+        return new Data(copiedData, handlesCount, errorMessage);
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
new file mode 100644
index 0000000..b0c5a54
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Testing {@link ValidationTestUtil}.
+ */
+public class ValidationTestUtilTest extends MojoTestCase {
+
+    /**
+     * Check that the input parser is correct on a given input.
+     */
+    public static void checkInputParser(
+            String input, boolean isInputValid, ByteBuffer expectedData, int expectedHandlesCount) {
+        ValidationTestUtil.Data data = ValidationTestUtil.parseData(input);
+        if (isInputValid) {
+            assertNull(data.getErrorMessage());
+            assertEquals(expectedData, data.getData());
+            assertEquals(expectedHandlesCount, data.getHandlesCount());
+        } else {
+            assertNotNull(data.getErrorMessage());
+            assertNull(data.getData());
+        }
+    }
+
+    /**
+     * Testing {@link ValidationTestUtil#parseData(String)}.
+     */
+    @SmallTest
+    public void testCorrectMessageParsing() {
+        {
+            // Test empty input.
+            String input = "";
+            ByteBuffer expected = ByteBuffer.allocateDirect(0);
+            expected.order(ByteOrder.nativeOrder());
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            // Test input that only consists of comments and whitespaces.
+            String input = "    \t  // hello world \n\r \t// the answer is 42   ";
+            ByteBuffer expected = ByteBuffer.allocateDirect(0);
+            expected.order(ByteOrder.nativeOrder());
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "[u1]0x10// hello world !! \n\r  \t [u2]65535 \n" +
+                    "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
+            ByteBuffer expected = ByteBuffer.allocateDirect(17);
+            expected.order(ByteOrder.nativeOrder());
+            expected.put((byte) 0x10);
+            expected.putShort((short) 65535);
+            expected.putInt(65536);
+            expected.putLong(-1);
+            expected.put((byte) 0);
+            expected.put((byte) 0xff);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
+            ByteBuffer expected = ByteBuffer.allocateDirect(15);
+            expected.order(ByteOrder.nativeOrder());
+            expected.putLong(-0x800);
+            expected.put((byte) -128);
+            expected.putShort((short) 0);
+            expected.putInt(-40);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "[b]00001011 [b]10000000  // hello world\r [b]00000000";
+            ByteBuffer expected = ByteBuffer.allocateDirect(3);
+            expected.order(ByteOrder.nativeOrder());
+            expected.put((byte) 11);
+            expected.put((byte) 128);
+            expected.put((byte) 0);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "[f]+.3e9 [d]-10.03";
+            ByteBuffer expected = ByteBuffer.allocateDirect(12);
+            expected.order(ByteOrder.nativeOrder());
+            expected.putFloat(+.3e9f);
+            expected.putDouble(-10.03);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
+            ByteBuffer expected = ByteBuffer.allocateDirect(14);
+            expected.order(ByteOrder.nativeOrder());
+            expected.putInt(14);
+            expected.put((byte) 0);
+            expected.putLong(9);
+            expected.put((byte) 0);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 0);
+        }
+        {
+            String input = "// This message has handles! \n[handles]50 [u8]2";
+            ByteBuffer expected = ByteBuffer.allocateDirect(8);
+            expected.order(ByteOrder.nativeOrder());
+            expected.putLong(2);
+            expected.flip();
+
+            checkInputParser(input, true, expected, 50);
+        }
+
+        // Test some failure cases.
+        {
+            String error_inputs[] = {
+                "/ hello world",
+                "[u1]x",
+                "[u2]-1000",
+                "[u1]0x100",
+                "[s2]-0x8001",
+                "[b]1",
+                "[b]1111111k",
+                "[dist4]unmatched",
+                "[anchr]hello [dist8]hello",
+                "[dist4]a [dist4]a [anchr]a",
+                "[dist4]a [anchr]a [dist4]a [anchr]a",
+                "0 [handles]50"
+            };
+
+            for (String input : error_inputs) {
+                ByteBuffer expected = ByteBuffer.allocateDirect(0);
+                expected.order(ByteOrder.nativeOrder());
+                checkInputParser(input, false, expected, 0);
+            }
+        }
+
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java
new file mode 100644
index 0000000..2a2b6b8
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java
@@ -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.
+
+package org.chromium.mojo.bindings.test.mojom.mojo;
+
+import org.chromium.mojo.bindings.MessageReceiver;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2.Method0Response;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2_Internal.IntegrationTestInterface2Method0ResponseParamsForwardToCallback;
+
+/**
+ * Helper class to access {@link IntegrationTestInterface2_Internal} package protected method for
+ * tests.
+ */
+public class IntegrationTestInterface2TestHelper {
+
+    private static final class SinkMethod0Response implements Method0Response {
+        @Override
+        public void call(byte[] arg1) {
+        }
+    }
+
+    /**
+     * Creates a new {@link MessageReceiver} to use for the callback of
+     * |IntegrationTestInterface2#method0(Method0Response)|.
+     */
+    public static MessageReceiver newIntegrationTestInterface2MethodCallback() {
+        return new IntegrationTestInterface2Method0ResponseParamsForwardToCallback(
+                new SinkMethod0Response());
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
new file mode 100644
index 0000000..355156e
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -0,0 +1,838 @@
+// Copyright 2014 The Chromium 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.system.impl;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
+import org.chromium.mojo.system.AsyncWaiter.Cancellable;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Core.WaitManyResult;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.InvalidHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.SharedBufferHandle;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Testing the core API.
+ */
+public class CoreImplTest extends MojoTestCase {
+
+    private static final long RUN_LOOP_TIMEOUT_MS = 5;
+
+    private static final ScheduledExecutorService WORKER =
+            Executors.newSingleThreadScheduledExecutor();
+
+    private List<Handle> mHandlesToClose = new ArrayList<Handle>();
+
+    /**
+     * @see MojoTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        MojoException toThrow = null;
+        for (Handle handle : mHandlesToClose) {
+            try {
+                handle.close();
+            } catch (MojoException e) {
+                if (toThrow == null) {
+                    toThrow = e;
+                }
+            }
+        }
+        if (toThrow != null) {
+            throw toThrow;
+        }
+        super.tearDown();
+    }
+
+    private void addHandleToClose(Handle handle) {
+        mHandlesToClose.add(handle);
+    }
+
+    private void addHandlePairToClose(Pair<? extends Handle, ? extends Handle> handles) {
+        mHandlesToClose.add(handles.first);
+        mHandlesToClose.add(handles.second);
+    }
+
+    /**
+     * Runnable that will close the given handle.
+     */
+    private static class CloseHandle implements Runnable {
+        private Handle mHandle;
+
+        CloseHandle(Handle handle) {
+            mHandle = handle;
+        }
+
+        @Override
+        public void run() {
+            mHandle.close();
+        }
+    }
+
+    private static void checkSendingMessage(MessagePipeHandle in, MessagePipeHandle out) {
+        Random random = new Random();
+
+        // Writing a random 8 bytes message.
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
+        buffer.put(bytes);
+        in.writeMessage(buffer, null, MessagePipeHandle.WriteFlags.NONE);
+
+        // Try to read into a small buffer.
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length / 2);
+        MessagePipeHandle.ReadMessageResult result = out.readMessage(
+                receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult());
+        assertEquals(bytes.length, result.getMessageSize());
+        assertEquals(0, result.getHandlesCount());
+
+        // Read into a correct buffer.
+        receiveBuffer = ByteBuffer.allocateDirect(bytes.length);
+        result = out.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(bytes.length, result.getMessageSize());
+        assertEquals(0, result.getHandlesCount());
+        assertEquals(0, receiveBuffer.position());
+        assertEquals(result.getMessageSize(), receiveBuffer.limit());
+        byte[] receivedBytes = new byte[result.getMessageSize()];
+        receiveBuffer.get(receivedBytes);
+        assertTrue(Arrays.equals(bytes, receivedBytes));
+
+    }
+
+    private static void checkSendingData(DataPipe.ProducerHandle in, DataPipe.ConsumerHandle out) {
+        Random random = new Random();
+
+        // Writing a random 8 bytes message.
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
+        buffer.put(bytes);
+        int result = in.writeData(buffer, DataPipe.WriteFlags.NONE);
+        assertEquals(bytes.length, result);
+
+        // Query number of bytes available.
+        result = out.readData(null, DataPipe.ReadFlags.none().query(true));
+        assertEquals(bytes.length, result);
+
+        // Read into a buffer.
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length);
+        result = out.readData(receiveBuffer, DataPipe.ReadFlags.NONE);
+        assertEquals(bytes.length, result);
+        assertEquals(0, receiveBuffer.position());
+        assertEquals(bytes.length, receiveBuffer.limit());
+        byte[] receivedBytes = new byte[bytes.length];
+        receiveBuffer.get(receivedBytes);
+        assertTrue(Arrays.equals(bytes, receivedBytes));
+    }
+
+    private static void checkSharing(SharedBufferHandle in, SharedBufferHandle out) {
+        Random random = new Random();
+
+        ByteBuffer buffer1 = in.map(0, 8, SharedBufferHandle.MapFlags.NONE);
+        assertEquals(8, buffer1.capacity());
+        ByteBuffer buffer2 = out.map(0, 8, SharedBufferHandle.MapFlags.NONE);
+        assertEquals(8, buffer2.capacity());
+
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        buffer1.put(bytes);
+
+        byte[] receivedBytes = new byte[bytes.length];
+        buffer2.get(receivedBytes);
+
+        assertTrue(Arrays.equals(bytes, receivedBytes));
+
+        in.unmap(buffer1);
+        out.unmap(buffer2);
+    }
+
+    /**
+     * Testing {@link Core#waitMany(List, long)}.
+     */
+    @SmallTest
+    public void testWaitMany() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        List<Pair<Handle, Core.HandleSignals>> handlesToWaitOn = new ArrayList<
+                Pair<Handle, Core.HandleSignals>>();
+        handlesToWaitOn.add(
+                new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE));
+        handlesToWaitOn.add(
+                new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE));
+        WaitManyResult result = core.waitMany(handlesToWaitOn, 0);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(1, result.getHandleIndex());
+
+        handlesToWaitOn.clear();
+        handlesToWaitOn.add(
+                new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE));
+        handlesToWaitOn.add(
+                new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE));
+        result = core.waitMany(handlesToWaitOn, 0);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(0, result.getHandleIndex());
+    }
+
+    /**
+     * Testing that Core can be retrieved from a handle.
+     */
+    @SmallTest
+    public void testGetCore() {
+        Core core = CoreImpl.getInstance();
+
+        Pair<? extends Handle, ? extends Handle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+        assertEquals(core, handles.first.getCore());
+        assertEquals(core, handles.second.getCore());
+
+        handles = core.createDataPipe(null);
+        addHandlePairToClose(handles);
+        assertEquals(core, handles.first.getCore());
+        assertEquals(core, handles.second.getCore());
+
+        SharedBufferHandle handle = core.createSharedBuffer(null, 100);
+        SharedBufferHandle handle2 = handle.duplicate(null);
+        addHandleToClose(handle);
+        addHandleToClose(handle2);
+        assertEquals(core, handle.getCore());
+        assertEquals(core, handle2.getCore());
+    }
+
+    private static void createAndCloseMessagePipe(MessagePipeHandle.CreateOptions options) {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(options);
+        handles.first.close();
+        handles.second.close();
+    }
+
+    /**
+     * Testing {@link MessagePipeHandle} creation.
+     */
+    @SmallTest
+    public void testMessagePipeCreation() {
+        // Test creation with null options.
+        createAndCloseMessagePipe(null);
+        // Test creation with default options.
+        createAndCloseMessagePipe(new MessagePipeHandle.CreateOptions());
+    }
+
+    /**
+     * Testing {@link MessagePipeHandle}.
+     */
+    @SmallTest
+    public void testMessagePipeEmpty() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+        // Testing wait.
+        assertEquals(MojoResult.OK,
+                handles.first.wait(Core.HandleSignals.none().setReadable(true).setWritable(true),
+                        0));
+        assertEquals(MojoResult.OK, handles.first.wait(Core.HandleSignals.WRITABLE, 0));
+        assertEquals(MojoResult.DEADLINE_EXCEEDED,
+                handles.first.wait(Core.HandleSignals.READABLE, 0));
+
+        // Testing read on an empty pipe.
+        MessagePipeHandle.ReadMessageResult result = handles.first.readMessage(null, 0,
+                MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.SHOULD_WAIT, result.getMojoResult());
+
+        // Closing a pipe while waiting.
+        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
+        assertEquals(MojoResult.CANCELLED,
+                handles.first.wait(Core.HandleSignals.READABLE, 1000000L));
+
+        handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        // Closing the other pipe while waiting.
+        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
+        assertEquals(MojoResult.FAILED_PRECONDITION,
+                handles.second.wait(Core.HandleSignals.READABLE, 1000000L));
+
+        // Waiting on a closed pipe.
+        assertEquals(MojoResult.FAILED_PRECONDITION,
+                handles.second.wait(Core.HandleSignals.READABLE, 0));
+        assertEquals(MojoResult.FAILED_PRECONDITION,
+                handles.second.wait(Core.HandleSignals.WRITABLE, 0));
+    }
+
+    /**
+     * Testing {@link MessagePipeHandle}.
+     */
+    @SmallTest
+    public void testMessagePipeSend() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        checkSendingMessage(handles.first, handles.second);
+        checkSendingMessage(handles.second, handles.first);
+    }
+
+    /**
+     * Testing {@link MessagePipeHandle}.
+     */
+    @SmallTest
+    public void testMessagePipeReceiveOnSmallBuffer() {
+        Random random = new Random();
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        // Writing a random 8 bytes message.
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
+        buffer.put(bytes);
+        handles.first.writeMessage(buffer, null, MessagePipeHandle.WriteFlags.NONE);
+
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(1);
+        MessagePipeHandle.ReadMessageResult result = handles.second
+                .readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult());
+        assertEquals(bytes.length, result.getMessageSize());
+        assertEquals(0, result.getHandlesCount());
+    }
+
+    /**
+     * Testing {@link MessagePipeHandle}.
+     */
+    @SmallTest
+    public void testMessagePipeSendHandles() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        Pair<MessagePipeHandle, MessagePipeHandle> handlesToShare = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+        addHandlePairToClose(handlesToShare);
+
+        handles.first.writeMessage(null,
+                Collections.<Handle> singletonList(handlesToShare.second),
+                MessagePipeHandle.WriteFlags.NONE);
+        assertFalse(handlesToShare.second.isValid());
+        MessagePipeHandle.ReadMessageResult readMessageResult =
+                handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(1, readMessageResult.getHandlesCount());
+        MessagePipeHandle newHandle = readMessageResult.getHandles().get(0)
+                .toMessagePipeHandle();
+        addHandleToClose(newHandle);
+        assertTrue(newHandle.isValid());
+        checkSendingMessage(handlesToShare.first, newHandle);
+        checkSendingMessage(newHandle, handlesToShare.first);
+    }
+
+    private static void createAndCloseDataPipe(DataPipe.CreateOptions options) {
+        Core core = CoreImpl.getInstance();
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(
+                options);
+        handles.first.close();
+        handles.second.close();
+    }
+
+    /**
+     * Testing {@link DataPipe}.
+     */
+    @SmallTest
+    public void testDataPipeCreation() {
+        // Create datapipe with null options.
+        createAndCloseDataPipe(null);
+        DataPipe.CreateOptions options = new DataPipe.CreateOptions();
+        // Create datapipe with element size set.
+        options.setElementNumBytes(24);
+        createAndCloseDataPipe(options);
+        // Create datapipe with a flag set.
+        options.getFlags().setMayDiscard(true);
+        createAndCloseDataPipe(options);
+        // Create datapipe with capacity set.
+        options.setCapacityNumBytes(1024 * options.getElementNumBytes());
+        createAndCloseDataPipe(options);
+    }
+
+    /**
+     * Testing {@link DataPipe}.
+     */
+    @SmallTest
+    public void testDataPipeSend() {
+        Core core = CoreImpl.getInstance();
+
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null);
+        addHandlePairToClose(handles);
+
+        checkSendingData(handles.first, handles.second);
+    }
+
+    /**
+     * Testing {@link DataPipe}.
+     */
+    @SmallTest
+    public void testDataPipeTwoPhaseSend() {
+        Random random = new Random();
+        Core core = CoreImpl.getInstance();
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null);
+        addHandlePairToClose(handles);
+
+        // Writing a random 8 bytes message.
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        ByteBuffer buffer = handles.first.beginWriteData(bytes.length,
+                DataPipe.WriteFlags.NONE);
+        assertTrue(buffer.capacity() >= bytes.length);
+        buffer.put(bytes);
+        handles.first.endWriteData(bytes.length);
+
+        // Read into a buffer.
+        ByteBuffer receiveBuffer = handles.second.beginReadData(bytes.length,
+                DataPipe.ReadFlags.NONE);
+        assertEquals(0, receiveBuffer.position());
+        assertEquals(bytes.length, receiveBuffer.limit());
+        byte[] receivedBytes = new byte[bytes.length];
+        receiveBuffer.get(receivedBytes);
+        assertTrue(Arrays.equals(bytes, receivedBytes));
+        handles.second.endReadData(bytes.length);
+    }
+
+    /**
+     * Testing {@link DataPipe}.
+     */
+    @SmallTest
+    public void testDataPipeDiscard() {
+        Random random = new Random();
+        Core core = CoreImpl.getInstance();
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null);
+        addHandlePairToClose(handles);
+
+        // Writing a random 8 bytes message.
+        byte[] bytes = new byte[8];
+        random.nextBytes(bytes);
+        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
+        buffer.put(bytes);
+        int result = handles.first.writeData(buffer, DataPipe.WriteFlags.NONE);
+        assertEquals(bytes.length, result);
+
+        // Discard bytes.
+        final int nbBytesToDiscard = 4;
+        assertEquals(nbBytesToDiscard,
+                handles.second.discardData(nbBytesToDiscard, DataPipe.ReadFlags.NONE));
+
+        // Read into a buffer.
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length - nbBytesToDiscard);
+        result = handles.second.readData(receiveBuffer, DataPipe.ReadFlags.NONE);
+        assertEquals(bytes.length - nbBytesToDiscard, result);
+        assertEquals(0, receiveBuffer.position());
+        assertEquals(bytes.length - nbBytesToDiscard, receiveBuffer.limit());
+        byte[] receivedBytes = new byte[bytes.length - nbBytesToDiscard];
+        receiveBuffer.get(receivedBytes);
+        assertTrue(Arrays.equals(Arrays.copyOfRange(bytes, nbBytesToDiscard, bytes.length),
+                receivedBytes));
+    }
+
+    /**
+     * Testing {@link SharedBufferHandle}.
+     */
+    @SmallTest
+    public void testSharedBufferCreation() {
+        Core core = CoreImpl.getInstance();
+        // Test creation with empty options.
+        core.createSharedBuffer(null, 8).close();
+        // Test creation with default options.
+        core.createSharedBuffer(new SharedBufferHandle.CreateOptions(), 8).close();
+    }
+
+    /**
+     * Testing {@link SharedBufferHandle}.
+     */
+    @SmallTest
+    public void testSharedBufferDuplication() {
+        Core core = CoreImpl.getInstance();
+        SharedBufferHandle handle = core.createSharedBuffer(null, 8);
+        addHandleToClose(handle);
+
+        // Test duplication with empty options.
+        handle.duplicate(null).close();
+        // Test creation with default options.
+        handle.duplicate(new SharedBufferHandle.DuplicateOptions()).close();
+    }
+
+    /**
+     * Testing {@link SharedBufferHandle}.
+     */
+    @SmallTest
+    public void testSharedBufferSending() {
+        Core core = CoreImpl.getInstance();
+        SharedBufferHandle handle = core.createSharedBuffer(null, 8);
+        addHandleToClose(handle);
+        SharedBufferHandle newHandle = handle.duplicate(null);
+        addHandleToClose(newHandle);
+
+        checkSharing(handle, newHandle);
+        checkSharing(newHandle, handle);
+    }
+
+    /**
+     * Testing that invalid handle can be used with this implementation.
+     */
+    @SmallTest
+    public void testInvalidHandle() {
+        Core core = CoreImpl.getInstance();
+        Handle handle = InvalidHandle.INSTANCE;
+
+        // Checking wait.
+        boolean exception = false;
+        try {
+            core.wait(handle, Core.HandleSignals.WRITABLE, 0);
+        } catch (MojoException e) {
+            assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult());
+            exception = true;
+        }
+        assertTrue(exception);
+
+        // Checking waitMany.
+        exception = false;
+        try {
+            List<Pair<Handle, Core.HandleSignals>> handles = new ArrayList<
+                    Pair<Handle, Core.HandleSignals>>();
+            handles.add(Pair.create(handle, Core.HandleSignals.WRITABLE));
+            core.waitMany(handles, 0);
+        } catch (MojoException e) {
+            assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult());
+            exception = true;
+        }
+        assertTrue(exception);
+
+        // Checking sending an invalid handle.
+        // Until the behavior is changed on the C++ side, handle gracefully 2 different use case:
+        // - Receive a INVALID_ARGUMENT exception
+        // - Receive an invalid handle on the other side.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+        try {
+            handles.first.writeMessage(null, Collections.<Handle> singletonList(handle),
+                    MessagePipeHandle.WriteFlags.NONE);
+            MessagePipeHandle.ReadMessageResult readMessageResult =
+                    handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE);
+            assertEquals(1, readMessageResult.getHandlesCount());
+            assertFalse(readMessageResult.getHandles().get(0).isValid());
+        } catch (MojoException e) {
+            assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult());
+        }
+    }
+
+    private static class AsyncWaiterResult implements Callback {
+        private int mResult = Integer.MIN_VALUE;
+        private MojoException mException = null;
+
+        /**
+         * @see Callback#onResult(int)
+         */
+        @Override
+        public void onResult(int result) {
+            this.mResult = result;
+        }
+
+        /**
+         * @see Callback#onError(MojoException)
+         */
+        @Override
+        public void onError(MojoException exception) {
+            this.mException = exception;
+        }
+
+        /**
+         * @return the result
+         */
+        public int getResult() {
+            return mResult;
+        }
+
+        /**
+         * @return the exception
+         */
+        public MojoException getException() {
+            return mException;
+        }
+
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterCorrectResult() {
+        Core core = CoreImpl.getInstance();
+
+        // Checking a correct result.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+                Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.second.writeMessage(ByteBuffer.allocateDirect(1), null,
+                MessagePipeHandle.WriteFlags.NONE);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertNull(asyncWaiterResult.getException());
+        assertEquals(MojoResult.OK, asyncWaiterResult.getResult());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterClosingPeerHandle() {
+        Core core = CoreImpl.getInstance();
+
+        // Closing the peer handle.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+                Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.second.close();
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertNull(asyncWaiterResult.getException());
+        assertEquals(MojoResult.FAILED_PRECONDITION, asyncWaiterResult.getResult());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterClosingWaitingHandle() {
+        Core core = CoreImpl.getInstance();
+
+        // Closing the peer handle.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+                Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        cancellable.cancel();
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        // TODO(qsr) Re-enable when MojoWaitMany handles it correctly.
+        // assertNull(asyncWaiterResult.getException());
+        // assertEquals(MojoResult.CANCELLED, asyncWaiterResult.getResult());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterWaitingWithTimeout() {
+        Core core = CoreImpl.getInstance();
+
+        // Closing the peer handle.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+                RUN_LOOP_TIMEOUT_MS, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        nativeRunLoop(10 * RUN_LOOP_TIMEOUT_MS);
+        assertNull(asyncWaiterResult.getException());
+        assertEquals(MojoResult.DEADLINE_EXCEEDED, asyncWaiterResult.getResult());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterCancelWaiting() {
+        Core core = CoreImpl.getInstance();
+
+        // Closing the peer handle.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+                Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        cancellable.cancel();
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.second.writeMessage(ByteBuffer.allocateDirect(1), null,
+                MessagePipeHandle.WriteFlags.NONE);
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterImmediateCancelOnInvalidHandle() {
+        Core core = CoreImpl.getInstance();
+
+        // Closing the peer handle.
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        handles.first.close();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+                Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+        cancellable.cancel();
+
+        nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+    }
+
+    /**
+     * Testing the pass method on message pipes.
+     */
+    @SmallTest
+    public void testMessagePipeHandlePass() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        assertTrue(handles.first.isValid());
+        MessagePipeHandle handleClone = handles.first.pass();
+
+        addHandleToClose(handleClone);
+
+        assertFalse(handles.first.isValid());
+        assertTrue(handleClone.isValid());
+        checkSendingMessage(handleClone, handles.second);
+        checkSendingMessage(handles.second, handleClone);
+    }
+
+    /**
+     * Testing the pass method on data pipes.
+     */
+    @SmallTest
+    public void testDataPipeHandlePass() {
+        Core core = CoreImpl.getInstance();
+        Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null);
+        addHandlePairToClose(handles);
+
+        DataPipe.ProducerHandle producerClone = handles.first.pass();
+        DataPipe.ConsumerHandle consumerClone = handles.second.pass();
+
+        addHandleToClose(producerClone);
+        addHandleToClose(consumerClone);
+
+        assertFalse(handles.first.isValid());
+        assertFalse(handles.second.isValid());
+        assertTrue(producerClone.isValid());
+        assertTrue(consumerClone.isValid());
+        checkSendingData(producerClone, consumerClone);
+    }
+
+    /**
+     * Testing the pass method on shared buffers.
+     */
+    @SmallTest
+    public void testSharedBufferPass() {
+        Core core = CoreImpl.getInstance();
+        SharedBufferHandle handle = core.createSharedBuffer(null, 8);
+        addHandleToClose(handle);
+        SharedBufferHandle newHandle = handle.duplicate(null);
+        addHandleToClose(newHandle);
+
+        SharedBufferHandle handleClone = handle.pass();
+        SharedBufferHandle newHandleClone = newHandle.pass();
+
+        addHandleToClose(handleClone);
+        addHandleToClose(newHandleClone);
+
+        assertFalse(handle.isValid());
+        assertTrue(handleClone.isValid());
+        checkSharing(handleClone, newHandleClone);
+        checkSharing(newHandleClone, handleClone);
+    }
+
+    /**
+     * esting handle conversion to native and back.
+     */
+    @SmallTest
+    public void testHandleConversion() {
+        Core core = CoreImpl.getInstance();
+        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        MessagePipeHandle converted =
+                core.acquireNativeHandle(handles.first.releaseNativeHandle()).toMessagePipeHandle();
+        addHandleToClose(converted);
+
+        assertFalse(handles.first.isValid());
+
+        checkSendingMessage(converted, handles.second);
+        checkSendingMessage(handles.second, converted);
+    }
+}
diff --git a/mojo/android/javatests/validation_test_util.cc b/mojo/android/javatests/validation_test_util.cc
new file mode 100644
index 0000000..2289e02
--- /dev/null
+++ b/mojo/android/javatests/validation_test_util.cc
@@ -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.
+
+#include "mojo/android/javatests/validation_test_util.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/test/test_support_android.h"
+#include "jni/ValidationTestUtil_jni.h"
+#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
+
+namespace mojo {
+namespace android {
+
+bool RegisterValidationTestUtil(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+jobject ParseData(JNIEnv* env, jclass jcaller, jstring data_as_string) {
+  std::string input =
+      base::android::ConvertJavaStringToUTF8(env, data_as_string);
+  std::vector<uint8_t> data;
+  size_t num_handles;
+  std::string error_message;
+  if (!test::ParseValidationTestInput(
+          input, &data, &num_handles, &error_message)) {
+    ScopedJavaLocalRef<jstring> j_error_message =
+        base::android::ConvertUTF8ToJavaString(env, error_message);
+    return Java_ValidationTestUtil_buildData(
+               env, NULL, 0, j_error_message.obj()).Release();
+  }
+  void* data_ptr = &data[0];
+  if (!data_ptr) {
+    DCHECK(!data.size());
+    data_ptr = &data;
+  }
+  jobject byte_buffer =
+      env->NewDirectByteBuffer(data_ptr, data.size());
+  return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles, NULL)
+      .Release();
+}
+
+}  // namespace android
+}  // namespace mojo
diff --git a/mojo/android/javatests/validation_test_util.h b/mojo/android/javatests/validation_test_util.h
new file mode 100644
index 0000000..f58dc07
--- /dev/null
+++ b/mojo/android/javatests/validation_test_util.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_ANDROID_JAVATESTS_VALIDATION_TEST_UTIL_H_
+#define MOJO_ANDROID_JAVATESTS_VALIDATION_TEST_UTIL_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterValidationTestUtil(JNIEnv* env);
+
+}  // namespace android
+}  // namespace mojo
+
+#endif  // MOJO_SYSTEM_ANDROID_JAVATESTS_VALIDATION_TEST_UTIL_H_
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
new file mode 100644
index 0000000..b862130
--- /dev/null
+++ b/mojo/android/system/core_impl.cc
@@ -0,0 +1,378 @@
+// Copyright 2014 The Chromium 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/android/system/core_impl.h"
+
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/CoreImpl_jni.h"
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace {
+
+// |AsyncWait| is guaranteed never to return 0.
+const MojoAsyncWaitID kInvalidHandleCancelID = 0;
+
+struct AsyncWaitCallbackData {
+  base::android::ScopedJavaGlobalRef<jobject> core_impl;
+  base::android::ScopedJavaGlobalRef<jobject> callback;
+  base::android::ScopedJavaGlobalRef<jobject> cancellable;
+
+  AsyncWaitCallbackData(JNIEnv* env, jobject core_impl, jobject callback) {
+    this->core_impl.Reset(env, core_impl);
+    this->callback.Reset(env, callback);
+  }
+};
+
+void AsyncWaitCallback(void* data, MojoResult result) {
+  scoped_ptr<AsyncWaitCallbackData> callback_data(
+      static_cast<AsyncWaitCallbackData*>(data));
+  mojo::android::Java_CoreImpl_onAsyncWaitResult(
+      base::android::AttachCurrentThread(),
+      callback_data->core_impl.obj(),
+      result,
+      callback_data->callback.obj(),
+      callback_data->cancellable.obj());
+}
+
+}  // namespace
+
+namespace mojo {
+namespace android {
+
+static jlong GetTimeTicksNow(JNIEnv* env, jobject jcaller) {
+  return MojoGetTimeTicksNow();
+}
+
+static jint WaitMany(JNIEnv* env,
+                     jobject jcaller,
+                     jobject buffer,
+                     jlong deadline) {
+  // Buffer contains first the list of handles, then the list of signals.
+  const void* buffer_start = env->GetDirectBufferAddress(buffer);
+  DCHECK(buffer_start);
+  DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
+  const size_t record_size = 8;
+  const size_t buffer_size = env->GetDirectBufferCapacity(buffer);
+  DCHECK_EQ(buffer_size % record_size, 0u);
+
+  const size_t nb_handles = buffer_size / record_size;
+  const MojoHandle* handle_start = static_cast<const MojoHandle*>(buffer_start);
+  const MojoHandleSignals* signals_start =
+      static_cast<const MojoHandleSignals*>(handle_start + nb_handles);
+  return MojoWaitMany(handle_start, signals_start, nb_handles, deadline);
+}
+
+static jobject CreateMessagePipe(JNIEnv* env,
+                                 jobject jcaller,
+                                 jobject options_buffer) {
+  const MojoCreateMessagePipeOptions* options = NULL;
+  if (options_buffer) {
+    const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+    DCHECK(buffer_start);
+    DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
+    const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+    DCHECK_EQ(buffer_size, sizeof(MojoCreateMessagePipeOptions));
+    options = static_cast<const MojoCreateMessagePipeOptions*>(buffer_start);
+    DCHECK_EQ(options->struct_size, buffer_size);
+  }
+  MojoHandle handle1;
+  MojoHandle handle2;
+  MojoResult result = MojoCreateMessagePipe(options, &handle1, &handle2);
+  return Java_CoreImpl_newNativeCreationResult(env, result, handle1, handle2)
+      .Release();
+}
+
+static jobject CreateDataPipe(JNIEnv* env,
+                              jobject jcaller,
+                              jobject options_buffer) {
+  const MojoCreateDataPipeOptions* options = NULL;
+  if (options_buffer) {
+    const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+    DCHECK(buffer_start);
+    DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
+    const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+    DCHECK_EQ(buffer_size, sizeof(MojoCreateDataPipeOptions));
+    options = static_cast<const MojoCreateDataPipeOptions*>(buffer_start);
+    DCHECK_EQ(options->struct_size, buffer_size);
+  }
+  MojoHandle handle1;
+  MojoHandle handle2;
+  MojoResult result = MojoCreateDataPipe(options, &handle1, &handle2);
+  return Java_CoreImpl_newNativeCreationResult(env, result, handle1, handle2)
+      .Release();
+}
+
+static jobject CreateSharedBuffer(JNIEnv* env,
+                                  jobject jcaller,
+                                  jobject options_buffer,
+                                  jlong num_bytes) {
+  const MojoCreateSharedBufferOptions* options = 0;
+  if (options_buffer) {
+    const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+    DCHECK(buffer_start);
+    DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
+    const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+    DCHECK_EQ(buffer_size, sizeof(MojoCreateSharedBufferOptions));
+    options = static_cast<const MojoCreateSharedBufferOptions*>(buffer_start);
+    DCHECK_EQ(options->struct_size, buffer_size);
+  }
+  MojoHandle handle;
+  MojoResult result = MojoCreateSharedBuffer(options, num_bytes, &handle);
+  return Java_CoreImpl_newNativeCreationResult(env, result, handle, 0)
+      .Release();
+}
+
+static jint Close(JNIEnv* env, jobject jcaller, jint mojo_handle) {
+  return MojoClose(mojo_handle);
+}
+
+static jint Wait(JNIEnv* env,
+                 jobject jcaller,
+                 jint mojo_handle,
+                 jint signals,
+                 jlong deadline) {
+  return MojoWait(mojo_handle, signals, deadline);
+}
+
+static jint WriteMessage(JNIEnv* env,
+                         jobject jcaller,
+                         jint mojo_handle,
+                         jobject bytes,
+                         jint num_bytes,
+                         jobject handles_buffer,
+                         jint flags) {
+  const void* buffer_start = 0;
+  uint32_t buffer_size = 0;
+  if (bytes) {
+    buffer_start = env->GetDirectBufferAddress(bytes);
+    DCHECK(buffer_start);
+    DCHECK(env->GetDirectBufferCapacity(bytes) >= num_bytes);
+    buffer_size = num_bytes;
+  }
+  const MojoHandle* handles = 0;
+  uint32_t num_handles = 0;
+  if (handles_buffer) {
+    handles =
+        static_cast<MojoHandle*>(env->GetDirectBufferAddress(handles_buffer));
+    num_handles = env->GetDirectBufferCapacity(handles_buffer) / 4;
+  }
+  // Java code will handle invalidating handles if the write succeeded.
+  return MojoWriteMessage(
+      mojo_handle, buffer_start, buffer_size, handles, num_handles, flags);
+}
+
+static jobject ReadMessage(JNIEnv* env,
+                           jobject jcaller,
+                           jint mojo_handle,
+                           jobject bytes,
+                           jobject handles_buffer,
+                           jint flags) {
+  void* buffer_start = 0;
+  uint32_t buffer_size = 0;
+  if (bytes) {
+    buffer_start = env->GetDirectBufferAddress(bytes);
+    DCHECK(buffer_start);
+    buffer_size = env->GetDirectBufferCapacity(bytes);
+  }
+  MojoHandle* handles = 0;
+  uint32_t num_handles = 0;
+  if (handles_buffer) {
+    handles =
+        static_cast<MojoHandle*>(env->GetDirectBufferAddress(handles_buffer));
+    num_handles = env->GetDirectBufferCapacity(handles_buffer) / 4;
+  }
+  MojoResult result = MojoReadMessage(
+      mojo_handle, buffer_start, &buffer_size, handles, &num_handles, flags);
+  // Jave code will handle taking ownership of any received handle.
+  return Java_CoreImpl_newReadMessageResult(
+             env, result, buffer_size, num_handles).Release();
+}
+
+static jint ReadData(JNIEnv* env,
+                     jobject jcaller,
+                     jint mojo_handle,
+                     jobject elements,
+                     jint elements_capacity,
+                     jint flags) {
+  void* buffer_start = 0;
+  uint32_t buffer_size = elements_capacity;
+  if (elements) {
+    buffer_start = env->GetDirectBufferAddress(elements);
+    DCHECK(buffer_start);
+    DCHECK(elements_capacity <= env->GetDirectBufferCapacity(elements));
+  }
+  MojoResult result =
+      MojoReadData(mojo_handle, buffer_start, &buffer_size, flags);
+  if (result < 0) {
+    return result;
+  }
+  return buffer_size;
+}
+
+static jobject BeginReadData(JNIEnv* env,
+                             jobject jcaller,
+                             jint mojo_handle,
+                             jint num_bytes,
+                             jint flags) {
+  void const* buffer = 0;
+  uint32_t buffer_size = num_bytes;
+  MojoResult result =
+      MojoBeginReadData(mojo_handle, &buffer, &buffer_size, flags);
+  jobject byte_buffer = 0;
+  if (result == MOJO_RESULT_OK) {
+    byte_buffer =
+        env->NewDirectByteBuffer(const_cast<void*>(buffer), buffer_size);
+  }
+  return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+      .Release();
+}
+
+static jint EndReadData(JNIEnv* env,
+                        jobject jcaller,
+                        jint mojo_handle,
+                        jint num_bytes_read) {
+  return MojoEndReadData(mojo_handle, num_bytes_read);
+}
+
+static jint WriteData(JNIEnv* env,
+                      jobject jcaller,
+                      jint mojo_handle,
+                      jobject elements,
+                      jint limit,
+                      jint flags) {
+  void* buffer_start = env->GetDirectBufferAddress(elements);
+  DCHECK(buffer_start);
+  DCHECK(limit <= env->GetDirectBufferCapacity(elements));
+  uint32_t buffer_size = limit;
+  MojoResult result =
+      MojoWriteData(mojo_handle, buffer_start, &buffer_size, flags);
+  if (result < 0) {
+    return result;
+  }
+  return buffer_size;
+}
+
+static jobject BeginWriteData(JNIEnv* env,
+                              jobject jcaller,
+                              jint mojo_handle,
+                              jint num_bytes,
+                              jint flags) {
+  void* buffer = 0;
+  uint32_t buffer_size = num_bytes;
+  MojoResult result =
+      MojoBeginWriteData(mojo_handle, &buffer, &buffer_size, flags);
+  jobject byte_buffer = 0;
+  if (result == MOJO_RESULT_OK) {
+    byte_buffer = env->NewDirectByteBuffer(buffer, buffer_size);
+  }
+  return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+      .Release();
+}
+
+static jint EndWriteData(JNIEnv* env,
+                         jobject jcaller,
+                         jint mojo_handle,
+                         jint num_bytes_written) {
+  return MojoEndWriteData(mojo_handle, num_bytes_written);
+}
+
+static jobject Duplicate(JNIEnv* env,
+                         jobject jcaller,
+                         jint mojo_handle,
+                         jobject options_buffer) {
+  const MojoDuplicateBufferHandleOptions* options = 0;
+  if (options_buffer) {
+    const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+    DCHECK(buffer_start);
+    const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+    DCHECK_EQ(buffer_size, sizeof(MojoDuplicateBufferHandleOptions));
+    options =
+        static_cast<const MojoDuplicateBufferHandleOptions*>(buffer_start);
+    DCHECK_EQ(options->struct_size, buffer_size);
+  }
+  MojoHandle handle;
+  MojoResult result = MojoDuplicateBufferHandle(mojo_handle, options, &handle);
+  return Java_CoreImpl_newNativeCreationResult(env, result, handle, 0)
+      .Release();
+}
+
+static jobject Map(JNIEnv* env,
+                   jobject jcaller,
+                   jint mojo_handle,
+                   jlong offset,
+                   jlong num_bytes,
+                   jint flags) {
+  void* buffer = 0;
+  MojoResult result =
+      MojoMapBuffer(mojo_handle, offset, num_bytes, &buffer, flags);
+  jobject byte_buffer = 0;
+  if (result == MOJO_RESULT_OK) {
+    byte_buffer = env->NewDirectByteBuffer(buffer, num_bytes);
+  }
+  return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+      .Release();
+}
+
+static int Unmap(JNIEnv* env, jobject jcaller, jobject buffer) {
+  void* buffer_start = env->GetDirectBufferAddress(buffer);
+  DCHECK(buffer_start);
+  return MojoUnmapBuffer(buffer_start);
+}
+
+static jobject AsyncWait(JNIEnv* env,
+                         jobject jcaller,
+                         jint mojo_handle,
+                         jint signals,
+                         jlong deadline,
+                         jobject callback) {
+  AsyncWaitCallbackData* callback_data =
+      new AsyncWaitCallbackData(env, jcaller, callback);
+  MojoAsyncWaitID cancel_id;
+  if (static_cast<MojoHandle>(mojo_handle) != MOJO_HANDLE_INVALID) {
+    cancel_id = Environment::GetDefaultAsyncWaiter()->AsyncWait(
+        mojo_handle, signals, deadline, AsyncWaitCallback, callback_data);
+  } else {
+    cancel_id = kInvalidHandleCancelID;
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &AsyncWaitCallback, callback_data, MOJO_RESULT_INVALID_ARGUMENT));
+  }
+  base::android::ScopedJavaLocalRef<jobject> cancellable =
+      Java_CoreImpl_newAsyncWaiterCancellableImpl(
+          env, jcaller, cancel_id, reinterpret_cast<intptr_t>(callback_data));
+  callback_data->cancellable.Reset(env, cancellable.obj());
+  return cancellable.Release();
+}
+
+static void CancelAsyncWait(JNIEnv* env,
+                            jobject jcaller,
+                            jlong id,
+                            jlong data_ptr) {
+  if (id == 0) {
+    // If |id| is |kInvalidHandleCancelID|, the async wait was done on an
+    // invalid handle, so the AsyncWaitCallback will be called and will clear
+    // the data_ptr.
+    return;
+  }
+  scoped_ptr<AsyncWaitCallbackData> deleter(
+      reinterpret_cast<AsyncWaitCallbackData*>(data_ptr));
+  Environment::GetDefaultAsyncWaiter()->CancelWait(id);
+}
+
+bool RegisterCoreImpl(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android
+}  // namespace mojo
diff --git a/mojo/android/system/core_impl.h b/mojo/android/system/core_impl.h
new file mode 100644
index 0000000..c624999
--- /dev/null
+++ b/mojo/android/system/core_impl.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
+#define MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterCoreImpl(JNIEnv* env);
+
+}  // namespace android
+}  // namespace mojo
+
+#endif  // MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
new file mode 100644
index 0000000..1fd8076
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -0,0 +1,671 @@
+// Copyright 2014 The Chromium 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.system.impl;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.SharedBufferHandle.DuplicateOptions;
+import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
+import org.chromium.mojo.system.UntypedHandle;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link Core}.
+ */
+@JNINamespace("mojo::android")
+public class CoreImpl implements Core, AsyncWaiter {
+
+    /**
+     * Discard flag for the |MojoReadData| operation.
+     */
+    private static final int MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
+
+    /**
+     * the size of a handle, in bytes.
+     */
+    private static final int HANDLE_SIZE = 4;
+
+    /**
+     * the size of a flag, in bytes.
+     */
+    private static final int FLAG_SIZE = 4;
+
+    /**
+     * The mojo handle for an invalid handle.
+     */
+    static final int INVALID_HANDLE = 0;
+
+    private static class LazyHolder {
+        private static final Core INSTANCE = new CoreImpl();
+    }
+
+    /**
+     * @return the instance.
+     */
+    public static Core getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    private CoreImpl() {
+    }
+
+    /**
+     * @see Core#getTimeTicksNow()
+     */
+    @Override
+    public long getTimeTicksNow() {
+        return nativeGetTimeTicksNow();
+    }
+
+    /**
+     * @see Core#waitMany(List, long)
+     */
+    @Override
+    public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline) {
+        // Allocate a direct buffer to allow native code not to reach back to java. Buffer will
+        // contain all mojo handles, followed by all flags values.
+        ByteBuffer buffer = allocateDirectBuffer(handles.size() * 8);
+        int index = 0;
+        for (Pair<Handle, HandleSignals> handle : handles) {
+            buffer.putInt(HANDLE_SIZE * index, getMojoHandle(handle.first));
+            buffer.putInt(HANDLE_SIZE * handles.size() + FLAG_SIZE * index,
+                    handle.second.getFlags());
+            index++;
+        }
+        int code = nativeWaitMany(buffer, deadline);
+        WaitManyResult result = new WaitManyResult();
+        // If result is greater than 0, result is the indexed of the available handle. To make sure
+        // it cannot be misinterpreted, set handleIndex to a negative number in case of error.
+        result.setHandleIndex(code);
+        result.setMojoResult(filterMojoResultForWait(code));
+        return result;
+    }
+
+    /**
+     * @see Core#wait(Handle, HandleSignals, long)
+     */
+    @Override
+    public int wait(Handle handle, HandleSignals signals, long deadline) {
+        return filterMojoResultForWait(nativeWait(getMojoHandle(handle),
+                signals.getFlags(), deadline));
+    }
+
+    /**
+     * @see Core#createMessagePipe(MessagePipeHandle.CreateOptions)
+     */
+    @Override
+    public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
+            MessagePipeHandle.CreateOptions options) {
+        ByteBuffer optionsBuffer = null;
+        if (options != null) {
+            optionsBuffer = allocateDirectBuffer(8);
+            optionsBuffer.putInt(0, 8);
+            optionsBuffer.putInt(4, options.getFlags().getFlags());
+        }
+        NativeCreationResult result = nativeCreateMessagePipe(optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return Pair.<MessagePipeHandle, MessagePipeHandle> create(
+                new MessagePipeHandleImpl(this, result.getMojoHandle1()),
+                new MessagePipeHandleImpl(this, result.getMojoHandle2()));
+    }
+
+    /**
+     * @see Core#createDataPipe(DataPipe.CreateOptions)
+     */
+    @Override
+    public Pair<ProducerHandle, ConsumerHandle> createDataPipe(DataPipe.CreateOptions options) {
+        ByteBuffer optionsBuffer = null;
+        if (options != null) {
+            optionsBuffer = allocateDirectBuffer(16);
+            optionsBuffer.putInt(0, 16);
+            optionsBuffer.putInt(4, options.getFlags().getFlags());
+            optionsBuffer.putInt(8, options.getElementNumBytes());
+            optionsBuffer.putInt(12, options.getCapacityNumBytes());
+        }
+        NativeCreationResult result = nativeCreateDataPipe(optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return Pair.<ProducerHandle, ConsumerHandle> create(
+                new DataPipeProducerHandleImpl(this, result.getMojoHandle1()),
+                new DataPipeConsumerHandleImpl(this, result.getMojoHandle2()));
+    }
+
+    /**
+     * @see Core#createSharedBuffer(SharedBufferHandle.CreateOptions, long)
+     */
+    @Override
+    public SharedBufferHandle createSharedBuffer(
+            SharedBufferHandle.CreateOptions options, long numBytes) {
+        ByteBuffer optionsBuffer = null;
+        if (options != null) {
+            optionsBuffer = allocateDirectBuffer(8);
+            optionsBuffer.putInt(0, 8);
+            optionsBuffer.putInt(4, options.getFlags().getFlags());
+        }
+        NativeCreationResult result = nativeCreateSharedBuffer(optionsBuffer, numBytes);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        assert result.getMojoHandle2() == 0;
+        return new SharedBufferHandleImpl(this, result.getMojoHandle1());
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Core#acquireNativeHandle(int)
+     */
+    @Override
+    public UntypedHandle acquireNativeHandle(int handle) {
+        return new UntypedHandleImpl(this, handle);
+    }
+
+    /**
+     * @see Core#getDefaultAsyncWaiter()
+     */
+    @Override
+    public AsyncWaiter getDefaultAsyncWaiter() {
+        return this;
+    }
+
+    /**
+     * @see AsyncWaiter#asyncWait(Handle, Core.HandleSignals, long, Callback)
+     */
+    @Override
+    public Cancellable asyncWait(Handle handle, HandleSignals signals, long deadline,
+            Callback callback) {
+        return nativeAsyncWait(getMojoHandle(handle), signals.getFlags(), deadline, callback);
+    }
+
+    int closeWithResult(int mojoHandle) {
+        return nativeClose(mojoHandle);
+    }
+
+    void close(int mojoHandle) {
+        int mojoResult = nativeClose(mojoHandle);
+        if (mojoResult != MojoResult.OK) {
+            throw new MojoException(mojoResult);
+        }
+    }
+
+    /**
+     * @see MessagePipeHandle#writeMessage(ByteBuffer, List, MessagePipeHandle.WriteFlags)
+     */
+    void writeMessage(MessagePipeHandleImpl pipeHandle, ByteBuffer bytes,
+            List<? extends Handle> handles, MessagePipeHandle.WriteFlags flags) {
+        ByteBuffer handlesBuffer = null;
+        if (handles != null && !handles.isEmpty()) {
+            handlesBuffer = allocateDirectBuffer(handles.size() * HANDLE_SIZE);
+            for (Handle handle : handles) {
+                handlesBuffer.putInt(getMojoHandle(handle));
+            }
+            handlesBuffer.position(0);
+        }
+        int mojoResult = nativeWriteMessage(pipeHandle.getMojoHandle(), bytes,
+                bytes == null ? 0 : bytes.limit(), handlesBuffer,
+                flags.getFlags());
+        if (mojoResult != MojoResult.OK) {
+            throw new MojoException(mojoResult);
+        }
+        // Success means the handles have been invalidated.
+        if (handles != null) {
+            for (Handle handle : handles) {
+                if (handle.isValid()) {
+                    ((HandleBase) handle).invalidateHandle();
+                }
+            }
+        }
+    }
+
+    /**
+     * @see MessagePipeHandle#readMessage(ByteBuffer, int, MessagePipeHandle.ReadFlags)
+     */
+    MessagePipeHandle.ReadMessageResult readMessage(MessagePipeHandleImpl handle,
+            ByteBuffer bytes, int maxNumberOfHandles,
+            MessagePipeHandle.ReadFlags flags) {
+        ByteBuffer handlesBuffer = null;
+        if (maxNumberOfHandles > 0) {
+            handlesBuffer = allocateDirectBuffer(maxNumberOfHandles * HANDLE_SIZE);
+        }
+        MessagePipeHandle.ReadMessageResult result = nativeReadMessage(
+                handle.getMojoHandle(), bytes, handlesBuffer, flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK &&
+                result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED &&
+                result.getMojoResult() != MojoResult.SHOULD_WAIT) {
+            throw new MojoException(result.getMojoResult());
+        }
+
+        if (result.getMojoResult() == MojoResult.OK) {
+            if (bytes != null) {
+                bytes.position(0);
+                bytes.limit(result.getMessageSize());
+            }
+
+            List<UntypedHandle> handles = new ArrayList<UntypedHandle>(
+                    result.getHandlesCount());
+            for (int i = 0; i < result.getHandlesCount(); ++i) {
+                int mojoHandle = handlesBuffer.getInt(HANDLE_SIZE * i);
+                handles.add(new UntypedHandleImpl(this, mojoHandle));
+            }
+            result.setHandles(handles);
+        }
+        return result;
+    }
+
+    /**
+     * @see ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+     */
+    int discardData(DataPipeConsumerHandleImpl handle, int numBytes,
+            DataPipe.ReadFlags flags) {
+        int result = nativeReadData(handle.getMojoHandle(), null, numBytes,
+                flags.getFlags() | MOJO_READ_DATA_FLAG_DISCARD);
+        if (result < 0) {
+            throw new MojoException(result);
+        }
+        return result;
+    }
+
+    /**
+     * @see ConsumerHandle#readData(ByteBuffer, DataPipe.ReadFlags)
+     */
+    int readData(DataPipeConsumerHandleImpl handle, ByteBuffer elements,
+            DataPipe.ReadFlags flags) {
+        int result = nativeReadData(handle.getMojoHandle(), elements,
+                elements == null ? 0 : elements.capacity(),
+                flags.getFlags());
+        if (result < 0) {
+            throw new MojoException(result);
+        }
+        if (elements != null) {
+            elements.limit(result);
+        }
+        return result;
+    }
+
+    /**
+     * @see ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+     */
+    ByteBuffer beginReadData(DataPipeConsumerHandleImpl handle,
+            int numBytes, DataPipe.ReadFlags flags) {
+        NativeCodeAndBufferResult result = nativeBeginReadData(
+                handle.getMojoHandle(),
+                numBytes,
+                flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getBuffer().asReadOnlyBuffer();
+    }
+
+    /**
+     * @see ConsumerHandle#endReadData(int)
+     */
+    void endReadData(DataPipeConsumerHandleImpl handle,
+            int numBytesRead) {
+        int result = nativeEndReadData(handle.getMojoHandle(), numBytesRead);
+        if (result != MojoResult.OK) {
+            throw new MojoException(result);
+        }
+    }
+
+    /**
+     * @see ProducerHandle#writeData(ByteBuffer, DataPipe.WriteFlags)
+     */
+    int writeData(DataPipeProducerHandleImpl handle, ByteBuffer elements,
+            DataPipe.WriteFlags flags) {
+        return nativeWriteData(handle.getMojoHandle(), elements, elements.limit(),
+                flags.getFlags());
+    }
+
+    /**
+     * @see ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
+     */
+    ByteBuffer beginWriteData(DataPipeProducerHandleImpl handle,
+            int numBytes, DataPipe.WriteFlags flags) {
+        NativeCodeAndBufferResult result = nativeBeginWriteData(
+                handle.getMojoHandle(),
+                numBytes,
+                flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getBuffer();
+    }
+
+    /**
+     * @see ProducerHandle#endWriteData(int)
+     */
+    void endWriteData(DataPipeProducerHandleImpl handle,
+            int numBytesWritten) {
+        int result = nativeEndWriteData(handle.getMojoHandle(), numBytesWritten);
+        if (result != MojoResult.OK) {
+            throw new MojoException(result);
+        }
+    }
+
+    /**
+     * @see SharedBufferHandle#duplicate(DuplicateOptions)
+     */
+    SharedBufferHandle duplicate(SharedBufferHandleImpl handle,
+            DuplicateOptions options) {
+        ByteBuffer optionsBuffer = null;
+        if (options != null) {
+            optionsBuffer = allocateDirectBuffer(8);
+            optionsBuffer.putInt(0, 8);
+            optionsBuffer.putInt(4, options.getFlags().getFlags());
+        }
+        NativeCreationResult result = nativeDuplicate(handle.getMojoHandle(),
+                optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        assert result.getMojoHandle2() == 0;
+        return new SharedBufferHandleImpl(this, result.getMojoHandle1());
+    }
+
+    /**
+     * @see SharedBufferHandle#map(long, long, MapFlags)
+     */
+    ByteBuffer map(SharedBufferHandleImpl handle, long offset, long numBytes,
+            MapFlags flags) {
+        NativeCodeAndBufferResult result = nativeMap(handle.getMojoHandle(), offset, numBytes,
+                flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getBuffer();
+    }
+
+    /**
+     * @see SharedBufferHandle#unmap(ByteBuffer)
+     */
+    void unmap(ByteBuffer buffer) {
+        int result = nativeUnmap(buffer);
+        if (result != MojoResult.OK) {
+            throw new MojoException(result);
+        }
+    }
+
+    /**
+     * @return the mojo handle associated to the given handle, considering invalid handles.
+     */
+    private int getMojoHandle(Handle handle) {
+        if (handle.isValid()) {
+            return ((HandleBase) handle).getMojoHandle();
+        }
+        return 0;
+    }
+
+    private static boolean isUnrecoverableError(int code) {
+        switch (code) {
+            case MojoResult.OK:
+            case MojoResult.DEADLINE_EXCEEDED:
+            case MojoResult.CANCELLED:
+            case MojoResult.FAILED_PRECONDITION:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    private static int filterMojoResult(int code) {
+        if (code >= 0) {
+            return MojoResult.OK;
+        }
+        return code;
+    }
+
+    private static int filterMojoResultForWait(int code) {
+        int finalCode = filterMojoResult(code);
+        if (isUnrecoverableError(finalCode)) {
+            throw new MojoException(finalCode);
+        }
+        return finalCode;
+    }
+
+    private static ByteBuffer allocateDirectBuffer(int capacity) {
+        ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+        buffer.order(ByteOrder.nativeOrder());
+        return buffer;
+    }
+
+    private static class NativeCodeAndBufferResult {
+        private int mMojoResult;
+        private ByteBuffer mBuffer;
+
+        /**
+         * @return the mojoResult
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * @return the buffer
+         */
+        public ByteBuffer getBuffer() {
+            return mBuffer;
+        }
+
+        /**
+         * @param buffer the buffer to set
+         */
+        public void setBuffer(ByteBuffer buffer) {
+            mBuffer = buffer;
+        }
+
+    }
+
+    /**
+     * Implementation of {@link org.chromium.mojo.system.AsyncWaiter.Cancellable}.
+     */
+    private class AsyncWaiterCancellableImpl implements AsyncWaiter.Cancellable {
+
+        private final long mId;
+        private final long mDataPtr;
+        private boolean mActive = true;
+
+        private AsyncWaiterCancellableImpl(long id, long dataPtr) {
+            this.mId = id;
+            this.mDataPtr = dataPtr;
+        }
+
+        /**
+         * @see org.chromium.mojo.system.AsyncWaiter.Cancellable#cancel()
+         */
+        @Override
+        public void cancel() {
+            if (mActive) {
+                mActive = false;
+                nativeCancelAsyncWait(mId, mDataPtr);
+            }
+        }
+
+        private boolean isActive() {
+            return mActive;
+        }
+
+        private void deactivate() {
+            mActive = false;
+        }
+    }
+
+    @CalledByNative
+    private AsyncWaiterCancellableImpl newAsyncWaiterCancellableImpl(long id, long dataPtr) {
+        return new AsyncWaiterCancellableImpl(id, dataPtr);
+    }
+
+    @CalledByNative
+    private void onAsyncWaitResult(int mojoResult,
+            AsyncWaiter.Callback callback,
+            AsyncWaiterCancellableImpl cancellable) {
+        if (!cancellable.isActive()) {
+            // If cancellable is not active, the user cancelled the wait.
+            return;
+        }
+        cancellable.deactivate();
+        int finalCode = filterMojoResult(mojoResult);
+        if (isUnrecoverableError(finalCode)) {
+            callback.onError(new MojoException(finalCode));
+            return;
+        }
+        callback.onResult(finalCode);
+    }
+
+    @CalledByNative
+    private static NativeCodeAndBufferResult newNativeCodeAndBufferResult(int mojoResult,
+            ByteBuffer buffer) {
+        NativeCodeAndBufferResult result = new NativeCodeAndBufferResult();
+        result.setMojoResult(mojoResult);
+        result.setBuffer(buffer);
+        return result;
+    }
+
+    @CalledByNative
+    private static MessagePipeHandle.ReadMessageResult newReadMessageResult(int mojoResult,
+            int messageSize,
+            int handlesCount) {
+        MessagePipeHandle.ReadMessageResult result = new MessagePipeHandle.ReadMessageResult();
+        if (mojoResult >= 0) {
+            result.setMojoResult(MojoResult.OK);
+        } else {
+            result.setMojoResult(mojoResult);
+        }
+        result.setMessageSize(messageSize);
+        result.setHandlesCount(handlesCount);
+        return result;
+    }
+
+    private static class NativeCreationResult {
+        private int mMojoResult;
+        private int mMojoHandle1;
+        private int mMojoHandle2;
+
+        /**
+         * @return the mojoResult
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * @return the mojoHandle1
+         */
+        public int getMojoHandle1() {
+            return mMojoHandle1;
+        }
+
+        /**
+         * @param mojoHandle1 the mojoHandle1 to set
+         */
+        public void setMojoHandle1(int mojoHandle1) {
+            mMojoHandle1 = mojoHandle1;
+        }
+
+        /**
+         * @return the mojoHandle2
+         */
+        public int getMojoHandle2() {
+            return mMojoHandle2;
+        }
+
+        /**
+         * @param mojoHandle2 the mojoHandle2 to set
+         */
+        public void setMojoHandle2(int mojoHandle2) {
+            mMojoHandle2 = mojoHandle2;
+        }
+    }
+
+    @CalledByNative
+    private static NativeCreationResult newNativeCreationResult(int mojoResult,
+            int mojoHandle1, int mojoHandle2) {
+        NativeCreationResult result = new NativeCreationResult();
+        result.setMojoResult(mojoResult);
+        result.setMojoHandle1(mojoHandle1);
+        result.setMojoHandle2(mojoHandle2);
+        return result;
+    }
+
+    private native long nativeGetTimeTicksNow();
+
+    private native int nativeWaitMany(ByteBuffer buffer, long deadline);
+
+    private native NativeCreationResult nativeCreateMessagePipe(ByteBuffer optionsBuffer);
+
+    private native NativeCreationResult nativeCreateDataPipe(ByteBuffer optionsBuffer);
+
+    private native NativeCreationResult nativeCreateSharedBuffer(ByteBuffer optionsBuffer,
+            long numBytes);
+
+    private native int nativeClose(int mojoHandle);
+
+    private native int nativeWait(int mojoHandle, int signals, long deadline);
+
+    private native int nativeWriteMessage(int mojoHandle, ByteBuffer bytes, int numBytes,
+            ByteBuffer handlesBuffer, int flags);
+
+    private native MessagePipeHandle.ReadMessageResult nativeReadMessage(int mojoHandle,
+            ByteBuffer bytes,
+            ByteBuffer handlesBuffer,
+            int flags);
+
+    private native int nativeReadData(int mojoHandle, ByteBuffer elements, int elementsSize,
+            int flags);
+
+    private native NativeCodeAndBufferResult nativeBeginReadData(int mojoHandle, int numBytes,
+            int flags);
+
+    private native int nativeEndReadData(int mojoHandle, int numBytesRead);
+
+    private native int nativeWriteData(int mojoHandle, ByteBuffer elements, int limit, int flags);
+
+    private native NativeCodeAndBufferResult nativeBeginWriteData(int mojoHandle, int numBytes,
+            int flags);
+
+    private native int nativeEndWriteData(int mojoHandle, int numBytesWritten);
+
+    private native NativeCreationResult nativeDuplicate(int mojoHandle, ByteBuffer optionsBuffer);
+
+    private native NativeCodeAndBufferResult nativeMap(int mojoHandle, long offset, long numBytes,
+            int flags);
+
+    private native int nativeUnmap(ByteBuffer buffer);
+
+    private native AsyncWaiterCancellableImpl nativeAsyncWait(int mojoHandle, int signals,
+            long deadline, AsyncWaiter.Callback callback);
+
+    private native void nativeCancelAsyncWait(long mId, long dataPtr);
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java
new file mode 100644
index 0000000..ab6f1ea
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium 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.system.impl;
+
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ReadFlags;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation of {@link ConsumerHandle}.
+ */
+class DataPipeConsumerHandleImpl extends HandleBase implements ConsumerHandle {
+
+    /**
+     * @see HandleBase#HandleBase(CoreImpl, int)
+     */
+    DataPipeConsumerHandleImpl(CoreImpl core, int mojoHandle) {
+        super(core, mojoHandle);
+    }
+
+    /**
+     * @see HandleBase#HandleBase(HandleBase)
+     */
+    DataPipeConsumerHandleImpl(HandleBase other) {
+        super(other);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#pass()
+     */
+    @Override
+    public ConsumerHandle pass() {
+        return new DataPipeConsumerHandleImpl(this);
+    }
+
+    /**
+     * @see ConsumerHandle#discardData(int, ReadFlags)
+     */
+    @Override
+    public int discardData(int numBytes, ReadFlags flags) {
+        return mCore.discardData(this, numBytes, flags);
+    }
+
+    /**
+     * @see ConsumerHandle#readData(ByteBuffer, ReadFlags)
+     */
+    @Override
+    public int readData(ByteBuffer elements, ReadFlags flags) {
+        return mCore.readData(this, elements, flags);
+    }
+
+    /**
+     * @see ConsumerHandle#beginReadData(int, ReadFlags)
+     */
+    @Override
+    public ByteBuffer beginReadData(int numBytes, ReadFlags flags) {
+        return mCore.beginReadData(this, numBytes, flags);
+    }
+
+    /**
+     * @see ConsumerHandle#endReadData(int)
+     */
+    @Override
+    public void endReadData(int numBytesRead) {
+        mCore.endReadData(this, numBytesRead);
+    }
+
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java
new file mode 100644
index 0000000..1087a59
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java
@@ -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.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.DataPipe.WriteFlags;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation of {@link ProducerHandle}.
+ */
+class DataPipeProducerHandleImpl extends HandleBase implements ProducerHandle {
+
+    /**
+     * @see HandleBase#HandleBase(CoreImpl, int)
+     */
+    DataPipeProducerHandleImpl(CoreImpl core, int mojoHandle) {
+        super(core, mojoHandle);
+    }
+
+    /**
+     * @see HandleBase#HandleBase(HandleBase)
+     */
+    DataPipeProducerHandleImpl(HandleBase handle) {
+        super(handle);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.DataPipe.ProducerHandle#pass()
+     */
+    @Override
+    public ProducerHandle pass() {
+        return new DataPipeProducerHandleImpl(this);
+    }
+
+    /**
+     * @see ProducerHandle#writeData(ByteBuffer, WriteFlags)
+     */
+    @Override
+    public int writeData(ByteBuffer elements, WriteFlags flags) {
+        return mCore.writeData(this, elements, flags);
+    }
+
+    /**
+     * @see ProducerHandle#beginWriteData(int, WriteFlags)
+     */
+    @Override
+    public ByteBuffer beginWriteData(int numBytes, WriteFlags flags) {
+        return mCore.beginWriteData(this, numBytes, flags);
+    }
+
+    /**
+     * @see ProducerHandle#endWriteData(int)
+     */
+    @Override
+    public void endWriteData(int numBytesWritten) {
+        mCore.endWriteData(this, numBytesWritten);
+    }
+
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
new file mode 100644
index 0000000..81bfac7
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
@@ -0,0 +1,140 @@
+// Copyright 2014 The Chromium 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.system.impl;
+
+import android.util.Log;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Core.HandleSignals;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.UntypedHandle;
+
+/**
+ * Implementation of {@link Handle}.
+ */
+abstract class HandleBase implements Handle {
+
+    private static final String TAG = "HandleImpl";
+
+    /**
+     * The pointer to the scoped handle owned by this object.
+     */
+    private int mMojoHandle;
+
+    /**
+     * The core implementation. Will be used to delegate all behavior.
+     */
+    protected CoreImpl mCore;
+
+    /**
+     * Base constructor. Takes ownership of the passed handle.
+     */
+    HandleBase(CoreImpl core, int mojoHandle) {
+        mCore = core;
+        mMojoHandle = mojoHandle;
+    }
+
+    /**
+     * Constructor for transforming {@link HandleBase} into a specific one. It is used to transform
+     * an {@link UntypedHandle} into a typed one, or any handle into an {@link UntypedHandle}.
+     */
+    protected HandleBase(HandleBase other) {
+        mCore = other.mCore;
+        HandleBase otherAsHandleImpl = other;
+        int mojoHandle = otherAsHandleImpl.mMojoHandle;
+        otherAsHandleImpl.mMojoHandle = CoreImpl.INVALID_HANDLE;
+        mMojoHandle = mojoHandle;
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#close()
+     */
+    @Override
+    public void close() {
+        if (mMojoHandle != CoreImpl.INVALID_HANDLE) {
+            // After a close, the handle is invalid whether the close succeed or not.
+            int handle = mMojoHandle;
+            mMojoHandle = CoreImpl.INVALID_HANDLE;
+            mCore.close(handle);
+        }
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#wait(HandleSignals, long)
+     */
+    @Override
+    public int wait(HandleSignals signals, long deadline) {
+        return mCore.wait(this, signals, deadline);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#isValid()
+     */
+    @Override
+    public boolean isValid() {
+        return mMojoHandle != CoreImpl.INVALID_HANDLE;
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#toUntypedHandle()
+     */
+    @Override
+    public UntypedHandle toUntypedHandle() {
+        return new UntypedHandleImpl(this);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#getCore()
+     */
+    @Override
+    public Core getCore() {
+        return mCore;
+    }
+
+    /**
+     * @see Handle#releaseNativeHandle()
+     */
+    @Override
+    public int releaseNativeHandle() {
+        int result = mMojoHandle;
+        mMojoHandle = CoreImpl.INVALID_HANDLE;
+        return result;
+    }
+
+    /**
+     * Getter for the native scoped handle.
+     *
+     * @return the native scoped handle.
+     */
+    int getMojoHandle() {
+        return mMojoHandle;
+    }
+
+    /**
+     * invalidate the handle. The caller must ensures that the handle does not leak.
+     */
+    void invalidateHandle() {
+        mMojoHandle = CoreImpl.INVALID_HANDLE;
+    }
+
+    /**
+     * Close the handle if it is valid. Necessary because we cannot let handle leak, and we cannot
+     * ensure that every handle will be manually closed.
+     *
+     * @see java.lang.Object#finalize()
+     */
+    @Override
+    protected final void finalize() throws Throwable {
+        if (isValid()) {
+            // This should not happen, as the user of this class should close the handle. Adding a
+            // warning.
+            Log.w(TAG, "Handle was not closed.");
+            // Ignore result at this point.
+            mCore.closeWithResult(mMojoHandle);
+        }
+        super.finalize();
+    }
+
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java
new file mode 100644
index 0000000..345a558
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java
@@ -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.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Implementation of {@link MessagePipeHandle}.
+ */
+class MessagePipeHandleImpl extends HandleBase implements MessagePipeHandle {
+
+    /**
+     * @see HandleBase#HandleBase(CoreImpl, int)
+     */
+    MessagePipeHandleImpl(CoreImpl core, int mojoHandle) {
+        super(core, mojoHandle);
+    }
+
+    /**
+     * @see HandleBase#HandleBase(HandleBase)
+     */
+    MessagePipeHandleImpl(HandleBase handle) {
+        super(handle);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.MessagePipeHandle#pass()
+     */
+    @Override
+    public MessagePipeHandle pass() {
+        return new MessagePipeHandleImpl(this);
+    }
+
+    /**
+     * @see MessagePipeHandle#writeMessage(ByteBuffer, List, WriteFlags)
+     */
+    @Override
+    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+        mCore.writeMessage(this, bytes, handles, flags);
+    }
+
+    /**
+     * @see MessagePipeHandle#readMessage(ByteBuffer, int, ReadFlags)
+     */
+    @Override
+    public ReadMessageResult readMessage(ByteBuffer bytes,
+            int maxNumberOfHandles,
+            ReadFlags flags) {
+        return mCore.readMessage(this, bytes, maxNumberOfHandles, flags);
+    }
+
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java
new file mode 100644
index 0000000..76ef739
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java
@@ -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.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.mojo.system.SharedBufferHandle;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation of {@link SharedBufferHandle}.
+ */
+class SharedBufferHandleImpl extends HandleBase implements SharedBufferHandle {
+
+    /**
+     * @see HandleBase#HandleBase(CoreImpl, int)
+     */
+    SharedBufferHandleImpl(CoreImpl core, int mojoHandle) {
+        super(core, mojoHandle);
+    }
+
+    /**
+     * @see HandleBase#HandleBase(HandleBase)
+     */
+    SharedBufferHandleImpl(HandleBase handle) {
+        super(handle);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.SharedBufferHandle#pass()
+     */
+    @Override
+    public SharedBufferHandle pass() {
+        return new SharedBufferHandleImpl(this);
+    }
+
+    /**
+     * @see SharedBufferHandle#duplicate(DuplicateOptions)
+     */
+    @Override
+    public SharedBufferHandle duplicate(DuplicateOptions options) {
+        return mCore.duplicate(this, options);
+    }
+
+    /**
+     * @see SharedBufferHandle#map(long, long, MapFlags)
+     */
+    @Override
+    public ByteBuffer map(long offset, long numBytes, MapFlags flags) {
+        return mCore.map(this, offset, numBytes, flags);
+    }
+
+    /**
+     * @see SharedBufferHandle#unmap(ByteBuffer)
+     */
+    @Override
+    public void unmap(ByteBuffer buffer) {
+        mCore.unmap(buffer);
+    }
+
+}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java
new file mode 100644
index 0000000..4774ab8
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java
@@ -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.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
+
+/**
+ * Implementation of {@link UntypedHandle}.
+ */
+class UntypedHandleImpl extends HandleBase implements UntypedHandle {
+
+    /**
+     * @see HandleBase#HandleBase(CoreImpl, int)
+     */
+    UntypedHandleImpl(CoreImpl core, int mojoHandle) {
+        super(core, mojoHandle);
+    }
+
+    /**
+     * @see HandleBase#HandleBase(HandleBase)
+     */
+    UntypedHandleImpl(HandleBase handle) {
+        super(handle);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#pass()
+     */
+    @Override
+    public UntypedHandle pass() {
+        return new UntypedHandleImpl(this);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#toMessagePipeHandle()
+     */
+    @Override
+    public MessagePipeHandle toMessagePipeHandle() {
+        return new MessagePipeHandleImpl(this);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#toDataPipeConsumerHandle()
+     */
+    @Override
+    public ConsumerHandle toDataPipeConsumerHandle() {
+        return new DataPipeConsumerHandleImpl(this);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#toDataPipeProducerHandle()
+     */
+    @Override
+    public ProducerHandle toDataPipeProducerHandle() {
+        return new DataPipeProducerHandleImpl(this);
+    }
+
+    /**
+     * @see org.chromium.mojo.system.UntypedHandle#toSharedBufferHandle()
+     */
+    @Override
+    public SharedBufferHandle toSharedBufferHandle() {
+        return new SharedBufferHandleImpl(this);
+    }
+
+}
diff --git a/mojo/application/BUILD.gn b/mojo/application/BUILD.gn
new file mode 100644
index 0000000..746f778
--- /dev/null
+++ b/mojo/application/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.
+
+# GYP version: mojo/mojo_base.gyp:mojo_application_chromium
+source_set("application") {
+  sources = [
+    "application_runner_chromium.cc",
+    "application_runner_chromium.h",
+  ]
+
+  public_deps = [
+    "//mojo/public/cpp/application",
+  ]
+  deps = [
+    "//base",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+  ]
+}
diff --git a/mojo/application/application_runner_chromium.cc b/mojo/application/application_runner_chromium.cc
new file mode 100644
index 0000000..fa139c7
--- /dev/null
+++ b/mojo/application/application_runner_chromium.cc
@@ -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.
+
+#include "mojo/application/application_runner_chromium.h"
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+
+namespace mojo {
+
+// static
+void ApplicationImpl::Terminate() {
+  if (base::MessageLoop::current()->is_running())
+    base::MessageLoop::current()->Quit();
+}
+
+ApplicationRunnerChromium::ApplicationRunnerChromium(
+    ApplicationDelegate* delegate)
+    : delegate_(scoped_ptr<ApplicationDelegate>(delegate)),
+      message_loop_type_(base::MessageLoop::TYPE_CUSTOM),
+      has_run_(false) {}
+
+ApplicationRunnerChromium::~ApplicationRunnerChromium() {}
+
+void ApplicationRunnerChromium::set_message_loop_type(
+    base::MessageLoop::Type type) {
+  DCHECK_NE(base::MessageLoop::TYPE_CUSTOM, type);
+  DCHECK(!has_run_);
+
+  message_loop_type_ = type;
+}
+
+MojoResult ApplicationRunnerChromium::Run(MojoHandle shell_handle) {
+  DCHECK(!has_run_);
+  has_run_ = true;
+
+  base::CommandLine::Init(0, NULL);
+#if !defined(COMPONENT_BUILD)
+  base::AtExitManager at_exit;
+#endif
+
+  {
+    scoped_ptr<base::MessageLoop> loop;
+    if (message_loop_type_ == base::MessageLoop::TYPE_CUSTOM)
+      loop.reset(new base::MessageLoop(common::MessagePumpMojo::Create()));
+    else
+      loop.reset(new base::MessageLoop(message_loop_type_));
+
+    ApplicationImpl impl(delegate_.get(),
+                         MakeScopedHandle(MessagePipeHandle(shell_handle)));
+    loop->Run();
+  }
+  delegate_.reset();
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
diff --git a/mojo/application/application_runner_chromium.h b/mojo/application/application_runner_chromium.h
new file mode 100644
index 0000000..8671342
--- /dev/null
+++ b/mojo/application/application_runner_chromium.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_APPLICATION_APPLICATION_RUNNER_CHROMIUM_H_
+#define MOJO_APPLICATION_APPLICATION_RUNNER_CHROMIUM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class ApplicationDelegate;
+
+// A utility for running a chromium based mojo Application. The typical use
+// case is to use when writing your MojoMain:
+//
+//  MojoResult MojoMain(MojoHandle shell_handle) {
+//    mojo::ApplicationRunnerChromium runner(new MyDelegate());
+//    return runner.Run(shell_handle);
+//  }
+//
+// ApplicationRunnerChromium takes care of chromium environment initialization
+// and shutdown, and starting a MessageLoop from which your application can run
+// and ultimately Quit().
+class ApplicationRunnerChromium {
+ public:
+  // Takes ownership of |delegate|.
+  explicit ApplicationRunnerChromium(ApplicationDelegate* delegate);
+  ~ApplicationRunnerChromium();
+
+  void set_message_loop_type(base::MessageLoop::Type type);
+
+  // Once the various parameters have been set above, use Run to initialize an
+  // ApplicationImpl wired to the provided delegate, and run a MessageLoop until
+  // the application exits.
+  MojoResult Run(MojoHandle shell_handle);
+
+ private:
+  scoped_ptr<ApplicationDelegate> delegate_;
+
+  // MessageLoop type. TYPE_CUSTOM is default (MessagePumpMojo will be used as
+  // the underlying message pump).
+  base::MessageLoop::Type message_loop_type_;
+  // Whether Run() has been called.
+  bool has_run_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationRunnerChromium);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_APPLICATION_APPLICATION_RUNNER_CHROMIUM_H_
diff --git a/mojo/application_manager/BUILD.gn b/mojo/application_manager/BUILD.gn
new file mode 100644
index 0000000..22c0edd
--- /dev/null
+++ b/mojo/application_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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo.gyp:mojo_application_manager
+component("application_manager") {
+  output_name = "mojo_application_manager"
+  sources = [
+    "application_loader.cc",
+    "application_loader.h",
+    "application_manager.cc",
+    "application_manager.h",
+    "application_manager_export.h",
+    "background_shell_application_loader.cc",
+    "background_shell_application_loader.h",
+  ]
+
+  defines = [
+    "MOJO_APPLICATION_MANAGER_IMPLEMENTATION",
+  ]
+
+  public_deps = [
+    "//base",
+    "//mojo/common",
+    "//mojo/public/interfaces/application:application",
+    "//mojo/services/public/interfaces/network:network",
+    "//url",
+  ]
+  deps = [
+    "//base/third_party/dynamic_annotations",
+    "//net",
+    "//url",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/interfaces/content_handler:content_handler",
+  ]
+}
+
+# GYP version: mojo.gyp:mojo_application_manager_unittests
+test("mojo_application_manager_unittests") {
+  sources = [
+    "application_manager_unittest.cc",
+    "background_shell_application_loader_unittest.cc",
+  ]
+
+  deps = [
+    ":application_manager",
+    ":test_bindings",
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+mojom("test_bindings") {
+  sources = [ "test.mojom" ]
+}
diff --git a/mojo/application_manager/application_loader.cc b/mojo/application_manager/application_loader.cc
new file mode 100644
index 0000000..60dec66
--- /dev/null
+++ b/mojo/application_manager/application_loader.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/application_manager/application_loader.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+
+ApplicationLoader::SimpleLoadCallbacks::SimpleLoadCallbacks(
+    ScopedMessagePipeHandle shell_handle)
+    : shell_handle_(shell_handle.Pass()) {
+}
+
+ApplicationLoader::SimpleLoadCallbacks::~SimpleLoadCallbacks() {
+}
+
+ScopedMessagePipeHandle
+ApplicationLoader::SimpleLoadCallbacks::RegisterApplication() {
+  return shell_handle_.Pass();
+}
+
+void ApplicationLoader::SimpleLoadCallbacks::LoadWithContentHandler(
+    const GURL& content_handle_url,
+    URLResponsePtr url_response) {
+  NOTREACHED();
+}
+
+}  // namespace mojo
diff --git a/mojo/application_manager/application_loader.h b/mojo/application_manager/application_loader.h
new file mode 100644
index 0000000..b7d9f19
--- /dev/null
+++ b/mojo/application_manager/application_loader.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_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+#define MOJO_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+
+#include "base/memory/ref_counted.h"
+#include "mojo/application_manager/application_manager_export.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class ApplicationManager;
+
+// Interface to allowing loading behavior to be established for schemes,
+// specific urls or as the default.
+// A ApplicationLoader is responsible to using whatever mechanism is appropriate
+// to load the application at url.
+// The handle to the shell is passed to that application so it can bind it to
+// a Shell instance. This will give the Application a way to connect to other
+// apps and services.
+class MOJO_APPLICATION_MANAGER_EXPORT ApplicationLoader {
+ public:
+  class MOJO_APPLICATION_MANAGER_EXPORT LoadCallbacks
+      : public base::RefCounted<LoadCallbacks> {
+   public:
+    // Register the requested application with ApplicationManager. If the
+    // returned handle is valid, it should be used to implement the
+    // mojo::Application interface.
+    virtual ScopedMessagePipeHandle RegisterApplication() = 0;
+
+    // Load the requested application with a content handler.
+    virtual void LoadWithContentHandler(const GURL& content_handler_url,
+                                        URLResponsePtr url_response) = 0;
+
+   protected:
+    friend base::RefCounted<LoadCallbacks>;
+    virtual ~LoadCallbacks() {}
+  };
+
+  // Implements RegisterApplication() by returning a handle that was specified
+  // at construction time. LoadWithContentHandler() is not supported.
+  class MOJO_APPLICATION_MANAGER_EXPORT SimpleLoadCallbacks
+      : public LoadCallbacks {
+   public:
+    SimpleLoadCallbacks(ScopedMessagePipeHandle shell_handle);
+    virtual ScopedMessagePipeHandle RegisterApplication() override;
+    virtual void LoadWithContentHandler(const GURL& content_handler_url,
+                                        URLResponsePtr response) override;
+
+   private:
+    ScopedMessagePipeHandle shell_handle_;
+    virtual ~SimpleLoadCallbacks();
+  };
+
+  virtual ~ApplicationLoader() {}
+
+  // Load the application named |url|. Applications can be loaded two ways:
+  //
+  // 1. |url| can refer directly to a Mojo application. In this case, call
+  //    callbacks->RegisterApplication(). The returned handle should be used to
+  //    implement the mojo.Application interface. Note that the returned handle
+  //    can be invalid in the case where the application has already been
+  //    loaded.
+  //
+  // 2. |url| can refer to some content that can be handled by some other Mojo
+  //    application. In this case, call callbacks->LoadWithContentHandler() and
+  //    specify the URL of the application that should handle the content.
+  //    The specified application must implement the mojo.ContentHandler
+  //    interface.
+  virtual void Load(ApplicationManager* application_manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) = 0;
+
+  // Called when the Application exits.
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) = 0;
+
+ protected:
+  ApplicationLoader() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_APPLICATION_MANAGER_APPLICATION_LOADER_H_
diff --git a/mojo/application_manager/application_manager.cc b/mojo/application_manager/application_manager.cc
new file mode 100644
index 0000000..b183d9f
--- /dev/null
+++ b/mojo/application_manager/application_manager.cc
@@ -0,0 +1,328 @@
+// Copyright 2014 The Chromium 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_manager/application_manager.h"
+
+#include <stdio.h>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace mojo {
+
+namespace {
+// Used by TestAPI.
+bool has_created_instance = false;
+
+class StubServiceProvider : public InterfaceImpl<ServiceProvider> {
+ public:
+  ServiceProvider* GetRemoteServiceProvider() { return client(); }
+
+ private:
+  virtual void ConnectToService(
+      const String& service_name,
+      ScopedMessagePipeHandle client_handle) override {}
+};
+
+}  // namespace
+
+ApplicationManager::Delegate::~Delegate() {}
+
+class ApplicationManager::LoadCallbacksImpl
+    : public ApplicationLoader::LoadCallbacks {
+ public:
+  LoadCallbacksImpl(base::WeakPtr<ApplicationManager> manager,
+                    const GURL& requested_url,
+                    const GURL& requestor_url,
+                    ServiceProviderPtr service_provider)
+      : manager_(manager),
+        requested_url_(requested_url),
+        requestor_url_(requestor_url),
+        service_provider_(service_provider.Pass()) {}
+
+ private:
+  virtual ~LoadCallbacksImpl() {}
+
+  // LoadCallbacks implementation
+  virtual ScopedMessagePipeHandle RegisterApplication() override {
+    ScopedMessagePipeHandle shell_handle;
+    if (manager_) {
+      manager_->RegisterLoadedApplication(requested_url_,
+                                          requestor_url_,
+                                          service_provider_.Pass(),
+                                          &shell_handle);
+    }
+    return shell_handle.Pass();
+  }
+
+  virtual void LoadWithContentHandler(const GURL& content_handler_url,
+                                      URLResponsePtr url_response) override {
+    if (manager_) {
+      manager_->LoadWithContentHandler(requested_url_,
+                                       requestor_url_,
+                                       content_handler_url,
+                                       url_response.Pass(),
+                                       service_provider_.Pass());
+    }
+  }
+
+  base::WeakPtr<ApplicationManager> manager_;
+  GURL requested_url_;
+  GURL requestor_url_;
+  ServiceProviderPtr service_provider_;
+};
+
+class ApplicationManager::ShellImpl : public InterfaceImpl<Shell> {
+ public:
+  ShellImpl(ApplicationManager* manager, const GURL& url)
+      : manager_(manager), url_(url) {}
+
+  virtual ~ShellImpl() {}
+
+  void ConnectToClient(const GURL& requestor_url,
+                       ServiceProviderPtr service_provider) {
+    client()->AcceptConnection(String::From(requestor_url),
+                               service_provider.Pass());
+  }
+
+  // ServiceProvider implementation:
+  virtual void ConnectToApplication(
+      const String& app_url,
+      InterfaceRequest<ServiceProvider> in_service_provider) override {
+    ServiceProviderPtr out_service_provider;
+    out_service_provider.Bind(in_service_provider.PassMessagePipe());
+    manager_->ConnectToApplication(
+        app_url.To<GURL>(), url_, out_service_provider.Pass());
+  }
+
+  const GURL& url() const { return url_; }
+
+ private:
+  virtual void OnConnectionError() override {
+    manager_->OnShellImplError(this);
+  }
+
+  ApplicationManager* const manager_;
+  const GURL url_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellImpl);
+};
+
+struct ApplicationManager::ContentHandlerConnection {
+  ContentHandlerConnection(ApplicationManager* manager,
+                           const GURL& content_handler_url) {
+    ServiceProviderPtr service_provider;
+    BindToProxy(&service_provider_impl, &service_provider);
+    manager->ConnectToApplication(
+        content_handler_url, GURL(), service_provider.Pass());
+    mojo::ConnectToService(service_provider_impl.client(), &content_handler);
+  }
+
+  StubServiceProvider service_provider_impl;
+  ContentHandlerPtr content_handler;
+};
+
+// static
+ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
+    : manager_(manager) {
+}
+
+ApplicationManager::TestAPI::~TestAPI() {
+}
+
+bool ApplicationManager::TestAPI::HasCreatedInstance() {
+  return has_created_instance;
+}
+
+bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
+  return manager_->url_to_shell_impl_.find(url) !=
+         manager_->url_to_shell_impl_.end();
+}
+
+ApplicationManager::ApplicationManager()
+    : delegate_(NULL),
+      interceptor_(NULL),
+      weak_ptr_factory_(this) {
+}
+
+ApplicationManager::~ApplicationManager() {
+  STLDeleteValues(&url_to_content_handler_);
+  TerminateShellConnections();
+  STLDeleteValues(&url_to_loader_);
+  STLDeleteValues(&scheme_to_loader_);
+}
+
+void ApplicationManager::TerminateShellConnections() {
+  STLDeleteValues(&url_to_shell_impl_);
+}
+
+// static
+ApplicationManager* ApplicationManager::GetInstance() {
+  static base::LazyInstance<ApplicationManager> instance =
+      LAZY_INSTANCE_INITIALIZER;
+  has_created_instance = true;
+  return &instance.Get();
+}
+
+void ApplicationManager::ConnectToApplication(
+    const GURL& url,
+    const GURL& requestor_url,
+    ServiceProviderPtr service_provider) {
+  URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url);
+  if (shell_it != url_to_shell_impl_.end()) {
+    ConnectToClient(
+        shell_it->second, url, requestor_url, service_provider.Pass());
+    return;
+  }
+
+  scoped_refptr<LoadCallbacksImpl> callbacks(
+      new LoadCallbacksImpl(weak_ptr_factory_.GetWeakPtr(),
+                            url,
+                            requestor_url,
+                            service_provider.Pass()));
+  GetLoaderForURL(url)->Load(this, url, callbacks);
+}
+
+void ApplicationManager::ConnectToClient(ShellImpl* shell_impl,
+                                         const GURL& url,
+                                         const GURL& requestor_url,
+                                         ServiceProviderPtr service_provider) {
+  if (interceptor_) {
+    shell_impl->ConnectToClient(
+        requestor_url,
+        interceptor_->OnConnectToClient(url, service_provider.Pass()));
+  } else {
+    shell_impl->ConnectToClient(requestor_url, service_provider.Pass());
+  }
+}
+
+void ApplicationManager::RegisterExternalApplication(
+    const GURL& url,
+    ScopedMessagePipeHandle shell_handle) {
+  url_to_shell_impl_[url] =
+      WeakBindToPipe(new ShellImpl(this, url), shell_handle.Pass());
+}
+
+void ApplicationManager::RegisterLoadedApplication(
+    const GURL& url,
+    const GURL& requestor_url,
+    ServiceProviderPtr service_provider,
+    ScopedMessagePipeHandle* shell_handle) {
+  ShellImpl* shell_impl = NULL;
+  URLToShellImplMap::iterator iter = url_to_shell_impl_.find(url);
+  if (iter != url_to_shell_impl_.end()) {
+    // This can happen because services are loaded asynchronously. So if we get
+    // two requests for the same service close to each other, we might get here
+    // and find that we already have it.
+    shell_impl = iter->second;
+  } else {
+    MessagePipe pipe;
+    URLToArgsMap::const_iterator args_it = url_to_args_.find(url);
+    Array<String> args;
+    if (args_it != url_to_args_.end())
+      args = Array<String>::From(args_it->second);
+    shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass());
+    url_to_shell_impl_[url] = shell_impl;
+    *shell_handle = pipe.handle0.Pass();
+    shell_impl->client()->Initialize(args.Pass());
+  }
+
+  ConnectToClient(shell_impl, url, requestor_url, service_provider.Pass());
+}
+
+void ApplicationManager::LoadWithContentHandler(
+    const GURL& content_url,
+    const GURL& requestor_url,
+    const GURL& content_handler_url,
+    URLResponsePtr url_response,
+    ServiceProviderPtr service_provider) {
+  ContentHandlerConnection* connection = NULL;
+  URLToContentHandlerMap::iterator iter =
+      url_to_content_handler_.find(content_handler_url);
+  if (iter != url_to_content_handler_.end()) {
+    connection = iter->second;
+  } else {
+    connection = new ContentHandlerConnection(this, content_handler_url);
+    url_to_content_handler_[content_handler_url] = connection;
+  }
+
+  InterfaceRequest<ServiceProvider> spir;
+  spir.Bind(service_provider.PassMessagePipe());
+  connection->content_handler->OnConnect(
+      content_url.spec(), url_response.Pass(), spir.Pass());
+}
+
+void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
+                                         const GURL& url) {
+  URLToLoaderMap::iterator it = url_to_loader_.find(url);
+  if (it != url_to_loader_.end())
+    delete it->second;
+  url_to_loader_[url] = loader.release();
+}
+
+void ApplicationManager::SetLoaderForScheme(
+    scoped_ptr<ApplicationLoader> loader,
+    const std::string& scheme) {
+  SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
+  if (it != scheme_to_loader_.end())
+    delete it->second;
+  scheme_to_loader_[scheme] = loader.release();
+}
+
+void ApplicationManager::SetArgsForURL(const std::vector<std::string>& args,
+                                       const GURL& url) {
+  url_to_args_[url] = args;
+}
+
+void ApplicationManager::SetInterceptor(Interceptor* interceptor) {
+  interceptor_ = interceptor;
+}
+
+ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
+  URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url);
+  if (url_it != url_to_loader_.end())
+    return url_it->second;
+  SchemeToLoaderMap::const_iterator scheme_it =
+      scheme_to_loader_.find(url.scheme());
+  if (scheme_it != scheme_to_loader_.end())
+    return scheme_it->second;
+  return default_loader_.get();
+}
+
+void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
+  // Called from ~ShellImpl, so we do not need to call Destroy here.
+  const GURL url = shell_impl->url();
+  URLToShellImplMap::iterator it = url_to_shell_impl_.find(url);
+  DCHECK(it != url_to_shell_impl_.end());
+  delete it->second;
+  url_to_shell_impl_.erase(it);
+  ApplicationLoader* loader = GetLoaderForURL(url);
+  if (loader)
+    loader->OnApplicationError(this, url);
+  if (delegate_)
+    delegate_->OnApplicationError(url);
+}
+
+ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
+    const GURL& application_url,
+    const std::string& interface_name) {
+  StubServiceProvider* stub_sp = new StubServiceProvider;
+  ServiceProviderPtr spp;
+  BindToProxy(stub_sp, &spp);
+  ConnectToApplication(application_url, GURL(), spp.Pass());
+  MessagePipe pipe;
+  stub_sp->GetRemoteServiceProvider()->ConnectToService(interface_name,
+                                                        pipe.handle1.Pass());
+  return pipe.handle0.Pass();
+}
+}  // namespace mojo
diff --git a/mojo/application_manager/application_manager.h b/mojo/application_manager/application_manager.h
new file mode 100644
index 0000000..af74d8b
--- /dev/null
+++ b/mojo/application_manager/application_manager.h
@@ -0,0 +1,163 @@
+// Copyright 2014 The Chromium 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_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+#define MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager_export.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class MOJO_APPLICATION_MANAGER_EXPORT ApplicationManager {
+ public:
+  class MOJO_APPLICATION_MANAGER_EXPORT Delegate {
+   public:
+    virtual ~Delegate();
+    // Send when the Application holding the handle on the other end of the
+    // Shell pipe goes away.
+    virtual void OnApplicationError(const GURL& url) = 0;
+  };
+
+  // API for testing.
+  class MOJO_APPLICATION_MANAGER_EXPORT TestAPI {
+   public:
+    explicit TestAPI(ApplicationManager* manager);
+    ~TestAPI();
+
+    // Returns true if the shared instance has been created.
+    static bool HasCreatedInstance();
+    // Returns true if there is a ShellImpl for this URL.
+    bool HasFactoryForURL(const GURL& url) const;
+
+   private:
+    ApplicationManager* manager_;
+
+    DISALLOW_COPY_AND_ASSIGN(TestAPI);
+  };
+
+  // Interface class for debugging only.
+  class Interceptor {
+   public:
+    virtual ~Interceptor() {}
+    // Called when ApplicationManager::Connect is called.
+    virtual ServiceProviderPtr OnConnectToClient(
+        const GURL& url,
+        ServiceProviderPtr service_provider) = 0;
+  };
+
+  ApplicationManager();
+  ~ApplicationManager();
+
+  // Returns a shared instance, creating it if necessary.
+  static ApplicationManager* GetInstance();
+
+  void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
+
+  // Loads a service if necessary and establishes a new client connection.
+  void ConnectToApplication(const GURL& application_url,
+                            const GURL& requestor_url,
+                            ServiceProviderPtr service_provider);
+
+  template <typename Interface>
+  inline void ConnectToService(const GURL& application_url,
+                               InterfacePtr<Interface>* ptr) {
+    ScopedMessagePipeHandle service_handle =
+        ConnectToServiceByName(application_url, Interface::Name_);
+    ptr->Bind(service_handle.Pass());
+  }
+
+  ScopedMessagePipeHandle ConnectToServiceByName(
+      const GURL& application_url,
+      const std::string& interface_name);
+
+  void RegisterExternalApplication(const GURL& application_url,
+                                   ScopedMessagePipeHandle shell);
+
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+  // Sets the default Loader to be used if not overridden by SetLoaderForURL()
+  // or SetLoaderForScheme().
+  void set_default_loader(scoped_ptr<ApplicationLoader> loader) {
+    default_loader_ = loader.Pass();
+  }
+  // Sets a Loader to be used for a specific url.
+  void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url);
+  // Sets a Loader to be used for a specific url scheme.
+  void SetLoaderForScheme(scoped_ptr<ApplicationLoader> loader,
+                          const std::string& scheme);
+  // These strings will be passed to the Initialize() method when an
+  // Application is instantiated.
+  void SetArgsForURL(const std::vector<std::string>& args, const GURL& url);
+
+  // Allows to interpose a debugger to service connections.
+  void SetInterceptor(Interceptor* interceptor);
+
+  // Destroys all Shell-ends of connections established with Applications.
+  // Applications connected by this ApplicationManager will observe pipe errors
+  // and have a chance to shutdown.
+  void TerminateShellConnections();
+
+ private:
+  struct ContentHandlerConnection;
+  class LoadCallbacksImpl;
+  class ShellImpl;
+
+  typedef std::map<std::string, ApplicationLoader*> SchemeToLoaderMap;
+  typedef std::map<GURL, ApplicationLoader*> URLToLoaderMap;
+  typedef std::map<GURL, ShellImpl*> URLToShellImplMap;
+  typedef std::map<GURL, ContentHandlerConnection*> URLToContentHandlerMap;
+  typedef std::map<GURL, std::vector<std::string> > URLToArgsMap;
+
+  void ConnectToClient(ShellImpl* shell_impl,
+                       const GURL& url,
+                       const GURL& requestor_url,
+                       ServiceProviderPtr service_provider);
+
+  void RegisterLoadedApplication(const GURL& service_url,
+                                 const GURL& requestor_url,
+                                 ServiceProviderPtr service_provider,
+                                 ScopedMessagePipeHandle* shell_handle);
+
+  void LoadWithContentHandler(const GURL& content_url,
+                              const GURL& requestor_url,
+                              const GURL& content_handler_url,
+                              URLResponsePtr url_response,
+                              ServiceProviderPtr service_provider);
+
+  // Returns the Loader to use for a url (using default if not overridden.)
+  // The preference is to use a loader that's been specified for an url first,
+  // then one that's been specified for a scheme, then the default.
+  ApplicationLoader* GetLoaderForURL(const GURL& url);
+
+  // Removes a ShellImpl when it encounters an error.
+  void OnShellImplError(ShellImpl* shell_impl);
+
+  Delegate* delegate_;
+  // Loader management.
+  URLToLoaderMap url_to_loader_;
+  SchemeToLoaderMap scheme_to_loader_;
+  scoped_ptr<ApplicationLoader> default_loader_;
+  Interceptor* interceptor_;
+
+  URLToShellImplMap url_to_shell_impl_;
+  URLToContentHandlerMap url_to_content_handler_;
+  URLToArgsMap url_to_args_;
+
+  base::WeakPtrFactory<ApplicationManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ApplicationManager);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
diff --git a/mojo/application_manager/application_manager_export.h b/mojo/application_manager/application_manager_export.h
new file mode 100644
index 0000000..9af43cf
--- /dev/null
+++ b/mojo/application_manager/application_manager_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_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
+#define MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION)
+#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION)
+#define MOJO_APPLICATION_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_APPLICATION_MANAGER_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_APPLICATION_MANAGER_EXPORT
+#endif
+
+#endif  // MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
diff --git a/mojo/application_manager/application_manager_unittest.cc b/mojo/application_manager/application_manager_unittest.cc
new file mode 100644
index 0000000..5763265
--- /dev/null
+++ b/mojo/application_manager/application_manager_unittest.cc
@@ -0,0 +1,686 @@
+// Copyright 2014 The Chromium 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 "base/message_loop/message_loop.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/application_manager/background_shell_application_loader.h"
+#include "mojo/application_manager/test.mojom.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.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kTestURLString[] = "test:testService";
+const char kTestAURLString[] = "test:TestA";
+const char kTestBURLString[] = "test:TestB";
+
+struct TestContext {
+  TestContext() : num_impls(0), num_loader_deletes(0) {}
+  std::string last_test_string;
+  int num_impls;
+  int num_loader_deletes;
+};
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+  QuitMessageLoopErrorHandler() {}
+  virtual ~QuitMessageLoopErrorHandler() {}
+
+  // |ErrorHandler| implementation:
+  virtual void OnConnectionError() override {
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+class TestServiceImpl : public InterfaceImpl<TestService> {
+ public:
+  explicit TestServiceImpl(TestContext* context) : context_(context) {
+    ++context_->num_impls;
+  }
+
+  virtual ~TestServiceImpl() { --context_->num_impls; }
+
+  virtual void OnConnectionError() override {
+    if (!base::MessageLoop::current()->is_running())
+      return;
+    base::MessageLoop::current()->Quit();
+  }
+
+  // TestService implementation:
+  virtual void Test(const String& test_string) override {
+    context_->last_test_string = test_string;
+    client()->AckTest();
+  }
+
+ private:
+  TestContext* context_;
+};
+
+class TestClientImpl : public TestClient {
+ public:
+  explicit TestClientImpl(TestServicePtr service)
+      : service_(service.Pass()), quit_after_ack_(false) {
+    service_.set_client(this);
+  }
+
+  virtual ~TestClientImpl() { service_.reset(); }
+
+  virtual void AckTest() override {
+    if (quit_after_ack_)
+      base::MessageLoop::current()->Quit();
+  }
+
+  void Test(std::string test_string) {
+    quit_after_ack_ = true;
+    service_->Test(test_string);
+  }
+
+ private:
+  TestServicePtr service_;
+  bool quit_after_ack_;
+  DISALLOW_COPY_AND_ASSIGN(TestClientImpl);
+};
+
+class TestApplicationLoader : public ApplicationLoader,
+                              public ApplicationDelegate,
+                              public InterfaceFactory<TestService> {
+ public:
+  TestApplicationLoader() : context_(NULL), num_loads_(0) {}
+
+  virtual ~TestApplicationLoader() {
+    if (context_)
+      ++context_->num_loader_deletes;
+    test_app_.reset(NULL);
+  }
+
+  void set_context(TestContext* context) { context_ = context; }
+  int num_loads() const { return num_loads_; }
+  std::vector<std::string> GetArgs() {
+    return test_app_->args().To<std::vector<std::string> >();
+  }
+
+ private:
+  // ApplicationLoader implementation.
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override {
+    ++num_loads_;
+    test_app_.reset(
+        new ApplicationImpl(this, callbacks->RegisterApplication().Pass()));
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override {}
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(this);
+    return true;
+  }
+
+  // InterfaceFactory implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestService> request) override {
+    BindToRequest(new TestServiceImpl(context_), &request);
+  }
+
+  scoped_ptr<ApplicationImpl> test_app_;
+  TestContext* context_;
+  int num_loads_;
+  DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+class TesterContext {
+ public:
+  explicit TesterContext(base::MessageLoop* loop)
+      : num_b_calls_(0),
+        num_c_calls_(0),
+        num_a_deletes_(0),
+        num_b_deletes_(0),
+        num_c_deletes_(0),
+        tester_called_quit_(false),
+        a_called_quit_(false),
+        loop_(loop) {}
+
+  void IncrementNumBCalls() {
+    base::AutoLock lock(lock_);
+    num_b_calls_++;
+  }
+
+  void IncrementNumCCalls() {
+    base::AutoLock lock(lock_);
+    num_c_calls_++;
+  }
+
+  void IncrementNumADeletes() {
+    base::AutoLock lock(lock_);
+    num_a_deletes_++;
+  }
+
+  void IncrementNumBDeletes() {
+    base::AutoLock lock(lock_);
+    num_b_deletes_++;
+  }
+
+  void IncrementNumCDeletes() {
+    base::AutoLock lock(lock_);
+    num_c_deletes_++;
+  }
+
+  void set_tester_called_quit() {
+    base::AutoLock lock(lock_);
+    tester_called_quit_ = true;
+  }
+
+  void set_a_called_quit() {
+    base::AutoLock lock(lock_);
+    a_called_quit_ = true;
+  }
+
+  int num_b_calls() {
+    base::AutoLock lock(lock_);
+    return num_b_calls_;
+  }
+  int num_c_calls() {
+    base::AutoLock lock(lock_);
+    return num_c_calls_;
+  }
+  int num_a_deletes() {
+    base::AutoLock lock(lock_);
+    return num_a_deletes_;
+  }
+  int num_b_deletes() {
+    base::AutoLock lock(lock_);
+    return num_b_deletes_;
+  }
+  int num_c_deletes() {
+    base::AutoLock lock(lock_);
+    return num_c_deletes_;
+  }
+  bool tester_called_quit() {
+    base::AutoLock lock(lock_);
+    return tester_called_quit_;
+  }
+  bool a_called_quit() {
+    base::AutoLock lock(lock_);
+    return a_called_quit_;
+  }
+
+  void QuitSoon() {
+    loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+  }
+
+ private:
+  // lock_ protects all members except for loop_ which must be unchanged for the
+  // lifetime of this class.
+  base::Lock lock_;
+  int num_b_calls_;
+  int num_c_calls_;
+  int num_a_deletes_;
+  int num_b_deletes_;
+  int num_c_deletes_;
+  bool tester_called_quit_;
+  bool a_called_quit_;
+
+  base::MessageLoop* loop_;
+};
+
+// Used to test that the requestor url will be correctly passed.
+class TestAImpl : public InterfaceImpl<TestA> {
+ public:
+  TestAImpl(ApplicationConnection* connection, TesterContext* test_context)
+      : test_context_(test_context) {
+    connection->ConnectToApplication(kTestBURLString)->ConnectToService(&b_);
+  }
+  virtual ~TestAImpl() {
+    test_context_->IncrementNumADeletes();
+    if (base::MessageLoop::current()->is_running())
+      Quit();
+  }
+
+ private:
+  virtual void CallB() override {
+    b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+  }
+
+  virtual void CallCFromB() override {
+    b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+  }
+
+  void Quit() {
+    base::MessageLoop::current()->Quit();
+    test_context_->set_a_called_quit();
+    test_context_->QuitSoon();
+  }
+
+  TesterContext* test_context_;
+  TestBPtr b_;
+};
+
+class TestBImpl : public InterfaceImpl<TestB> {
+ public:
+  TestBImpl(ApplicationConnection* connection, TesterContext* test_context)
+      : test_context_(test_context) {
+    connection->ConnectToService(&c_);
+  }
+
+  virtual ~TestBImpl() {
+    test_context_->IncrementNumBDeletes();
+    if (base::MessageLoop::current()->is_running())
+      base::MessageLoop::current()->Quit();
+    test_context_->QuitSoon();
+  }
+
+ private:
+  virtual void B(const mojo::Callback<void()>& callback) override {
+    test_context_->IncrementNumBCalls();
+    callback.Run();
+  }
+
+  virtual void CallC(const mojo::Callback<void()>& callback) override {
+    test_context_->IncrementNumBCalls();
+    c_->C(callback);
+  }
+
+  TesterContext* test_context_;
+  TestCPtr c_;
+};
+
+class TestCImpl : public InterfaceImpl<TestC> {
+ public:
+  TestCImpl(ApplicationConnection* connection, TesterContext* test_context)
+      : test_context_(test_context) {}
+
+  virtual ~TestCImpl() { test_context_->IncrementNumCDeletes(); }
+
+ private:
+  virtual void C(const mojo::Callback<void()>& callback) override {
+    test_context_->IncrementNumCCalls();
+    callback.Run();
+  }
+  TesterContext* test_context_;
+};
+
+class Tester : public ApplicationDelegate,
+               public ApplicationLoader,
+               public InterfaceFactory<TestA>,
+               public InterfaceFactory<TestB>,
+               public InterfaceFactory<TestC> {
+ public:
+  Tester(TesterContext* context, const std::string& requestor_url)
+      : context_(context), requestor_url_(requestor_url) {}
+  virtual ~Tester() {}
+
+ private:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override {
+    app_.reset(
+        new ApplicationImpl(this, callbacks->RegisterApplication().Pass()));
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override {}
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    if (!requestor_url_.empty() &&
+        requestor_url_ != connection->GetRemoteApplicationURL()) {
+      context_->set_tester_called_quit();
+      context_->QuitSoon();
+      base::MessageLoop::current()->Quit();
+      return false;
+    }
+    // If we're coming from A, then add B, otherwise A.
+    if (connection->GetRemoteApplicationURL() == kTestAURLString)
+      connection->AddService<TestB>(this);
+    else
+      connection->AddService<TestA>(this);
+    return true;
+  }
+
+  virtual bool ConfigureOutgoingConnection(
+      ApplicationConnection* connection) override {
+    // If we're connecting to B, then add C.
+    if (connection->GetRemoteApplicationURL() == kTestBURLString)
+      connection->AddService<TestC>(this);
+    return true;
+  }
+
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestA> request) override {
+    BindToRequest(new TestAImpl(connection, context_), &request);
+  }
+
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestB> request) override {
+    BindToRequest(new TestBImpl(connection, context_), &request);
+  }
+
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<TestC> request) override {
+    BindToRequest(new TestCImpl(connection, context_), &request);
+  }
+
+  TesterContext* context_;
+  scoped_ptr<ApplicationImpl> app_;
+  std::string requestor_url_;
+};
+
+class TestServiceInterceptor : public ApplicationManager::Interceptor {
+ public:
+  TestServiceInterceptor() : call_count_(0) {}
+
+  virtual ServiceProviderPtr OnConnectToClient(
+      const GURL& url,
+      ServiceProviderPtr service_provider) override {
+    ++call_count_;
+    url_ = url;
+    return service_provider.Pass();
+  }
+
+  std::string url_spec() const {
+    if (!url_.is_valid())
+      return "invalid url";
+    return url_.spec();
+  }
+
+  int call_count() const { return call_count_; }
+
+ private:
+  int call_count_;
+  GURL url_;
+  DISALLOW_COPY_AND_ASSIGN(TestServiceInterceptor);
+};
+
+}  // namespace
+
+class ApplicationManagerTest : public testing::Test {
+ public:
+  ApplicationManagerTest() : tester_context_(&loop_) {}
+
+  virtual ~ApplicationManagerTest() {}
+
+  virtual void SetUp() override {
+    application_manager_.reset(new ApplicationManager);
+    TestApplicationLoader* default_loader = new TestApplicationLoader;
+    default_loader->set_context(&context_);
+    application_manager_->set_default_loader(
+        scoped_ptr<ApplicationLoader>(default_loader));
+
+    TestServicePtr service_proxy;
+    application_manager_->ConnectToService(GURL(kTestURLString),
+                                           &service_proxy);
+    test_client_.reset(new TestClientImpl(service_proxy.Pass()));
+  }
+
+  virtual void TearDown() override {
+    test_client_.reset(NULL);
+    application_manager_.reset(NULL);
+  }
+
+  scoped_ptr<BackgroundShellApplicationLoader> MakeLoader(
+      const std::string& requestor_url) {
+    scoped_ptr<ApplicationLoader> real_loader(
+        new Tester(&tester_context_, requestor_url));
+    scoped_ptr<BackgroundShellApplicationLoader> loader(
+        new BackgroundShellApplicationLoader(real_loader.Pass(),
+                                             std::string(),
+                                             base::MessageLoop::TYPE_DEFAULT));
+    return loader.Pass();
+  }
+
+  void AddLoaderForURL(const GURL& url, const std::string& requestor_url) {
+    application_manager_->SetLoaderForURL(
+        MakeLoader(requestor_url).PassAs<ApplicationLoader>(), url);
+  }
+
+  bool HasFactoryForTestURL() {
+    ApplicationManager::TestAPI manager_test_api(application_manager_.get());
+    return manager_test_api.HasFactoryForURL(GURL(kTestURLString));
+  }
+
+ protected:
+  base::ShadowingAtExitManager at_exit_;
+  TesterContext tester_context_;
+  TestContext context_;
+  base::MessageLoop loop_;
+  scoped_ptr<TestClientImpl> test_client_;
+  scoped_ptr<ApplicationManager> application_manager_;
+  DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest);
+};
+
+TEST_F(ApplicationManagerTest, Basic) {
+  test_client_->Test("test");
+  loop_.Run();
+  EXPECT_EQ(std::string("test"), context_.last_test_string);
+}
+
+// Confirm that no arguments are sent to an application by default.
+TEST_F(ApplicationManagerTest, NoArgs) {
+  ApplicationManager am;
+  GURL test_url("test:test");
+  TestContext context;
+  TestApplicationLoader* loader = new TestApplicationLoader;
+  loader->set_context(&context);
+  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
+  TestServicePtr test_service;
+  am.ConnectToService(test_url, &test_service);
+  TestClientImpl test_client(test_service.Pass());
+  test_client.Test("test");
+  loop_.Run();
+  std::vector<std::string> app_args = loader->GetArgs();
+  EXPECT_EQ(0U, app_args.size());
+}
+
+// Confirm that arguments are sent to an application.
+TEST_F(ApplicationManagerTest, Args) {
+  ApplicationManager am;
+  GURL test_url("test:test");
+  std::vector<std::string> args;
+  args.push_back("test_arg1");
+  args.push_back("test_arg2");
+  am.SetArgsForURL(args, test_url);
+  TestContext context;
+  TestApplicationLoader* loader = new TestApplicationLoader;
+  loader->set_context(&context);
+  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
+  TestServicePtr test_service;
+  am.ConnectToService(test_url, &test_service);
+  TestClientImpl test_client(test_service.Pass());
+  test_client.Test("test");
+  loop_.Run();
+  std::vector<std::string> app_args = loader->GetArgs();
+  ASSERT_EQ(args.size(), app_args.size());
+  EXPECT_EQ(args[0], app_args[0]);
+  EXPECT_EQ(args[1], app_args[1]);
+}
+
+TEST_F(ApplicationManagerTest, ClientError) {
+  test_client_->Test("test");
+  EXPECT_TRUE(HasFactoryForTestURL());
+  loop_.Run();
+  EXPECT_EQ(1, context_.num_impls);
+  test_client_.reset(NULL);
+  loop_.Run();
+  EXPECT_EQ(0, context_.num_impls);
+  EXPECT_TRUE(HasFactoryForTestURL());
+}
+
+TEST_F(ApplicationManagerTest, Deletes) {
+  {
+    ApplicationManager am;
+    TestApplicationLoader* default_loader = new TestApplicationLoader;
+    default_loader->set_context(&context_);
+    TestApplicationLoader* url_loader1 = new TestApplicationLoader;
+    TestApplicationLoader* url_loader2 = new TestApplicationLoader;
+    url_loader1->set_context(&context_);
+    url_loader2->set_context(&context_);
+    TestApplicationLoader* scheme_loader1 = new TestApplicationLoader;
+    TestApplicationLoader* scheme_loader2 = new TestApplicationLoader;
+    scheme_loader1->set_context(&context_);
+    scheme_loader2->set_context(&context_);
+    am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader));
+    am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1),
+                       GURL("test:test1"));
+    am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2),
+                       GURL("test:test1"));
+    am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader1),
+                          "test");
+    am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader2),
+                          "test");
+  }
+  EXPECT_EQ(5, context_.num_loader_deletes);
+}
+
+// Confirm that both urls and schemes can have their loaders explicitly set.
+TEST_F(ApplicationManagerTest, SetLoaders) {
+  TestApplicationLoader* default_loader = new TestApplicationLoader;
+  TestApplicationLoader* url_loader = new TestApplicationLoader;
+  TestApplicationLoader* scheme_loader = new TestApplicationLoader;
+  application_manager_->set_default_loader(
+      scoped_ptr<ApplicationLoader>(default_loader));
+  application_manager_->SetLoaderForURL(
+      scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1"));
+  application_manager_->SetLoaderForScheme(
+      scoped_ptr<ApplicationLoader>(scheme_loader), "test");
+
+  // test::test1 should go to url_loader.
+  TestServicePtr test_service;
+  application_manager_->ConnectToService(GURL("test:test1"), &test_service);
+  EXPECT_EQ(1, url_loader->num_loads());
+  EXPECT_EQ(0, scheme_loader->num_loads());
+  EXPECT_EQ(0, default_loader->num_loads());
+
+  // test::test2 should go to scheme loader.
+  application_manager_->ConnectToService(GURL("test:test2"), &test_service);
+  EXPECT_EQ(1, url_loader->num_loads());
+  EXPECT_EQ(1, scheme_loader->num_loads());
+  EXPECT_EQ(0, default_loader->num_loads());
+
+  // http::test1 should go to default loader.
+  application_manager_->ConnectToService(GURL("http:test1"), &test_service);
+  EXPECT_EQ(1, url_loader->num_loads());
+  EXPECT_EQ(1, scheme_loader->num_loads());
+  EXPECT_EQ(1, default_loader->num_loads());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads.
+TEST_F(ApplicationManagerTest, ACallB) {
+  // Any url can load a.
+  AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+  // Only a can load b.
+  AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+  TestAPtr a;
+  application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+  a->CallB();
+  loop_.Run();
+  EXPECT_EQ(1, tester_context_.num_b_calls());
+  EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// A calls B which calls C.
+TEST_F(ApplicationManagerTest, BCallC) {
+  // Any url can load a.
+  AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+  // Only a can load b.
+  AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+  TestAPtr a;
+  application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+  a->CallCFromB();
+  loop_.Run();
+
+  EXPECT_EQ(1, tester_context_.num_b_calls());
+  EXPECT_EQ(1, tester_context_.num_c_calls());
+  EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// Confirm that a service impl will be deleted if the app that connected to
+// it goes away.
+TEST_F(ApplicationManagerTest, BDeleted) {
+  AddLoaderForURL(GURL(kTestAURLString), std::string());
+  AddLoaderForURL(GURL(kTestBURLString), std::string());
+
+  TestAPtr a;
+  application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+
+  a->CallB();
+  loop_.Run();
+
+  // Kills the a app.
+  application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(),
+                                        GURL(kTestAURLString));
+  loop_.Run();
+
+  EXPECT_EQ(1, tester_context_.num_b_deletes());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads, and that it can be rejected.
+TEST_F(ApplicationManagerTest, ANoLoadB) {
+  // Any url can load a.
+  AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+  // Only c can load b, so this will fail.
+  AddLoaderForURL(GURL(kTestBURLString), "test:TestC");
+
+  TestAPtr a;
+  application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+  a->CallB();
+  loop_.Run();
+  EXPECT_EQ(0, tester_context_.num_b_calls());
+
+  EXPECT_FALSE(tester_context_.a_called_quit());
+  EXPECT_TRUE(tester_context_.tester_called_quit());
+}
+
+TEST_F(ApplicationManagerTest, NoServiceNoLoad) {
+  AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+  // There is no TestC service implementation registered with
+  // ApplicationManager, so this cannot succeed (but also shouldn't crash).
+  TestCPtr c;
+  application_manager_->ConnectToService(GURL(kTestAURLString), &c);
+  QuitMessageLoopErrorHandler quitter;
+  c.set_error_handler(&quitter);
+
+  loop_.Run();
+  EXPECT_TRUE(c.encountered_error());
+}
+
+TEST_F(ApplicationManagerTest, Interceptor) {
+  TestServiceInterceptor interceptor;
+  TestApplicationLoader* default_loader = new TestApplicationLoader;
+  application_manager_->set_default_loader(
+      scoped_ptr<ApplicationLoader>(default_loader));
+  application_manager_->SetInterceptor(&interceptor);
+
+  std::string url("test:test3");
+  TestServicePtr test_service;
+  application_manager_->ConnectToService(GURL(url), &test_service);
+
+  EXPECT_EQ(1, interceptor.call_count());
+  EXPECT_EQ(url, interceptor.url_spec());
+  EXPECT_EQ(1, default_loader->num_loads());
+}
+
+}  // namespace mojo
diff --git a/mojo/application_manager/background_shell_application_loader.cc b/mojo/application_manager/background_shell_application_loader.cc
new file mode 100644
index 0000000..e6664db
--- /dev/null
+++ b/mojo/application_manager/background_shell_application_loader.cc
@@ -0,0 +1,127 @@
+// Copyright 2014 The Chromium 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_manager/background_shell_application_loader.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "mojo/application_manager/application_manager.h"
+
+namespace mojo {
+
+class BackgroundShellApplicationLoader::BackgroundLoader {
+ public:
+  explicit BackgroundLoader(ApplicationLoader* loader) : loader_(loader) {}
+  ~BackgroundLoader() {}
+
+  void Load(ApplicationManager* manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle) {
+    scoped_refptr<LoadCallbacks> callbacks(
+        new ApplicationLoader::SimpleLoadCallbacks(shell_handle.Pass()));
+    loader_->Load(manager, url, callbacks);
+  }
+
+  void OnApplicationError(ApplicationManager* manager, const GURL& url) {
+    loader_->OnApplicationError(manager, url);
+  }
+
+ private:
+  ApplicationLoader* loader_;  // Owned by BackgroundShellApplicationLoader
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundLoader);
+};
+
+BackgroundShellApplicationLoader::BackgroundShellApplicationLoader(
+    scoped_ptr<ApplicationLoader> real_loader,
+    const std::string& thread_name,
+    base::MessageLoop::Type message_loop_type)
+    : loader_(real_loader.Pass()),
+      message_loop_type_(message_loop_type),
+      thread_name_(thread_name),
+      message_loop_created_(true, false),
+      background_loader_(NULL) {
+}
+
+BackgroundShellApplicationLoader::~BackgroundShellApplicationLoader() {
+  if (thread_)
+    thread_->Join();
+}
+
+void BackgroundShellApplicationLoader::Load(
+    ApplicationManager* manager,
+    const GURL& url,
+    scoped_refptr<LoadCallbacks> callbacks) {
+  ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+  if (!shell_handle.is_valid())
+    return;
+
+  if (!thread_) {
+    // TODO(tim): It'd be nice if we could just have each Load call
+    // result in a new thread like DynamicService{Loader, Runner}. But some
+    // loaders are creating multiple ApplicationImpls (NetworkApplicationLoader)
+    // sharing a delegate (etc). So we have to keep it single threaded, wait
+    // for the thread to initialize, and post to the TaskRunner for subsequent
+    // Load calls for now.
+    thread_.reset(new base::DelegateSimpleThread(this, thread_name_));
+    thread_->Start();
+    message_loop_created_.Wait();
+    DCHECK(task_runner_.get());
+  }
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &BackgroundShellApplicationLoader::LoadOnBackgroundThread,
+          base::Unretained(this),
+          manager,
+          url,
+          base::Owned(new ScopedMessagePipeHandle(shell_handle.Pass()))));
+}
+
+void BackgroundShellApplicationLoader::OnApplicationError(
+    ApplicationManager* manager,
+    const GURL& url) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&BackgroundShellApplicationLoader::
+                                        OnApplicationErrorOnBackgroundThread,
+                                    base::Unretained(this),
+                                    manager,
+                                    url));
+}
+
+void BackgroundShellApplicationLoader::Run() {
+  base::MessageLoop message_loop(message_loop_type_);
+  base::RunLoop loop;
+  task_runner_ = message_loop.task_runner();
+  quit_closure_ = loop.QuitClosure();
+  message_loop_created_.Signal();
+  loop.Run();
+
+  delete background_loader_;
+  background_loader_ = NULL;
+  // Destroy |loader_| on the thread it's actually used on.
+  loader_.reset();
+}
+
+void BackgroundShellApplicationLoader::LoadOnBackgroundThread(
+    ApplicationManager* manager,
+    const GURL& url,
+    ScopedMessagePipeHandle* shell_handle) {
+  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  if (!background_loader_)
+    background_loader_ = new BackgroundLoader(loader_.get());
+  background_loader_->Load(manager, url, shell_handle->Pass());
+}
+
+void BackgroundShellApplicationLoader::OnApplicationErrorOnBackgroundThread(
+    ApplicationManager* manager,
+    const GURL& url) {
+  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  if (!background_loader_)
+    background_loader_ = new BackgroundLoader(loader_.get());
+  background_loader_->OnApplicationError(manager, url);
+}
+
+}  // namespace mojo
diff --git a/mojo/application_manager/background_shell_application_loader.h b/mojo/application_manager/background_shell_application_loader.h
new file mode 100644
index 0000000..3c56290
--- /dev/null
+++ b/mojo/application_manager/background_shell_application_loader.h
@@ -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.
+
+#ifndef MOJO_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_
+#define MOJO_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/application_manager/application_loader.h"
+
+namespace mojo {
+
+// TODO(tim): Eventually this should be Android-only to support services
+// that we need to bundle with the shell (such as NetworkService). Perhaps
+// we should move it to shell/ as well.
+class MOJO_APPLICATION_MANAGER_EXPORT BackgroundShellApplicationLoader
+    : public ApplicationLoader,
+      public base::DelegateSimpleThread::Delegate {
+ public:
+  BackgroundShellApplicationLoader(scoped_ptr<ApplicationLoader> real_loader,
+                                   const std::string& thread_name,
+                                   base::MessageLoop::Type message_loop_type);
+  virtual ~BackgroundShellApplicationLoader();
+
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+ private:
+  class BackgroundLoader;
+
+  // |base::DelegateSimpleThread::Delegate| method:
+  virtual void Run() override;
+
+  // These functions are exected on the background thread. They call through
+  // to |background_loader_| to do the actual loading.
+  // TODO: having this code take a |manager| is fragile (as ApplicationManager
+  // isn't thread safe).
+  void LoadOnBackgroundThread(ApplicationManager* manager,
+                              const GURL& url,
+                              ScopedMessagePipeHandle* shell_handle);
+  void OnApplicationErrorOnBackgroundThread(ApplicationManager* manager,
+                                            const GURL& url);
+  bool quit_on_shutdown_;
+  scoped_ptr<ApplicationLoader> loader_;
+
+  const base::MessageLoop::Type message_loop_type_;
+  const std::string thread_name_;
+
+  // Created on |thread_| during construction of |this|. Protected against
+  // uninitialized use by |message_loop_created_|, and protected against
+  // use-after-free by holding a reference to the thread-safe object. Note
+  // that holding a reference won't hold |thread_| from exiting.
+  scoped_refptr<base::TaskRunner> task_runner_;
+  base::WaitableEvent message_loop_created_;
+
+  // Lives on |thread_|.
+  base::Closure quit_closure_;
+
+  scoped_ptr<base::DelegateSimpleThread> thread_;
+
+  // Lives on |thread_|. Trivial interface that calls through to |loader_|.
+  BackgroundLoader* background_loader_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundShellApplicationLoader);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_
diff --git a/mojo/application_manager/background_shell_application_loader_unittest.cc b/mojo/application_manager/background_shell_application_loader_unittest.cc
new file mode 100644
index 0000000..acf1a01
--- /dev/null
+++ b/mojo/application_manager/background_shell_application_loader_unittest.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 "mojo/application_manager/background_shell_application_loader.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+namespace {
+
+class DummyLoader : public ApplicationLoader {
+ public:
+  DummyLoader() : simulate_app_quit_(true) {}
+  virtual ~DummyLoader() {}
+
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override {
+    if (simulate_app_quit_)
+      base::MessageLoop::current()->Quit();
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override {}
+
+  void DontSimulateAppQuit() { simulate_app_quit_ = false; }
+
+ private:
+  bool simulate_app_quit_;
+};
+
+}  // namespace
+
+// Tests that the loader can start and stop gracefully.
+TEST(BackgroundShellApplicationLoaderTest, StartStop) {
+  scoped_ptr<ApplicationLoader> real_loader(new DummyLoader());
+  BackgroundShellApplicationLoader loader(
+      real_loader.Pass(), "test", base::MessageLoop::TYPE_DEFAULT);
+}
+
+// Tests that the loader can load a service that is well behaved (quits
+// itself).
+TEST(BackgroundShellApplicationLoaderTest, Load) {
+  scoped_ptr<ApplicationLoader> real_loader(new DummyLoader());
+  BackgroundShellApplicationLoader loader(
+      real_loader.Pass(), "test", base::MessageLoop::TYPE_DEFAULT);
+  MessagePipe dummy;
+  scoped_refptr<ApplicationLoader::SimpleLoadCallbacks> callbacks(
+      new ApplicationLoader::SimpleLoadCallbacks(dummy.handle0.Pass()));
+  loader.Load(NULL, GURL(), callbacks);
+}
+
+}  // namespace mojo
diff --git a/mojo/application_manager/test.mojom b/mojo/application_manager/test.mojom
new file mode 100644
index 0000000..6a03581
--- /dev/null
+++ b/mojo/application_manager/test.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.
+
+module mojo {
+
+[Client=TestClient]
+interface TestService {
+  Test(string? test_string);
+};
+
+interface TestClient {
+  AckTest();
+};
+
+interface TestA {
+  CallB();
+  CallCFromB();
+};
+
+interface TestB {
+  B() => ();
+  CallC() => ();
+};
+
+interface TestC {
+  C() => ();
+};
+
+}
diff --git a/mojo/apps/js/BUILD.gn b/mojo/apps/js/BUILD.gn
new file mode 100644
index 0000000..16f1907
--- /dev/null
+++ b/mojo/apps/js/BUILD.gn
@@ -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.
+
+# GYP version: part of mojo/mojo_apps.gypi:mojo_js_lib
+source_set("js") {
+  sources = [
+    "mojo_runner_delegate.cc",
+    "mojo_runner_delegate.h",
+  ]
+
+  public_deps = [
+    "//mojo/bindings/js",
+  ]
+  deps = [
+    "//base",
+    "//gin",
+    "//mojo/apps/js/bindings",
+    "//mojo/apps/js/bindings/gl",
+    "//v8",
+  ]
+}
+
+# GYP version: mojo/mojo_apps.gypi:mojo_js_apps_lib
+source_set("js_apps") {
+  sources = [
+    "application_delegate_impl.cc",
+    "js_app.cc",
+    "mojo_module.cc",
+  ]
+
+  public_deps = [
+    "//mojo/bindings/js",
+  ]
+  deps = [
+    ":js",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/utility",
+  ]
+}
+
+shared_library("mojo_js_content_handler") {
+  sources = [
+    "content_handler_impl.cc",
+    "content_handler_main.cc",
+  ]
+
+  deps = [
+    ":js_apps",
+    "//base:i18n",
+    "//mojo/application:application",
+    "//mojo/environment:chromium",
+    "//mojo/services/public/interfaces/content_handler",
+  ]
+}
+
+shared_library("mojo_js_standalone") {
+  sources = [
+    "standalone_main.cc"
+  ]
+
+  deps = [
+    ":js_apps",
+    "//base:i18n",
+    "//mojo/application:application",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/application",
+  ]
+}
+
+
+
diff --git a/mojo/apps/js/DEPS b/mojo/apps/js/DEPS
new file mode 100644
index 0000000..d974b68
--- /dev/null
+++ b/mojo/apps/js/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+gin",
+  "+v8",
+]
diff --git a/mojo/apps/js/application_delegate_impl.cc b/mojo/apps/js/application_delegate_impl.cc
new file mode 100644
index 0000000..eb784a9
--- /dev/null
+++ b/mojo/apps/js/application_delegate_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/apps/js/application_delegate_impl.h"
+
+#include "gin/array_buffer.h"
+#include "gin/public/isolate_holder.h"
+#include "mojo/apps/js/js_app.h"
+#include "mojo/public/cpp/application/application_impl.h"
+
+namespace mojo {
+namespace apps {
+
+ApplicationDelegateImpl::ApplicationDelegateImpl()
+    : application_impl_(nullptr) {
+}
+
+void ApplicationDelegateImpl::Initialize(ApplicationImpl* app) {
+  application_impl_ = app;
+  gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+                                 gin::ArrayBufferAllocator::SharedInstance());
+}
+
+ApplicationDelegateImpl::~ApplicationDelegateImpl() {
+}
+
+void ApplicationDelegateImpl::StartJSApp(scoped_ptr<JSApp> app_ptr) {
+  JSApp *app = app_ptr.release();
+  app_vector_.push_back(app);
+  // TODO(hansmuller): deal with the Start() return value.
+  app->Start();
+}
+
+void ApplicationDelegateImpl::QuitJSApp(JSApp* app) {
+  AppVector::iterator itr =
+      std::find(app_vector_.begin(), app_vector_.end(), app);
+  if (itr != app_vector_.end())
+    app_vector_.erase(itr);
+}
+
+void ApplicationDelegateImpl::ConnectToService(
+    ScopedMessagePipeHandle pipe_handle,
+    const std::string& application_url,
+    const std::string& interface_name) {
+  CHECK(application_impl_);
+  ServiceProvider* service_provider =
+      application_impl_->ConnectToApplication(application_url)
+          ->GetServiceProvider();
+  service_provider->ConnectToService(interface_name, pipe_handle.Pass());
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/application_delegate_impl.h b/mojo/apps/js/application_delegate_impl.h
new file mode 100644
index 0000000..4c2f2e2
--- /dev/null
+++ b/mojo/apps/js/application_delegate_impl.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_APPS_JS_CONTENT_HANDLER_H_
+#define MOJO_APPS_JS_CONTENT_HANDLER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace mojo {
+
+class ApplcationImpl;
+
+namespace apps {
+
+class ApplicationDelegateImpl;
+class JSApp;
+
+// Manages the JSApps started by this content handler. This class owns the one
+// reference to the Mojo shell. JSApps post a task to the content handler's
+// thread to connect to a service or to quit.
+//
+// The lifetime each JSApp is defined by its entry in AppVector. When the entry
+// is removed ("erased") by QuitJSApp(), the JSApp is destroyed.
+
+class ApplicationDelegateImpl : public ApplicationDelegate {
+ public:
+  ApplicationDelegateImpl();
+  virtual ~ApplicationDelegateImpl();
+
+  // Add app to the AppVector and call its Start() method.
+  void StartJSApp(scoped_ptr<JSApp> app);
+
+  // Remove app from the AppVector; destroys the app.
+  void QuitJSApp(JSApp *app);
+
+  void ConnectToService(ScopedMessagePipeHandle pipe_handle,
+                        const std::string& application_url,
+                        const std::string& interface_name);
+
+ protected:
+  // ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override;
+
+ private:
+  typedef ScopedVector<JSApp> AppVector;
+  ApplicationImpl* application_impl_;
+  AppVector app_vector_;
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_CONTENT_HANDLER_H_
diff --git a/mojo/apps/js/bindings/BUILD.gn b/mojo/apps/js/bindings/BUILD.gn
new file mode 100644
index 0000000..bc72747
--- /dev/null
+++ b/mojo/apps/js/bindings/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.
+
+# GYP version: part of mojo/mojo_apps.gypi:mojo_js_lib
+source_set("bindings") {
+  deps = [
+    "//base",
+    "//gin",
+    "//v8",
+    "//mojo/bindings/js",
+  ]
+
+  sources = [
+    "threading.cc",
+    "threading.h",
+    "monotonic_clock.cc",
+    "monotonic_clock.h",
+  ]
+}
diff --git a/mojo/apps/js/bindings/connection_unittests.js b/mojo/apps/js/bindings/connection_unittests.js
new file mode 100644
index 0000000..0b0a71b
--- /dev/null
+++ b/mojo/apps/js/bindings/connection_unittests.js
@@ -0,0 +1,256 @@
+// 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.
+
+// Mock out the support module to avoid depending on the message loop.
+define("mojo/public/js/bindings/support", ["timer"], function(timer) {
+  var waitingCallbacks = [];
+
+  function WaitCookie(id) {
+    this.id = id;
+  }
+
+  function asyncWait(handle, flags, callback) {
+    var id = waitingCallbacks.length;
+    waitingCallbacks.push(callback);
+    return new WaitCookie(id);
+  }
+
+  function cancelWait(cookie) {
+    waitingCallbacks[cookie.id] = null;
+  }
+
+  function numberOfWaitingCallbacks() {
+    var count = 0;
+    for (var i = 0; i < waitingCallbacks.length; ++i) {
+      if (waitingCallbacks[i])
+        ++count;
+    }
+    return count;
+  }
+
+  function pumpOnce(result) {
+    var callbacks = waitingCallbacks;
+    waitingCallbacks = [];
+    for (var i = 0; i < callbacks.length; ++i) {
+      if (callbacks[i])
+        callbacks[i](result);
+    }
+  }
+
+  // Queue up a pumpOnce call to execute after the stack unwinds. Use
+  // this to trigger a pump after all Promises are executed.
+  function queuePump(result) {
+    timer.createOneShot(0, pumpOnce.bind(undefined, result));
+  }
+
+  var exports = {};
+  exports.asyncWait = asyncWait;
+  exports.cancelWait = cancelWait;
+  exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks;
+  exports.pumpOnce = pumpOnce;
+  exports.queuePump = queuePump;
+  return exports;
+});
+
+define([
+    "gin/test/expect",
+    "mojo/public/js/bindings/support",
+    "mojo/public/js/bindings/core",
+    "mojo/public/js/bindings/connection",
+    "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+    "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+    "mojo/apps/js/bindings/threading",
+    "gc",
+], function(expect,
+            mockSupport,
+            core,
+            connection,
+            sample_interfaces,
+            sample_service,
+            threading,
+            gc) {
+  testClientServer();
+  testWriteToClosedPipe();
+  testRequestResponse().then(function() {
+    this.result = "PASS";
+    gc.collectGarbage();  // should not crash
+    threading.quit();
+  }.bind(this)).catch(function(e) {
+    this.result = "FAIL: " + (e.stack || e);
+    threading.quit();
+  }.bind(this));
+
+  function testClientServer() {
+    var receivedFrobinate = false;
+    var receivedDidFrobinate = false;
+
+    // ServiceImpl -------------------------------------------------------------
+
+    function ServiceImpl(peer) {
+      this.peer = peer;
+    }
+
+    ServiceImpl.prototype = Object.create(sample_service.ServiceStub.prototype);
+
+    ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+      receivedFrobinate = true;
+
+      expect(foo.name).toBe("Example name");
+      expect(baz).toBeTruthy();
+      expect(core.close(port)).toBe(core.RESULT_OK);
+
+      this.peer.didFrobinate(42);
+    };
+
+    // ServiceImpl -------------------------------------------------------------
+
+    function ServiceClientImpl(peer) {
+      this.peer = peer;
+    }
+
+    ServiceClientImpl.prototype =
+        Object.create(sample_service.ServiceClientStub.prototype);
+
+    ServiceClientImpl.prototype.didFrobinate = function(result) {
+      receivedDidFrobinate = true;
+
+      expect(result).toBe(42);
+    };
+
+    var pipe = core.createMessagePipe();
+    var anotherPipe = core.createMessagePipe();
+    var sourcePipe = core.createMessagePipe();
+
+    var connection0 = new connection.Connection(
+        pipe.handle0, ServiceImpl, sample_service.ServiceClientProxy);
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, ServiceClientImpl, sample_service.ServiceProxy);
+
+    var foo = new sample_service.Foo();
+    foo.bar = new sample_service.Bar();
+    foo.name = "Example name";
+    foo.source = sourcePipe.handle0;
+    connection1.remote.frobinate(foo, true, anotherPipe.handle0);
+
+    mockSupport.pumpOnce(core.RESULT_OK);
+
+    expect(receivedFrobinate).toBeTruthy();
+    expect(receivedDidFrobinate).toBeTruthy();
+
+    connection0.close();
+    connection1.close();
+
+    expect(mockSupport.numberOfWaitingCallbacks()).toBe(0);
+
+    // sourcePipe.handle0 was closed automatically when sent over IPC.
+    expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+    // sourcePipe.handle1 hasn't been closed yet.
+    expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+    // anotherPipe.handle0 was closed automatically when sent over IPC.
+    expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+    // anotherPipe.handle1 hasn't been closed yet.
+    expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
+
+    // The Connection object is responsible for closing these handles.
+    expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+    expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT);
+  }
+
+  function testWriteToClosedPipe() {
+    var pipe = core.createMessagePipe();
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, function() {}, sample_service.ServiceProxy);
+
+    // Close the other end of the pipe.
+    core.close(pipe.handle0);
+
+    // Not observed yet because we haven't pumped events yet.
+    expect(connection1.encounteredError()).toBeFalsy();
+
+    var foo = new sample_service.Foo();
+    foo.bar = new sample_service.Bar();
+    // TODO(darin): crbug.com/357043: pass null in place of |foo| here.
+    connection1.remote.frobinate(foo, true, null);
+
+    // Write failures are not reported.
+    expect(connection1.encounteredError()).toBeFalsy();
+
+    // Pump events, and then we should start observing the closed pipe.
+    mockSupport.pumpOnce(core.RESULT_OK);
+
+    expect(connection1.encounteredError()).toBeTruthy();
+
+    connection1.close();
+  }
+
+  function testRequestResponse() {
+
+    // ProviderImpl ------------------------------------------------------------
+
+    function ProviderImpl(peer) {
+      this.peer = peer;
+    }
+
+    ProviderImpl.prototype =
+        Object.create(sample_interfaces.ProviderStub.prototype);
+
+    ProviderImpl.prototype.echoString = function(a) {
+      mockSupport.queuePump(core.RESULT_OK);
+      return Promise.resolve({a: a});
+    };
+
+    ProviderImpl.prototype.echoStrings = function(a, b) {
+      mockSupport.queuePump(core.RESULT_OK);
+      return Promise.resolve({a: a, b: b});
+    };
+
+    // ProviderClientImpl ------------------------------------------------------
+
+    function ProviderClientImpl(peer) {
+      this.peer = peer;
+    }
+
+    ProviderClientImpl.prototype =
+        Object.create(sample_interfaces.ProviderClientStub.prototype);
+
+    var pipe = core.createMessagePipe();
+
+    var connection0 = new connection.Connection(
+        pipe.handle0, ProviderImpl, sample_interfaces.ProviderClientProxy);
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, ProviderClientImpl, sample_interfaces.ProviderProxy);
+
+    var origReadMessage = core.readMessage;
+    // echoString
+    mockSupport.queuePump(core.RESULT_OK);
+    return connection1.remote.echoString("hello").then(function(response) {
+      expect(response.a).toBe("hello");
+    }).then(function() {
+      // echoStrings
+      mockSupport.queuePump(core.RESULT_OK);
+      return connection1.remote.echoStrings("hello", "world");
+    }).then(function(response) {
+      expect(response.a).toBe("hello");
+      expect(response.b).toBe("world");
+    }).then(function() {
+      // Mock a read failure, expect it to fail.
+      core.readMessage = function() {
+        return { result: core.RESULT_UNKNOWN };
+      };
+      mockSupport.queuePump(core.RESULT_OK);
+      return connection1.remote.echoString("goodbye");
+    }).then(function() {
+      throw Error("Expected echoString to fail.");
+    }, function(error) {
+      expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
+
+      // Clean up.
+      core.readMessage = origReadMessage;
+    });
+  }
+});
diff --git a/mojo/apps/js/bindings/gl/BUILD.gn b/mojo/apps/js/bindings/gl/BUILD.gn
new file mode 100644
index 0000000..0ee7e13
--- /dev/null
+++ b/mojo/apps/js/bindings/gl/BUILD.gn
@@ -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.
+
+# GYP version: part of mojo/mojo_apps.gypi:mojo_js_lib
+source_set("gl") {
+  sources = [
+    "context.cc",
+    "context.h",
+    "module.cc",
+    "module.h",
+  ]
+
+  deps = [
+    "//base",
+    "//gin",
+    "//v8",
+    "//mojo/bindings/js",
+    "//mojo/environment:chromium",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/gles2:bindings",
+  ]
+}
diff --git a/mojo/apps/js/bindings/gl/context.cc b/mojo/apps/js/bindings/gl/context.cc
new file mode 100644
index 0000000..4754d4e
--- /dev/null
+++ b/mojo/apps/js/bindings/gl/context.cc
@@ -0,0 +1,187 @@
+// 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/apps/js/bindings/gl/context.h"
+
+#include <GLES2/gl2.h>
+
+#include "gin/arguments.h"
+#include "gin/array_buffer.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_context_data.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace gin {
+template<>
+struct Converter<GLboolean> {
+  static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+                     GLboolean* out) {
+    bool bool_val = false;
+    if (!Converter<bool>::FromV8(isolate, val, &bool_val))
+      return false;
+    *out = static_cast<GLboolean>(bool_val);
+    return true;
+  }
+};
+}
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+gin::WrapperInfo Context::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+gin::Handle<Context> Context::Create(
+    v8::Isolate* isolate,
+    mojo::Handle handle,
+    v8::Handle<v8::Function> context_lost_callback) {
+  return gin::CreateHandle(isolate,
+                           new Context(isolate, handle, context_lost_callback));
+}
+
+void Context::BufferData(GLenum target, const gin::ArrayBufferView& buffer,
+                         GLenum usage) {
+  glBufferData(target, static_cast<GLsizeiptr>(buffer.num_bytes()),
+               buffer.bytes(), usage);
+}
+
+void Context::CompileShader(const gin::Arguments& args, GLuint shader) {
+  glCompileShader(shader);
+  GLint compiled = 0;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+  if (!compiled) {
+    args.ThrowTypeError(std::string("Could not compile shader: ") +
+                        GetShaderInfoLog(shader));
+  }
+}
+
+GLuint Context::CreateBuffer() {
+  GLuint result = 0;
+  glGenBuffers(1, &result);
+  return result;
+}
+
+void Context::DrawElements(GLenum mode, GLsizei count, GLenum type,
+                           uint64_t indices) {
+  // This looks scary, but it's what WebGL does too:
+  // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.1
+  glDrawElements(mode, count, type, reinterpret_cast<void*>(indices));
+}
+
+GLint Context::GetAttribLocation(GLuint program, const std::string& name) {
+  return glGetAttribLocation(program, name.c_str());
+}
+
+std::string Context::GetProgramInfoLog(GLuint program) {
+  GLint info_log_length = 0;
+  glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
+  std::string info_log(info_log_length, 0);
+  glGetProgramInfoLog(program, info_log_length, NULL, &info_log.at(0));
+  return info_log;
+}
+
+std::string Context::GetShaderInfoLog(GLuint shader) {
+  GLint info_log_length = 0;
+  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
+  std::string info_log(info_log_length, 0);
+  glGetShaderInfoLog(shader, info_log_length, NULL, &info_log.at(0));
+  return info_log;
+}
+
+GLint Context::GetUniformLocation(GLuint program, const std::string& name) {
+  return glGetUniformLocation(program, name.c_str());
+}
+
+void Context::ShaderSource(GLuint shader, const std::string& source) {
+  const char* source_chars = source.c_str();
+  glShaderSource(shader, 1, &source_chars, NULL);
+}
+
+void Context::UniformMatrix4fv(GLint location, GLboolean transpose,
+                               const gin::ArrayBufferView& buffer) {
+  glUniformMatrix4fv(location, 1, transpose,
+                     static_cast<float*>(buffer.bytes()));
+}
+
+void Context::VertexAttribPointer(GLuint index, GLint size, GLenum type,
+                                  GLboolean normalized, GLsizei stride,
+                                  uint64_t offset) {
+  glVertexAttribPointer(index, size, type, normalized, stride,
+                        reinterpret_cast<void*>(offset));
+}
+
+gin::ObjectTemplateBuilder Context::GetObjectTemplateBuilder(
+    v8::Isolate* isolate) {
+  return gin::ObjectTemplateBuilder(isolate)
+      .SetValue("ARRAY_BUFFER", GL_ARRAY_BUFFER)
+      .SetValue("COLOR_BUFFER_BIT", GL_COLOR_BUFFER_BIT)
+      .SetValue("ELEMENT_ARRAY_BUFFER", GL_ELEMENT_ARRAY_BUFFER)
+      .SetValue("FLOAT", GL_FLOAT)
+      .SetValue("FRAGMENT_SHADER", GL_FRAGMENT_SHADER)
+      .SetValue("STATIC_DRAW", GL_STATIC_DRAW)
+      .SetValue("TRIANGLES", GL_TRIANGLES)
+      .SetValue("UNSIGNED_SHORT", GL_UNSIGNED_SHORT)
+      .SetValue("VERTEX_SHADER", GL_VERTEX_SHADER)
+      .SetMethod("attachShader", glAttachShader)
+      .SetMethod("bindBuffer", glBindBuffer)
+      .SetMethod("bufferData", BufferData)
+      .SetMethod("clear", glClear)
+      .SetMethod("clearColor", glClearColor)
+      .SetMethod("compileShader", CompileShader)
+      .SetMethod("createBuffer", CreateBuffer)
+      .SetMethod("createProgram", glCreateProgram)
+      .SetMethod("createShader", glCreateShader)
+      .SetMethod("deleteShader", glDeleteShader)
+      .SetMethod("drawElements", DrawElements)
+      .SetMethod("enableVertexAttribArray", glEnableVertexAttribArray)
+      .SetMethod("getAttribLocation", GetAttribLocation)
+      .SetMethod("getProgramInfoLog", GetProgramInfoLog)
+      .SetMethod("getShaderInfoLog", GetShaderInfoLog)
+      .SetMethod("getUniformLocation", GetUniformLocation)
+      .SetMethod("linkProgram", glLinkProgram)
+      .SetMethod("shaderSource", ShaderSource)
+      .SetMethod("swapBuffers", MojoGLES2SwapBuffers)
+      .SetMethod("uniformMatrix4fv", UniformMatrix4fv)
+      .SetMethod("useProgram", glUseProgram)
+      .SetMethod("vertexAttribPointer", VertexAttribPointer)
+      .SetMethod("viewport", glViewport);
+}
+
+Context::Context(v8::Isolate* isolate,
+                 mojo::Handle handle,
+                 v8::Handle<v8::Function> context_lost_callback) {
+  v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+  runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+  context_lost_callback_.Reset(isolate, context_lost_callback);
+  context_ = MojoGLES2CreateContext(handle.value(),
+                                    &ContextLostThunk,
+                                    this,
+                                    Environment::GetDefaultAsyncWaiter());
+  MojoGLES2MakeCurrent(context_);
+}
+
+Context::~Context() {
+  MojoGLES2DestroyContext(context_);
+}
+
+void Context::ContextLost() {
+  if (!runner_)
+    return;
+  gin::Runner::Scope scope(runner_.get());
+  v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+  v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(
+      isolate, context_lost_callback_);
+
+  runner_->Call(callback, runner_->global(), 0, NULL);
+}
+
+void Context::ContextLostThunk(void* closure) {
+  static_cast<Context*>(closure)->ContextLost();
+}
+
+}  // namespace gl
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/apps/js/bindings/gl/context.h b/mojo/apps/js/bindings/gl/context.h
new file mode 100644
index 0000000..2166860
--- /dev/null
+++ b/mojo/apps/js/bindings/gl/context.h
@@ -0,0 +1,77 @@
+// 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_APPS_JS_BINDINGS_GL_CONTEXT_H_
+#define MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_
+
+#include <GLES2/gl2.h>
+
+#include "base/memory/weak_ptr.h"
+#include "gin/handle.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+class Arguments;
+class ArrayBufferView;
+}
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+// Context implements WebGLRenderingContext.
+class Context : public gin::Wrappable<Context> {
+ public:
+  static gin::WrapperInfo kWrapperInfo;
+
+  // TODO(piman): draw animation frame callback.
+  static gin::Handle<Context> Create(
+      v8::Isolate* isolate,
+      mojo::Handle handle,
+      v8::Handle<v8::Function> context_lost_callback);
+
+  static void BufferData(GLenum target, const gin::ArrayBufferView& buffer,
+                         GLenum usage);
+  static void CompileShader(const gin::Arguments& args, GLuint shader);
+  static GLuint CreateBuffer();
+  static void DrawElements(GLenum mode, GLsizei count, GLenum type,
+                           uint64_t indices);
+  static GLint GetAttribLocation(GLuint program, const std::string& name);
+  static std::string GetProgramInfoLog(GLuint program);
+  static std::string GetShaderInfoLog(GLuint shader);
+  static GLint GetUniformLocation(GLuint program, const std::string& name);
+  static void ShaderSource(GLuint shader, const std::string& source);
+  static void UniformMatrix4fv(GLint location, GLboolean transpose,
+                               const gin::ArrayBufferView& buffer);
+  static void VertexAttribPointer(GLuint index, GLint size, GLenum type,
+                                  GLboolean normalized, GLsizei stride,
+                                  uint64_t offset);
+
+ private:
+  virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+      v8::Isolate* isolate) override;
+
+  explicit Context(v8::Isolate* isolate,
+                   mojo::Handle handle,
+                   v8::Handle<v8::Function> context_lost_callback);
+  virtual ~Context();
+
+  void ContextLost();
+  static void ContextLostThunk(void* closure);
+
+  base::WeakPtr<gin::Runner> runner_;
+  v8::Persistent<v8::Function> context_lost_callback_;
+  MojoGLES2Context context_;
+};
+
+}  // namespace gl
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_
diff --git a/mojo/apps/js/bindings/gl/module.cc b/mojo/apps/js/bindings/gl/module.cc
new file mode 100644
index 0000000..413f22e
--- /dev/null
+++ b/mojo/apps/js/bindings/gl/module.cc
@@ -0,0 +1,50 @@
+// 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/apps/js/bindings/gl/module.h"
+
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/wrappable.h"
+#include "mojo/apps/js/bindings/gl/context.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+const char* kModuleName = "mojo/apps/js/bindings/gl";
+
+namespace {
+
+gin::WrapperInfo kWrapperInfo = { gin::kEmbedderNativeGin };
+
+gin::Handle<Context> CreateContext(
+    const gin::Arguments& args,
+    mojo::Handle handle,
+    v8::Handle<v8::Function> context_lost_callback) {
+  return Context::Create(args.isolate(), handle, context_lost_callback);
+}
+
+}  // namespace
+
+v8::Local<v8::Value> GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(&kWrapperInfo);
+
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+        .SetMethod("Context", CreateContext)
+        .Build();
+    data->SetObjectTemplate(&kWrapperInfo, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace gl
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/apps/js/bindings/gl/module.h b/mojo/apps/js/bindings/gl/module.h
new file mode 100644
index 0000000..0ae0d14
--- /dev/null
+++ b/mojo/apps/js/bindings/gl/module.h
@@ -0,0 +1,22 @@
+// 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_APPS_JS_BINDINGS_GL_MODULE_H_
+#define MOJO_APPS_JS_BINDINGS_GL_MODULE_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+extern const char* kModuleName;
+v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+
+}  // namespace gl
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_BINDINGS_GL_H_
diff --git a/mojo/apps/js/bindings/monotonic_clock.cc b/mojo/apps/js/bindings/monotonic_clock.cc
new file mode 100644
index 0000000..733af8a
--- /dev/null
+++ b/mojo/apps/js/bindings/monotonic_clock.cc
@@ -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.
+
+#include "mojo/apps/js/bindings/monotonic_clock.h"
+
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+double GetMonotonicSeconds() {
+  const double kMicrosecondsPerSecond = 1000000;
+  return static_cast<double>(mojo::GetTimeTicksNow()) / kMicrosecondsPerSecond;
+}
+
+}  // namespace
+
+const char MonotonicClock::kModuleName[] = "monotonic_clock";
+
+v8::Local<v8::Value> MonotonicClock::GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ =
+      data->GetObjectTemplate(&g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+        .SetMethod("seconds", GetMonotonicSeconds)
+        .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+  return templ->NewInstance();
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/bindings/monotonic_clock.h b/mojo/apps/js/bindings/monotonic_clock.h
new file mode 100644
index 0000000..6278821
--- /dev/null
+++ b/mojo/apps/js/bindings/monotonic_clock.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
+#define MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace apps {
+
+class MonotonicClock {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
diff --git a/mojo/apps/js/bindings/monotonic_clock_unittests.js b/mojo/apps/js/bindings/monotonic_clock_unittests.js
new file mode 100644
index 0000000..a1a253e
--- /dev/null
+++ b/mojo/apps/js/bindings/monotonic_clock_unittests.js
@@ -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.
+
+define([
+  "console",
+  "gin/test/expect",
+  "monotonic_clock",
+  "timer",
+  "mojo/apps/js/bindings/threading"
+], function(console, expect, monotonicClock, timer, threading) {
+  var global = this;
+  var then = monotonicClock.seconds();
+  var t = timer.createOneShot(100, function() {
+    var now = monotonicClock.seconds();
+    expect(now).toBeGreaterThan(then);
+    global.result = "PASS";
+    threading.quit();
+  });
+});
diff --git a/mojo/apps/js/bindings/sample_service_unittests.js b/mojo/apps/js/bindings/sample_service_unittests.js
new file mode 100644
index 0000000..2b8c31c
--- /dev/null
+++ b/mojo/apps/js/bindings/sample_service_unittests.js
@@ -0,0 +1,168 @@
+// 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.
+
+define([
+    "console",
+    "mojo/apps/js/test/hexdump",
+    "gin/test/expect",
+    "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+    "mojo/public/interfaces/bindings/tests/sample_import.mojom",
+    "mojo/public/interfaces/bindings/tests/sample_import2.mojom",
+  ], function(console, hexdump, expect, sample, imported, imported2) {
+
+  var global = this;
+
+  // Set this variable to true to print the binary message in hex.
+  var dumpMessageAsHex = false;
+
+  function makeFoo() {
+    var bar = new sample.Bar();
+    bar.alpha = 20;
+    bar.beta = 40;
+    bar.gamma = 60;
+    bar.type = sample.Bar.Type.VERTICAL;
+
+    var extra_bars = new Array(3);
+    for (var i = 0; i < extra_bars.length; ++i) {
+      var base = i * 100;
+      var type = i % 2 ?
+          sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL;
+      extra_bars[i] = new sample.Bar();
+      extra_bars[i].alpha = base;
+      extra_bars[i].beta = base + 20;
+      extra_bars[i].gamma = base + 40;
+      extra_bars[i].type = type;
+    }
+
+    var data = new Array(10);
+    for (var i = 0; i < data.length; ++i) {
+      data[i] = data.length - i;
+    }
+
+    var source = 0xFFFF;  // Invent a dummy handle.
+
+    var foo = new sample.Foo();
+    foo.name = "foopy";
+    foo.x = 1;
+    foo.y = 2;
+    foo.a = false;
+    foo.b = true;
+    foo.c = false;
+    foo.bar = bar;
+    foo.extra_bars = extra_bars;
+    foo.data = data;
+    foo.source = source;
+    return foo;
+  }
+
+  // Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+  function checkFoo(foo) {
+    expect(foo.name).toBe("foopy");
+    expect(foo.x).toBe(1);
+    expect(foo.y).toBe(2);
+    expect(foo.a).toBeFalsy();
+    expect(foo.b).toBeTruthy();
+    expect(foo.c).toBeFalsy();
+    expect(foo.bar.alpha).toBe(20);
+    expect(foo.bar.beta).toBe(40);
+    expect(foo.bar.gamma).toBe(60);
+    expect(foo.bar.type).toBe(sample.Bar.Type.VERTICAL);
+
+    expect(foo.extra_bars.length).toBe(3);
+    for (var i = 0; i < foo.extra_bars.length; ++i) {
+      var base = i * 100;
+      var type = i % 2 ?
+          sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL;
+      expect(foo.extra_bars[i].alpha).toBe(base);
+      expect(foo.extra_bars[i].beta).toBe(base + 20);
+      expect(foo.extra_bars[i].gamma).toBe(base + 40);
+      expect(foo.extra_bars[i].type).toBe(type);
+    }
+
+    expect(foo.data.length).toBe(10);
+    for (var i = 0; i < foo.data.length; ++i)
+      expect(foo.data[i]).toBe(foo.data.length - i);
+
+    expect(foo.source).toBe(0xFFFF);
+  }
+
+  // Check that values are set to the defaults if we don't override them.
+  function checkDefaultValues() {
+    var bar = new sample.Bar();
+    expect(bar.alpha).toBe(255);
+    expect(bar.type).toBe(sample.Bar.Type.VERTICAL);
+
+    var foo = new sample.Foo();
+    expect(foo.name).toBe("Fooby");
+    expect(foo.a).toBeTruthy();
+    expect(foo.data).toBeNull();
+
+    var defaults = new sample.DefaultsTest();
+    expect(defaults.a0).toBe(-12);
+    expect(defaults.a1).toBe(sample.kTwelve);
+    expect(defaults.a2).toBe(1234);
+    expect(defaults.a3).toBe(34567);
+    expect(defaults.a4).toBe(123456);
+    expect(defaults.a5).toBe(3456789012);
+    expect(defaults.a6).toBe(-111111111111);
+    // JS doesn't have a 64 bit integer type so this is just checking that the
+    // expected and actual values have the same closest double value.
+    expect(defaults.a7).toBe(9999999999999999999);
+    expect(defaults.a8).toBe(0x12345);
+    expect(defaults.a9).toBe(-0x12345);
+    expect(defaults.a10).toBe(1234);
+    expect(defaults.a11).toBe(true);
+    expect(defaults.a12).toBe(false);
+    expect(defaults.a13).toBe(123.25);
+    expect(defaults.a14).toBe(1234567890.123);
+    expect(defaults.a15).toBe(1E10);
+    expect(defaults.a16).toBe(-1.2E+20);
+    expect(defaults.a17).toBe(1.23E-20);
+    expect(defaults.a20).toBe(sample.Bar.Type.BOTH);
+    expect(defaults.a21).toBeNull();
+    expect(defaults.a22).toBeTruthy();
+    expect(defaults.a22.shape).toBe(imported.Shape.RECTANGLE);
+    expect(defaults.a22.color).toBe(imported2.Color.BLACK);
+    expect(defaults.a21).toBeNull();
+    expect(defaults.a23).toBe(0xFFFFFFFFFFFFFFFF);
+    expect(defaults.a24).toBe(0x123456789);
+    expect(defaults.a25).toBe(-0x123456789);
+  }
+
+  function ServiceImpl() {
+  }
+
+  ServiceImpl.prototype = Object.create(sample.ServiceStub.prototype);
+
+  ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+    checkFoo(foo);
+    expect(baz).toBe(sample.ServiceStub.BazOptions.EXTRA);
+    expect(port).toBe(10);
+    global.result = "PASS";
+  };
+
+  function SimpleMessageReceiver() {
+  }
+
+  SimpleMessageReceiver.prototype.accept = function(message) {
+    if (dumpMessageAsHex) {
+      var uint8Array = new Uint8Array(message.buffer.arrayBuffer);
+      console.log(hexdump.dumpArray(uint8Array));
+    }
+    // Imagine some IPC happened here.
+    var serviceImpl = new ServiceImpl();
+    serviceImpl.accept(message);
+  };
+
+  var receiver = new SimpleMessageReceiver();
+  var serviceProxy = new sample.ServiceProxy(receiver);
+
+  checkDefaultValues();
+
+  var foo = makeFoo();
+  checkFoo(foo);
+
+  var port = 10;
+  serviceProxy.frobinate(foo, sample.ServiceProxy.BazOptions.EXTRA, port);
+});
diff --git a/mojo/apps/js/bindings/threading.cc b/mojo/apps/js/bindings/threading.cc
new file mode 100644
index 0000000..64e32fc
--- /dev/null
+++ b/mojo/apps/js/bindings/threading.cc
@@ -0,0 +1,47 @@
+// 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/apps/js/bindings/threading.h"
+
+#include "base/message_loop/message_loop.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+void Quit() {
+  base::MessageLoop::current()->QuitNow();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Threading::kModuleName[] = "mojo/apps/js/bindings/threading";
+
+v8::Local<v8::Value> Threading::GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+      &g_wrapper_info);
+
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+        .SetMethod("quit", Quit)
+        .Build();
+
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+Threading::Threading() {
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/bindings/threading.h b/mojo/apps/js/bindings/threading.h
new file mode 100644
index 0000000..ac8e221
--- /dev/null
+++ b/mojo/apps/js/bindings/threading.h
@@ -0,0 +1,25 @@
+// 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_APPS_JS_BINDINGS_THREADING_H_
+#define MOJO_APPS_JS_BINDINGS_THREADING_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace apps {
+
+class Threading {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+ private:
+  Threading();
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_BINDINGS_THREADING_H_
diff --git a/mojo/apps/js/content_handler_impl.cc b/mojo/apps/js/content_handler_impl.cc
new file mode 100644
index 0000000..f3dffa7
--- /dev/null
+++ b/mojo/apps/js/content_handler_impl.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 "mojo/apps/js/content_handler_impl.h"
+
+#include "mojo/apps/js/application_delegate_impl.h"
+#include "mojo/apps/js/js_app.h"
+#include "mojo/common/data_pipe_utils.h"
+
+namespace mojo {
+namespace apps {
+
+class ContentHandlerJSApp : public JSApp {
+ public:
+  ContentHandlerJSApp(ApplicationDelegateImpl* app_delegate_impl,
+                      const std::string& url,
+                      URLResponsePtr content)
+      : JSApp(app_delegate_impl),
+        url_(url),
+        content_(content.Pass()) {
+  }
+
+  virtual bool Load(std::string* source, std::string* file_name) override {
+    *file_name = url_;
+    if (content_.is_null())
+      return false;
+    return common::BlockingCopyToString(content_->body.Pass(), source);
+  }
+
+ private:
+  std::string url_;
+  URLResponsePtr content_;
+};
+
+
+ContentHandlerImpl::ContentHandlerImpl(ApplicationDelegateImpl* app)
+    : app_delegate_impl_(app) {
+}
+
+ContentHandlerImpl::~ContentHandlerImpl() {
+}
+
+void ContentHandlerImpl::OnConnect(
+    const mojo::String& url,
+    URLResponsePtr content,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  scoped_ptr<JSApp> js_app(
+      new ContentHandlerJSApp(app_delegate_impl_,
+                              url.To<std::string>(),
+                              content.Pass()));
+  app_delegate_impl_->StartJSApp(js_app.Pass());
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/content_handler_impl.h b/mojo/apps/js/content_handler_impl.h
new file mode 100644
index 0000000..04c2603
--- /dev/null
+++ b/mojo/apps/js/content_handler_impl.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_APPS_JS_CONTENT_HANDLER_IMPL_H_
+#define MOJO_APPS_JS_CONTENT_HANDLER_IMPL_H_
+
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace mojo {
+namespace apps {
+
+class ApplicationDelegateImpl;
+
+// Starts a new JSApp for each OnConnect call().
+class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
+ public:
+  ContentHandlerImpl(ApplicationDelegateImpl* app_delegate_impl);
+
+ private:
+  virtual ~ContentHandlerImpl();
+  virtual void OnConnect(
+      const mojo::String& url,
+      URLResponsePtr content,
+      InterfaceRequest<ServiceProvider> service_provider) override;
+
+  ApplicationDelegateImpl* app_delegate_impl_;
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_CONTENT_HANDLER_IMPL_H_
diff --git a/mojo/apps/js/content_handler_main.cc b/mojo/apps/js/content_handler_main.cc
new file mode 100644
index 0000000..3ea9a3e
--- /dev/null
+++ b/mojo/apps/js/content_handler_main.cc
@@ -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.
+
+
+#include "base/i18n/icu_util.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/apps/js/application_delegate_impl.h"
+#include "mojo/apps/js/content_handler_impl.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace mojo {
+namespace apps {
+
+class ContentHandlerApplicationDelegateImpl : public ApplicationDelegateImpl {
+ public:
+  ContentHandlerApplicationDelegateImpl() : content_handler_factory_(this) {
+  }
+
+ private:
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(&content_handler_factory_);
+    return true;
+  }
+
+  InterfaceFactoryImplWithContext<ContentHandlerImpl, ApplicationDelegateImpl>
+      content_handler_factory_;
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  base::i18n::InitializeICU();
+  mojo::ApplicationRunnerChromium runner(
+      new mojo::apps::ContentHandlerApplicationDelegateImpl());
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/apps/js/js_app.cc b/mojo/apps/js/js_app.cc
new file mode 100644
index 0000000..7b400b2
--- /dev/null
+++ b/mojo/apps/js/js_app.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/apps/js/js_app.h"
+
+#include "base/bind.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "mojo/apps/js/application_delegate_impl.h"
+#include "mojo/apps/js/mojo_module.h"
+
+namespace mojo {
+namespace apps {
+
+JSApp::JSApp(ApplicationDelegateImpl* app_delegate_impl)
+    : app_delegate_impl_(app_delegate_impl),
+      thread_("Mojo JS"),
+      app_delegate_impl_task_runner_(
+          base::MessageLoop::current()->task_runner()) {
+  CHECK(on_app_delegate_impl_thread());
+  runner_delegate_.AddBuiltinModule(Mojo::kModuleName,
+                                    base::Bind(Mojo::GetModule, this));
+}
+
+JSApp::~JSApp() {
+}
+
+bool JSApp::Start() {
+  CHECK(!js_app_task_runner_.get() && on_app_delegate_impl_thread());
+  base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
+  thread_.StartWithOptions(thread_options);
+
+  // TODO(hansmuller): check thread_.StartWithOptions() return value.
+  // TODO(hansmuller): need to funnel Run() failures back to the caller.
+
+  thread_.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&JSApp::Run, base::Unretained(this)));
+  return true;
+}
+
+void JSApp::Quit() {
+  CHECK(on_js_app_thread());
+
+  // The terminate operation is posted to the message_loop so that
+  // the shell_runner isn't destroyed before this JS function returns.
+  thread_.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&JSApp::Terminate, base::Unretained(this)));
+}
+
+Handle JSApp::ConnectToService(const std::string& application_url,
+                               const std::string& interface_name) {
+  CHECK(on_js_app_thread());
+  MessagePipe pipe;
+
+  app_delegate_impl_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ApplicationDelegateImpl::ConnectToService,
+                 base::Unretained(app_delegate_impl_),
+                 base::Passed(pipe.handle1.Pass()),
+                 application_url,
+                 interface_name));
+
+  return pipe.handle0.release();
+}
+
+void JSApp::Run() {
+  CHECK(!js_app_task_runner_.get() && !on_app_delegate_impl_thread());
+  js_app_task_runner_ = base::MessageLoop::current()->task_runner();
+
+  std::string source;
+  std::string file_name;
+  Load(&source, &file_name);  // TODO(hansmuller): handle Load() failure.
+
+  isolate_holder_.reset(new gin::IsolateHolder());
+  isolate_holder_->AddRunMicrotasksObserver();
+
+  shell_runner_.reset(
+      new gin::ShellRunner(&runner_delegate_, isolate_holder_->isolate()));
+
+  gin::Runner::Scope scope(shell_runner_.get());
+  shell_runner_->Run(source.c_str(), file_name.c_str());
+}
+
+void JSApp::Terminate() {
+  isolate_holder_->RemoveRunMicrotasksObserver();
+  shell_runner_.reset(nullptr);
+
+  // This JSApp's thread must be stopped on the thread that started it. Ask the
+  // app_delegate_impl_ to erase its AppVector entry for this app, which
+  // implicitly destroys this JSApp and stops its thread.
+  app_delegate_impl_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ApplicationDelegateImpl::QuitJSApp,
+                 base::Unretained(app_delegate_impl_),
+                 base::Unretained(this)));
+}
+
+bool JSApp::on_app_delegate_impl_thread() const {
+  return app_delegate_impl_task_runner_.get() &&
+         app_delegate_impl_task_runner_.get() ==
+             base::MessageLoop::current()->task_runner().get();
+}
+
+bool JSApp::on_js_app_thread() const {
+  return js_app_task_runner_.get() &&
+         js_app_task_runner_.get() ==
+             base::MessageLoop::current()->task_runner().get();
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/js_app.h b/mojo/apps/js/js_app.h
new file mode 100644
index 0000000..6bbaed4
--- /dev/null
+++ b/mojo/apps/js/js_app.h
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium 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_APPS_JS_JS_APP_H_
+#define MOJO_APPS_JS_JS_APP_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/shell_runner.h"
+#include "mojo/apps/js/mojo_runner_delegate.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace mojo {
+namespace apps {
+
+class JSApp;
+class ApplicationDelegateImpl;
+
+// Each JavaScript app started by content handler runs on its own thread and
+// in its own V8 isolate. This class represents one running JS app.
+
+class JSApp {
+ public:
+  JSApp(ApplicationDelegateImpl* app_delegate_impl);
+  virtual ~JSApp();
+
+  // Launch this app on a new thread. This method can be called on any thread.
+  // This method causes Load() and then Run() to run on a new thread.
+  bool Start();
+
+
+  // Subclasses must return the JS source code for this app's main script and
+  // the filename or URL that identifies the script's origin. This method will
+  // be called from this app's thread.
+  virtual bool Load(std::string* source, std::string* file_name) = 0;
+
+  // Called by the JS mojo module to quit this JS app. See mojo_module.cc.
+  void Quit();
+
+  // Called by the JS mojo module to connect to a Mojo service.
+  Handle ConnectToService(const std::string& application_url,
+                          const std::string& interface_name);
+
+ private:
+  void Run();
+  void Terminate();
+
+  // Used to CHECK that we're running on the correct thread.
+  bool on_app_delegate_impl_thread() const;
+  bool on_js_app_thread() const;
+
+  ApplicationDelegateImpl* app_delegate_impl_;
+  base::Thread thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> app_delegate_impl_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> js_app_task_runner_;
+  MojoRunnerDelegate runner_delegate_;
+  scoped_ptr<gin::IsolateHolder> isolate_holder_;
+  scoped_ptr<gin::ShellRunner> shell_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(JSApp);
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_JS_APP_H_
diff --git a/mojo/apps/js/main.js b/mojo/apps/js/main.js
new file mode 100644
index 0000000..c8eaac5
--- /dev/null
+++ b/mojo/apps/js/main.js
@@ -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.
+
+// This trivial app just loads "cnn.com" using the Mojo Network and URLLoader
+// services and then prints a brief summary of the response. It's intended to
+// be run using the mojo_js content handler and assumes that this source file
+// is specified as a URL. For example:
+//
+//   (cd YOUR_DIR/mojo/apps/js; python -m SimpleHTTPServer ) &
+//   mojo_shell --content-handlers=application/javascript,mojo://mojo_js \
+//       http://localhost:8000/test.js
+
+define("test", [
+  "mojo/public/js/bindings/core",
+  "mojo/public/js/bindings/connection",
+  "mojo/public/js/bindings/support",
+  "mojo/services/public/interfaces/network/network_service.mojom",
+  "mojo/services/public/interfaces/network/url_loader.mojom",
+  "mojo",
+  "console"
+], function(core, connection, support, net, loader, mojo, console) {
+
+  var netServiceHandle = mojo.connectToService(
+      "mojo:mojo_network_service", "mojo::NetworkService");
+  var netConnection = new connection.Connection(
+      netServiceHandle, net.NetworkServiceStub, net.NetworkServiceProxy);
+
+  var urlLoaderPipe = new core.createMessagePipe();
+  netConnection.remote.createURLLoader(urlLoaderPipe.handle1);
+  var urlLoaderConnection = new connection.Connection(
+      urlLoaderPipe.handle0, loader.URLLoaderStub, loader.URLLoaderProxy);
+
+  var urlRequest = new loader.URLRequest();
+  urlRequest.url = "http://www.cnn.com";
+  urlRequest.method = "GET";
+  urlRequest.auto_follow_redirects = true;
+
+  var urlRequestPromise = urlLoaderConnection.remote.start(urlRequest);
+  urlRequestPromise.then(function(result) {
+    for(var key in result.response)
+      console.log(key + " => " + result.response[key]);
+    var drainDataPromise = core.drainData(result.response.body);
+    drainDataPromise.then(function(result) {
+      console.log("read " + result.buffer.byteLength + " bytes");
+    }).then(function() {
+      mojo.quit();
+    });
+  });
+});
diff --git a/mojo/apps/js/mojo_module.cc b/mojo/apps/js/mojo_module.cc
new file mode 100644
index 0000000..d3c0f76
--- /dev/null
+++ b/mojo/apps/js/mojo_module.cc
@@ -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.
+
+#include "mojo/apps/js/mojo_module.h"
+
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/apps/js/js_app.h"
+#include "mojo/apps/js/mojo_module.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/common/data_pipe_utils.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+gin::WrapperInfo g_wrapper_info = {gin::kEmbedderNativeGin};
+
+}  // namespace
+
+const char Mojo::kModuleName[] = "mojo";
+
+v8::Local<v8::Value> Mojo::GetModule(JSApp* js_app, v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ =
+      data->GetObjectTemplate(&g_wrapper_info);
+
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+                .SetMethod("connectToService",
+                           base::Bind(&JSApp::ConnectToService,
+                                      base::Unretained(js_app)))
+                .SetMethod("quit",
+                           base::Bind(&JSApp::Quit, base::Unretained(js_app)))
+                .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/mojo_module.h b/mojo/apps/js/mojo_module.h
new file mode 100644
index 0000000..cb4a73a
--- /dev/null
+++ b/mojo/apps/js/mojo_module.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_APPS_JS_MOJO_MODULE_H_
+#define MOJO_APPS_JS_MOJO_MODULE_H_
+
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace apps {
+
+class JSApp;
+
+class Mojo {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(JSApp* js_app, v8::Isolate* isolate);
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_MOJO_MODULE_H_
diff --git a/mojo/apps/js/mojo_runner_delegate.cc b/mojo/apps/js/mojo_runner_delegate.cc
new file mode 100644
index 0000000..8bec392
--- /dev/null
+++ b/mojo/apps/js/mojo_runner_delegate.cc
@@ -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.
+
+#include "mojo/apps/js/mojo_runner_delegate.h"
+
+#include "base/bind.h"
+#include "base/path_service.h"
+#include "gin/converter.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/try_catch.h"
+#include "mojo/apps/js/bindings/gl/module.h"
+#include "mojo/apps/js/bindings/monotonic_clock.h"
+#include "mojo/apps/js/bindings/threading.h"
+#include "mojo/bindings/js/core.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/bindings/js/support.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+// TODO(abarth): Rather than loading these modules from the file system, we
+// should load them from the network via Mojo IPC.
+std::vector<base::FilePath> GetModuleSearchPaths() {
+  std::vector<base::FilePath> search_paths(2);
+  PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+  PathService::Get(base::DIR_EXE, &search_paths[1]);
+  search_paths[1] = search_paths[1].AppendASCII("gen");
+  return search_paths;
+}
+
+void StartCallback(base::WeakPtr<gin::Runner> runner,
+                   MojoHandle pipe,
+                   v8::Handle<v8::Value> module) {
+  v8::Isolate* isolate = runner->GetContextHolder()->isolate();
+  v8::Handle<v8::Function> start;
+  CHECK(gin::ConvertFromV8(isolate, module, &start));
+
+  v8::Handle<v8::Value> args[] = {
+      gin::ConvertToV8(isolate, Handle(pipe)) };
+  runner->Call(start, runner->global(), 1, args);
+}
+
+}  // namespace
+
+MojoRunnerDelegate::MojoRunnerDelegate()
+    : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+  AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+  AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule);
+  AddBuiltinModule(js::Core::kModuleName, js::Core::GetModule);
+  AddBuiltinModule(js::Support::kModuleName, js::Support::GetModule);
+  AddBuiltinModule(js::gl::kModuleName, js::gl::GetModule);
+  AddBuiltinModule(MonotonicClock::kModuleName, MonotonicClock::GetModule);
+  AddBuiltinModule(Threading::kModuleName, Threading::GetModule);
+}
+
+MojoRunnerDelegate::~MojoRunnerDelegate() {
+}
+
+void MojoRunnerDelegate::Start(gin::Runner* runner,
+                               MojoHandle pipe,
+                               const std::string& module) {
+  gin::Runner::Scope scope(runner);
+  gin::ModuleRegistry* registry =
+      gin::ModuleRegistry::From(runner->GetContextHolder()->context());
+  registry->LoadModule(runner->GetContextHolder()->isolate(), module,
+                       base::Bind(StartCallback, runner->GetWeakPtr(), pipe));
+  AttemptToLoadMoreModules(runner);
+}
+
+void MojoRunnerDelegate::UnhandledException(gin::ShellRunner* runner,
+                                            gin::TryCatch& try_catch) {
+  gin::ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+  LOG(ERROR) << try_catch.GetStackTrace();
+}
+
+}  // namespace apps
+}  // namespace mojo
diff --git a/mojo/apps/js/mojo_runner_delegate.h b/mojo/apps/js/mojo_runner_delegate.h
new file mode 100644
index 0000000..8a7b448
--- /dev/null
+++ b/mojo/apps/js/mojo_runner_delegate.h
@@ -0,0 +1,33 @@
+// 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_APPS_JS_MOJO_RUNNER_DELEGATE_H_
+#define MOJO_APPS_JS_MOJO_RUNNER_DELEGATE_H_
+
+#include "base/macros.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "mojo/public/c/system/core.h"
+
+namespace mojo {
+namespace apps {
+
+class MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
+ public:
+  MojoRunnerDelegate();
+  virtual ~MojoRunnerDelegate();
+
+  void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module);
+
+ private:
+  // From ModuleRunnerDelegate:
+  virtual void UnhandledException(gin::ShellRunner* runner,
+                                  gin::TryCatch& try_catch) override;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate);
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+#endif  // MOJO_APPS_JS_MOJO_RUNNER_DELEGATE_H_
diff --git a/mojo/apps/js/standalone_main.cc b/mojo/apps/js/standalone_main.cc
new file mode 100644
index 0000000..efdfcb8
--- /dev/null
+++ b/mojo/apps/js/standalone_main.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 "base/files/file_util.h"
+#include "base/i18n/icu_util.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/apps/js/application_delegate_impl.h"
+#include "mojo/apps/js/js_app.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+
+namespace mojo {
+namespace apps {
+
+class StandaloneJSApp : public JSApp {
+ public:
+  StandaloneJSApp(ApplicationDelegateImpl* app_delegate_impl,
+                  const base::FilePath& path)
+      : JSApp(app_delegate_impl),
+        path_(path) {
+  }
+
+  virtual bool Load(std::string* source, std::string* file_name) override {
+    *file_name = path_.AsUTF8Unsafe();
+    return ReadFileToString(path_, source);
+  }
+
+ private:
+  base::FilePath path_;
+};
+
+class StandaloneApplicationDelegateImpl : public ApplicationDelegateImpl {
+ private:
+  virtual void Initialize(ApplicationImpl* app) override {
+    ApplicationDelegateImpl::Initialize(app);
+
+    for (size_t i = 1; i < app->args().size(); i++) {
+      base::FilePath path(base::FilePath::FromUTF8Unsafe(app->args()[i]));
+      scoped_ptr<JSApp> js_app(new StandaloneJSApp(this, path));
+      StartJSApp(js_app.Pass());
+    }
+  }
+};
+
+}  // namespace apps
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  base::i18n::InitializeICU();
+  mojo::ApplicationRunnerChromium runner(
+      new mojo::apps::StandaloneApplicationDelegateImpl());
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/apps/js/test/BUILD.gn b/mojo/apps/js/test/BUILD.gn
new file mode 100644
index 0000000..2b09e71
--- /dev/null
+++ b/mojo/apps/js/test/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_apps.gypi:mojo_apps_js_unittests
+test("mojo_apps_js_unittests") {
+  deps = [
+    ":js_to_cpp_bindings",
+    "//gin:gin_test",
+    "//mojo/apps/js",
+    "//mojo/common",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+  ]
+
+  sources = [
+    "handle_unittest.cc",
+    "js_to_cpp_unittest.cc",
+    "run_apps_js_tests.cc",
+  ]
+}
+
+mojom("js_to_cpp_bindings") {
+  sources = [ "js_to_cpp.mojom" ]
+}
diff --git a/mojo/apps/js/test/handle_unittest.cc b/mojo/apps/js/test/handle_unittest.cc
new file mode 100644
index 0000000..ec089c9
--- /dev/null
+++ b/mojo/apps/js/test/handle_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium 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/bindings/js/handle.h"
+#include "mojo/bindings/js/handle_close_observer.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+
+class HandleWrapperTest : public testing::Test,
+                          public gin::HandleCloseObserver {
+ public:
+  HandleWrapperTest() : closes_observed_(0) {}
+
+  virtual void OnWillCloseHandle() override { closes_observed_++; }
+
+ protected:
+  int closes_observed_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HandleWrapperTest);
+};
+
+class TestHandleWrapper : public gin::HandleWrapper {
+ public:
+  explicit TestHandleWrapper(MojoHandle handle) : HandleWrapper(handle) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestHandleWrapper);
+};
+
+// Test that calling Close() on a HandleWrapper for an invalid handle does not
+// notify observers.
+TEST_F(HandleWrapperTest, CloseWithInvalidHandle) {
+  {
+    TestHandleWrapper wrapper(MOJO_HANDLE_INVALID);
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+    wrapper.Close();
+    EXPECT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(0, closes_observed_);
+}
+
+// Test that destroying a HandleWrapper for an invalid handle does not notify
+// observers.
+TEST_F(HandleWrapperTest, DestroyWithInvalidHandle) {
+  {
+    TestHandleWrapper wrapper(MOJO_HANDLE_INVALID);
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(0, closes_observed_);
+}
+
+// Test that calling Close on a HandleWrapper for a valid handle notifies
+// observers once.
+TEST_F(HandleWrapperTest, CloseWithValidHandle) {
+  {
+    mojo::MessagePipe pipe;
+    TestHandleWrapper wrapper(pipe.handle0.release().value());
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+    wrapper.Close();
+    EXPECT_EQ(1, closes_observed_);
+    // Check that calling close again doesn't notify observers.
+    wrapper.Close();
+    EXPECT_EQ(1, closes_observed_);
+  }
+  // Check that destroying a closed HandleWrapper doesn't notify observers.
+  EXPECT_EQ(1, closes_observed_);
+}
+
+// Test that destroying a HandleWrapper for a valid handle notifies observers.
+TEST_F(HandleWrapperTest, DestroyWithValidHandle) {
+  {
+    mojo::MessagePipe pipe;
+    TestHandleWrapper wrapper(pipe.handle0.release().value());
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(1, closes_observed_);
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/apps/js/test/hexdump.js b/mojo/apps/js/test/hexdump.js
new file mode 100644
index 0000000..b36c47f
--- /dev/null
+++ b/mojo/apps/js/test/hexdump.js
@@ -0,0 +1,34 @@
+// 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.
+
+define(function() {
+  function hexify(value, length) {
+    var hex = value.toString(16);
+    while (hex.length < length)
+      hex = "0" + hex;
+    return hex;
+  }
+
+  function dumpArray(bytes) {
+    var dumped = "";
+    for (var i = 0; i < bytes.length; ++i) {
+      dumped += hexify(bytes[i], 2);
+
+      if (i % 16 == 15) {
+        dumped += "\n";
+        continue;
+      }
+
+      if (i % 2 == 1)
+        dumped += " ";
+      if (i % 8 == 7)
+        dumped += " ";
+    }
+    return dumped;
+  }
+
+  var exports = {};
+  exports.dumpArray = dumpArray;
+  return exports;
+});
diff --git a/mojo/apps/js/test/js_to_cpp.mojom b/mojo/apps/js/test/js_to_cpp.mojom
new file mode 100644
index 0000000..9914e9b
--- /dev/null
+++ b/mojo/apps/js/test/js_to_cpp.mojom
@@ -0,0 +1,55 @@
+module js_to_cpp {
+
+// This struct encompasses all of the basic types, so that they
+// may be sent from C++ to JS and back for validation.
+struct EchoArgs {
+  int64 si64;
+  int32 si32;
+  int16 si16;
+  int8  si8;
+  uint64 ui64;
+  uint32 ui32;
+  uint16 ui16;
+  uint8  ui8;
+  float float_val;
+  float float_inf;
+  float float_nan;
+  double double_val;
+  double double_inf;
+  double double_nan;
+  string? name;
+  array<string>? string_array;
+  handle<message_pipe>? message_handle;
+  handle<data_pipe_consumer>? data_handle;
+};
+
+struct EchoArgsList {
+  EchoArgsList? next;
+  EchoArgs? item;
+};
+
+// Note: For messages which control test flow, pick numbers that are unlikely
+// to be hit as a result of our deliberate corruption of response messages.
+interface CppSide {
+  // Sent for all tests to notify that the JS side is now ready.
+  StartTest@88888888();
+
+  // Indicates end for echo, bit-flip, and back-pointer tests.
+  TestFinished@99999999();
+
+  // Responses from specific tests.
+  PingResponse();
+  EchoResponse(EchoArgsList list);
+  BitFlipResponse(EchoArgsList arg);
+  BackPointerResponse(EchoArgsList arg);
+};
+
+[Client=CppSide]
+interface JsSide {
+  Ping();
+  Echo(int32 numIterations, EchoArgs arg);
+  BitFlip(EchoArgs arg);
+  BackPointer(EchoArgs arg);
+};
+
+}
diff --git a/mojo/apps/js/test/js_to_cpp_unittest.cc b/mojo/apps/js/test/js_to_cpp_unittest.cc
new file mode 100644
index 0000000..44526a4
--- /dev/null
+++ b/mojo/apps/js/test/js_to_cpp_unittest.cc
@@ -0,0 +1,458 @@
+// Copyright 2014 The Chromium 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/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gin/array_buffer.h"
+#include "gin/public/isolate_holder.h"
+#include "mojo/apps/js/mojo_runner_delegate.h"
+#include "mojo/apps/js/test/js_to_cpp.mojom.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/edk/test/test_utils.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+
+// Global value updated by some checks to prevent compilers from optimizing
+// reads out of existence.
+uint32 g_waste_accumulator = 0;
+
+namespace {
+
+// Negative numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const int8  kExpectedInt8Value = -65;
+const int16 kExpectedInt16Value = -16961;
+const int32 kExpectedInt32Value = -1145258561;
+const int64 kExpectedInt64Value = -77263311946305LL;
+
+// Positive numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const uint8  kExpectedUInt8Value = 65;
+const uint16 kExpectedUInt16Value = 16961;
+const uint32 kExpectedUInt32Value = 1145258561;
+const uint64 kExpectedUInt64Value = 77263311946305LL;
+
+// Double/float values, including special case constants.
+const double kExpectedDoubleVal = 3.14159265358979323846;
+const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
+const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
+const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
+const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
+const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
+
+// NaN has the property that it is not equal to itself.
+#define EXPECT_NAN(x) EXPECT_NE(x, x)
+
+bool IsRunningOnIsolatedBot() {
+  // TODO(yzshen): Remove this check once isolated tests are supported on the
+  // Chromium waterfall. (http://crbug.com/351214)
+  const base::FilePath test_file_path(
+      test::GetFilePathForJSResource(
+          "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom"));
+  if (!base::PathExists(test_file_path)) {
+    LOG(WARNING) << "Mojom binding files don't exist. Skipping the test.";
+    return true;
+  }
+  return false;
+}
+
+void CheckDataPipe(MojoHandle data_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = MojoReadData(
+      data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(64u, buffer_size);
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(i, buffer[i]);
+  }
+}
+
+void CheckMessagePipe(MojoHandle message_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = MojoReadMessage(
+      message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(64u, buffer_size);
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(255 - i, buffer[i]);
+  }
+}
+
+js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() {
+  js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New());
+  args->si64 = kExpectedInt64Value;
+  args->si32 = kExpectedInt32Value;
+  args->si16 = kExpectedInt16Value;
+  args->si8 = kExpectedInt8Value;
+  args->ui64 = kExpectedUInt64Value;
+  args->ui32 = kExpectedUInt32Value;
+  args->ui16 = kExpectedUInt16Value;
+  args->ui8 = kExpectedUInt8Value;
+  args->float_val = kExpectedFloatVal;
+  args->float_inf = kExpectedFloatInf;
+  args->float_nan = kExpectedFloatNan;
+  args->double_val = kExpectedDoubleVal;
+  args->double_inf = kExpectedDoubleInf;
+  args->double_nan = kExpectedDoubleNan;
+  args->name = "coming";
+  Array<String> string_array(3);
+  string_array[0] = "one";
+  string_array[1] = "two";
+  string_array[2] = "three";
+  args->string_array = string_array.Pass();
+  return args.Pass();
+}
+
+void CheckSampleEchoArgs(const js_to_cpp::EchoArgs& arg) {
+  EXPECT_EQ(kExpectedInt64Value, arg.si64);
+  EXPECT_EQ(kExpectedInt32Value, arg.si32);
+  EXPECT_EQ(kExpectedInt16Value, arg.si16);
+  EXPECT_EQ(kExpectedInt8Value, arg.si8);
+  EXPECT_EQ(kExpectedUInt64Value, arg.ui64);
+  EXPECT_EQ(kExpectedUInt32Value, arg.ui32);
+  EXPECT_EQ(kExpectedUInt16Value, arg.ui16);
+  EXPECT_EQ(kExpectedUInt8Value, arg.ui8);
+  EXPECT_EQ(kExpectedFloatVal, arg.float_val);
+  EXPECT_EQ(kExpectedFloatInf, arg.float_inf);
+  EXPECT_NAN(arg.float_nan);
+  EXPECT_EQ(kExpectedDoubleVal, arg.double_val);
+  EXPECT_EQ(kExpectedDoubleInf, arg.double_inf);
+  EXPECT_NAN(arg.double_nan);
+  EXPECT_EQ(std::string("coming"), arg.name.get());
+  EXPECT_EQ(std::string("one"), arg.string_array[0].get());
+  EXPECT_EQ(std::string("two"), arg.string_array[1].get());
+  EXPECT_EQ(std::string("three"), arg.string_array[2].get());
+  CheckDataPipe(arg.data_handle.get().value());
+  CheckMessagePipe(arg.message_handle.get().value());
+}
+
+void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckSampleEchoArgs(*list->item);
+  CheckSampleEchoArgsList(list->next);
+}
+
+// More forgiving checks are needed in the face of potentially corrupt
+// messages. The values don't matter so long as all accesses are within
+// bounds.
+void CheckCorruptedString(const String& arg) {
+  if (arg.is_null())
+    return;
+  for (size_t i = 0; i < arg.size(); ++i)
+    g_waste_accumulator += arg[i];
+}
+
+void CheckCorruptedStringArray(const Array<String>& string_array) {
+  if (string_array.is_null())
+    return;
+  for (size_t i = 0; i < string_array.size(); ++i)
+    CheckCorruptedString(string_array[i]);
+}
+
+void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = MojoReadData(
+      data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < buffer_size; ++i)
+    g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = MojoReadMessage(
+      message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < buffer_size; ++i)
+    g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
+  if (arg.is_null())
+    return;
+  CheckCorruptedString(arg->name);
+  CheckCorruptedStringArray(arg->string_array);
+  if (arg->data_handle.is_valid())
+    CheckCorruptedDataPipe(arg->data_handle.get().value());
+  if (arg->message_handle.is_valid())
+    CheckCorruptedMessagePipe(arg->message_handle.get().value());
+}
+
+void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckCorruptedEchoArgs(list->item);
+  CheckCorruptedEchoArgsList(list->next);
+}
+
+// Base Provider implementation class. It's expected that tests subclass and
+// override the appropriate Provider functions. When test is done quit the
+// run_loop().
+class CppSideConnection : public js_to_cpp::CppSide {
+ public:
+  CppSideConnection() :
+      run_loop_(NULL),
+      js_side_(NULL),
+      mishandled_messages_(0) {
+  }
+  virtual ~CppSideConnection() {}
+
+  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
+  base::RunLoop* run_loop() { return run_loop_; }
+
+  void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
+  js_to_cpp::JsSide* js_side() { return js_side_; }
+
+  // js_to_cpp::CppSide:
+  virtual void StartTest() override {
+    NOTREACHED();
+  }
+
+  virtual void TestFinished() override {
+    NOTREACHED();
+  }
+
+  virtual void PingResponse() override {
+    mishandled_messages_ += 1;
+  }
+
+  virtual void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  virtual void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  virtual void BackPointerResponse(
+      js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+ protected:
+  base::RunLoop* run_loop_;
+  js_to_cpp::JsSide* js_side_;
+  int mishandled_messages_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
+};
+
+// Trivial test to verify a message sent from JS is received.
+class PingCppSideConnection : public CppSideConnection {
+ public:
+  PingCppSideConnection() : got_message_(false) {}
+  virtual ~PingCppSideConnection() {}
+
+  // js_to_cpp::CppSide:
+  virtual void StartTest() override {
+    js_side_->Ping();
+  }
+
+  virtual void PingResponse() override {
+    got_message_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return got_message_ && !mishandled_messages_;
+  }
+
+ private:
+  bool got_message_;
+  DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
+};
+
+// Test that parameters are passed with correct values.
+class EchoCppSideConnection : public CppSideConnection {
+ public:
+  EchoCppSideConnection() :
+      message_count_(0),
+      termination_seen_(false) {
+  }
+  virtual ~EchoCppSideConnection() {}
+
+  // js_to_cpp::CppSide:
+  virtual void StartTest() override {
+    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
+  }
+
+  virtual void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+    const js_to_cpp::EchoArgsPtr& special_arg = list->item;
+    message_count_ += 1;
+    EXPECT_EQ(-1, special_arg->si64);
+    EXPECT_EQ(-1, special_arg->si32);
+    EXPECT_EQ(-1, special_arg->si16);
+    EXPECT_EQ(-1, special_arg->si8);
+    EXPECT_EQ(std::string("going"), special_arg->name.To<std::string>());
+    CheckSampleEchoArgsList(list->next);
+  }
+
+  virtual void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_ &&
+        !mishandled_messages_ &&
+        message_count_ == kExpectedMessageCount;
+  }
+
+ private:
+  static const int kExpectedMessageCount = 10;
+  int message_count_;
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
+};
+
+// Test that corrupted messages don't wreak havoc.
+class BitFlipCppSideConnection : public CppSideConnection {
+ public:
+  BitFlipCppSideConnection() : termination_seen_(false) {}
+  virtual ~BitFlipCppSideConnection() {}
+
+  // js_to_cpp::CppSide:
+  virtual void StartTest() override {
+    js_side_->BitFlip(BuildSampleEchoArgs());
+  }
+
+  virtual void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  virtual void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_;
+  }
+
+ private:
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
+};
+
+// Test that severely random messages don't wreak havoc.
+class BackPointerCppSideConnection : public CppSideConnection {
+ public:
+  BackPointerCppSideConnection() : termination_seen_(false) {}
+  virtual ~BackPointerCppSideConnection() {}
+
+  // js_to_cpp::CppSide:
+  virtual void StartTest() override {
+    js_side_->BackPointer(BuildSampleEchoArgs());
+  }
+
+  virtual void BackPointerResponse(
+      js_to_cpp::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  virtual void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_;
+  }
+
+ private:
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection);
+};
+
+}  // namespace
+
+class JsToCppTest : public testing::Test {
+ public:
+  JsToCppTest() {}
+
+  void RunTest(const std::string& test, CppSideConnection* cpp_side) {
+    cpp_side->set_run_loop(&run_loop_);
+
+    MessagePipe pipe;
+    js_to_cpp::JsSidePtr js_side =
+        MakeProxy<js_to_cpp::JsSide>(pipe.handle0.Pass());
+    js_side.set_client(cpp_side);
+
+    js_side.internal_state()->router_for_testing()->EnableTestingMode();
+
+    cpp_side->set_js_side(js_side.get());
+
+    gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+                                   gin::ArrayBufferAllocator::SharedInstance());
+    gin::IsolateHolder instance;
+    apps::MojoRunnerDelegate delegate;
+    gin::ShellRunner runner(&delegate, instance.isolate());
+    delegate.Start(&runner, pipe.handle1.release().value(), test);
+
+    run_loop_.Run();
+  }
+
+ private:
+  base::ShadowingAtExitManager at_exit_;
+  base::MessageLoop loop;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
+};
+
+TEST_F(JsToCppTest, Ping) {
+  if (IsRunningOnIsolatedBot())
+    return;
+
+  PingCppSideConnection cpp_side_connection;
+  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, Echo) {
+  if (IsRunningOnIsolatedBot())
+    return;
+
+  EchoCppSideConnection cpp_side_connection;
+  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BitFlip) {
+  if (IsRunningOnIsolatedBot())
+    return;
+
+  BitFlipCppSideConnection cpp_side_connection;
+  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BackPointer) {
+  if (IsRunningOnIsolatedBot())
+    return;
+
+  BackPointerCppSideConnection cpp_side_connection;
+  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/apps/js/test/js_to_cpp_unittest.js b/mojo/apps/js/test/js_to_cpp_unittest.js
new file mode 100644
index 0000000..b1073c8
--- /dev/null
+++ b/mojo/apps/js/test/js_to_cpp_unittest.js
@@ -0,0 +1,220 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define('mojo/apps/js/test/js_to_cpp_unittest', [
+  'console',
+  'mojo/apps/js/test/js_to_cpp.mojom',
+  'mojo/public/js/bindings/connection',
+  'mojo/public/js/bindings/connector',
+  'mojo/public/js/bindings/core',
+], function (console, jsToCpp, connection, connector, core) {
+  var retainedConnection;
+  var sampleData;
+  var sampleMessage;
+  var BAD_VALUE = 13;
+  var DATA_PIPE_PARAMS = {
+    flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+    elementNumBytes: 1,
+    capacityNumBytes: 64
+  };
+
+  function JsSideConnection(cppSide) {
+    this.cppSide_ = cppSide;
+    cppSide.startTest();
+  }
+
+  JsSideConnection.prototype = Object.create(jsToCpp.JsSideStub.prototype);
+
+  JsSideConnection.prototype.ping = function (arg) {
+    this.cppSide_.pingResponse();
+  };
+
+  JsSideConnection.prototype.echo = function (numIterations, arg) {
+    var dataPipe1;
+    var dataPipe2;
+    var i;
+    var messagePipe1;
+    var messagePipe2;
+    var specialArg;
+
+    // Ensure expected negative values are negative.
+    if (arg.si64 > 0)
+      arg.si64 = BAD_VALUE;
+
+    if (arg.si32 > 0)
+      arg.si32 = BAD_VALUE;
+
+    if (arg.si16 > 0)
+      arg.si16 = BAD_VALUE;
+
+    if (arg.si8 > 0)
+      arg.si8 = BAD_VALUE;
+
+    for (i = 0; i < numIterations; ++i) {
+      dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS);
+      dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS);
+      messagePipe1 = core.createMessagePipe();
+      messagePipe2 = core.createMessagePipe();
+
+      arg.data_handle = dataPipe1.consumerHandle;
+      arg.message_handle = messagePipe1.handle1;
+
+      specialArg = new jsToCpp.EchoArgs();
+      specialArg.si64 = -1;
+      specialArg.si32 = -1;
+      specialArg.si16 = -1;
+      specialArg.si8 = -1;
+      specialArg.name = 'going';
+      specialArg.data_handle = dataPipe2.consumerHandle;
+      specialArg.message_handle = messagePipe2.handle1;
+
+      writeDataPipe(dataPipe1, sampleData);
+      writeDataPipe(dataPipe2, sampleData);
+      writeMessagePipe(messagePipe1, sampleMessage);
+      writeMessagePipe(messagePipe2, sampleMessage);
+
+      this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg));
+
+      core.close(dataPipe1.producerHandle);
+      core.close(dataPipe2.producerHandle);
+      core.close(messagePipe1.handle0);
+      core.close(messagePipe2.handle0);
+    }
+    this.cppSide_.testFinished();
+  };
+
+  JsSideConnection.prototype.bitFlip = function (arg) {
+    var iteration = 0;
+    var dataPipe;
+    var messagePipe;
+    var proto = connector.Connector.prototype;
+    var stopSignalled = false;
+
+    proto.realAccept = proto.accept;
+    proto.accept = function (message) {
+      var offset = iteration / 8;
+      var mask;
+      var value;
+      if (offset < message.buffer.arrayBuffer.byteLength) {
+        mask = 1 << (iteration % 8);
+        value = message.buffer.getUint8(offset) ^ mask;
+        message.buffer.setUint8(offset, value);
+        return this.realAccept(message);
+      }
+      stopSignalled = true;
+      return false;
+    };
+
+    while (!stopSignalled) {
+      dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
+      messagePipe = core.createMessagePipe();
+      writeDataPipe(dataPipe, sampleData);
+      writeMessagePipe(messagePipe, sampleMessage);
+      arg.data_handle = dataPipe.consumerHandle;
+      arg.message_handle = messagePipe.handle1;
+
+      this.cppSide_.bitFlipResponse(createEchoArgsList(arg));
+
+      core.close(dataPipe.producerHandle);
+      core.close(messagePipe.handle0);
+      iteration += 1;
+    }
+
+    proto.accept = proto.realAccept;
+    proto.realAccept = null;
+    this.cppSide_.testFinished();
+  };
+
+  JsSideConnection.prototype.backPointer = function (arg) {
+    var iteration = 0;
+    var dataPipe;
+    var messagePipe;
+    var proto = connector.Connector.prototype;
+    var stopSignalled = false;
+
+    proto.realAccept = proto.accept;
+    proto.accept = function (message) {
+      var delta = 8 * (1 + iteration % 32);
+      var offset = 8 * ((iteration / 32) | 0);
+      if (offset < message.buffer.arrayBuffer.byteLength - 4) {
+        message.buffer.dataView.setUint32(offset, 0x100000000 - delta, true);
+        message.buffer.dataView.setUint32(offset + 4, 0xffffffff, true);
+        return this.realAccept(message);
+      }
+      stopSignalled = true;
+      return false;
+    };
+
+    while (!stopSignalled) {
+      dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
+      messagePipe = core.createMessagePipe();
+      writeDataPipe(dataPipe, sampleData);
+      writeMessagePipe(messagePipe, sampleMessage);
+      arg.data_handle = dataPipe.consumerHandle;
+      arg.message_handle = messagePipe.handle1;
+
+      this.cppSide_.backPointerResponse(createEchoArgsList(arg));
+
+      core.close(dataPipe.producerHandle);
+      core.close(messagePipe.handle0);
+      iteration += 1;
+    }
+
+    proto.accept = proto.realAccept;
+    proto.realAccept = null;
+    this.cppSide_.testFinished();
+  };
+
+  function writeDataPipe(pipe, data) {
+    var writeResult = core.writeData(
+      pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE);
+
+    if (writeResult.result != core.RESULT_OK) {
+      console.log('ERROR: Data pipe write result was ' + writeResult.result);
+      return false;
+    }
+    if (writeResult.numBytes != data.length) {
+      console.log('ERROR: Data pipe write length was ' + writeResult.numBytes);
+      return false;
+    }
+    return true;
+  }
+
+  function writeMessagePipe(pipe, arrayBuffer) {
+    var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0);
+    if (result != core.RESULT_OK) {
+      console.log('ERROR: Message pipe write result was ' + result);
+      return false;
+    }
+    return true;
+  }
+
+  function createEchoArgsListElement(item, next) {
+    var list = new jsToCpp.EchoArgsList();
+    list.item = item;
+    list.next = next;
+    return list;
+  }
+
+  function createEchoArgsList() {
+    var genuineArray = Array.prototype.slice.call(arguments);
+    return genuineArray.reduceRight(function (previous, current) {
+      return createEchoArgsListElement(current, previous);
+    }, null);
+  }
+
+  return function(handle) {
+    var i;
+    sampleData = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes);
+    for (i = 0; i < sampleData.length; ++i) {
+      sampleData[i] = i;
+    }
+    sampleMessage = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes);
+    for (i = 0; i < sampleMessage.length; ++i) {
+      sampleMessage[i] = 255 - i;
+    }
+    retainedConnection = new connection.Connection(handle, JsSideConnection,
+                                                   jsToCpp.CppSideProxy);
+  };
+});
diff --git a/mojo/apps/js/test/run_apps_js_tests.cc b/mojo/apps/js/test/run_apps_js_tests.cc
new file mode 100644
index 0000000..36b185b
--- /dev/null
+++ b/mojo/apps/js/test/run_apps_js_tests.cc
@@ -0,0 +1,64 @@
+// 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/files/file_path.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/apps/js/bindings/monotonic_clock.h"
+#include "mojo/apps/js/bindings/threading.h"
+#include "mojo/bindings/js/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+namespace {
+
+class TestRunnerDelegate : public gin::FileRunnerDelegate {
+ public:
+  TestRunnerDelegate() {
+    AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+    AddBuiltinModule(Core::kModuleName, Core::GetModule);
+    AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule);
+    AddBuiltinModule(apps::MonotonicClock::kModuleName,
+                     apps::MonotonicClock::GetModule);
+    AddBuiltinModule(apps::Threading::kModuleName, apps::Threading::GetModule);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test, bool run_until_idle) {
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("mojo")
+             .AppendASCII("apps")
+             .AppendASCII("js")
+             .AppendASCII("bindings")
+             .AppendASCII(test);
+  TestRunnerDelegate delegate;
+  gin::RunTestFromFile(path, &delegate, run_until_idle);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+
+TEST(JSTest, sample_test) {
+  RunTest("sample_service_unittests.js", true);
+}
+
+TEST(JSTest, connection) {
+  RunTest("connection_unittests.js", false);
+}
+
+TEST(JSTest, monotonic_clock) {
+  RunTest("monotonic_clock_unittests.js", false);
+}
+
+}  // namespace
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/aura/BUILD.gn b/mojo/aura/BUILD.gn
new file mode 100644
index 0000000..f6d6450
--- /dev/null
+++ b/mojo/aura/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.gyp:mojo_aura_support
+source_set("aura") {
+  sources = [
+    "aura_init.cc",
+    "aura_init.h",
+    "context_factory_mojo.cc",
+    "context_factory_mojo.h",
+    "screen_mojo.cc",
+    "screen_mojo.h",
+    "window_tree_host_mojo.cc",
+    "window_tree_host_mojo.h",
+    "window_tree_host_mojo_delegate.h",
+  ]
+
+  public_deps = [
+    "//mojo/services/public/cpp/view_manager",
+  ]
+  deps = [
+    "//cc",
+    "//skia",
+    "//ui/aura",
+    "//ui/compositor",
+    "//ui/events",
+    "//ui/events:events_base",
+    "//ui/gl",
+    "//mojo/cc",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/interfaces/native_viewport",
+  ]
+}
diff --git a/mojo/aura/DEPS b/mojo/aura/DEPS
new file mode 100644
index 0000000..660a477
--- /dev/null
+++ b/mojo/aura/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+skia",
+  "+ui/aura",
+  "+ui/compositor",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/gl",
+]
diff --git a/mojo/aura/aura_init.cc b/mojo/aura/aura_init.cc
new file mode 100644
index 0000000..df17ab7
--- /dev/null
+++ b/mojo/aura/aura_init.cc
@@ -0,0 +1,26 @@
+// 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/aura/aura_init.h"
+
+#include "mojo/aura/context_factory_mojo.h"
+#include "mojo/aura/screen_mojo.h"
+#include "ui/aura/env.h"
+
+namespace mojo {
+
+AuraInit::AuraInit() {
+  aura::Env::CreateInstance(false);
+
+  context_factory_.reset(new ContextFactoryMojo);
+  aura::Env::GetInstance()->set_context_factory(context_factory_.get());
+
+  screen_.reset(ScreenMojo::Create());
+  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+}
+
+AuraInit::~AuraInit() {
+}
+
+}  // namespace mojo
diff --git a/mojo/aura/aura_init.h b/mojo/aura/aura_init.h
new file mode 100644
index 0000000..dea1684
--- /dev/null
+++ b/mojo/aura/aura_init.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_AURA_AURA_INIT_MOJO_H_
+#define MOJO_AURA_AURA_INIT_MOJO_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace ui {
+class ContextFactory;
+}
+
+namespace mojo {
+
+class ScreenMojo;
+
+// Sets up necessary state for aura when run with the viewmanager.
+class AuraInit {
+ public:
+  AuraInit();
+  ~AuraInit();
+
+ private:
+  scoped_ptr<ui::ContextFactory> context_factory_;
+  scoped_ptr<ScreenMojo> screen_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuraInit);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_AURA_AURA_INIT_MOJO_H_
diff --git a/mojo/aura/context_factory_mojo.cc b/mojo/aura/context_factory_mojo.cc
new file mode 100644
index 0000000..5b55cba
--- /dev/null
+++ b/mojo/aura/context_factory_mojo.cc
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium 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/aura/context_factory_mojo.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "cc/output/output_surface.h"
+#include "cc/output/software_output_device.h"
+#include "cc/resources/shared_bitmap_manager.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/compositor/reflector.h"
+
+namespace mojo {
+namespace {
+
+void FreeSharedBitmap(cc::SharedBitmap* shared_bitmap) {
+  delete shared_bitmap->memory();
+}
+
+void IgnoreSharedBitmap(cc::SharedBitmap* shared_bitmap) {}
+
+class SoftwareOutputDeviceViewManager : public cc::SoftwareOutputDevice {
+ public:
+  explicit SoftwareOutputDeviceViewManager(ui::Compositor* compositor)
+      : compositor_(compositor) {
+  }
+  virtual ~SoftwareOutputDeviceViewManager() {}
+
+  // cc::SoftwareOutputDevice:
+  virtual void EndPaint(cc::SoftwareFrameData* frame_data) override {
+    WindowTreeHostMojo* window_tree_host =
+        WindowTreeHostMojo::ForCompositor(compositor_);
+    DCHECK(window_tree_host);
+    window_tree_host->SetContents(
+        skia::GetTopDevice(*canvas_)->accessBitmap(true));
+
+    SoftwareOutputDevice::EndPaint(frame_data);
+  }
+
+ private:
+  ui::Compositor* compositor_;
+
+  DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceViewManager);
+};
+
+// TODO(sky): this is a copy from cc/test. Copy to a common place.
+class TestSharedBitmapManager : public cc::SharedBitmapManager {
+ public:
+  TestSharedBitmapManager() {}
+  virtual ~TestSharedBitmapManager() {}
+
+  virtual scoped_ptr<cc::SharedBitmap> AllocateSharedBitmap(
+      const gfx::Size& size) override {
+    base::AutoLock lock(lock_);
+    scoped_ptr<base::SharedMemory> memory(new base::SharedMemory);
+    memory->CreateAndMapAnonymous(size.GetArea() * 4);
+    cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
+    bitmap_map_[id] = memory.get();
+    return scoped_ptr<cc::SharedBitmap>(
+        new cc::SharedBitmap(memory.release(), id,
+                             base::Bind(&FreeSharedBitmap)));
+  }
+
+  virtual scoped_ptr<cc::SharedBitmap> GetSharedBitmapFromId(
+      const gfx::Size&,
+      const cc::SharedBitmapId& id) override {
+    base::AutoLock lock(lock_);
+    if (bitmap_map_.find(id) == bitmap_map_.end())
+      return scoped_ptr<cc::SharedBitmap>();
+    return scoped_ptr<cc::SharedBitmap>(
+        new cc::SharedBitmap(bitmap_map_[id], id,
+                             base::Bind(&IgnoreSharedBitmap)));
+  }
+
+  virtual scoped_ptr<cc::SharedBitmap> GetBitmapForSharedMemory(
+      base::SharedMemory* memory) override {
+    base::AutoLock lock(lock_);
+    cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
+    bitmap_map_[id] = memory;
+    return scoped_ptr<cc::SharedBitmap>(
+        new cc::SharedBitmap(memory, id, base::Bind(&IgnoreSharedBitmap)));
+  }
+
+ private:
+  base::Lock lock_;
+  std::map<cc::SharedBitmapId, base::SharedMemory*> bitmap_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSharedBitmapManager);
+};
+
+}  // namespace
+
+ContextFactoryMojo::ContextFactoryMojo()
+    : shared_bitmap_manager_(new TestSharedBitmapManager()) {
+}
+
+ContextFactoryMojo::~ContextFactoryMojo() {}
+
+scoped_ptr<cc::OutputSurface> ContextFactoryMojo::CreateOutputSurface(
+    ui::Compositor* compositor,
+    bool software_fallback) {
+  scoped_ptr<cc::SoftwareOutputDevice> output_device(
+      new SoftwareOutputDeviceViewManager(compositor));
+  return make_scoped_ptr(new cc::OutputSurface(output_device.Pass()));
+}
+
+scoped_refptr<ui::Reflector> ContextFactoryMojo::CreateReflector(
+    ui::Compositor* mirroed_compositor,
+    ui::Layer* mirroring_layer) {
+  return new ui::Reflector();
+}
+
+void ContextFactoryMojo::RemoveReflector(
+    scoped_refptr<ui::Reflector> reflector) {
+}
+
+scoped_refptr<cc::ContextProvider>
+ContextFactoryMojo::SharedMainThreadContextProvider() {
+  return scoped_refptr<cc::ContextProvider>(NULL);
+}
+
+void ContextFactoryMojo::RemoveCompositor(ui::Compositor* compositor) {}
+
+bool ContextFactoryMojo::DoesCreateTestContexts() { return false; }
+
+cc::SharedBitmapManager* ContextFactoryMojo::GetSharedBitmapManager() {
+  return shared_bitmap_manager_.get();
+}
+
+base::MessageLoopProxy* ContextFactoryMojo::GetCompositorMessageLoop() {
+  return NULL;
+}
+
+}  // namespace mojo
diff --git a/mojo/aura/context_factory_mojo.h b/mojo/aura/context_factory_mojo.h
new file mode 100644
index 0000000..437359d
--- /dev/null
+++ b/mojo/aura/context_factory_mojo.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_AURA_CONTEXT_FACTORY_MOJO_H_
+#define MOJO_AURA_CONTEXT_FACTORY_MOJO_H_
+
+#include "base/macros.h"
+#include "ui/compositor/compositor.h"
+
+namespace mojo {
+
+class ContextFactoryMojo : public ui::ContextFactory {
+ public:
+  ContextFactoryMojo();
+  virtual ~ContextFactoryMojo();
+
+ private:
+  // ContextFactory:
+  virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(
+      ui::Compositor* compositor,
+      bool software_fallback) override;
+  virtual scoped_refptr<ui::Reflector> CreateReflector(
+      ui::Compositor* mirrored_compositor,
+      ui::Layer* mirroring_layer) override;
+  virtual void RemoveReflector(scoped_refptr<ui::Reflector> reflector) override;
+  virtual scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider()
+      override;
+  virtual void RemoveCompositor(ui::Compositor* compositor) override;
+  virtual bool DoesCreateTestContexts() override;
+  virtual cc::SharedBitmapManager* GetSharedBitmapManager() override;
+  virtual base::MessageLoopProxy* GetCompositorMessageLoop() override;
+
+  scoped_ptr<cc::SharedBitmapManager> shared_bitmap_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextFactoryMojo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_AURA_CONTEXT_FACTORY_MOJO_H_
diff --git a/mojo/aura/screen_mojo.cc b/mojo/aura/screen_mojo.cc
new file mode 100644
index 0000000..b07c320
--- /dev/null
+++ b/mojo/aura/screen_mojo.cc
@@ -0,0 +1,76 @@
+// 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/aura/screen_mojo.h"
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace mojo {
+
+// static
+ScreenMojo* ScreenMojo::Create() {
+  return new ScreenMojo(gfx::Rect(0, 0, 800, 600));
+}
+
+ScreenMojo::~ScreenMojo() {
+}
+
+bool ScreenMojo::IsDIPEnabled() {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+gfx::Point ScreenMojo::GetCursorScreenPoint() {
+  NOTIMPLEMENTED();
+  return gfx::Point();
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowUnderCursor() {
+  return GetWindowAtScreenPoint(GetCursorScreenPoint());
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowAtScreenPoint(const gfx::Point& point) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+int ScreenMojo::GetNumDisplays() const {
+  return 1;
+}
+
+std::vector<gfx::Display> ScreenMojo::GetAllDisplays() const {
+  return std::vector<gfx::Display>(1, display_);
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestWindow(
+    gfx::NativeWindow window) const {
+  return display_;
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestPoint(const gfx::Point& point) const {
+  return display_;
+}
+
+gfx::Display ScreenMojo::GetDisplayMatching(const gfx::Rect& match_rect) const {
+  return display_;
+}
+
+gfx::Display ScreenMojo::GetPrimaryDisplay() const {
+  return display_;
+}
+
+void ScreenMojo::AddObserver(gfx::DisplayObserver* observer) {
+}
+
+void ScreenMojo::RemoveObserver(gfx::DisplayObserver* observer) {
+}
+
+ScreenMojo::ScreenMojo(const gfx::Rect& screen_bounds) {
+  static int64 synthesized_display_id = 2000;
+  display_.set_id(synthesized_display_id++);
+  display_.SetScaleAndBounds(1.0f, screen_bounds);
+}
+
+}  // namespace mojo
diff --git a/mojo/aura/screen_mojo.h b/mojo/aura/screen_mojo.h
new file mode 100644
index 0000000..184ccff
--- /dev/null
+++ b/mojo/aura/screen_mojo.h
@@ -0,0 +1,54 @@
+// 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_AURA_SCREEN_MOJO_H_
+#define MOJO_AURA_SCREEN_MOJO_H_
+
+#include "base/macros.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+
+namespace gfx {
+class Rect;
+class Transform;
+}
+
+namespace mojo {
+
+// A minimal implementation of gfx::Screen for mojo.
+class ScreenMojo : public gfx::Screen {
+ public:
+  static ScreenMojo* Create();
+  virtual ~ScreenMojo();
+
+ protected:
+  // gfx::Screen overrides:
+  virtual bool IsDIPEnabled() override;
+  virtual gfx::Point GetCursorScreenPoint() override;
+  virtual gfx::NativeWindow GetWindowUnderCursor() override;
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      override;
+  virtual int GetNumDisplays() const override;
+  virtual std::vector<gfx::Display> GetAllDisplays() const override;
+  virtual gfx::Display GetDisplayNearestWindow(
+      gfx::NativeView view) const override;
+  virtual gfx::Display GetDisplayNearestPoint(
+      const gfx::Point& point) const override;
+  virtual gfx::Display GetDisplayMatching(
+      const gfx::Rect& match_rect) const override;
+  virtual gfx::Display GetPrimaryDisplay() const override;
+  virtual void AddObserver(gfx::DisplayObserver* observer) override;
+  virtual void RemoveObserver(gfx::DisplayObserver* observer) override;
+
+ private:
+  explicit ScreenMojo(const gfx::Rect& screen_bounds);
+
+  gfx::Display display_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenMojo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_AURA_SCREEN_MOJO_H_
diff --git a/mojo/aura/window_tree_host_mojo.cc b/mojo/aura/window_tree_host_mojo.cc
new file mode 100644
index 0000000..8ef87c6
--- /dev/null
+++ b/mojo/aura/window_tree_host_mojo.cc
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium 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/aura/window_tree_host_mojo.h"
+
+#include <vector>
+
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+
+namespace mojo {
+namespace {
+
+const char kTreeHostsKey[] = "tree_hosts";
+
+typedef std::vector<WindowTreeHostMojo*> Managers;
+
+class TreeHosts : public base::SupportsUserData::Data {
+ public:
+  TreeHosts() {}
+  virtual ~TreeHosts() {}
+
+  static TreeHosts* Get() {
+    TreeHosts* hosts = static_cast<TreeHosts*>(
+        aura::Env::GetInstance()->GetUserData(kTreeHostsKey));
+    if (!hosts) {
+      hosts = new TreeHosts;
+      aura::Env::GetInstance()->SetUserData(kTreeHostsKey, hosts);
+    }
+    return hosts;
+  }
+
+  void Add(WindowTreeHostMojo* manager) {
+    managers_.push_back(manager);
+  }
+
+  void Remove(WindowTreeHostMojo* manager) {
+    Managers::iterator i = std::find(managers_.begin(), managers_.end(),
+                                     manager);
+    DCHECK(i != managers_.end());
+    managers_.erase(i);
+  }
+
+  const std::vector<WindowTreeHostMojo*> managers() const {
+    return managers_;
+  }
+
+ private:
+  Managers managers_;
+
+  DISALLOW_COPY_AND_ASSIGN(TreeHosts);
+};
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, public:
+
+WindowTreeHostMojo::WindowTreeHostMojo(View* view,
+                                       WindowTreeHostMojoDelegate* delegate)
+    : view_(view),
+      bounds_(view->bounds()),
+      delegate_(delegate) {
+  view_->AddObserver(this);
+  CreateCompositor(GetAcceleratedWidget());
+
+  TreeHosts::Get()->Add(this);
+}
+
+WindowTreeHostMojo::~WindowTreeHostMojo() {
+  view_->RemoveObserver(this);
+  TreeHosts::Get()->Remove(this);
+  DestroyCompositor();
+  DestroyDispatcher();
+}
+
+// static
+WindowTreeHostMojo* WindowTreeHostMojo::ForCompositor(
+    ui::Compositor* compositor) {
+  const Managers& managers = TreeHosts::Get()->managers();
+  for (size_t i = 0; i < managers.size(); ++i) {
+    if (managers[i]->compositor() == compositor)
+      return managers[i];
+  }
+  return NULL;
+}
+
+void WindowTreeHostMojo::SetContents(const SkBitmap& contents) {
+  delegate_->CompositorContentsChanged(contents);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, aura::WindowTreeHost implementation:
+
+ui::EventSource* WindowTreeHostMojo::GetEventSource() {
+  return this;
+}
+
+gfx::AcceleratedWidget WindowTreeHostMojo::GetAcceleratedWidget() {
+  return gfx::kNullAcceleratedWidget;
+}
+
+void WindowTreeHostMojo::Show() {
+  window()->Show();
+}
+
+void WindowTreeHostMojo::Hide() {
+}
+
+gfx::Rect WindowTreeHostMojo::GetBounds() const {
+  return bounds_;
+}
+
+void WindowTreeHostMojo::SetBounds(const gfx::Rect& bounds) {
+  window()->SetBounds(gfx::Rect(bounds.size()));
+}
+
+gfx::Point WindowTreeHostMojo::GetLocationOnNativeScreen() const {
+  return gfx::Point(0, 0);
+}
+
+void WindowTreeHostMojo::SetCapture() {
+  NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::ReleaseCapture() {
+  NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::PostNativeEvent(
+    const base::NativeEvent& native_event) {
+  NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::SetCursorNative(gfx::NativeCursor cursor) {
+  NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::MoveCursorToNative(const gfx::Point& location) {
+  NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::OnCursorVisibilityChangedNative(bool show) {
+  NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, ui::EventSource implementation:
+
+ui::EventProcessor* WindowTreeHostMojo::GetEventProcessor() {
+  return dispatcher();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, ViewObserver implementation:
+
+void WindowTreeHostMojo::OnViewBoundsChanged(
+    View* view,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds) {
+  bounds_ = new_bounds;
+  if (old_bounds.origin() != new_bounds.origin())
+    OnHostMoved(bounds_.origin());
+  if (old_bounds.size() != new_bounds.size())
+    OnHostResized(bounds_.size());
+}
+
+}  // namespace mojo
diff --git a/mojo/aura/window_tree_host_mojo.h b/mojo/aura/window_tree_host_mojo.h
new file mode 100644
index 0000000..af00203
--- /dev/null
+++ b/mojo/aura/window_tree_host_mojo.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
+#define MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
+
+#include "base/macros.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event_source.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkBitmap;
+
+namespace ui {
+class Compositor;
+}
+
+namespace mojo {
+
+class WindowTreeHostMojoDelegate;
+
+class WindowTreeHostMojo : public aura::WindowTreeHost,
+                           public ui::EventSource,
+                           public ViewObserver {
+ public:
+  WindowTreeHostMojo(View* view, WindowTreeHostMojoDelegate* delegate);
+  virtual ~WindowTreeHostMojo();
+
+  // Returns the WindowTreeHostMojo for the specified compositor.
+  static WindowTreeHostMojo* ForCompositor(ui::Compositor* compositor);
+
+  const gfx::Rect& bounds() const { return bounds_; }
+
+  // Sets the contents to show in this WindowTreeHost. This forwards to the
+  // delegate.
+  void SetContents(const SkBitmap& contents);
+
+  ui::EventDispatchDetails SendEventToProcessor(ui::Event* event) {
+    return ui::EventSource::SendEventToProcessor(event);
+  }
+
+ private:
+  // WindowTreeHost:
+  virtual ui::EventSource* GetEventSource() override;
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() override;
+  virtual void Show() override;
+  virtual void Hide() override;
+  virtual gfx::Rect GetBounds() const override;
+  virtual void SetBounds(const gfx::Rect& bounds) override;
+  virtual gfx::Point GetLocationOnNativeScreen() const override;
+  virtual void SetCapture() override;
+  virtual void ReleaseCapture() override;
+  virtual void PostNativeEvent(const base::NativeEvent& native_event) override;
+  virtual void SetCursorNative(gfx::NativeCursor cursor) override;
+  virtual void MoveCursorToNative(const gfx::Point& location) override;
+  virtual void OnCursorVisibilityChangedNative(bool show) override;
+
+  // ui::EventSource:
+  virtual ui::EventProcessor* GetEventProcessor() override;
+
+  // ViewObserver:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override;
+
+  View* view_;
+
+  gfx::Rect bounds_;
+
+  WindowTreeHostMojoDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMojo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
diff --git a/mojo/aura/window_tree_host_mojo_delegate.h b/mojo/aura/window_tree_host_mojo_delegate.h
new file mode 100644
index 0000000..9ab13b2
--- /dev/null
+++ b/mojo/aura/window_tree_host_mojo_delegate.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_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
+
+class SkBitmap;
+
+namespace mojo {
+
+class WindowTreeHostMojoDelegate {
+ public:
+  // Invoked when the contents of the composite associated with the
+  // WindowTreeHostMojo are updated.
+  virtual void CompositorContentsChanged(const SkBitmap& bitmap) = 0;
+
+ protected:
+  virtual ~WindowTreeHostMojoDelegate() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
diff --git a/mojo/bindings/js/BUILD.gn b/mojo/bindings/js/BUILD.gn
new file mode 100644
index 0000000..7e64c0f
--- /dev/null
+++ b/mojo/bindings/js/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.gyp:mojo_js_bindings
+source_set("js") {
+  sources = [
+    "core.cc",
+    "core.h",
+    "drain_data.cc",
+    "drain_data.h",
+    "handle.cc",
+    "handle.h",
+    "handle_close_observer.h",
+    "support.cc",
+    "support.h",
+    "waiting_callback.cc",
+    "waiting_callback.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//gin",
+    "//mojo/common",
+    "//v8",
+  ]
+}
diff --git a/mojo/bindings/js/DEPS b/mojo/bindings/js/DEPS
new file mode 100644
index 0000000..d974b68
--- /dev/null
+++ b/mojo/bindings/js/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+gin",
+  "+v8",
+]
diff --git a/mojo/bindings/js/core.cc b/mojo/bindings/js/core.cc
new file mode 100644
index 0000000..9ab2f19
--- /dev/null
+++ b/mojo/bindings/js/core.cc
@@ -0,0 +1,320 @@
+// Copyright 2014 The Chromium 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/bindings/js/core.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/dictionary.h"
+#include "gin/function_template.h"
+#include "gin/handle.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/drain_data.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+MojoResult CloseHandle(gin::Handle<gin::HandleWrapper> handle) {
+  if (!handle->get().is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  handle->Close();
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WaitHandle(mojo::Handle handle,
+                      MojoHandleSignals signals,
+                      MojoDeadline deadline) {
+  return MojoWait(handle.value(), signals, deadline);
+}
+
+MojoResult WaitMany(
+    const std::vector<mojo::Handle>& handles,
+    const std::vector<MojoHandleSignals>& signals,
+    MojoDeadline deadline) {
+  return mojo::WaitMany(handles, signals, deadline);
+}
+
+gin::Dictionary CreateMessagePipe(const gin::Arguments& args) {
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
+
+  MojoHandle handle0 = MOJO_HANDLE_INVALID;
+  MojoHandle handle1 = MOJO_HANDLE_INVALID;
+  MojoResult result = MOJO_RESULT_OK;
+
+  v8::Handle<v8::Value> options_value = args.PeekNext();
+  if (options_value.IsEmpty() || options_value->IsNull() ||
+      options_value->IsUndefined()) {
+    result = MojoCreateMessagePipe(NULL, &handle0, &handle1);
+  } else if (options_value->IsObject()) {
+    gin::Dictionary options_dict(args.isolate(), options_value->ToObject());
+    MojoCreateMessagePipeOptions options;
+    // For future struct_size, we can probably infer that from the presence of
+    // properties in options_dict. For now, it's always 8.
+    options.struct_size = 8;
+    // Ideally these would be optional. But the interface makes it hard to
+    // typecheck them then.
+    if (!options_dict.Get("flags", &options.flags)) {
+      return dictionary;
+    }
+
+    result = MojoCreateMessagePipe(&options, &handle0, &handle1);
+  } else {
+      return dictionary;
+  }
+
+  CHECK_EQ(MOJO_RESULT_OK, result);
+
+  dictionary.Set("result", result);
+  dictionary.Set("handle0", mojo::Handle(handle0));
+  dictionary.Set("handle1", mojo::Handle(handle1));
+  return dictionary;
+}
+
+MojoResult WriteMessage(
+    mojo::Handle handle,
+    const gin::ArrayBufferView& buffer,
+    const std::vector<gin::Handle<gin::HandleWrapper> >& handles,
+    MojoWriteMessageFlags flags) {
+  std::vector<MojoHandle> raw_handles(handles.size());
+  for (size_t i = 0; i < handles.size(); ++i)
+    raw_handles[i] = handles[i]->get().value();
+  MojoResult rv = MojoWriteMessage(handle.value(),
+                          buffer.bytes(),
+                          static_cast<uint32_t>(buffer.num_bytes()),
+                          raw_handles.empty() ? NULL : &raw_handles[0],
+                          static_cast<uint32_t>(raw_handles.size()),
+                          flags);
+  // MojoWriteMessage takes ownership of the handles upon success, so
+  // release them here.
+  if (rv == MOJO_RESULT_OK) {
+    for (size_t i = 0; i < handles.size(); ++i)
+      ignore_result(handles[i]->release());
+  }
+  return rv;
+}
+
+gin::Dictionary ReadMessage(const gin::Arguments& args,
+                            mojo::Handle handle,
+                            MojoReadMessageFlags flags) {
+  uint32_t num_bytes = 0;
+  uint32_t num_handles = 0;
+  MojoResult result = MojoReadMessage(
+      handle.value(), NULL, &num_bytes, NULL, &num_handles, flags);
+  if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+    dictionary.Set("result", result);
+    return dictionary;
+  }
+
+  v8::Handle<v8::ArrayBuffer> array_buffer =
+      v8::ArrayBuffer::New(args.isolate(), num_bytes);
+  std::vector<mojo::Handle> handles(num_handles);
+
+  gin::ArrayBuffer buffer;
+  ConvertFromV8(args.isolate(), array_buffer, &buffer);
+  CHECK(buffer.num_bytes() == num_bytes);
+
+  result = MojoReadMessage(handle.value(),
+                           buffer.bytes(),
+                           &num_bytes,
+                           handles.empty() ? NULL :
+                               reinterpret_cast<MojoHandle*>(&handles[0]),
+                           &num_handles,
+                           flags);
+
+  CHECK(buffer.num_bytes() == num_bytes);
+  CHECK(handles.size() == num_handles);
+
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  dictionary.Set("result", result);
+  dictionary.Set("buffer", array_buffer);
+  dictionary.Set("handles", handles);
+  return dictionary;
+}
+
+gin::Dictionary CreateDataPipe(const gin::Arguments& args) {
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
+
+  MojoHandle producer_handle = MOJO_HANDLE_INVALID;
+  MojoHandle consumer_handle = MOJO_HANDLE_INVALID;
+  MojoResult result = MOJO_RESULT_OK;
+
+  v8::Handle<v8::Value> options_value = args.PeekNext();
+  if (options_value.IsEmpty() || options_value->IsNull() ||
+      options_value->IsUndefined()) {
+    result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle);
+  } else if (options_value->IsObject()) {
+    gin::Dictionary options_dict(args.isolate(), options_value->ToObject());
+    MojoCreateDataPipeOptions options;
+    // For future struct_size, we can probably infer that from the presence of
+    // properties in options_dict. For now, it's always 16.
+    options.struct_size = 16;
+    // Ideally these would be optional. But the interface makes it hard to
+    // typecheck them then.
+    if (!options_dict.Get("flags", &options.flags) ||
+        !options_dict.Get("elementNumBytes", &options.element_num_bytes) ||
+        !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) {
+      return dictionary;
+    }
+
+    result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle);
+  } else {
+    return dictionary;
+  }
+
+  CHECK_EQ(MOJO_RESULT_OK, result);
+
+  dictionary.Set("result", result);
+  dictionary.Set("producerHandle", mojo::Handle(producer_handle));
+  dictionary.Set("consumerHandle", mojo::Handle(consumer_handle));
+  return dictionary;
+}
+
+gin::Dictionary WriteData(const gin::Arguments& args,
+                          mojo::Handle handle,
+                          const gin::ArrayBufferView& buffer,
+                          MojoWriteDataFlags flags) {
+  uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes());
+  MojoResult result =
+      MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags);
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  dictionary.Set("result", result);
+  dictionary.Set("numBytes", num_bytes);
+  return dictionary;
+}
+
+gin::Dictionary ReadData(const gin::Arguments& args,
+                         mojo::Handle handle,
+                         MojoReadDataFlags flags) {
+  uint32_t num_bytes = 0;
+  MojoResult result = MojoReadData(
+      handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY);
+  if (result != MOJO_RESULT_OK) {
+    gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+    dictionary.Set("result", result);
+    return dictionary;
+  }
+
+  v8::Handle<v8::ArrayBuffer> array_buffer =
+      v8::ArrayBuffer::New(args.isolate(), num_bytes);
+  gin::ArrayBuffer buffer;
+  ConvertFromV8(args.isolate(), array_buffer, &buffer);
+  CHECK_EQ(num_bytes, buffer.num_bytes());
+
+  result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags);
+  CHECK_EQ(num_bytes, buffer.num_bytes());
+
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  dictionary.Set("result", result);
+  dictionary.Set("buffer", array_buffer);
+  return dictionary;
+}
+
+// Asynchronously read all of the data available for the specified data pipe
+// consumer handle until the remote handle is closed or an error occurs. A
+// Promise is returned whose settled value is an object like this:
+// {result: core.RESULT_OK, buffer: dataArrayBuffer}. If the read failed,
+// then the Promise is rejected, the result will be the actual error code,
+// and the buffer will contain whatever was read before the error occurred.
+// The drainData data pipe handle argument is closed automatically.
+
+v8::Handle<v8::Value> DoDrainData(gin::Arguments* args, mojo::Handle handle) {
+  return (new DrainData(args->isolate(), handle))->GetPromise();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Core::kModuleName[] = "mojo/public/js/bindings/core";
+
+v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+      &g_wrapper_info);
+
+  if (templ.IsEmpty()) {
+     templ = gin::ObjectTemplateBuilder(isolate)
+        // TODO(mpcomplete): Should these just be methods on the JS Handle
+        // object?
+        .SetMethod("close", CloseHandle)
+        .SetMethod("wait", WaitHandle)
+        .SetMethod("waitMany", WaitMany)
+        .SetMethod("createMessagePipe", CreateMessagePipe)
+        .SetMethod("writeMessage", WriteMessage)
+        .SetMethod("readMessage", ReadMessage)
+        .SetMethod("createDataPipe", CreateDataPipe)
+        .SetMethod("writeData", WriteData)
+        .SetMethod("readData", ReadData)
+        .SetMethod("drainData", DoDrainData)
+
+        .SetValue("RESULT_OK", MOJO_RESULT_OK)
+        .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
+        .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN)
+        .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT)
+        .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED)
+        .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND)
+        .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS)
+        .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED)
+        .SetValue("RESULT_RESOURCE_EXHAUSTED", MOJO_RESULT_RESOURCE_EXHAUSTED)
+        .SetValue("RESULT_FAILED_PRECONDITION", MOJO_RESULT_FAILED_PRECONDITION)
+        .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED)
+        .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE)
+        .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED)
+        .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL)
+        .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE)
+        .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS)
+        .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY)
+        .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT)
+
+        .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE)
+
+        .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE)
+        .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
+        .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE)
+
+        .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE",
+                  MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE)
+
+        .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
+
+        .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
+        .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
+                  MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
+
+        .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
+                  MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
+        .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD",
+                  MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)
+
+        .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE)
+        .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE",
+                  MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+
+        .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE)
+        .SetValue("READ_DATA_FLAG_ALL_OR_NONE",
+                  MOJO_READ_DATA_FLAG_ALL_OR_NONE)
+        .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
+        .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
+        .Build();
+
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/core.h b/mojo/bindings/js/core.h
new file mode 100644
index 0000000..bde327c
--- /dev/null
+++ b/mojo/bindings/js/core.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_BINDINGS_JS_CORE_H_
+#define MOJO_BINDINGS_JS_CORE_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+class Core {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_BINDINGS_JS_CORE_H_
diff --git a/mojo/bindings/js/drain_data.cc b/mojo/bindings/js/drain_data.cc
new file mode 100644
index 0000000..a615cd6
--- /dev/null
+++ b/mojo/bindings/js/drain_data.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium 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/bindings/js/drain_data.h"
+
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/dictionary.h"
+#include "gin/per_context_data.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace js {
+
+DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle)
+    : isolate_(isolate),
+      handle_(DataPipeConsumerHandle(handle.value())),
+      wait_id_(0) {
+
+  v8::Handle<v8::Context> context(isolate_->GetCurrentContext());
+  runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+
+  WaitForData();
+}
+
+v8::Handle<v8::Value> DrainData::GetPromise() {
+  CHECK(resolver_.IsEmpty());
+  v8::Handle<v8::Promise::Resolver> resolver(
+      v8::Promise::Resolver::New(isolate_));
+  resolver_.Reset(isolate_, resolver);
+  return resolver->GetPromise();
+}
+
+DrainData::~DrainData() {
+  if (wait_id_)
+    Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_);
+  resolver_.Reset();
+}
+
+void DrainData::WaitForData() {
+  wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait(
+      handle_.get().value(),
+      MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_DEADLINE_INDEFINITE,
+      &DrainData::WaitCompleted,
+      this);
+}
+
+void DrainData::DataReady(MojoResult result) {
+  wait_id_ = 0;
+  if (result != MOJO_RESULT_OK) {
+    DeliverData(result);
+    return;
+  }
+  while (result == MOJO_RESULT_OK) {
+    result = ReadData();
+    if (result == MOJO_RESULT_SHOULD_WAIT)
+      WaitForData();
+    else if (result != MOJO_RESULT_OK)
+      DeliverData(result);
+  }
+}
+
+MojoResult DrainData::ReadData() {
+  const void* buffer;
+  uint32_t num_bytes = 0;
+  MojoResult result = BeginReadDataRaw(
+      handle_.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return result;
+  const char* p = static_cast<const char*>(buffer);
+  DataBuffer* data_buffer = new DataBuffer(p, p + num_bytes);
+  data_buffers_.push_back(data_buffer);
+  return EndReadDataRaw(handle_.get(), num_bytes);
+}
+
+void DrainData::DeliverData(MojoResult result) {
+  if (!runner_) {
+    delete this;
+    return;
+  }
+
+  size_t total_bytes = 0;
+  for (unsigned i = 0; i < data_buffers_.size(); i++)
+    total_bytes += data_buffers_[i]->size();
+
+  // Create a total_bytes length ArrayBuffer return value.
+  gin::Runner::Scope scope(runner_.get());
+  v8::Handle<v8::ArrayBuffer> array_buffer =
+      v8::ArrayBuffer::New(isolate_, total_bytes);
+  gin::ArrayBuffer buffer;
+  ConvertFromV8(isolate_, array_buffer, &buffer);
+  CHECK_EQ(total_bytes, buffer.num_bytes());
+
+  // Copy the data_buffers into the ArrayBuffer.
+  char* array_buffer_ptr = static_cast<char*>(buffer.bytes());
+  size_t offset = 0;
+  for (size_t i = 0; i < data_buffers_.size(); i++) {
+    size_t num_bytes = data_buffers_[i]->size();
+    if (num_bytes == 0)
+      continue;
+    const char* data_buffer_ptr = &((*data_buffers_[i])[0]);
+    memcpy(array_buffer_ptr + offset, data_buffer_ptr, num_bytes);
+    offset += num_bytes;
+  }
+
+  // The "settled" value of the promise always includes all of the data
+  // that was read before either an error occurred or the remote pipe handle
+  // was closed. The latter is indicated by MOJO_RESULT_FAILED_PRECONDITION.
+
+  v8::Handle<v8::Promise::Resolver> resolver(
+      v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_));
+
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate_);
+  dictionary.Set("result", result);
+  dictionary.Set("buffer", array_buffer);
+  v8::Handle<v8::Value> settled_value(ConvertToV8(isolate_, dictionary));
+
+  if (result == MOJO_RESULT_FAILED_PRECONDITION)
+    resolver->Resolve(settled_value);
+  else
+    resolver->Reject(settled_value);
+
+  delete this;
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/drain_data.h b/mojo/bindings/js/drain_data.h
new file mode 100644
index 0000000..27d8f52
--- /dev/null
+++ b/mojo/bindings/js/drain_data.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_BINDINGS_JS_DRAIN_DATA_H_
+#define MOJO_BINDINGS_JS_DRAIN_DATA_H_
+
+#include "base/memory/scoped_vector.h"
+#include "gin/runner.h"
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/system/core.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+// This class is the implementation of the Mojo JavaScript core module's
+// drainData() method. It is not intended to be used directly. The caller
+// allocates a DrainData on the heap and returns GetPromise() to JS. The
+// implementation deletes itself after reading as much data as possible
+// and rejecting or resolving the Promise.
+
+class DrainData {
+ public:
+  // Starts waiting for data on the specified data pipe consumer handle.
+  // See WaitForData(). The constructor does not block.
+  DrainData(v8::Isolate* isolate, mojo::Handle handle);
+
+  // Returns a Promise that will be settled when no more data can be read.
+  // Should be called just once on a newly allocated DrainData object.
+  v8::Handle<v8::Value> GetPromise();
+
+ private:
+  ~DrainData();
+
+  // Registers an "async waiter" that calls DataReady() via WaitCompleted().
+  void WaitForData();
+  static void WaitCompleted(void* self, MojoResult result) {
+    static_cast<DrainData*>(self)->DataReady(result);
+  }
+
+  // Use ReadData() to read whatever is availble now on handle_ and save
+  // it in data_buffers_.
+  void DataReady(MojoResult result);
+  MojoResult ReadData();
+
+  // When the remote data pipe handle is closed, or an error occurs, deliver
+  // all of the buffered data to the JS Promise and then delete this.
+  void DeliverData(MojoResult result);
+
+  typedef std::vector<char> DataBuffer;
+
+  v8::Isolate* isolate_;
+  ScopedDataPipeConsumerHandle handle_;
+  MojoAsyncWaitID wait_id_;
+  base::WeakPtr<gin::Runner> runner_;
+  v8::UniquePersistent<v8::Promise::Resolver> resolver_;
+  ScopedVector<DataBuffer> data_buffers_;
+};
+
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_BINDINGS_JS_DRAIN_DATA_H_
diff --git a/mojo/bindings/js/handle.cc b/mojo/bindings/js/handle.cc
new file mode 100644
index 0000000..a25a911
--- /dev/null
+++ b/mojo/bindings/js/handle.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/bindings/js/handle.h"
+
+#include "mojo/bindings/js/handle_close_observer.h"
+
+namespace gin {
+
+gin::WrapperInfo HandleWrapper::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+HandleWrapper::HandleWrapper(MojoHandle handle)
+    : handle_(mojo::Handle(handle)) {
+}
+
+HandleWrapper::~HandleWrapper() {
+  NotifyCloseObservers();
+}
+
+void HandleWrapper::Close() {
+  NotifyCloseObservers();
+  handle_.reset();
+}
+
+void HandleWrapper::AddCloseObserver(HandleCloseObserver* observer) {
+  close_observers_.AddObserver(observer);
+}
+
+void HandleWrapper::RemoveCloseObserver(HandleCloseObserver* observer) {
+  close_observers_.RemoveObserver(observer);
+}
+
+void HandleWrapper::NotifyCloseObservers() {
+  if (!handle_.is_valid())
+    return;
+
+  FOR_EACH_OBSERVER(HandleCloseObserver, close_observers_, OnWillCloseHandle());
+}
+
+v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate,
+                                                    const mojo::Handle& val) {
+  if (!val.is_valid())
+    return v8::Null(isolate);
+  return HandleWrapper::Create(isolate, val.value()).ToV8();
+}
+
+bool Converter<mojo::Handle>::FromV8(v8::Isolate* isolate,
+                                     v8::Handle<v8::Value> val,
+                                     mojo::Handle* out) {
+  if (val->IsNull()) {
+    *out = mojo::Handle();
+    return true;
+  }
+
+  gin::Handle<HandleWrapper> handle;
+  if (!Converter<gin::Handle<HandleWrapper> >::FromV8(isolate, val, &handle))
+    return false;
+
+  *out = handle->get();
+  return true;
+}
+
+}  // namespace gin
diff --git a/mojo/bindings/js/handle.h b/mojo/bindings/js/handle.h
new file mode 100644
index 0000000..e0f00dd
--- /dev/null
+++ b/mojo/bindings/js/handle.h
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium 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_BINDINGS_JS_HANDLE_H_
+#define MOJO_BINDINGS_JS_HANDLE_H_
+
+#include "base/observer_list.h"
+#include "gin/converter.h"
+#include "gin/handle.h"
+#include "gin/wrappable.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace gin {
+class HandleCloseObserver;
+
+// Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle
+// is Closed when its JS object is garbage collected.
+class HandleWrapper : public gin::Wrappable<HandleWrapper> {
+ public:
+  static gin::WrapperInfo kWrapperInfo;
+
+  static gin::Handle<HandleWrapper> Create(v8::Isolate* isolate,
+                                           MojoHandle handle) {
+    return gin::CreateHandle(isolate, new HandleWrapper(handle));
+  }
+
+  mojo::Handle get() const { return handle_.get(); }
+  mojo::Handle release() { return handle_.release(); }
+  void Close();
+
+  void AddCloseObserver(HandleCloseObserver* observer);
+  void RemoveCloseObserver(HandleCloseObserver* observer);
+
+ protected:
+  HandleWrapper(MojoHandle handle);
+  virtual ~HandleWrapper();
+  void NotifyCloseObservers();
+
+  mojo::ScopedHandle handle_;
+  ObserverList<HandleCloseObserver> close_observers_;
+};
+
+// Note: It's important to use this converter rather than the one for
+// MojoHandle, since that will do a simple int32 conversion. It's unfortunate
+// there's no way to prevent against accidental use.
+// TODO(mpcomplete): define converters for all Handle subtypes.
+template<>
+struct Converter<mojo::Handle> {
+  static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+                                    const mojo::Handle& val);
+  static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+                     mojo::Handle* out);
+};
+
+// We need to specialize the normal gin::Handle converter in order to handle
+// converting |null| to a wrapper for an empty mojo::Handle.
+template<>
+struct Converter<gin::Handle<gin::HandleWrapper> > {
+  static v8::Handle<v8::Value> ToV8(
+        v8::Isolate* isolate, const gin::Handle<gin::HandleWrapper>& val) {
+    return val.ToV8();
+  }
+
+  static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+                     gin::Handle<gin::HandleWrapper>* out) {
+    if (val->IsNull()) {
+      *out = HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID);
+      return true;
+    }
+
+    gin::HandleWrapper* object = NULL;
+    if (!Converter<gin::HandleWrapper*>::FromV8(isolate, val, &object)) {
+      return false;
+    }
+    *out = gin::Handle<gin::HandleWrapper>(val, object);
+    return true;
+  }
+};
+
+}  // namespace gin
+
+#endif  // MOJO_BINDINGS_JS_HANDLE_H_
diff --git a/mojo/bindings/js/handle_close_observer.h b/mojo/bindings/js/handle_close_observer.h
new file mode 100644
index 0000000..854603e
--- /dev/null
+++ b/mojo/bindings/js/handle_close_observer.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
+#define MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
+
+namespace gin {
+
+class HandleCloseObserver {
+ public:
+  virtual void OnWillCloseHandle() = 0;
+
+ protected:
+  virtual ~HandleCloseObserver() {}
+};
+
+}  // namespace gin
+
+#endif  // MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
diff --git a/mojo/bindings/js/support.cc b/mojo/bindings/js/support.cc
new file mode 100644
index 0000000..1c82f17
--- /dev/null
+++ b/mojo/bindings/js/support.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/bindings/js/support.h"
+
+#include "base/bind.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/bindings/js/waiting_callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+WaitingCallback* AsyncWait(const gin::Arguments& args,
+                           gin::Handle<gin::HandleWrapper> handle,
+                           MojoHandleSignals signals,
+                           v8::Handle<v8::Function> callback) {
+  return WaitingCallback::Create(args.isolate(), callback, handle, signals)
+             .get();
+}
+
+void CancelWait(WaitingCallback* waiting_callback) {
+  waiting_callback->Cancel();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Support::kModuleName[] = "mojo/public/js/bindings/support";
+
+v8::Local<v8::Value> Support::GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+      &g_wrapper_info);
+
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+                .SetMethod("asyncWait", AsyncWait)
+                .SetMethod("cancelWait", CancelWait)
+                .Build();
+
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/support.h b/mojo/bindings/js/support.h
new file mode 100644
index 0000000..0f6eb07
--- /dev/null
+++ b/mojo/bindings/js/support.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_BINDINGS_JS_SUPPORT_H_
+#define MOJO_BINDINGS_JS_SUPPORT_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+class Support {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_BINDINGS_JS_SUPPORT_H_
diff --git a/mojo/bindings/js/tests/BUILD.gn b/mojo/bindings/js/tests/BUILD.gn
new file mode 100644
index 0000000..c85e68f
--- /dev/null
+++ b/mojo/bindings/js/tests/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_js_unittests
+test("mojo_js_unittests") {
+  deps = [
+    "//gin:gin_test",
+    "//mojo/bindings/js",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+  ]
+
+  sources = [ "run_js_tests.cc" ]
+}
diff --git a/mojo/bindings/js/tests/DEPS b/mojo/bindings/js/tests/DEPS
new file mode 100644
index 0000000..2424ea1
--- /dev/null
+++ b/mojo/bindings/js/tests/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+base",
+  "+gin",
+  "+v8",
+  "+mojo/bindings/js/core.h",
+  "+mojo/bindings/js/support.h",
+]
diff --git a/mojo/bindings/js/tests/run_js_tests.cc b/mojo/bindings/js/tests/run_js_tests.cc
new file mode 100644
index 0000000..54d9806
--- /dev/null
+++ b/mojo/bindings/js/tests/run_js_tests.cc
@@ -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.
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/bindings/js/core.h"
+#include "mojo/bindings/js/support.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+namespace {
+
+class TestRunnerDelegate : public gin::FileRunnerDelegate {
+ public:
+  TestRunnerDelegate() {
+    AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+    AddBuiltinModule(Core::kModuleName, Core::GetModule);
+    AddBuiltinModule(Support::kModuleName, Support::GetModule);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test, bool run_until_idle) {
+  Environment env;
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("mojo")
+             .AppendASCII("public")
+             .AppendASCII("js")
+             .AppendASCII("bindings")
+             .AppendASCII(test);
+  TestRunnerDelegate delegate;
+  gin::RunTestFromFile(path, &delegate, run_until_idle);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+TEST(JSTest, core) {
+  RunTest("core_unittests.js", true);
+}
+
+TEST(JSTest, codec) {
+  RunTest("codec_unittests.js", true);
+}
+
+TEST(JSTest, struct) {
+  RunTest("struct_unittests.js", true);
+}
+
+TEST(JSTest, validation) {
+  RunTest("validation_unittests.js", true);
+}
+
+}  // namespace
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/waiting_callback.cc b/mojo/bindings/js/waiting_callback.cc
new file mode 100644
index 0000000..a88f956
--- /dev/null
+++ b/mojo/bindings/js/waiting_callback.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/bindings/js/waiting_callback.h"
+
+#include "gin/per_context_data.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) {
+  return gin::StringToSymbol(isolate, "::mojo::js::WaitingCallback");
+}
+
+}  // namespace
+
+gin::WrapperInfo WaitingCallback::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+// static
+gin::Handle<WaitingCallback> WaitingCallback::Create(
+    v8::Isolate* isolate,
+    v8::Handle<v8::Function> callback,
+    gin::Handle<gin::HandleWrapper> handle_wrapper,
+    MojoHandleSignals signals) {
+  gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle(
+      isolate, new WaitingCallback(isolate, callback, handle_wrapper));
+  waiting_callback->wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait(
+      handle_wrapper->get().value(),
+      signals,
+      MOJO_DEADLINE_INDEFINITE,
+      &WaitingCallback::CallOnHandleReady,
+      waiting_callback.get());
+  return waiting_callback;
+}
+
+void WaitingCallback::Cancel() {
+  if (!wait_id_)
+    return;
+
+  handle_wrapper_->RemoveCloseObserver(this);
+  handle_wrapper_ = NULL;
+  Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_);
+  wait_id_ = 0;
+}
+
+WaitingCallback::WaitingCallback(v8::Isolate* isolate,
+                                 v8::Handle<v8::Function> callback,
+                                 gin::Handle<gin::HandleWrapper> handle_wrapper)
+    : wait_id_(0), handle_wrapper_(handle_wrapper.get()) {
+  handle_wrapper_->AddCloseObserver(this);
+  v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+  runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+  GetWrapper(isolate)->SetHiddenValue(GetHiddenPropertyName(isolate), callback);
+}
+
+WaitingCallback::~WaitingCallback() {
+  Cancel();
+}
+
+// static
+void WaitingCallback::CallOnHandleReady(void* closure, MojoResult result) {
+  static_cast<WaitingCallback*>(closure)->OnHandleReady(result);
+}
+
+void WaitingCallback::OnHandleReady(MojoResult result) {
+  wait_id_ = 0;
+  handle_wrapper_->RemoveCloseObserver(this);
+  handle_wrapper_ = NULL;
+
+  if (!runner_)
+    return;
+
+  gin::Runner::Scope scope(runner_.get());
+  v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+  v8::Handle<v8::Value> hidden_value =
+      GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate));
+  v8::Handle<v8::Function> callback;
+  CHECK(gin::ConvertFromV8(isolate, hidden_value, &callback));
+
+  v8::Handle<v8::Value> args[] = { gin::ConvertToV8(isolate, result) };
+  runner_->Call(callback, runner_->global(), 1, args);
+}
+
+void WaitingCallback::OnWillCloseHandle() {
+  Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_);
+  OnHandleReady(MOJO_RESULT_INVALID_ARGUMENT);
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/waiting_callback.h b/mojo/bindings/js/waiting_callback.h
new file mode 100644
index 0000000..b3a4fda
--- /dev/null
+++ b/mojo/bindings/js/waiting_callback.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_BINDINGS_JS_WAITING_CALLBACK_H_
+#define MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
+
+#include "gin/handle.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/bindings/js/handle_close_observer.h"
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace js {
+
+class WaitingCallback : public gin::Wrappable<WaitingCallback>,
+                        public gin::HandleCloseObserver {
+ public:
+  static gin::WrapperInfo kWrapperInfo;
+
+  // Creates a new WaitingCallback.
+  static gin::Handle<WaitingCallback> Create(
+      v8::Isolate* isolate,
+      v8::Handle<v8::Function> callback,
+      gin::Handle<gin::HandleWrapper> handle_wrapper,
+      MojoHandleSignals signals);
+
+  // Cancels the callback. Does nothing if a callback is not pending. This is
+  // implicitly invoked from the destructor but can be explicitly invoked as
+  // necessary.
+  void Cancel();
+
+ private:
+  WaitingCallback(v8::Isolate* isolate,
+                  v8::Handle<v8::Function> callback,
+                  gin::Handle<gin::HandleWrapper> handle_wrapper);
+  virtual ~WaitingCallback();
+
+  // Callback from MojoAsyncWaiter. |closure| is the WaitingCallback.
+  static void CallOnHandleReady(void* closure, MojoResult result);
+
+  // Invoked from CallOnHandleReady() (CallOnHandleReady() must be static).
+  void OnHandleReady(MojoResult result);
+
+  // Invoked by the HandleWrapper if the handle is closed while this wait is
+  // still in progress.
+  virtual void OnWillCloseHandle() override;
+
+  base::WeakPtr<gin::Runner> runner_;
+  MojoAsyncWaitID wait_id_;
+
+  gin::HandleWrapper* handle_wrapper_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitingCallback);
+};
+
+}  // namespace js
+}  // namespace mojo
+
+#endif  // MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
diff --git a/mojo/build/package_app.gypi b/mojo/build/package_app.gypi
new file mode 100644
index 0000000..00c6785
--- /dev/null
+++ b/mojo/build/package_app.gypi
@@ -0,0 +1,31 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'type': 'none',
+  'dependencies': [
+    '<(app_name)',
+  ],
+  'conditions': [
+    ['OS=="android"', {
+      'variables': {
+        'mojo_app_dir': '<(PRODUCT_DIR)/apps',
+        'source_binary': '<(PRODUCT_DIR)/<(SHARED_LIB_PREFIX)<(app_name)<(SHARED_LIB_SUFFIX)',
+        'target_binary': '<(mojo_app_dir)/<(SHARED_LIB_PREFIX)<(app_name)<(SHARED_LIB_SUFFIX)',
+      },
+      'actions': [{
+        'action_name': 'strip',
+        'inputs': [ '<(source_binary)', ],
+        'outputs': [ '<(target_binary)', ],
+        'action': [
+          '<(android_strip)',
+          '<@(_inputs)',
+          '--strip-unneeded',
+          '-o',
+          '<@(_outputs)',
+        ],
+      }],
+    }],
+  ],
+}
diff --git a/mojo/cc/BUILD.gn b/mojo/cc/BUILD.gn
new file mode 100644
index 0000000..af2a8e5
--- /dev/null
+++ b/mojo/cc/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP version: mojo/mojo.gyp:mojo_cc_support
+source_set("cc") {
+  deps = [
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//skia",
+    "//gpu/command_buffer/client:gles2_implementation",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/surfaces",
+    "//mojo/services/public/interfaces/surfaces",
+  ]
+
+  sources = [
+    "context_provider_mojo.cc",
+    "context_provider_mojo.h",
+    "output_surface_mojo.cc",
+    "output_surface_mojo.h",
+  ]
+}
diff --git a/mojo/cc/DEPS b/mojo/cc/DEPS
new file mode 100644
index 0000000..f789994
--- /dev/null
+++ b/mojo/cc/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+]
diff --git a/mojo/cc/context_provider_mojo.cc b/mojo/cc/context_provider_mojo.cc
new file mode 100644
index 0000000..16c6f8c
--- /dev/null
+++ b/mojo/cc/context_provider_mojo.cc
@@ -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.
+
+#include "mojo/cc/context_provider_mojo.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+
+ContextProviderMojo::ContextProviderMojo(
+    ScopedMessagePipeHandle command_buffer_handle)
+    : command_buffer_handle_(command_buffer_handle.Pass()),
+      context_lost_(false) {
+}
+
+bool ContextProviderMojo::BindToCurrentThread() {
+  DCHECK(command_buffer_handle_.is_valid());
+  context_ = MojoGLES2CreateContext(command_buffer_handle_.release().value(),
+                                    &ContextLostThunk,
+                                    this,
+                                    Environment::GetDefaultAsyncWaiter());
+  return !!context_;
+}
+
+gpu::gles2::GLES2Interface* ContextProviderMojo::ContextGL() {
+  if (!context_)
+    return NULL;
+  return static_cast<gpu::gles2::GLES2Interface*>(
+      MojoGLES2GetGLES2Interface(context_));
+}
+
+gpu::ContextSupport* ContextProviderMojo::ContextSupport() {
+  if (!context_)
+    return NULL;
+  return static_cast<gpu::ContextSupport*>(
+      MojoGLES2GetContextSupport(context_));
+}
+
+class GrContext* ContextProviderMojo::GrContext() { return NULL; }
+
+cc::ContextProvider::Capabilities ContextProviderMojo::ContextCapabilities() {
+  return capabilities_;
+}
+
+bool ContextProviderMojo::IsContextLost() {
+  return context_lost_;
+}
+bool ContextProviderMojo::DestroyedOnMainThread() { return !context_; }
+
+ContextProviderMojo::~ContextProviderMojo() {
+  if (context_)
+    MojoGLES2DestroyContext(context_);
+}
+
+void ContextProviderMojo::ContextLost() {
+  context_lost_ = true;
+}
+
+}  // namespace mojo
diff --git a/mojo/cc/context_provider_mojo.h b/mojo/cc/context_provider_mojo.h
new file mode 100644
index 0000000..294bd5d
--- /dev/null
+++ b/mojo/cc/context_provider_mojo.h
@@ -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.
+
+#ifndef MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
+#define MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
+
+#include "base/macros.h"
+#include "cc/output/context_provider.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class ContextProviderMojo : public cc::ContextProvider {
+ public:
+  explicit ContextProviderMojo(ScopedMessagePipeHandle command_buffer_handle);
+
+  // cc::ContextProvider implementation.
+  virtual bool BindToCurrentThread() override;
+  virtual gpu::gles2::GLES2Interface* ContextGL() override;
+  virtual gpu::ContextSupport* ContextSupport() override;
+  virtual class GrContext* GrContext() override;
+  virtual Capabilities ContextCapabilities() override;
+  virtual bool IsContextLost() override;
+  virtual void VerifyContexts() override {}
+  virtual void DeleteCachedResources() override {}
+  virtual bool DestroyedOnMainThread() override;
+  virtual void SetLostContextCallback(
+      const LostContextCallback& lost_context_callback) override {}
+  virtual void SetMemoryPolicyChangedCallback(
+      const MemoryPolicyChangedCallback& memory_policy_changed_callback)
+      override {}
+
+ protected:
+  friend class base::RefCountedThreadSafe<ContextProviderMojo>;
+  virtual ~ContextProviderMojo();
+
+ private:
+  static void ContextLostThunk(void* closure) {
+    static_cast<ContextProviderMojo*>(closure)->ContextLost();
+  }
+  void ContextLost();
+
+  cc::ContextProvider::Capabilities capabilities_;
+  ScopedMessagePipeHandle command_buffer_handle_;
+  MojoGLES2Context context_;
+  bool context_lost_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextProviderMojo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
diff --git a/mojo/cc/output_surface_mojo.cc b/mojo/cc/output_surface_mojo.cc
new file mode 100644
index 0000000..fbd2c9e
--- /dev/null
+++ b/mojo/cc/output_surface_mojo.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/cc/output_surface_mojo.h"
+
+#include "cc/output/compositor_frame.h"
+#include "cc/output/output_surface_client.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+
+namespace mojo {
+
+OutputSurfaceMojo::OutputSurfaceMojo(
+    OutputSurfaceMojoClient* client,
+    const scoped_refptr<cc::ContextProvider>& context_provider,
+    SurfacePtr surface,
+    uint32_t id_namespace)
+    : cc::OutputSurface(context_provider),
+      output_surface_mojo_client_(client),
+      surface_(surface.Pass()),
+      id_allocator_(id_namespace) {
+  capabilities_.delegated_rendering = true;
+  capabilities_.max_frames_pending = 1;
+}
+
+OutputSurfaceMojo::~OutputSurfaceMojo() {
+}
+
+void OutputSurfaceMojo::ReturnResources(Array<ReturnedResourcePtr> resources) {
+}
+
+bool OutputSurfaceMojo::BindToClient(cc::OutputSurfaceClient* client) {
+  surface_.set_client(this);
+  return cc::OutputSurface::BindToClient(client);
+}
+
+void OutputSurfaceMojo::SwapBuffers(cc::CompositorFrame* frame) {
+  gfx::Size frame_size =
+      frame->delegated_frame_data->render_pass_list.back()->output_rect.size();
+  if (frame_size != surface_size_) {
+    if (!surface_id_.is_null()) {
+      surface_->DestroySurface(SurfaceId::From(surface_id_));
+    }
+    surface_id_ = id_allocator_.GenerateId();
+    surface_->CreateSurface(SurfaceId::From(surface_id_),
+                            Size::From(frame_size));
+    output_surface_mojo_client_->DidCreateSurface(surface_id_);
+    surface_size_ = frame_size;
+  }
+
+  surface_->SubmitFrame(SurfaceId::From(surface_id_), Frame::From(*frame));
+
+  client_->DidSwapBuffers();
+  client_->DidSwapBuffersComplete();
+}
+
+}  // namespace mojo
diff --git a/mojo/cc/output_surface_mojo.h b/mojo/cc/output_surface_mojo.h
new file mode 100644
index 0000000..b8f2bab
--- /dev/null
+++ b/mojo/cc/output_surface_mojo.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CC_OUTPUT_SURFACE_MOJO_H_
+#define MOJO_CC_OUTPUT_SURFACE_MOJO_H_
+
+#include "base/macros.h"
+#include "cc/output/output_surface.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+
+namespace mojo {
+
+class OutputSurfaceMojoClient {
+ public:
+  virtual ~OutputSurfaceMojoClient() {}
+
+  virtual void DidCreateSurface(cc::SurfaceId id) = 0;
+};
+
+class OutputSurfaceMojo : public cc::OutputSurface, public SurfaceClient {
+ public:
+  OutputSurfaceMojo(OutputSurfaceMojoClient* client,
+                    const scoped_refptr<cc::ContextProvider>& context_provider,
+                    SurfacePtr surface,
+                    uint32_t id_namespace);
+
+  // SurfaceClient implementation.
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) override;
+
+  // cc::OutputSurface implementation.
+  virtual void SwapBuffers(cc::CompositorFrame* frame) override;
+  virtual bool BindToClient(cc::OutputSurfaceClient* client) override;
+
+ protected:
+  virtual ~OutputSurfaceMojo();
+
+ private:
+  OutputSurfaceMojoClient* output_surface_mojo_client_;
+  SurfacePtr surface_;
+  cc::SurfaceIdAllocator id_allocator_;
+  cc::SurfaceId surface_id_;
+  gfx::Size surface_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(OutputSurfaceMojo);
+};
+}
+
+#endif  // MOJO_CC_OUTPUT_SURFACE_MOJO_H_
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
new file mode 100644
index 0000000..a825189
--- /dev/null
+++ b/mojo/common/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_lib
+component("common") {
+  output_name = "mojo_common_lib"
+
+  sources = [
+    "common_type_converters.cc",
+    "common_type_converters.h",
+    "data_pipe_utils.cc",
+    "data_pipe_utils.h",
+    "handle_watcher.cc",
+    "handle_watcher.h",
+    "message_pump_mojo.cc",
+    "message_pump_mojo.h",
+    "message_pump_mojo_handler.h",
+    "time_helper.cc",
+    "time_helper.h",
+  ]
+
+  defines = [ "MOJO_COMMON_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//mojo/public/c/system:for_component",
+    "//url",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_unittests
+test("mojo_common_unittests") {
+  deps = [
+    ":common",
+    "//base",
+    "//base:message_loop_tests",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+    "//url",
+  ]
+
+  sources = [
+    "common_type_converters_unittest.cc",
+    "handle_watcher_unittest.cc",
+    "message_pump_mojo_unittest.cc",
+  ]
+}
diff --git a/mojo/common/DEPS b/mojo/common/DEPS
new file mode 100644
index 0000000..e8ac428
--- /dev/null
+++ b/mojo/common/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  # common must not depend on embedder.
+  "-mojo",
+  "+mojo/common",
+  "+mojo/public",
+]
diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc
new file mode 100644
index 0000000..114b409
--- /dev/null
+++ b/mojo/common/common_type_converters.cc
@@ -0,0 +1,65 @@
+// 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/common/common_type_converters.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+// static
+String TypeConverter<String, base::StringPiece>::Convert(
+    const base::StringPiece& input) {
+  if (input.empty()) {
+    char c = 0;
+    return String(&c, 0);
+  }
+  return String(input.data(), input.size());
+}
+// static
+base::StringPiece TypeConverter<base::StringPiece, String>::Convert(
+    const String& input) {
+  return input.get();
+}
+
+// static
+String TypeConverter<String, base::string16>::Convert(
+    const base::string16& input) {
+  return TypeConverter<String, base::StringPiece>::Convert(
+      base::UTF16ToUTF8(input));
+}
+// static
+base::string16 TypeConverter<base::string16, String>::Convert(
+    const String& input) {
+  return base::UTF8ToUTF16(input.To<base::StringPiece>());
+}
+
+String TypeConverter<String, GURL>::Convert(const GURL& input) {
+  return String(input.spec());
+}
+
+GURL TypeConverter<GURL, String>::Convert(const String& input) {
+  return GURL(input.get());
+}
+
+std::string TypeConverter<std::string, Array<uint8_t> >::Convert(
+    const Array<uint8_t>& input) {
+  if (input.is_null())
+    return std::string();
+
+  return std::string(reinterpret_cast<const char*>(&input.front()),
+                     input.size());
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, std::string>::Convert(
+    const std::string& input) {
+  Array<uint8_t> result(input.size());
+  memcpy(&result.front(), input.c_str(), input.size());
+  return result.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h
new file mode 100644
index 0000000..7b0260a
--- /dev/null
+++ b/mojo/common/common_type_converters.h
@@ -0,0 +1,65 @@
+// 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_COMMON_COMMON_TYPE_CONVERTERS_H_
+#define MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+class GURL;
+
+namespace mojo {
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<String, base::StringPiece> {
+  static String Convert(const base::StringPiece& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::StringPiece, String> {
+  static base::StringPiece Convert(const String& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<String, base::string16> {
+  static String Convert(const base::string16& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::string16, String> {
+  static base::string16 Convert(const String& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<String, GURL> {
+  static String Convert(const GURL& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<GURL, String> {
+  static GURL Convert(const String& input);
+};
+
+// TODO(erg): In the very long term, we will want to remove conversion between
+// std::strings and arrays of unsigned bytes. However, there is too much code
+// across chrome which uses std::string as a bag of bytes that we probably
+// don't want to roll this function at each callsite.
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<std::string, Array<uint8_t> > {
+  static std::string Convert(const Array<uint8_t>& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, std::string> {
+  static Array<uint8_t> Convert(const std::string& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
diff --git a/mojo/common/common_type_converters_unittest.cc b/mojo/common/common_type_converters_unittest.cc
new file mode 100644
index 0000000..f97be0a
--- /dev/null
+++ b/mojo/common/common_type_converters_unittest.cc
@@ -0,0 +1,110 @@
+// 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/common/common_type_converters.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+namespace {
+
+void ExpectEqualsStringPiece(const std::string& expected,
+                             const base::StringPiece& str) {
+  EXPECT_EQ(expected, str.as_string());
+}
+
+void ExpectEqualsMojoString(const std::string& expected,
+                            const String& str) {
+  EXPECT_EQ(expected, str.get());
+}
+
+void ExpectEqualsString16(const base::string16& expected,
+                          const base::string16& actual) {
+  EXPECT_EQ(expected, actual);
+}
+
+void ExpectEqualsMojoString(const base::string16& expected,
+                            const String& str) {
+  EXPECT_EQ(expected, str.To<base::string16>());
+}
+
+}  // namespace
+
+TEST(CommonTypeConvertersTest, StringPiece) {
+  std::string kText("hello world");
+
+  base::StringPiece string_piece(kText);
+  String mojo_string(String::From(string_piece));
+
+  ExpectEqualsMojoString(kText, mojo_string);
+  ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+  // Test implicit construction and conversion:
+  ExpectEqualsMojoString(kText, String::From(string_piece));
+  ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+  // Test null String:
+  base::StringPiece empty_string_piece = String().To<base::StringPiece>();
+  EXPECT_TRUE(empty_string_piece.empty());
+}
+
+TEST(CommonTypeConvertersTest, String16) {
+  const base::string16 string16(base::ASCIIToUTF16("hello world"));
+  const String mojo_string(String::From(string16));
+
+  ExpectEqualsMojoString(string16, mojo_string);
+  EXPECT_EQ(string16, mojo_string.To<base::string16>());
+
+  // Test implicit construction and conversion:
+  ExpectEqualsMojoString(string16, String::From(string16));
+  ExpectEqualsString16(string16, mojo_string.To<base::string16>());
+
+  // Test empty string conversion.
+  ExpectEqualsMojoString(base::string16(), String::From(base::string16()));
+}
+
+TEST(CommonTypeConvertersTest, URL) {
+  GURL url("mojo:foo");
+  String mojo_string(String::From(url));
+
+  ASSERT_EQ(url.spec(), mojo_string);
+  EXPECT_EQ(url.spec(), mojo_string.To<GURL>().spec());
+  EXPECT_EQ(url.spec(), String::From(url));
+
+  GURL invalid = String().To<GURL>();
+  ASSERT_TRUE(invalid.spec().empty());
+
+  String string_from_invalid = String::From(invalid);
+  EXPECT_FALSE(string_from_invalid.is_null());
+  ASSERT_EQ(0U, string_from_invalid.size());
+}
+
+TEST(CommonTypeConvertersTest, ArrayUint8ToStdString) {
+  Array<uint8_t> data(4);
+  data[0] = 'd';
+  data[1] = 'a';
+  data[2] = 't';
+  data[3] = 'a';
+
+  EXPECT_EQ("data", data.To<std::string>());
+}
+
+TEST(CommonTypeConvertersTest, StdStringToArrayUint8) {
+  std::string input("data");
+  Array<uint8_t> data = Array<uint8_t>::From(input);
+
+  ASSERT_EQ(4ul, data.size());
+  EXPECT_EQ('d', data[0]);
+  EXPECT_EQ('a', data[1]);
+  EXPECT_EQ('t', data[2]);
+  EXPECT_EQ('a', data[3]);
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/data_pipe_utils.cc b/mojo/common/data_pipe_utils.cc
new file mode 100644
index 0000000..428a589
--- /dev/null
+++ b/mojo/common/data_pipe_utils.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/data_pipe_utils.h"
+
+#include <stdio.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner_util.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+bool BlockingCopyHelper(ScopedDataPipeConsumerHandle source,
+    const base::Callback<size_t(const void*, uint32_t)>& write_bytes) {
+  for (;;) {
+    const void* buffer;
+    uint32_t num_bytes;
+    MojoResult result = BeginReadDataRaw(
+        source.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_OK) {
+      size_t bytes_written = write_bytes.Run(buffer, num_bytes);
+      result = EndReadDataRaw(source.get(), num_bytes);
+      if (bytes_written < num_bytes || result != MOJO_RESULT_OK)
+        return false;
+    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+      result = Wait(source.get(),
+                    MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE);
+      if (result != MOJO_RESULT_OK) {
+        // If the producer handle was closed, then treat as EOF.
+        return result == MOJO_RESULT_FAILED_PRECONDITION;
+      }
+    } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+      // If the producer handle was closed, then treat as EOF.
+      return true;
+    } else {
+      // Some other error occurred.
+      break;
+    }
+  }
+
+  return false;
+}
+
+size_t CopyToStringHelper(
+    std::string* result, const void* buffer, uint32_t num_bytes) {
+  result->append(static_cast<const char*>(buffer), num_bytes);
+  return num_bytes;
+}
+
+size_t CopyToFileHelper(FILE* fp, const void* buffer, uint32_t num_bytes) {
+  return fwrite(buffer, 1, num_bytes, fp);
+}
+
+} // namespace
+
+
+// TODO(hansmuller): Add a max_size parameter.
+bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
+                          std::string* result) {
+  CHECK(result);
+  result->clear();
+  return BlockingCopyHelper(
+      source.Pass(), base::Bind(&CopyToStringHelper, result));
+}
+
+bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+                        const base::FilePath& destination) {
+  base::ScopedFILE fp(base::OpenFile(destination, "wb"));
+  if (!fp)
+    return false;
+  return BlockingCopyHelper(
+      source.Pass(), base::Bind(&CopyToFileHelper, fp.get()));
+}
+
+void CopyToFile(ScopedDataPipeConsumerHandle source,
+                const base::FilePath& destination,
+                base::TaskRunner* task_runner,
+                const base::Callback<void(bool)>& callback) {
+  base::PostTaskAndReplyWithResult(
+      task_runner,
+      FROM_HERE,
+      base::Bind(&BlockingCopyToFile, base::Passed(&source), destination),
+      callback);
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/data_pipe_utils.h b/mojo/common/data_pipe_utils.h
new file mode 100644
index 0000000..dc2197b
--- /dev/null
+++ b/mojo/common/data_pipe_utils.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_SHELL_DATA_PIPE_UTILS_H_
+#define MOJO_SHELL_DATA_PIPE_UTILS_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+class TaskRunner;
+}
+
+namespace mojo {
+namespace common {
+
+// Asynchronously copies data from source to the destination file. The given
+// |callback| is run upon completion. File writes will be scheduled to the
+// given |task_runner|.
+void MOJO_COMMON_EXPORT CopyToFile(
+    ScopedDataPipeConsumerHandle source,
+    const base::FilePath& destination,
+    base::TaskRunner* task_runner,
+    const base::Callback<void(bool /*success*/)>& callback);
+
+// Copies the data from |source| into |contents| and returns true on success and
+// false on error.  In case of I/O error, |contents| holds the data that could
+// be read from source before the error occurred.
+bool MOJO_COMMON_EXPORT BlockingCopyToString(
+    ScopedDataPipeConsumerHandle source,
+    std::string* contents);
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_DATA_PIPE_UTILS_H_
diff --git a/mojo/common/handle_watcher.cc b/mojo/common/handle_watcher.cc
new file mode 100644
index 0000000..e8df765
--- /dev/null
+++ b/mojo/common/handle_watcher.cc
@@ -0,0 +1,475 @@
+// 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/common/handle_watcher.h"
+
+#include <map>
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/common/message_pump_mojo_handler.h"
+#include "mojo/common/time_helper.h"
+
+namespace mojo {
+namespace common {
+
+typedef int WatcherID;
+
+namespace {
+
+const char kWatcherThreadName[] = "handle-watcher-thread";
+
+base::TimeTicks MojoDeadlineToTimeTicks(MojoDeadline deadline) {
+  return deadline == MOJO_DEADLINE_INDEFINITE ? base::TimeTicks() :
+      internal::NowTicks() + base::TimeDelta::FromMicroseconds(deadline);
+}
+
+// Tracks the data for a single call to Start().
+struct WatchData {
+  WatchData()
+      : id(0),
+        handle_signals(MOJO_HANDLE_SIGNAL_NONE),
+        message_loop(NULL) {}
+
+  WatcherID id;
+  Handle handle;
+  MojoHandleSignals handle_signals;
+  base::TimeTicks deadline;
+  base::Callback<void(MojoResult)> callback;
+  scoped_refptr<base::MessageLoopProxy> message_loop;
+};
+
+// WatcherBackend --------------------------------------------------------------
+
+// WatcherBackend is responsible for managing the requests and interacting with
+// MessagePumpMojo. All access (outside of creation/destruction) is done on the
+// thread WatcherThreadManager creates.
+class WatcherBackend : public MessagePumpMojoHandler {
+ public:
+  WatcherBackend();
+  virtual ~WatcherBackend();
+
+  void StartWatching(const WatchData& data);
+
+  // Cancels a previously scheduled request to start a watch.
+  void StopWatching(WatcherID watcher_id);
+
+ private:
+  typedef std::map<Handle, WatchData> HandleToWatchDataMap;
+
+  // Invoked when a handle needs to be removed and notified.
+  void RemoveAndNotify(const Handle& handle, MojoResult result);
+
+  // Searches through |handle_to_data_| for |watcher_id|. Returns true if found
+  // and sets |handle| to the Handle. Returns false if not a known id.
+  bool GetMojoHandleByWatcherID(WatcherID watcher_id, Handle* handle) const;
+
+  // MessagePumpMojoHandler overrides:
+  virtual void OnHandleReady(const Handle& handle) override;
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override;
+
+  // Maps from assigned id to WatchData.
+  HandleToWatchDataMap handle_to_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatcherBackend);
+};
+
+WatcherBackend::WatcherBackend() {
+}
+
+WatcherBackend::~WatcherBackend() {
+}
+
+void WatcherBackend::StartWatching(const WatchData& data) {
+  RemoveAndNotify(data.handle, MOJO_RESULT_CANCELLED);
+
+  DCHECK_EQ(0u, handle_to_data_.count(data.handle));
+
+  handle_to_data_[data.handle] = data;
+  MessagePumpMojo::current()->AddHandler(this, data.handle,
+                                         data.handle_signals,
+                                         data.deadline);
+}
+
+void WatcherBackend::StopWatching(WatcherID watcher_id) {
+  // Because of the thread hop it is entirely possible to get here and not
+  // have a valid handle registered for |watcher_id|.
+  Handle handle;
+  if (GetMojoHandleByWatcherID(watcher_id, &handle)) {
+    handle_to_data_.erase(handle);
+    MessagePumpMojo::current()->RemoveHandler(handle);
+  }
+}
+
+void WatcherBackend::RemoveAndNotify(const Handle& handle,
+                                     MojoResult result) {
+  if (handle_to_data_.count(handle) == 0)
+    return;
+
+  const WatchData data(handle_to_data_[handle]);
+  handle_to_data_.erase(handle);
+  MessagePumpMojo::current()->RemoveHandler(handle);
+  data.message_loop->PostTask(FROM_HERE, base::Bind(data.callback, result));
+}
+
+bool WatcherBackend::GetMojoHandleByWatcherID(WatcherID watcher_id,
+                                              Handle* handle) const {
+  for (HandleToWatchDataMap::const_iterator i = handle_to_data_.begin();
+       i != handle_to_data_.end(); ++i) {
+    if (i->second.id == watcher_id) {
+      *handle = i->second.handle;
+      return true;
+    }
+  }
+  return false;
+}
+
+void WatcherBackend::OnHandleReady(const Handle& handle) {
+  RemoveAndNotify(handle, MOJO_RESULT_OK);
+}
+
+void WatcherBackend::OnHandleError(const Handle& handle, MojoResult result) {
+  RemoveAndNotify(handle, result);
+}
+
+// WatcherThreadManager --------------------------------------------------------
+
+// WatcherThreadManager manages the background thread that listens for handles
+// to be ready. All requests are handled by WatcherBackend.
+}  // namespace
+
+class WatcherThreadManager {
+ public:
+  ~WatcherThreadManager();
+
+  // Returns the shared instance.
+  static WatcherThreadManager* GetInstance();
+
+  // Starts watching the requested handle. Returns a unique ID that is used to
+  // stop watching the handle. When the handle is ready |callback| is notified
+  // on the thread StartWatching() was invoked on.
+  // This may be invoked on any thread.
+  WatcherID StartWatching(const Handle& handle,
+                          MojoHandleSignals handle_signals,
+                          base::TimeTicks deadline,
+                          const base::Callback<void(MojoResult)>& callback);
+
+  // Stops watching a handle.
+  // This may be invoked on any thread.
+  void StopWatching(WatcherID watcher_id);
+
+ private:
+  enum RequestType {
+    REQUEST_START,
+    REQUEST_STOP,
+  };
+
+  // See description of |requests_| for details.
+  struct RequestData {
+    RequestData() : type(REQUEST_START), stop_id(0), stop_event(NULL) {}
+
+    RequestType type;
+    WatchData start_data;
+    WatcherID stop_id;
+    base::WaitableEvent* stop_event;
+  };
+
+  typedef std::vector<RequestData> Requests;
+
+  friend struct DefaultSingletonTraits<WatcherThreadManager>;
+
+  WatcherThreadManager();
+
+  // Schedules a request on the background thread. See |requests_| for details.
+  void AddRequest(const RequestData& data);
+
+  // Processes requests added to |requests_|. This is invoked on the backend
+  // thread.
+  void ProcessRequestsOnBackendThread();
+
+  base::Thread thread_;
+
+  base::AtomicSequenceNumber watcher_id_generator_;
+
+  WatcherBackend backend_;
+
+  // Protects |requests_|.
+  base::Lock lock_;
+
+  // Start/Stop result in adding a RequestData to |requests_| (protected by
+  // |lock_|). When the background thread wakes up it processes the requests.
+  Requests requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatcherThreadManager);
+};
+
+WatcherThreadManager::~WatcherThreadManager() {
+  thread_.Stop();
+}
+
+WatcherThreadManager* WatcherThreadManager::GetInstance() {
+  return Singleton<WatcherThreadManager>::get();
+}
+
+WatcherID WatcherThreadManager::StartWatching(
+    const Handle& handle,
+    MojoHandleSignals handle_signals,
+    base::TimeTicks deadline,
+    const base::Callback<void(MojoResult)>& callback) {
+  RequestData request_data;
+  request_data.type = REQUEST_START;
+  request_data.start_data.id = watcher_id_generator_.GetNext();
+  request_data.start_data.handle = handle;
+  request_data.start_data.callback = callback;
+  request_data.start_data.handle_signals = handle_signals;
+  request_data.start_data.deadline = deadline;
+  request_data.start_data.message_loop = base::MessageLoopProxy::current();
+  DCHECK_NE(static_cast<base::MessageLoopProxy*>(NULL),
+            request_data.start_data.message_loop.get());
+  AddRequest(request_data);
+  return request_data.start_data.id;
+}
+
+void WatcherThreadManager::StopWatching(WatcherID watcher_id) {
+  // Handle the case of StartWatching() followed by StopWatching() before
+  // |thread_| woke up.
+  {
+    base::AutoLock auto_lock(lock_);
+    for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) {
+      if (i->type == REQUEST_START && i->start_data.id == watcher_id) {
+        // Watcher ids are not reused, so if we find it we can stop.
+        requests_.erase(i);
+        return;
+      }
+    }
+  }
+
+  base::ThreadRestrictions::ScopedAllowWait allow_wait;
+  base::WaitableEvent event(true, false);
+  RequestData request_data;
+  request_data.type = REQUEST_STOP;
+  request_data.stop_id = watcher_id;
+  request_data.stop_event = &event;
+  AddRequest(request_data);
+
+  // We need to block until the handle is actually removed.
+  event.Wait();
+}
+
+void WatcherThreadManager::AddRequest(const RequestData& data) {
+  {
+    base::AutoLock auto_lock(lock_);
+    const bool was_empty = requests_.empty();
+    requests_.push_back(data);
+    if (!was_empty)
+      return;
+  }
+  // We own |thread_|, so it's safe to use Unretained() here.
+  thread_.message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&WatcherThreadManager::ProcessRequestsOnBackendThread,
+                 base::Unretained(this)));
+}
+
+void WatcherThreadManager::ProcessRequestsOnBackendThread() {
+  DCHECK_EQ(thread_.message_loop(), base::MessageLoop::current());
+
+  Requests requests;
+  {
+    base::AutoLock auto_lock(lock_);
+    requests_.swap(requests);
+  }
+  for (size_t i = 0; i < requests.size(); ++i) {
+    if (requests[i].type == REQUEST_START) {
+      backend_.StartWatching(requests[i].start_data);
+    } else {
+      backend_.StopWatching(requests[i].stop_id);
+      requests[i].stop_event->Signal();
+    }
+  }
+}
+
+WatcherThreadManager::WatcherThreadManager()
+    : thread_(kWatcherThreadName) {
+  base::Thread::Options thread_options;
+  thread_options.message_pump_factory = base::Bind(&MessagePumpMojo::Create);
+  thread_.StartWithOptions(thread_options);
+}
+
+// HandleWatcher::StateBase and subclasses -------------------------------------
+
+// The base class of HandleWatcher's state. Owns the user's callback and
+// monitors the current thread's MessageLoop to know when to force the callback
+// to run (with an error) even though the pipe hasn't been signaled yet.
+class HandleWatcher::StateBase : public base::MessageLoop::DestructionObserver {
+ public:
+  StateBase(HandleWatcher* watcher,
+            const base::Callback<void(MojoResult)>& callback)
+      : watcher_(watcher),
+        callback_(callback),
+        got_ready_(false) {
+    base::MessageLoop::current()->AddDestructionObserver(this);
+  }
+
+  virtual ~StateBase() {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+  }
+
+ protected:
+  void NotifyHandleReady(MojoResult result) {
+    got_ready_ = true;
+    NotifyAndDestroy(result);
+  }
+
+  bool got_ready() const { return got_ready_; }
+
+ private:
+  virtual void WillDestroyCurrentMessageLoop() override {
+    // The current thread is exiting. Simulate a watch error.
+    NotifyAndDestroy(MOJO_RESULT_ABORTED);
+  }
+
+  void NotifyAndDestroy(MojoResult result) {
+    base::Callback<void(MojoResult)> callback = callback_;
+    watcher_->Stop();  // Destroys |this|.
+
+    callback.Run(result);
+  }
+
+  HandleWatcher* watcher_;
+  base::Callback<void(MojoResult)> callback_;
+
+  // Have we been notified that the handle is ready?
+  bool got_ready_;
+
+  DISALLOW_COPY_AND_ASSIGN(StateBase);
+};
+
+// If the thread on which HandleWatcher is used runs MessagePumpMojo,
+// SameThreadWatchingState is used to directly watch the handle on the same
+// thread.
+class HandleWatcher::SameThreadWatchingState : public StateBase,
+                                               public MessagePumpMojoHandler {
+ public:
+  SameThreadWatchingState(HandleWatcher* watcher,
+                          const Handle& handle,
+                          MojoHandleSignals handle_signals,
+                          MojoDeadline deadline,
+                          const base::Callback<void(MojoResult)>& callback)
+      : StateBase(watcher, callback),
+        handle_(handle) {
+    DCHECK(MessagePumpMojo::IsCurrent());
+
+    MessagePumpMojo::current()->AddHandler(
+        this, handle, handle_signals, MojoDeadlineToTimeTicks(deadline));
+  }
+
+  virtual ~SameThreadWatchingState() {
+    if (!got_ready())
+      MessagePumpMojo::current()->RemoveHandler(handle_);
+  }
+
+ private:
+  // MessagePumpMojoHandler overrides:
+  virtual void OnHandleReady(const Handle& handle) override {
+    StopWatchingAndNotifyReady(handle, MOJO_RESULT_OK);
+  }
+
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    StopWatchingAndNotifyReady(handle, result);
+  }
+
+  void StopWatchingAndNotifyReady(const Handle& handle, MojoResult result) {
+    DCHECK_EQ(handle.value(), handle_.value());
+    MessagePumpMojo::current()->RemoveHandler(handle_);
+    NotifyHandleReady(result);
+  }
+
+  Handle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(SameThreadWatchingState);
+};
+
+// If the thread on which HandleWatcher is used runs a message pump different
+// from MessagePumpMojo, SecondaryThreadWatchingState is used to watch the
+// handle on the handle watcher thread.
+class HandleWatcher::SecondaryThreadWatchingState : public StateBase {
+ public:
+  SecondaryThreadWatchingState(HandleWatcher* watcher,
+                               const Handle& handle,
+                               MojoHandleSignals handle_signals,
+                               MojoDeadline deadline,
+                               const base::Callback<void(MojoResult)>& callback)
+      : StateBase(watcher, callback),
+        weak_factory_(this) {
+    watcher_id_ = WatcherThreadManager::GetInstance()->StartWatching(
+        handle,
+        handle_signals,
+        MojoDeadlineToTimeTicks(deadline),
+        base::Bind(&SecondaryThreadWatchingState::NotifyHandleReady,
+                   weak_factory_.GetWeakPtr()));
+  }
+
+  virtual ~SecondaryThreadWatchingState() {
+    // If we've been notified the handle is ready (|got_ready()| is true) then
+    // the watch has been implicitly removed by
+    // WatcherThreadManager/MessagePumpMojo and we don't have to call
+    // StopWatching(). To do so would needlessly entail posting a task and
+    // blocking until the background thread services it.
+    if (!got_ready())
+      WatcherThreadManager::GetInstance()->StopWatching(watcher_id_);
+  }
+
+ private:
+  WatcherID watcher_id_;
+
+  // Used to weakly bind |this| to the WatcherThreadManager.
+  base::WeakPtrFactory<SecondaryThreadWatchingState> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SecondaryThreadWatchingState);
+};
+
+// HandleWatcher ---------------------------------------------------------------
+
+HandleWatcher::HandleWatcher() {
+}
+
+HandleWatcher::~HandleWatcher() {
+}
+
+void HandleWatcher::Start(const Handle& handle,
+                          MojoHandleSignals handle_signals,
+                          MojoDeadline deadline,
+                          const base::Callback<void(MojoResult)>& callback) {
+  DCHECK(handle.is_valid());
+  DCHECK_NE(MOJO_HANDLE_SIGNAL_NONE, handle_signals);
+
+  if (MessagePumpMojo::IsCurrent()) {
+    state_.reset(new SameThreadWatchingState(
+        this, handle, handle_signals, deadline, callback));
+  } else {
+    state_.reset(new SecondaryThreadWatchingState(
+        this, handle, handle_signals, deadline, callback));
+  }
+}
+
+void HandleWatcher::Stop() {
+  state_.reset();
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/handle_watcher.h b/mojo/common/handle_watcher.h
new file mode 100644
index 0000000..dfcb145
--- /dev/null
+++ b/mojo/common/handle_watcher.h
@@ -0,0 +1,64 @@
+// 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_COMMON_HANDLE_WATCHER_H_
+#define MOJO_COMMON_HANDLE_WATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class Thread;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+class HandleWatcherTest;
+}
+
+// HandleWatcher is used to asynchronously wait on a handle and notify a Closure
+// when the handle is ready, or the deadline has expired.
+class MOJO_COMMON_EXPORT HandleWatcher {
+ public:
+  HandleWatcher();
+
+  // The destructor implicitly stops listening. See Stop() for details.
+  ~HandleWatcher();
+
+  // Starts listening for |handle|. This implicitly invokes Stop(). In other
+  // words, Start() performs one asynchronous watch at a time. It is ok to call
+  // Start() multiple times, but it cancels any existing watches. |callback| is
+  // notified when the handle is ready, invalid or deadline has passed and is
+  // notified on the thread Start() was invoked on. If the current thread exits
+  // before the handle is ready, then |callback| is invoked with a result of
+  // MOJO_RESULT_ABORTED.
+  void Start(const Handle& handle,
+             MojoHandleSignals handle_signals,
+             MojoDeadline deadline,
+             const base::Callback<void(MojoResult)>& callback);
+
+  // Stops listening. Does nothing if not in the process of listening. Blocks
+  // until no longer listening on the handle.
+  void Stop();
+
+ private:
+  class StateBase;
+  class SameThreadWatchingState;
+  class SecondaryThreadWatchingState;
+
+  // If non-NULL Start() has been invoked.
+  scoped_ptr<StateBase> state_;
+
+  DISALLOW_COPY_AND_ASSIGN(HandleWatcher);
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_HANDLE_WATCHER_H_
diff --git a/mojo/common/handle_watcher_unittest.cc b/mojo/common/handle_watcher_unittest.cc
new file mode 100644
index 0000000..61913cd
--- /dev/null
+++ b/mojo/common/handle_watcher_unittest.cc
@@ -0,0 +1,467 @@
+// 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/common/handle_watcher.h"
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/threading/thread.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/common/time_helper.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+enum MessageLoopConfig {
+  MESSAGE_LOOP_CONFIG_DEFAULT = 0,
+  MESSAGE_LOOP_CONFIG_MOJO = 1
+};
+
+void ObserveCallback(bool* was_signaled,
+                     MojoResult* result_observed,
+                     MojoResult result) {
+  *was_signaled = true;
+  *result_observed = result;
+}
+
+void RunUntilIdle() {
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+}
+
+void DeleteWatcherAndForwardResult(
+    HandleWatcher* watcher,
+    base::Callback<void(MojoResult)> next_callback,
+    MojoResult result) {
+  delete watcher;
+  next_callback.Run(result);
+}
+
+scoped_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
+  scoped_ptr<base::MessageLoop> loop;
+  if (config == MESSAGE_LOOP_CONFIG_DEFAULT)
+    loop.reset(new base::MessageLoop());
+  else
+    loop.reset(new base::MessageLoop(MessagePumpMojo::Create()));
+  return loop.Pass();
+}
+
+// Helper class to manage the callback and running the message loop waiting for
+// message to be received. Typical usage is something like:
+//   Schedule callback returned from GetCallback().
+//   RunUntilGotCallback();
+//   EXPECT_TRUE(got_callback());
+//   clear_callback();
+class CallbackHelper {
+ public:
+  CallbackHelper()
+      : got_callback_(false),
+        run_loop_(NULL),
+        weak_factory_(this) {}
+  ~CallbackHelper() {}
+
+  // See description above |got_callback_|.
+  bool got_callback() const { return got_callback_; }
+  void clear_callback() { got_callback_ = false; }
+
+  // Runs the current MessageLoop until the callback returned from GetCallback()
+  // is notified.
+  void RunUntilGotCallback() {
+    ASSERT_TRUE(run_loop_ == NULL);
+    base::RunLoop run_loop;
+    base::AutoReset<base::RunLoop*> reseter(&run_loop_, &run_loop);
+    run_loop.Run();
+  }
+
+  base::Callback<void(MojoResult)> GetCallback() {
+    return base::Bind(&CallbackHelper::OnCallback, weak_factory_.GetWeakPtr());
+  }
+
+  void Start(HandleWatcher* watcher, const MessagePipeHandle& handle) {
+    StartWithCallback(watcher, handle, GetCallback());
+  }
+
+  void StartWithCallback(HandleWatcher* watcher,
+                         const MessagePipeHandle& handle,
+                         const base::Callback<void(MojoResult)>& callback) {
+    watcher->Start(handle, MOJO_HANDLE_SIGNAL_READABLE,
+                   MOJO_DEADLINE_INDEFINITE, callback);
+  }
+
+ private:
+  void OnCallback(MojoResult result) {
+    got_callback_ = true;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  // Set to true when the callback is called.
+  bool got_callback_;
+
+  // If non-NULL we're in RunUntilGotCallback().
+  base::RunLoop* run_loop_;
+
+  base::WeakPtrFactory<CallbackHelper> weak_factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
+};
+
+class HandleWatcherTest : public testing::TestWithParam<MessageLoopConfig> {
+ public:
+  HandleWatcherTest() : message_loop_(CreateMessageLoop(GetParam())) {}
+  virtual ~HandleWatcherTest() {
+    test::SetTickClockForTest(NULL);
+  }
+
+ protected:
+  void TearDownMessageLoop() {
+    message_loop_.reset();
+  }
+
+  void InstallTickClock() {
+    test::SetTickClockForTest(&tick_clock_);
+  }
+
+  base::SimpleTestTickClock tick_clock_;
+
+ private:
+  base::ShadowingAtExitManager at_exit_;
+  scoped_ptr<base::MessageLoop> message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(HandleWatcherTest);
+};
+
+INSTANTIATE_TEST_CASE_P(
+    MultipleMessageLoopConfigs, HandleWatcherTest,
+    testing::Values(MESSAGE_LOOP_CONFIG_DEFAULT, MESSAGE_LOOP_CONFIG_MOJO));
+
+// Trivial test case with a single handle to watch.
+TEST_P(HandleWatcherTest, SingleHandler) {
+  MessagePipe test_pipe;
+  ASSERT_TRUE(test_pipe.handle0.is_valid());
+  CallbackHelper callback_helper;
+  HandleWatcher watcher;
+  callback_helper.Start(&watcher, test_pipe.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper.got_callback());
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
+                                           std::string()));
+  callback_helper.RunUntilGotCallback();
+  EXPECT_TRUE(callback_helper.got_callback());
+}
+
+// Creates three handles and notfies them in reverse order ensuring each one is
+// notified appropriately.
+TEST_P(HandleWatcherTest, ThreeHandles) {
+  MessagePipe test_pipe1;
+  MessagePipe test_pipe2;
+  MessagePipe test_pipe3;
+  CallbackHelper callback_helper1;
+  CallbackHelper callback_helper2;
+  CallbackHelper callback_helper3;
+  ASSERT_TRUE(test_pipe1.handle0.is_valid());
+  ASSERT_TRUE(test_pipe2.handle0.is_valid());
+  ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+  HandleWatcher watcher1;
+  callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+
+  HandleWatcher watcher2;
+  callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+
+  HandleWatcher watcher3;
+  callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+
+  // Write to 3 and make sure it's notified.
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+                                           std::string()));
+  callback_helper3.RunUntilGotCallback();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_TRUE(callback_helper3.got_callback());
+  callback_helper3.clear_callback();
+
+  // Write to 1 and 3. Only 1 should be notified since 3 was is no longer
+  // running.
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+                                           std::string()));
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+                                           std::string()));
+  callback_helper1.RunUntilGotCallback();
+  EXPECT_TRUE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+  callback_helper1.clear_callback();
+
+  // Write to 1 and 2. Only 2 should be notified (since 1 was already notified).
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+                                           std::string()));
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+                                           std::string()));
+  callback_helper2.RunUntilGotCallback();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_TRUE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+// Verifies Start() invoked a second time works.
+TEST_P(HandleWatcherTest, Restart) {
+  MessagePipe test_pipe1;
+  MessagePipe test_pipe2;
+  CallbackHelper callback_helper1;
+  CallbackHelper callback_helper2;
+  ASSERT_TRUE(test_pipe1.handle0.is_valid());
+  ASSERT_TRUE(test_pipe2.handle0.is_valid());
+
+  HandleWatcher watcher1;
+  callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+
+  HandleWatcher watcher2;
+  callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+
+  // Write to 1 and make sure it's notified.
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+                                           std::string()));
+  callback_helper1.RunUntilGotCallback();
+  EXPECT_TRUE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  callback_helper1.clear_callback();
+  EXPECT_TRUE(mojo::test::DiscardMessage(test_pipe1.handle0.get()));
+
+  // Write to 2 and make sure it's notified.
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+                                           std::string()));
+  callback_helper2.RunUntilGotCallback();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_TRUE(callback_helper2.got_callback());
+  callback_helper2.clear_callback();
+
+  // Listen on 1 again.
+  callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+
+  // Write to 1 and make sure it's notified.
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+                                           std::string()));
+  callback_helper1.RunUntilGotCallback();
+  EXPECT_TRUE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+}
+
+// Verifies deadline is honored.
+TEST_P(HandleWatcherTest, Deadline) {
+  InstallTickClock();
+
+  MessagePipe test_pipe1;
+  MessagePipe test_pipe2;
+  MessagePipe test_pipe3;
+  CallbackHelper callback_helper1;
+  CallbackHelper callback_helper2;
+  CallbackHelper callback_helper3;
+  ASSERT_TRUE(test_pipe1.handle0.is_valid());
+  ASSERT_TRUE(test_pipe2.handle0.is_valid());
+  ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+  // Add a watcher with an infinite timeout.
+  HandleWatcher watcher1;
+  callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+
+  // Add another watcher wth a timeout of 500 microseconds.
+  HandleWatcher watcher2;
+  watcher2.Start(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, 500,
+                 callback_helper2.GetCallback());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_FALSE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+
+  // Advance the clock passed the deadline. We also have to start another
+  // watcher to wake up the background thread.
+  tick_clock_.Advance(base::TimeDelta::FromMicroseconds(501));
+
+  HandleWatcher watcher3;
+  callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+
+  callback_helper2.RunUntilGotCallback();
+  EXPECT_FALSE(callback_helper1.got_callback());
+  EXPECT_TRUE(callback_helper2.got_callback());
+  EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+TEST_P(HandleWatcherTest, DeleteInCallback) {
+  MessagePipe test_pipe;
+  CallbackHelper callback_helper;
+
+  HandleWatcher* watcher = new HandleWatcher();
+  callback_helper.StartWithCallback(watcher, test_pipe.handle1.get(),
+                                    base::Bind(&DeleteWatcherAndForwardResult,
+                                               watcher,
+                                               callback_helper.GetCallback()));
+  EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle0.get(),
+                                           std::string()));
+  callback_helper.RunUntilGotCallback();
+  EXPECT_TRUE(callback_helper.got_callback());
+}
+
+TEST_P(HandleWatcherTest, AbortedOnMessageLoopDestruction) {
+  bool was_signaled = false;
+  MojoResult result = MOJO_RESULT_OK;
+
+  MessagePipe pipe;
+  HandleWatcher watcher;
+  watcher.Start(pipe.handle0.get(),
+                MOJO_HANDLE_SIGNAL_READABLE,
+                MOJO_DEADLINE_INDEFINITE,
+                base::Bind(&ObserveCallback, &was_signaled, &result));
+
+  // Now, let the MessageLoop get torn down. We expect our callback to run.
+  TearDownMessageLoop();
+
+  EXPECT_TRUE(was_signaled);
+  EXPECT_EQ(MOJO_RESULT_ABORTED, result);
+}
+
+void NeverReached(MojoResult result) {
+  FAIL() << "Callback should never be invoked " << result;
+}
+
+// Called on the main thread when a thread is done. Decrements |active_count|
+// and if |active_count| is zero quits |run_loop|.
+void StressThreadDone(base::RunLoop* run_loop, int* active_count) {
+  (*active_count)--;
+  EXPECT_GE(*active_count, 0);
+  if (*active_count == 0)
+    run_loop->Quit();
+}
+
+// See description of StressTest. This is called on the background thread.
+// |count| is the number of HandleWatchers to create. |active_count| is the
+// number of outstanding threads, |task_runner| the task runner for the main
+// thread and |run_loop| the run loop that should be quit when there are no more
+// threads running. When done StressThreadDone() is invoked on the main thread.
+// |active_count| and |run_loop| should only be used on the main thread.
+void RunStressTest(int count,
+                   scoped_refptr<base::TaskRunner> task_runner,
+                   base::RunLoop* run_loop,
+                   int* active_count) {
+  struct TestData {
+    MessagePipe pipe;
+    HandleWatcher watcher;
+  };
+  ScopedVector<TestData> data_vector;
+  for (int i = 0; i < count; ++i) {
+    if (i % 20 == 0) {
+      // Every so often we wait. This results in some level of thread balancing
+      // as well as making sure HandleWatcher has time to actually start some
+      // watches.
+      MessagePipe test_pipe;
+      ASSERT_TRUE(test_pipe.handle0.is_valid());
+      CallbackHelper callback_helper;
+      HandleWatcher watcher;
+      callback_helper.Start(&watcher, test_pipe.handle0.get());
+      RunUntilIdle();
+      EXPECT_FALSE(callback_helper.got_callback());
+      EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
+                                               std::string()));
+      base::MessageLoop::ScopedNestableTaskAllower scoper(
+          base::MessageLoop::current());
+      callback_helper.RunUntilGotCallback();
+      EXPECT_TRUE(callback_helper.got_callback());
+    } else {
+      scoped_ptr<TestData> test_data(new TestData);
+      ASSERT_TRUE(test_data->pipe.handle0.is_valid());
+      test_data->watcher.Start(test_data->pipe.handle0.get(),
+                    MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE,
+                    base::Bind(&NeverReached));
+      data_vector.push_back(test_data.release());
+    }
+    if (i % 15 == 0)
+      data_vector.clear();
+  }
+  task_runner->PostTask(FROM_HERE,
+                        base::Bind(&StressThreadDone, run_loop,
+                                   active_count));
+}
+
+// This test is meant to stress HandleWatcher. It uses from various threads
+// repeatedly starting and stopping watches. It spins up kThreadCount
+// threads. Each thread creates kWatchCount watches. Every so often each thread
+// writes to a pipe and waits for the response.
+TEST(HandleWatcherCleanEnvironmentTest, StressTest) {
+#if defined(NDEBUG)
+  const int kThreadCount = 15;
+  const int kWatchCount = 400;
+#else
+  const int kThreadCount = 10;
+  const int kWatchCount = 250;
+#endif
+
+  base::ShadowingAtExitManager at_exit;
+  base::MessageLoop message_loop;
+  base::RunLoop run_loop;
+  ScopedVector<base::Thread> threads;
+  int threads_active_counter = kThreadCount;
+  // Starts the threads first and then post the task in hopes of having more
+  // threads running at once.
+  for (int i = 0; i < kThreadCount; ++i) {
+    scoped_ptr<base::Thread> thread(new base::Thread("test thread"));
+    if (i % 2) {
+      base::Thread::Options thread_options;
+      thread_options.message_pump_factory =
+          base::Bind(&MessagePumpMojo::Create);
+      thread->StartWithOptions(thread_options);
+    } else {
+      thread->Start();
+    }
+    threads.push_back(thread.release());
+  }
+  for (int i = 0; i < kThreadCount; ++i) {
+    threads[i]->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&RunStressTest, kWatchCount,
+                              message_loop.task_runner(),
+                              &run_loop, &threads_active_counter));
+  }
+  run_loop.Run();
+  ASSERT_EQ(0, threads_active_counter);
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/message_pump_mojo.cc b/mojo/common/message_pump_mojo.cc
new file mode 100644
index 0000000..91b78d1
--- /dev/null
+++ b/mojo/common/message_pump_mojo.cc
@@ -0,0 +1,278 @@
+// 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/common/message_pump_mojo.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/debug/alias.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/time/time.h"
+#include "mojo/common/message_pump_mojo_handler.h"
+#include "mojo/common/time_helper.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<MessagePumpMojo> >::Leaky
+    g_tls_current_pump = LAZY_INSTANCE_INITIALIZER;
+
+MojoDeadline TimeTicksToMojoDeadline(base::TimeTicks time_ticks,
+                                     base::TimeTicks now) {
+  // The is_null() check matches that of HandleWatcher as well as how
+  // |delayed_work_time| is used.
+  if (time_ticks.is_null())
+    return MOJO_DEADLINE_INDEFINITE;
+  const int64_t delta = (time_ticks - now).InMicroseconds();
+  return delta < 0 ? static_cast<MojoDeadline>(0) :
+                     static_cast<MojoDeadline>(delta);
+}
+
+}  // namespace
+
+// State needed for one iteration of WaitMany. The first handle and flags
+// corresponds to that of the control pipe.
+struct MessagePumpMojo::WaitState {
+  std::vector<Handle> handles;
+  std::vector<MojoHandleSignals> wait_signals;
+};
+
+struct MessagePumpMojo::RunState {
+  RunState() : should_quit(false) {
+    CreateMessagePipe(NULL, &read_handle, &write_handle);
+  }
+
+  base::TimeTicks delayed_work_time;
+
+  // Used to wake up WaitForWork().
+  ScopedMessagePipeHandle read_handle;
+  ScopedMessagePipeHandle write_handle;
+
+  bool should_quit;
+};
+
+MessagePumpMojo::MessagePumpMojo() : run_state_(NULL), next_handler_id_(0) {
+  DCHECK(!current())
+      << "There is already a MessagePumpMojo instance on this thread.";
+  g_tls_current_pump.Pointer()->Set(this);
+}
+
+MessagePumpMojo::~MessagePumpMojo() {
+  DCHECK_EQ(this, current());
+  g_tls_current_pump.Pointer()->Set(NULL);
+}
+
+// static
+scoped_ptr<base::MessagePump> MessagePumpMojo::Create() {
+  return scoped_ptr<MessagePump>(new MessagePumpMojo());
+}
+
+// static
+MessagePumpMojo* MessagePumpMojo::current() {
+  return g_tls_current_pump.Pointer()->Get();
+}
+
+void MessagePumpMojo::AddHandler(MessagePumpMojoHandler* handler,
+                                 const Handle& handle,
+                                 MojoHandleSignals wait_signals,
+                                 base::TimeTicks deadline) {
+  CHECK(handler);
+  DCHECK(handle.is_valid());
+  // Assume it's an error if someone tries to reregister an existing handle.
+  CHECK_EQ(0u, handlers_.count(handle));
+  Handler handler_data;
+  handler_data.handler = handler;
+  handler_data.wait_signals = wait_signals;
+  handler_data.deadline = deadline;
+  handler_data.id = next_handler_id_++;
+  handlers_[handle] = handler_data;
+}
+
+void MessagePumpMojo::RemoveHandler(const Handle& handle) {
+  handlers_.erase(handle);
+}
+
+void MessagePumpMojo::Run(Delegate* delegate) {
+  RunState run_state;
+  // TODO: better deal with error handling.
+  CHECK(run_state.read_handle.is_valid());
+  CHECK(run_state.write_handle.is_valid());
+  RunState* old_state = NULL;
+  {
+    base::AutoLock auto_lock(run_state_lock_);
+    old_state = run_state_;
+    run_state_ = &run_state;
+  }
+  DoRunLoop(&run_state, delegate);
+  {
+    base::AutoLock auto_lock(run_state_lock_);
+    run_state_ = old_state;
+  }
+}
+
+void MessagePumpMojo::Quit() {
+  base::AutoLock auto_lock(run_state_lock_);
+  if (run_state_)
+    run_state_->should_quit = true;
+}
+
+void MessagePumpMojo::ScheduleWork() {
+  base::AutoLock auto_lock(run_state_lock_);
+  if (run_state_)
+    SignalControlPipe(*run_state_);
+}
+
+void MessagePumpMojo::ScheduleDelayedWork(
+    const base::TimeTicks& delayed_work_time) {
+  base::AutoLock auto_lock(run_state_lock_);
+  if (!run_state_)
+    return;
+  run_state_->delayed_work_time = delayed_work_time;
+}
+
+void MessagePumpMojo::DoRunLoop(RunState* run_state, Delegate* delegate) {
+  bool more_work_is_plausible = true;
+  for (;;) {
+    const bool block = !more_work_is_plausible;
+    DoInternalWork(*run_state, block);
+
+    // There isn't a good way to know if there are more handles ready, we assume
+    // not.
+    more_work_is_plausible = false;
+
+    if (run_state->should_quit)
+      break;
+
+    more_work_is_plausible |= delegate->DoWork();
+    if (run_state->should_quit)
+      break;
+
+    more_work_is_plausible |= delegate->DoDelayedWork(
+        &run_state->delayed_work_time);
+    if (run_state->should_quit)
+      break;
+
+    if (more_work_is_plausible)
+      continue;
+
+    more_work_is_plausible = delegate->DoIdleWork();
+    if (run_state->should_quit)
+      break;
+  }
+}
+
+void MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) {
+  const MojoDeadline deadline = block ? GetDeadlineForWait(run_state) : 0;
+  const WaitState wait_state = GetWaitState(run_state);
+  const MojoResult result =
+      WaitMany(wait_state.handles, wait_state.wait_signals, deadline);
+  if (result == 0) {
+    // Control pipe was written to.
+    uint32_t num_bytes = 0;
+    ReadMessageRaw(run_state.read_handle.get(), NULL, &num_bytes, NULL, NULL,
+                   MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+  } else if (result > 0) {
+    const size_t index = static_cast<size_t>(result);
+    DCHECK(handlers_.find(wait_state.handles[index]) != handlers_.end());
+    handlers_[wait_state.handles[index]].handler->OnHandleReady(
+        wait_state.handles[index]);
+  } else {
+    switch (result) {
+      case MOJO_RESULT_CANCELLED:
+      case MOJO_RESULT_FAILED_PRECONDITION:
+        RemoveFirstInvalidHandle(wait_state);
+        break;
+      case MOJO_RESULT_DEADLINE_EXCEEDED:
+        break;
+      default:
+        base::debug::Alias(&result);
+        // Unexpected result is likely fatal, crash so we can determine cause.
+        CHECK(false);
+    }
+  }
+
+  // Notify and remove any handlers whose time has expired. Make a copy in case
+  // someone tries to add/remove new handlers from notification.
+  const HandleToHandler cloned_handlers(handlers_);
+  const base::TimeTicks now(internal::NowTicks());
+  for (HandleToHandler::const_iterator i = cloned_handlers.begin();
+       i != cloned_handlers.end(); ++i) {
+    // Since we're iterating over a clone of the handlers, verify the handler is
+    // still valid before notifying.
+    if (!i->second.deadline.is_null() && i->second.deadline < now &&
+        handlers_.find(i->first) != handlers_.end() &&
+        handlers_[i->first].id == i->second.id) {
+      i->second.handler->OnHandleError(i->first, MOJO_RESULT_DEADLINE_EXCEEDED);
+    }
+  }
+}
+
+void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) {
+  // TODO(sky): deal with control pipe going bad.
+  for (size_t i = 0; i < wait_state.handles.size(); ++i) {
+    const MojoResult result =
+        Wait(wait_state.handles[i], wait_state.wait_signals[i], 0);
+    if (result == MOJO_RESULT_INVALID_ARGUMENT) {
+      // We should never have an invalid argument. If we do it indicates
+      // RemoveHandler() was not invoked and is likely to cause problems else
+      // where in the stack if we ignore it.
+      CHECK(false);
+    } else if (result == MOJO_RESULT_FAILED_PRECONDITION ||
+               result == MOJO_RESULT_CANCELLED) {
+      CHECK_NE(i, 0u);  // Indicates the control pipe went bad.
+
+      // Remove the handle first, this way if OnHandleError() tries to remove
+      // the handle our iterator isn't invalidated.
+      CHECK(handlers_.find(wait_state.handles[i]) != handlers_.end());
+      MessagePumpMojoHandler* handler =
+          handlers_[wait_state.handles[i]].handler;
+      handlers_.erase(wait_state.handles[i]);
+      handler->OnHandleError(wait_state.handles[i], result);
+      return;
+    }
+  }
+}
+
+void MessagePumpMojo::SignalControlPipe(const RunState& run_state) {
+  const MojoResult result =
+      WriteMessageRaw(run_state.write_handle.get(), NULL, 0, NULL, 0,
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  // If we can't write we likely won't wake up the thread and there is a strong
+  // chance we'll deadlock.
+  CHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+MessagePumpMojo::WaitState MessagePumpMojo::GetWaitState(
+    const RunState& run_state) const {
+  WaitState wait_state;
+  wait_state.handles.push_back(run_state.read_handle.get());
+  wait_state.wait_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+
+  for (HandleToHandler::const_iterator i = handlers_.begin();
+       i != handlers_.end(); ++i) {
+    wait_state.handles.push_back(i->first);
+    wait_state.wait_signals.push_back(i->second.wait_signals);
+  }
+  return wait_state;
+}
+
+MojoDeadline MessagePumpMojo::GetDeadlineForWait(
+    const RunState& run_state) const {
+  const base::TimeTicks now(internal::NowTicks());
+  MojoDeadline deadline = TimeTicksToMojoDeadline(run_state.delayed_work_time,
+                                                  now);
+  for (HandleToHandler::const_iterator i = handlers_.begin();
+       i != handlers_.end(); ++i) {
+    deadline = std::min(
+        TimeTicksToMojoDeadline(i->second.deadline, now), deadline);
+  }
+  return deadline;
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/message_pump_mojo.h b/mojo/common/message_pump_mojo.h
new file mode 100644
index 0000000..3ec3457
--- /dev/null
+++ b/mojo/common/message_pump_mojo.h
@@ -0,0 +1,114 @@
+// 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_COMMON_MESSAGE_PUMP_MOJO_H_
+#define MOJO_COMMON_MESSAGE_PUMP_MOJO_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_pump.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+class MessagePumpMojoHandler;
+
+// Mojo implementation of MessagePump.
+class MOJO_COMMON_EXPORT MessagePumpMojo : public base::MessagePump {
+ public:
+  MessagePumpMojo();
+  virtual ~MessagePumpMojo();
+
+  // Static factory function (for using with |base::Thread::Options|, wrapped
+  // using |base::Bind()|).
+  static scoped_ptr<base::MessagePump> Create();
+
+  // Returns the MessagePumpMojo instance of the current thread, if it exists.
+  static MessagePumpMojo* current();
+
+  static bool IsCurrent() { return !!current(); }
+
+  // Registers a MessagePumpMojoHandler for the specified handle. Only one
+  // handler can be registered for a specified handle.
+  // NOTE: a value of 0 for |deadline| indicates an indefinite timeout.
+  void AddHandler(MessagePumpMojoHandler* handler,
+                  const Handle& handle,
+                  MojoHandleSignals wait_signals,
+                  base::TimeTicks deadline);
+
+  void RemoveHandler(const Handle& handle);
+
+  // MessagePump:
+  virtual void Run(Delegate* delegate) override;
+  virtual void Quit() override;
+  virtual void ScheduleWork() override;
+  virtual void ScheduleDelayedWork(
+      const base::TimeTicks& delayed_work_time) override;
+
+ private:
+  struct RunState;
+  struct WaitState;
+
+  // Contains the data needed to track a request to AddHandler().
+  struct Handler {
+    Handler() : handler(NULL), wait_signals(MOJO_HANDLE_SIGNAL_NONE), id(0) {}
+
+    MessagePumpMojoHandler* handler;
+    MojoHandleSignals wait_signals;
+    base::TimeTicks deadline;
+    // See description of |MessagePumpMojo::next_handler_id_| for details.
+    int id;
+  };
+
+  typedef std::map<Handle, Handler> HandleToHandler;
+
+  // Implementation of Run().
+  void DoRunLoop(RunState* run_state, Delegate* delegate);
+
+  // Services the set of handles ready. If |block| is true this waits for a
+  // handle to become ready, otherwise this does not block.
+  void DoInternalWork(const RunState& run_state, bool block);
+
+  // Removes the first invalid handle. This is called if MojoWaitMany finds an
+  // invalid handle.
+  void RemoveFirstInvalidHandle(const WaitState& wait_state);
+
+  void SignalControlPipe(const RunState& run_state);
+
+  WaitState GetWaitState(const RunState& run_state) const;
+
+  // Returns the deadline for the call to MojoWaitMany().
+  MojoDeadline GetDeadlineForWait(const RunState& run_state) const;
+
+  // If non-NULL we're running (inside Run()). Member is reference to value on
+  // stack.
+  RunState* run_state_;
+
+  // Lock for accessing |run_state_|. In general the only method that we have to
+  // worry about is ScheduleWork(). All other methods are invoked on the same
+  // thread.
+  base::Lock run_state_lock_;
+
+  HandleToHandler handlers_;
+
+  // An ever increasing value assigned to each Handler::id. Used to detect
+  // uniqueness while notifying. That is, while notifying expired timers we copy
+  // |handlers_| and only notify handlers whose id match. If the id does not
+  // match it means the handler was removed then added so that we shouldn't
+  // notify it.
+  int next_handler_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePumpMojo);
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_MESSAGE_PUMP_MOJO_H_
diff --git a/mojo/common/message_pump_mojo_handler.h b/mojo/common/message_pump_mojo_handler.h
new file mode 100644
index 0000000..5809051
--- /dev/null
+++ b/mojo/common/message_pump_mojo_handler.h
@@ -0,0 +1,29 @@
+// 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_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
+#define MOJO_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
+
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+// Used by MessagePumpMojo to notify when a handle is either ready or has become
+// invalid.
+class MOJO_COMMON_EXPORT MessagePumpMojoHandler {
+ public:
+  virtual void OnHandleReady(const Handle& handle) = 0;
+
+  virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+  virtual ~MessagePumpMojoHandler() {}
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
diff --git a/mojo/common/message_pump_mojo_unittest.cc b/mojo/common/message_pump_mojo_unittest.cc
new file mode 100644
index 0000000..fb5ae24
--- /dev/null
+++ b/mojo/common/message_pump_mojo_unittest.cc
@@ -0,0 +1,22 @@
+// 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/common/message_pump_mojo.h"
+
+#include "base/message_loop/message_loop_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+scoped_ptr<base::MessagePump> CreateMojoMessagePump() {
+  return scoped_ptr<base::MessagePump>(new MessagePumpMojo());
+}
+
+RUN_MESSAGE_LOOP_TESTS(Mojo, &CreateMojoMessagePump);
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/mojo_common_export.h b/mojo/common/mojo_common_export.h
new file mode 100644
index 0000000..48d21d0
--- /dev/null
+++ b/mojo/common/mojo_common_export.h
@@ -0,0 +1,32 @@
+// 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_COMMON_MOJO_COMMON_EXPORT_H_
+#define MOJO_COMMON_MOJO_COMMON_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_COMMON_IMPLEMENTATION)
+#define MOJO_COMMON_EXPORT __declspec(dllexport)
+#else
+#define MOJO_COMMON_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_COMMON_IMPLEMENTATION)
+#define MOJO_COMMON_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_COMMON_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_COMMON_EXPORT
+#endif
+
+#endif  // MOJO_COMMON_MOJO_COMMON_EXPORT_H_
diff --git a/mojo/common/test/BUILD.gn b/mojo/common/test/BUILD.gn
new file mode 100644
index 0000000..ebad97e
--- /dev/null
+++ b/mojo/common/test/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_base.gyp:mojo_run_all_unittests
+source_set("run_all_unittests") {
+  testonly = true
+  deps = [
+    ":test_support_impl",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/public/c/test_support",
+    "//testing/gtest",
+  ]
+
+  sources = [ "run_all_unittests.cc" ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_run_all_perftests
+source_set("run_all_perftests") {
+  testonly = true
+  deps = [
+    ":test_support_impl",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/public/c/test_support",
+  ]
+
+  sources = [ "run_all_perftests.cc" ]
+}
+
+
+# GYP version: mojo/mojo_base.gyp:mojo_test_support_impl
+source_set("test_support_impl") {
+  testonly = true
+  deps = [
+    "//base",
+  ]
+
+  sources = [
+    "test_support_impl.cc",
+    "test_support_impl.h",
+  ]
+}
diff --git a/mojo/common/test/DEPS b/mojo/common/test/DEPS
new file mode 100644
index 0000000..61ef5fe
--- /dev/null
+++ b/mojo/common/test/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/edk/embedder",
+]
diff --git a/mojo/common/test/run_all_perftests.cc b/mojo/common/test/run_all_perftests.cc
new file mode 100644
index 0000000..e3f41aa
--- /dev/null
+++ b/mojo/common/test/run_all_perftests.cc
@@ -0,0 +1,14 @@
+// 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/test/perf_test_suite.h"
+#include "mojo/common/test/test_support_impl.h"
+#include "mojo/edk/embedder/test_embedder.h"
+#include "mojo/public/tests/test_support_private.h"
+
+int main(int argc, char** argv) {
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+  mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl());
+  return base::PerfTestSuite(argc, argv).Run();
+}
diff --git a/mojo/common/test/run_all_unittests.cc b/mojo/common/test/run_all_unittests.cc
new file mode 100644
index 0000000..48c054e
--- /dev/null
+++ b/mojo/common/test/run_all_unittests.cc
@@ -0,0 +1,26 @@
+// 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/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/common/test/test_support_impl.h"
+#include "mojo/edk/embedder/test_embedder.h"
+#include "mojo/public/tests/test_support_private.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  // Silence death test thread warnings on Linux. We can afford to run our death
+  // tests a little more slowly (< 10 ms per death test on a Z620).
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+
+  base::TestSuite test_suite(argc, argv);
+
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+  mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl());
+
+  return base::LaunchUnitTests(
+      argc, argv, base::Bind(&base::TestSuite::Run,
+                             base::Unretained(&test_suite)));
+}
diff --git a/mojo/common/test/test_support_impl.cc b/mojo/common/test/test_support_impl.cc
new file mode 100644
index 0000000..89e6de1
--- /dev/null
+++ b/mojo/common/test/test_support_impl.cc
@@ -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.
+
+#include "mojo/common/test/test_support_impl.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/test/perf_log.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+base::FilePath ResolveSourceRootRelativePath(const char* relative_path) {
+  base::FilePath path;
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, &path))
+    return base::FilePath();
+
+  std::vector<std::string> components;
+  base::SplitString(relative_path, '/', &components);
+
+  for (size_t i = 0; i < components.size(); ++i) {
+    if (!components[i].empty())
+      path = path.AppendASCII(components[i]);
+  }
+
+  return path;
+}
+
+}  // namespace
+
+TestSupportImpl::TestSupportImpl() {
+}
+
+TestSupportImpl::~TestSupportImpl() {
+}
+
+void TestSupportImpl::LogPerfResult(const char* test_name,
+                                    double value,
+                                    const char* units) {
+  base::LogPerfResult(test_name, value, units);
+}
+
+FILE* TestSupportImpl::OpenSourceRootRelativeFile(const char* relative_path) {
+  return base::OpenFile(ResolveSourceRootRelativePath(relative_path), "rb");
+}
+
+char** TestSupportImpl::EnumerateSourceRootRelativeDirectory(
+    const char* relative_path) {
+  std::vector<std::string> names;
+  base::FileEnumerator e(ResolveSourceRootRelativePath(relative_path), false,
+                         base::FileEnumerator::FILES);
+  for (base::FilePath name = e.Next(); !name.empty(); name = e.Next())
+    names.push_back(name.BaseName().AsUTF8Unsafe());
+
+  // |names.size() + 1| for null terminator.
+  char** rv = static_cast<char**>(calloc(names.size() + 1, sizeof(char*)));
+  for (size_t i = 0; i < names.size(); ++i)
+    rv[i] = base::strdup(names[i].c_str());
+  return rv;
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/common/test/test_support_impl.h b/mojo/common/test/test_support_impl.h
new file mode 100644
index 0000000..9f69bfe
--- /dev/null
+++ b/mojo/common/test/test_support_impl.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_COMMON_TEST_TEST_SUPPORT_IMPL_H_
+#define MOJO_COMMON_TEST_TEST_SUPPORT_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/public/tests/test_support_private.h"
+
+namespace mojo {
+namespace test {
+
+class TestSupportImpl : public TestSupport {
+ public:
+  TestSupportImpl();
+  virtual ~TestSupportImpl();
+
+  virtual void LogPerfResult(const char* test_name,
+                             double value,
+                             const char* units) override;
+  virtual FILE* OpenSourceRootRelativeFile(const char* relative_path) override;
+  virtual char** EnumerateSourceRootRelativeDirectory(const char* relative_path)
+      override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSupportImpl);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_TEST_TEST_SUPPORT_IMPL_H_
diff --git a/mojo/common/time_helper.cc b/mojo/common/time_helper.cc
new file mode 100644
index 0000000..36fd087
--- /dev/null
+++ b/mojo/common/time_helper.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/time_helper.h"
+
+#include "base/time/tick_clock.h"
+
+namespace mojo {
+namespace common {
+
+namespace {
+
+base::TickClock* tick_clock = NULL;
+
+}  // namespace
+
+namespace test {
+
+void SetTickClockForTest(base::TickClock* clock) {
+  tick_clock = clock;
+}
+}  // namespace test
+
+namespace internal {
+
+base::TimeTicks NowTicks() {
+  return tick_clock ? tick_clock->NowTicks() : base::TimeTicks::Now();
+}
+
+}  // namespace internal
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/time_helper.h b/mojo/common/time_helper.h
new file mode 100644
index 0000000..365ae04
--- /dev/null
+++ b/mojo/common/time_helper.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_COMMON_TIME_HELPER_H_
+#define MOJO_COMMON_TIME_HELPER_H_
+
+#include "base/time/time.h"
+#include "mojo/common/mojo_common_export.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+
+// Sets the TickClock used for getting TimeTicks::Now(). This is currently used
+// by both HandleWatcher and MessagePumpMojo.
+MOJO_COMMON_EXPORT void SetTickClockForTest(base::TickClock* clock);
+
+}  // namespace test
+
+namespace internal {
+
+// Returns now. Used internally; generally not useful.
+MOJO_COMMON_EXPORT base::TimeTicks NowTicks();
+
+}  // namespace internal
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_TIME_HELPER_H_
diff --git a/mojo/edk/DEPS b/mojo/edk/DEPS
new file mode 100644
index 0000000..5fe1410
--- /dev/null
+++ b/mojo/edk/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "-mojo",
+  "+mojo/edk",
+  "+mojo/public",
+]
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
new file mode 100644
index 0000000..9c2f39f
--- /dev/null
+++ b/mojo/edk/embedder/BUILD.gn
@@ -0,0 +1,71 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("embedder") {
+  # This isn't really a standalone target, it must be linked into the
+  # mojo_system_impl component.
+  visibility = [ "//mojo/edk/system" ]
+
+  deps = [ "//base", ]
+
+  defines = [
+    "MOJO_SYSTEM_IMPL_IMPLEMENTATION",
+    "MOJO_SYSTEM_IMPLEMENTATION",
+  ]
+
+  configs += [ "//mojo/edk/system:system_config" ]
+
+  sources = [
+    "channel_init.cc",
+    "channel_init.h",
+    "embedder.cc",
+    "embedder.h",
+    "platform_channel_pair.cc",
+    "platform_channel_pair.h",
+    "platform_channel_pair_posix.cc",
+    "platform_channel_pair_win.cc",
+    "platform_channel_utils_posix.cc",
+    "platform_channel_utils_posix.h",
+    "platform_handle.cc",
+    "platform_handle.h",
+    "platform_handle_utils.h",
+    "platform_handle_utils_posix.cc",
+    "platform_handle_utils_win.cc",
+    "platform_handle_vector.h",
+    "platform_shared_buffer.h",
+    "platform_support.h",
+    "scoped_platform_handle.h",
+    "simple_platform_shared_buffer.cc",
+    "simple_platform_shared_buffer.h",
+    "simple_platform_shared_buffer_posix.cc",
+    "simple_platform_shared_buffer_win.cc",
+    "simple_platform_support.cc",
+    "simple_platform_support.h",
+    # Test-only code:
+    # TODO(vtl): It's a little unfortunate that these end up in the same
+    # component as non-test-only code. In the static build, this code should
+    # hopefully be dead-stripped.
+    "test_embedder.cc",
+    "test_embedder.h",
+  ]
+}
+
+source_set("embedder_unittests") {
+  testonly = true
+  visibility = [ "//mojo/edk/system:mojo_system_unittests" ]
+  testonly = true
+
+  deps = [
+    "//base",
+    "//mojo/edk/test:test_support",
+    "//mojo/edk/system",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "embedder_unittest.cc",
+    "platform_channel_pair_posix_unittest.cc",
+    "simple_platform_shared_buffer_unittest.cc",
+  ]
+}
diff --git a/mojo/edk/embedder/DEPS b/mojo/edk/embedder/DEPS
new file mode 100644
index 0000000..c3a0d22
--- /dev/null
+++ b/mojo/edk/embedder/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+mojo/edk/system/system_impl_export.h",
+]
+
+specific_include_rules = {
+  # Implementation files may freely access mojo/edk/system, but we don't want to
+  # leak implementation details through the headers.
+  ".*\.cc": [
+    "+mojo/edk/system",
+  ]
+}
diff --git a/mojo/edk/embedder/PRESUBMIT.py b/mojo/edk/embedder/PRESUBMIT.py
new file mode 100644
index 0000000..9cd39bc
--- /dev/null
+++ b/mojo/edk/embedder/PRESUBMIT.py
@@ -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.
+
+"""Presubmit script for mojo/edk/embedder.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
+                                                              output_api)
+  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
+  return results
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
new file mode 100644
index 0000000..f976fcb
--- /dev/null
+++ b/mojo/edk/embedder/README.md
@@ -0,0 +1,13 @@
+Mojo Embedder API
+=================
+
+The Mojo Embedder API is an unstable, internal API to the Mojo system
+implementation. It should be used by code running on top of the system-level
+APIs to set up the Mojo environment (instead of directly instantiating things
+from src/mojo/edk/system).
+
+Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium
+code, to set up the Mojo IPC system for use between processes. Note that most
+code should use the Mojo Public API (under src/mojo/public) instead. The
+Embedder API should only be used to initialize the environment, set up the
+initial MessagePipe between two processes, etc.
diff --git a/mojo/edk/embedder/channel_init.cc b/mojo/edk/embedder/channel_init.cc
new file mode 100644
index 0000000..9ea984b
--- /dev/null
+++ b/mojo/edk/embedder/channel_init.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 "mojo/edk/embedder/channel_init.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+
+namespace mojo {
+namespace embedder {
+
+ChannelInit::ChannelInit() : channel_info_(nullptr), weak_factory_(this) {
+}
+
+ChannelInit::~ChannelInit() {
+  if (channel_info_)
+    DestroyChannel(channel_info_);
+}
+
+ScopedMessagePipeHandle ChannelInit::Init(
+    base::PlatformFile file,
+    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+  DCHECK(!io_thread_task_runner_.get());  // Should only init once.
+  io_thread_task_runner_ = io_thread_task_runner;
+  ScopedMessagePipeHandle message_pipe =
+      CreateChannel(ScopedPlatformHandle(PlatformHandle(file)),
+                    io_thread_task_runner,
+                    base::Bind(&ChannelInit::OnCreatedChannel,
+                               weak_factory_.GetWeakPtr(),
+                               io_thread_task_runner),
+                    base::MessageLoop::current()->message_loop_proxy()).Pass();
+  return message_pipe.Pass();
+}
+
+void ChannelInit::WillDestroySoon() {
+  if (channel_info_)
+    WillDestroyChannelSoon(channel_info_);
+}
+
+// static
+void ChannelInit::OnCreatedChannel(base::WeakPtr<ChannelInit> self,
+                                   scoped_refptr<base::TaskRunner> io_thread,
+                                   ChannelInfo* channel) {
+  // If |self| was already destroyed, shut the channel down.
+  if (!self) {
+    DestroyChannel(channel);
+    return;
+  }
+
+  self->channel_info_ = channel;
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/channel_init.h b/mojo/edk/embedder/channel_init.h
new file mode 100644
index 0000000..797cc6d
--- /dev/null
+++ b/mojo/edk/embedder/channel_init.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_EDK_EMBEDDER_CHANNEL_INIT_H_
+#define MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_
+
+#include "base/files/file.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class MessageLoopProxy;
+class TaskRunner;
+}
+
+namespace mojo {
+namespace embedder {
+struct ChannelInfo;
+}
+
+namespace embedder {
+
+// |ChannelInit| handles creation (and destruction) of the Mojo channel. It is
+// not thread-safe, but may be used on any single thread (with a |MessageLoop|).
+class MOJO_SYSTEM_IMPL_EXPORT ChannelInit {
+ public:
+  ChannelInit();
+  ~ChannelInit();
+
+  // Initializes the channel. This takes ownership of |file|. Returns the
+  // primordial MessagePipe for the channel.
+  mojo::ScopedMessagePipeHandle Init(
+      base::PlatformFile file,
+      scoped_refptr<base::TaskRunner> io_thread_task_runner);
+
+  // Notifies the channel that we (hence it) will soon be destroyed.
+  void WillDestroySoon();
+
+ private:
+  // Invoked on the thread on which this object lives once the channel has been
+  // established. (This is a static method that takes a weak pointer to self,
+  // since we want to destroy the channel even if we're destroyed.)
+  static void OnCreatedChannel(base::WeakPtr<ChannelInit> self,
+                               scoped_refptr<base::TaskRunner> io_thread,
+                               ChannelInfo* channel);
+
+  scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+  // If non-null the channel has been established.
+  ChannelInfo* channel_info_;
+
+  base::WeakPtrFactory<ChannelInit> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelInit);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
new file mode 100644
index 0000000..b3033c4
--- /dev/null
+++ b/mojo/edk/embedder/embedder.cc
@@ -0,0 +1,235 @@
+// Copyright 2014 The Chromium 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/edk/embedder/embedder.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/platform_support.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/entrypoints.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+#include "mojo/edk/system/raw_channel.h"
+
+namespace mojo {
+namespace embedder {
+
+// This is defined here (instead of a header file), since it's opaque to the
+// outside world. But we need to define it before our (internal-only) functions
+// that use it.
+struct ChannelInfo {
+  ChannelInfo() {}
+  ~ChannelInfo() {}
+
+  scoped_refptr<system::Channel> channel;
+
+  // May be null, in which case |DestroyChannelOnIOThread()| must be used (from
+  // the IO thread), instead of |DestroyChannel()|.
+  scoped_refptr<base::TaskRunner> io_thread_task_runner;
+};
+
+namespace {
+
+// Helper for |CreateChannel...()|. (Note: May return null for some failures.)
+scoped_refptr<system::Channel> MakeChannel(
+    system::Core* core,
+    ScopedPlatformHandle platform_handle,
+    scoped_refptr<system::ChannelEndpoint> channel_endpoint) {
+  DCHECK(platform_handle.is_valid());
+
+  // Create and initialize a |system::Channel|.
+  scoped_refptr<system::Channel> channel =
+      new system::Channel(core->platform_support());
+  if (!channel->Init(system::RawChannel::Create(platform_handle.Pass()))) {
+    // This is very unusual (e.g., maybe |platform_handle| was invalid or we
+    // reached some system resource limit).
+    LOG(ERROR) << "Channel::Init() failed";
+    // Return null, since |Shutdown()| shouldn't be called in this case.
+    return scoped_refptr<system::Channel>();
+  }
+  // Once |Init()| has succeeded, we have to return |channel| (since
+  // |Shutdown()| will have to be called on it).
+
+  // Attach the endpoint.
+  system::MessageInTransit::EndpointId endpoint_id =
+      channel->AttachEndpoint(channel_endpoint);
+  if (endpoint_id == system::MessageInTransit::kInvalidEndpointId) {
+    // This means that, e.g., the other endpoint of the message pipe was closed
+    // first. But it's not necessarily an error per se.
+    DVLOG(2) << "Channel::AttachEndpoint() failed";
+    return channel;
+  }
+  CHECK_EQ(endpoint_id, system::Channel::kBootstrapEndpointId);
+
+  channel->RunEndpoint(channel_endpoint, system::Channel::kBootstrapEndpointId);
+
+  return channel;
+}
+
+void CreateChannelHelper(
+    system::Core* core,
+    ScopedPlatformHandle platform_handle,
+    scoped_ptr<ChannelInfo> channel_info,
+    scoped_refptr<system::ChannelEndpoint> channel_endpoint,
+    DidCreateChannelCallback callback,
+    scoped_refptr<base::TaskRunner> callback_thread_task_runner) {
+  channel_info->channel =
+      MakeChannel(core, platform_handle.Pass(), channel_endpoint);
+
+  // Hand the channel back to the embedder.
+  if (callback_thread_task_runner.get()) {
+    callback_thread_task_runner->PostTask(
+        FROM_HERE, base::Bind(callback, channel_info.release()));
+  } else {
+    callback.Run(channel_info.release());
+  }
+}
+
+}  // namespace
+
+void Init(scoped_ptr<PlatformSupport> platform_support) {
+  system::entrypoints::SetCore(new system::Core(platform_support.Pass()));
+}
+
+// TODO(vtl): Write tests for this.
+ScopedMessagePipeHandle CreateChannelOnIOThread(
+    ScopedPlatformHandle platform_handle,
+    ChannelInfo** channel_info) {
+  DCHECK(platform_handle.is_valid());
+  DCHECK(channel_info);
+
+  scoped_refptr<system::ChannelEndpoint> channel_endpoint;
+  scoped_refptr<system::MessagePipeDispatcher> dispatcher =
+      system::MessagePipeDispatcher::CreateRemoteMessagePipe(&channel_endpoint);
+
+  system::Core* core = system::entrypoints::GetCore();
+  DCHECK(core);
+  ScopedMessagePipeHandle rv(
+      MessagePipeHandle(core->AddDispatcher(dispatcher)));
+
+  *channel_info = new ChannelInfo();
+  (*channel_info)->channel =
+      MakeChannel(core, platform_handle.Pass(), channel_endpoint);
+
+  return rv.Pass();
+}
+
+ScopedMessagePipeHandle CreateChannel(
+    ScopedPlatformHandle platform_handle,
+    scoped_refptr<base::TaskRunner> io_thread_task_runner,
+    DidCreateChannelCallback callback,
+    scoped_refptr<base::TaskRunner> callback_thread_task_runner) {
+  DCHECK(platform_handle.is_valid());
+
+  scoped_refptr<system::ChannelEndpoint> channel_endpoint;
+  scoped_refptr<system::MessagePipeDispatcher> dispatcher =
+      system::MessagePipeDispatcher::CreateRemoteMessagePipe(&channel_endpoint);
+
+  system::Core* core = system::entrypoints::GetCore();
+  DCHECK(core);
+  ScopedMessagePipeHandle rv(
+      MessagePipeHandle(core->AddDispatcher(dispatcher)));
+
+  scoped_ptr<ChannelInfo> channel_info(new ChannelInfo());
+  channel_info->io_thread_task_runner = io_thread_task_runner;
+
+  if (rv.is_valid()) {
+    io_thread_task_runner->PostTask(FROM_HERE,
+                                    base::Bind(&CreateChannelHelper,
+                                               base::Unretained(core),
+                                               base::Passed(&platform_handle),
+                                               base::Passed(&channel_info),
+                                               channel_endpoint,
+                                               callback,
+                                               callback_thread_task_runner));
+  } else {
+    (callback_thread_task_runner.get() ? callback_thread_task_runner
+                                       : io_thread_task_runner)
+        ->PostTask(FROM_HERE, base::Bind(callback, channel_info.release()));
+  }
+
+  return rv.Pass();
+}
+
+void DestroyChannelOnIOThread(ChannelInfo* channel_info) {
+  DCHECK(channel_info);
+  if (!channel_info->channel.get()) {
+    // Presumably, |Init()| on the channel failed.
+    return;
+  }
+
+  channel_info->channel->Shutdown();
+  delete channel_info;
+}
+
+// TODO(vtl): Write tests for this.
+void DestroyChannel(ChannelInfo* channel_info) {
+  DCHECK(channel_info);
+  DCHECK(channel_info->io_thread_task_runner.get());
+
+  if (!channel_info->channel.get()) {
+    // Presumably, |Init()| on the channel failed.
+    return;
+  }
+
+  channel_info->channel->WillShutdownSoon();
+  channel_info->io_thread_task_runner->PostTask(
+      FROM_HERE, base::Bind(&DestroyChannelOnIOThread, channel_info));
+}
+
+void WillDestroyChannelSoon(ChannelInfo* channel_info) {
+  DCHECK(channel_info);
+  channel_info->channel->WillShutdownSoon();
+}
+
+MojoResult CreatePlatformHandleWrapper(
+    ScopedPlatformHandle platform_handle,
+    MojoHandle* platform_handle_wrapper_handle) {
+  DCHECK(platform_handle_wrapper_handle);
+
+  scoped_refptr<system::Dispatcher> dispatcher(
+      new system::PlatformHandleDispatcher(platform_handle.Pass()));
+
+  system::Core* core = system::entrypoints::GetCore();
+  DCHECK(core);
+  MojoHandle h = core->AddDispatcher(dispatcher);
+  if (h == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  *platform_handle_wrapper_handle = h;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle,
+                                     ScopedPlatformHandle* platform_handle) {
+  DCHECK(platform_handle);
+
+  system::Core* core = system::entrypoints::GetCore();
+  DCHECK(core);
+  scoped_refptr<system::Dispatcher> dispatcher(
+      core->GetDispatcher(platform_handle_wrapper_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  *platform_handle =
+      static_cast<system::PlatformHandleDispatcher*>(dispatcher.get())
+          ->PassPlatformHandle()
+          .Pass();
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
new file mode 100644
index 0000000..ce949d0
--- /dev/null
+++ b/mojo/edk/embedder/embedder.h
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium 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_EDK_EMBEDDER_EMBEDDER_H_
+#define MOJO_EDK_EMBEDDER_EMBEDDER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace embedder {
+
+class PlatformSupport;
+
+// Must be called first to initialize the (global, singleton) system.
+MOJO_SYSTEM_IMPL_EXPORT void Init(scoped_ptr<PlatformSupport> platform_support);
+
+// A "channel" is a connection on top of an OS "pipe", on top of which Mojo
+// message pipes (etc.) can be multiplexed. It must "live" on some I/O thread.
+//
+// There are two "channel" creation/destruction APIs: the synchronous
+// |CreateChannelOnIOThread()|/|DestroyChannelOnIOThread()|, which must be
+// called from the I/O thread, and the asynchronous
+// |CreateChannel()|/|DestroyChannel()|, which may be called from any thread.
+//
+// Both creation functions have a |platform_handle| argument, which should be an
+// OS-dependent handle to one side of a suitable bidirectional OS "pipe" (e.g.,
+// a file descriptor to a socket on POSIX, a handle to a named pipe on Windows);
+// this "pipe" should be connected and ready for operation (e.g., to be written
+// to or read from).
+//
+// Both (synchronously) return a handle to the bootstrap message pipe on the
+// channel that was (or is to be) created, or |MOJO_HANDLE_INVALID| on error
+// (but note that this will happen only if, e.g., the handle table is full).
+// This message pipe may be used immediately, but since channel operation
+// actually begins asynchronously, other errors may still occur (e.g., if the
+// other end of the "pipe" is closed) and be reported in the usual way to the
+// returned handle.
+//
+// (E.g., a message written immediately to the returned handle will be queued
+// and the handle immediately closed, before the channel begins operation. In
+// this case, the channel should connect as usual, send the queued message, and
+// report that the handle was closed to the other side. The message sent may
+// have other handles, so there may still be message pipes "on" this channel.)
+//
+// Both also produce a |ChannelInfo*| (a pointer to an opaque object) -- the
+// first synchronously and second asynchronously.
+//
+// The destruction functions are similarly synchronous and asynchronous,
+// respectively, and take the |ChannelInfo*| produced by the creation function.
+// (Note: One may call |DestroyChannelOnIOThread()| with the result of
+// |CreateChannel()|, but not |DestroyChannel()| with the result of
+// |CreateChannelOnIOThread()|.)
+//
+// TODO(vtl): Figure out channel teardown.
+struct ChannelInfo;
+
+// Creates a channel; must only be called from the I/O thread. |platform_handle|
+// should be a handle to a connected OS "pipe". Eventually (even on failure),
+// the "out" value |*channel_info| should be passed to
+// |DestroyChannelOnIOThread()| to tear down the channel. Returns a handle to
+// the bootstrap message pipe.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+    CreateChannelOnIOThread(ScopedPlatformHandle platform_handle,
+                            ChannelInfo** channel_info);
+
+typedef base::Callback<void(ChannelInfo*)> DidCreateChannelCallback;
+// Creates a channel asynchronously; may be called from any thread.
+// |platform_handle| should be a handle to a connected OS "pipe".
+// |io_thread_task_runner| should be the |TaskRunner| for the I/O thread.
+// |callback| should be the callback to call with the |ChannelInfo*|, which
+// should eventually be passed to |DestroyChannel()| (or
+// |DestroyChannelOnIOThread()|) to tear down the channel; the callback will be
+// called using |callback_thread_task_runner| if that is non-null, or otherwise
+// it will be called using |io_thread_task_runner|. Returns a handle to the
+// bootstrap message pipe.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+    CreateChannel(ScopedPlatformHandle platform_handle,
+                  scoped_refptr<base::TaskRunner> io_thread_task_runner,
+                  DidCreateChannelCallback callback,
+                  scoped_refptr<base::TaskRunner> callback_thread_task_runner);
+
+// Destroys a channel that was created using either |CreateChannelOnIOThread()|
+// or |CreateChannel()|; must only be called from the I/O thread. |channel_info|
+// should be the "out" value from |CreateChannelOnIOThread()| or the value
+// provided to the callback to |CreateChannel()|.
+MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread(
+    ChannelInfo* channel_info);
+
+// Destroys a channel (asynchronously) that was created using |CreateChannel()|
+// (note: NOT |CreateChannelOnIOThread()|); may be called from any thread.
+// |channel_info| should be the value provided to the callback to
+// |CreateChannel()|.
+MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel(ChannelInfo* channel_info);
+
+// Inform the channel that it will soon be destroyed (doing so is optional).
+// This may be called from any thread, but the caller must ensure that this is
+// called before |DestroyChannel()| or |DestroyChannelOnIOThread()|.
+MOJO_SYSTEM_IMPL_EXPORT void WillDestroyChannelSoon(ChannelInfo* channel_info);
+
+// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking
+// ownership of it). This |MojoHandle| can then, e.g., be passed through message
+// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on
+// failure, which is different from what you'd expect from a Mojo API, but it
+// makes for a more convenient embedder API.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+    CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle,
+                                MojoHandle* platform_handle_wrapper_handle);
+// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using
+// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still
+// be closed separately.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+    PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle,
+                              ScopedPlatformHandle* platform_handle);
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_EMBEDDER_H_
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
new file mode 100644
index 0000000..551ea27
--- /dev/null
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -0,0 +1,602 @@
+// Copyright 2014 The Chromium 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/edk/embedder/embedder.h"
+
+#include <string.h>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_io_thread.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/test_embedder.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/public/c/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace embedder {
+namespace {
+
+class ScopedTestChannel {
+ public:
+  // Creates a channel that lives on a given I/O thread (determined by the given
+  // |TaskRunner|) attached to the given |platform_handle|. After construction,
+  // |bootstrap_message_pipe()| gives the Mojo handle for the bootstrap message
+  // pipe on this channel; it is up to the caller to close this handle.
+  // Note: The I/O thread must outlive this object (and its message loop must
+  // continue pumping messages while this object is alive).
+  ScopedTestChannel(scoped_refptr<base::TaskRunner> io_thread_task_runner,
+                    ScopedPlatformHandle platform_handle)
+      : io_thread_task_runner_(io_thread_task_runner),
+        bootstrap_message_pipe_(MOJO_HANDLE_INVALID),
+        did_create_channel_event_(true, false),
+        channel_info_(nullptr) {
+    bootstrap_message_pipe_ =
+        CreateChannel(platform_handle.Pass(),
+                      io_thread_task_runner_,
+                      base::Bind(&ScopedTestChannel::DidCreateChannel,
+                                 base::Unretained(this)),
+                      nullptr)
+            .release()
+            .value();
+    CHECK_NE(bootstrap_message_pipe_, MOJO_HANDLE_INVALID);
+  }
+
+  // Destructor: Shuts down the channel. (As noted above, for this to happen,
+  // the I/O thread must be alive and pumping messages.)
+  ~ScopedTestChannel() {
+    system::test::PostTaskAndWait(
+        io_thread_task_runner_,
+        FROM_HERE,
+        base::Bind(&ScopedTestChannel::DestroyChannel, base::Unretained(this)));
+  }
+
+  // Waits for channel creation to be completed.
+  void WaitForChannelCreationCompletion() { did_create_channel_event_.Wait(); }
+
+  MojoHandle bootstrap_message_pipe() const { return bootstrap_message_pipe_; }
+
+  // Call only after |WaitForChannelCreationCompletion()|. Use only to check
+  // that it's not null.
+  const ChannelInfo* channel_info() const { return channel_info_; }
+
+ private:
+  void DidCreateChannel(ChannelInfo* channel_info) {
+    CHECK(channel_info);
+    CHECK(!channel_info_);
+    channel_info_ = channel_info;
+    did_create_channel_event_.Signal();
+  }
+
+  void DestroyChannel() {
+    CHECK(channel_info_);
+    DestroyChannelOnIOThread(channel_info_);
+    channel_info_ = nullptr;
+  }
+
+  scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+  // Valid from creation until whenever it gets closed (by the "owner" of this
+  // object).
+  // Note: We don't want use the C++ wrappers here, since we want to test the
+  // API at the lowest level.
+  MojoHandle bootstrap_message_pipe_;
+
+  // Set after channel creation has been completed (i.e., the callback to
+  // |CreateChannel()| has been called).
+  base::WaitableEvent did_create_channel_event_;
+
+  // Valid after channel creation completion until destruction.
+  ChannelInfo* channel_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTestChannel);
+};
+
+class EmbedderTest : public testing::Test {
+ public:
+  EmbedderTest() : test_io_thread_(base::TestIOThread::kAutoStart) {}
+  virtual ~EmbedderTest() {}
+
+ protected:
+  base::TestIOThread* test_io_thread() { return &test_io_thread_; }
+
+ private:
+  base::TestIOThread test_io_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
+};
+
+TEST_F(EmbedderTest, ChannelsBasic) {
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+
+  {
+    PlatformChannelPair channel_pair;
+    ScopedTestChannel server_channel(test_io_thread()->task_runner(),
+                                     channel_pair.PassServerHandle());
+    MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+    EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+    ScopedTestChannel client_channel(test_io_thread()->task_runner(),
+                                     channel_pair.PassClientHandle());
+    MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+    EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+
+    // We can write to a message pipe handle immediately.
+    const char kHello[] = "hello";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp,
+                               kHello,
+                               static_cast<uint32_t>(sizeof(kHello)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Now wait for the other side to become readable.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(
+            client_mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+    char buffer[1000] = {};
+    uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(client_mp,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(sizeof(kHello), num_bytes);
+    EXPECT_STREQ(kHello, buffer);
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+
+    // By this point, these waits should basically be no-ops (since we've waited
+    // for the client message pipe to become readable, which implies that both
+    // the server and client channels were completely created).
+    server_channel.WaitForChannelCreationCompletion();
+    client_channel.WaitForChannelCreationCompletion();
+    EXPECT_TRUE(server_channel.channel_info());
+    EXPECT_TRUE(client_channel.channel_info());
+  }
+
+  EXPECT_TRUE(test::Shutdown());
+}
+
+TEST_F(EmbedderTest, ChannelsHandlePassing) {
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+
+  {
+    PlatformChannelPair channel_pair;
+    ScopedTestChannel server_channel(test_io_thread()->task_runner(),
+                                     channel_pair.PassServerHandle());
+    MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+    EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+    ScopedTestChannel client_channel(test_io_thread()->task_runner(),
+                                     channel_pair.PassClientHandle());
+    MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+    EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+
+    MojoHandle h0, h1;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1));
+
+    // Write a message to |h0| (attaching nothing).
+    const char kHello[] = "hello";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h0,
+                               kHello,
+                               static_cast<uint32_t>(sizeof(kHello)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Write one message to |server_mp|, attaching |h1|.
+    const char kWorld[] = "world!!!";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp,
+                               kWorld,
+                               static_cast<uint32_t>(sizeof(kWorld)),
+                               &h1,
+                               1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    h1 = MOJO_HANDLE_INVALID;
+
+    // Write another message to |h0|.
+    const char kFoo[] = "foo";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h0,
+                               kFoo,
+                               static_cast<uint32_t>(sizeof(kFoo)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for |client_mp| to become readable.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(
+            client_mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+    // Read a message from |client_mp|.
+    char buffer[1000] = {};
+    uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    MojoHandle handles[10] = {};
+    uint32_t num_handles = arraysize(handles);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(client_mp,
+                              buffer,
+                              &num_bytes,
+                              handles,
+                              &num_handles,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(sizeof(kWorld), num_bytes);
+    EXPECT_STREQ(kWorld, buffer);
+    EXPECT_EQ(1u, num_handles);
+    EXPECT_NE(handles[0], MOJO_HANDLE_INVALID);
+    h1 = handles[0];
+
+    // Wait for |h1| to become readable.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+    // Read a message from |h1|.
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    memset(handles, 0, sizeof(handles));
+    num_handles = arraysize(handles);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(h1,
+                              buffer,
+                              &num_bytes,
+                              handles,
+                              &num_handles,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(sizeof(kHello), num_bytes);
+    EXPECT_STREQ(kHello, buffer);
+    EXPECT_EQ(0u, num_handles);
+
+    // Wait for |h1| to become readable (again).
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+    // Read the second message from |h1|.
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(h1,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(sizeof(kFoo), num_bytes);
+    EXPECT_STREQ(kFoo, buffer);
+
+    // Write a message to |h1|.
+    const char kBarBaz[] = "barbaz";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h1,
+                               kBarBaz,
+                               static_cast<uint32_t>(sizeof(kBarBaz)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for |h0| to become readable.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+    // Read a message from |h0|.
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(h0,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(sizeof(kBarBaz), num_bytes);
+    EXPECT_STREQ(kBarBaz, buffer);
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+
+    server_channel.WaitForChannelCreationCompletion();
+    client_channel.WaitForChannelCreationCompletion();
+    EXPECT_TRUE(server_channel.channel_info());
+    EXPECT_TRUE(client_channel.channel_info());
+  }
+
+  EXPECT_TRUE(test::Shutdown());
+}
+
+// The sequence of messages sent is:
+//       server_mp   client_mp   mp0         mp1         mp2         mp3
+//   1.  "hello"
+//   2.              "world!"
+//   3.                          "FOO"
+//   4.  "Bar"+mp1
+//   5.  (close)
+//   6.              (close)
+//   7.                                                              "baz"
+//   8.                                                              (closed)
+//   9.                                      "quux"+mp2
+//  10.                          (close)
+//  11.                                      (wait/cl.)
+//  12.                                                  (wait/cl.)
+TEST_F(EmbedderTest, MultiprocessChannels) {
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+  mojo::test::MultiprocessTestHelper multiprocess_test_helper;
+  multiprocess_test_helper.StartChild("MultiprocessChannelsClient");
+
+  {
+    ScopedTestChannel server_channel(
+        test_io_thread()->task_runner(),
+        multiprocess_test_helper.server_platform_handle.Pass());
+    MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+    EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+    server_channel.WaitForChannelCreationCompletion();
+    EXPECT_TRUE(server_channel.channel_info());
+
+    // 1. Write a message to |server_mp| (attaching nothing).
+    const char kHello[] = "hello";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp,
+                               kHello,
+                               static_cast<uint32_t>(sizeof(kHello)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // TODO(vtl): If the scope were ended immediately here (maybe after closing
+    // |server_mp|), we die with a fatal error in |Channel::HandleLocalError()|.
+
+    // 2. Read a message from |server_mp|.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(
+            server_mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    char buffer[1000] = {};
+    uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(server_mp,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kWorld[] = "world!";
+    EXPECT_EQ(sizeof(kWorld), num_bytes);
+    EXPECT_STREQ(kWorld, buffer);
+
+    // Create a new message pipe (endpoints |mp0| and |mp1|).
+    MojoHandle mp0, mp1;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mp0, &mp1));
+
+    // 3. Write something to |mp0|.
+    const char kFoo[] = "FOO";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp0,
+                               kFoo,
+                               static_cast<uint32_t>(sizeof(kFoo)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // 4. Write a message to |server_mp|, attaching |mp1|.
+    const char kBar[] = "Bar";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp,
+                               kBar,
+                               static_cast<uint32_t>(sizeof(kBar)),
+                               &mp1,
+                               1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    mp1 = MOJO_HANDLE_INVALID;
+
+    // 5. Close |server_mp|.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+
+    // 9. Read a message from |mp0|, which should have |mp2| attached.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(mp0, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    MojoHandle mp2 = MOJO_HANDLE_INVALID;
+    uint32_t num_handles = 1;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(mp0,
+                              buffer,
+                              &num_bytes,
+                              &mp2,
+                              &num_handles,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kQuux[] = "quux";
+    EXPECT_EQ(sizeof(kQuux), num_bytes);
+    EXPECT_STREQ(kQuux, buffer);
+    EXPECT_EQ(1u, num_handles);
+    EXPECT_NE(mp2, MOJO_HANDLE_INVALID);
+
+    // 7. Read a message from |mp2|.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(mp2,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kBaz[] = "baz";
+    EXPECT_EQ(sizeof(kBaz), num_bytes);
+    EXPECT_STREQ(kBaz, buffer);
+
+    // 10. Close |mp0|.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp0));
+
+// 12. Wait on |mp2| (which should eventually fail) and then close it.
+// TODO(vtl): crbug.com/351768
+#if 0
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE));
+#endif
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp2));
+  }
+
+  EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown());
+  EXPECT_TRUE(test::Shutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) {
+  ScopedPlatformHandle client_platform_handle =
+      mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+  EXPECT_TRUE(client_platform_handle.is_valid());
+
+  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
+  mojo::embedder::test::InitWithSimplePlatformSupport();
+
+  {
+    ScopedTestChannel client_channel(test_io_thread.task_runner(),
+                                     client_platform_handle.Pass());
+    MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+    EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+    client_channel.WaitForChannelCreationCompletion();
+    CHECK(client_channel.channel_info() != nullptr);
+
+    // 1. Read the first message from |client_mp|.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(
+            client_mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    char buffer[1000] = {};
+    uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(client_mp,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kHello[] = "hello";
+    EXPECT_EQ(sizeof(kHello), num_bytes);
+    EXPECT_STREQ(kHello, buffer);
+
+    // 2. Write a message to |client_mp| (attaching nothing).
+    const char kWorld[] = "world!";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(client_mp,
+                               kWorld,
+                               static_cast<uint32_t>(sizeof(kWorld)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // 4. Read a message from |client_mp|, which should have |mp1| attached.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(
+            client_mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    // TODO(vtl): If the scope were to end here (and |client_mp| closed), we'd
+    // die (again due to |Channel::HandleLocalError()|).
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    MojoHandle mp1 = MOJO_HANDLE_INVALID;
+    uint32_t num_handles = 1;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(client_mp,
+                              buffer,
+                              &num_bytes,
+                              &mp1,
+                              &num_handles,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kBar[] = "Bar";
+    EXPECT_EQ(sizeof(kBar), num_bytes);
+    EXPECT_STREQ(kBar, buffer);
+    EXPECT_EQ(1u, num_handles);
+    EXPECT_NE(mp1, MOJO_HANDLE_INVALID);
+    // TODO(vtl): If the scope were to end here (and the two handles closed),
+    // we'd die due to |Channel::RunRemoteMessagePipeEndpoint()| not handling
+    // write errors (assuming the parent had closed the pipe).
+
+    // 6. Close |client_mp|.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+
+    // Create a new message pipe (endpoints |mp2| and |mp3|).
+    MojoHandle mp2, mp3;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mp2, &mp3));
+
+    // 7. Write a message to |mp3|.
+    const char kBaz[] = "baz";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp3,
+                               kBaz,
+                               static_cast<uint32_t>(sizeof(kBaz)),
+                               nullptr,
+                               0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // 8. Close |mp3|.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp3));
+
+    // 9. Write a message to |mp1|, attaching |mp2|.
+    const char kQuux[] = "quux";
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp1,
+                               kQuux,
+                               static_cast<uint32_t>(sizeof(kQuux)),
+                               &mp2,
+                               1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    mp2 = MOJO_HANDLE_INVALID;
+
+    // 3. Read a message from |mp1|.
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    memset(buffer, 0, sizeof(buffer));
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(mp1,
+                              buffer,
+                              &num_bytes,
+                              nullptr,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    const char kFoo[] = "FOO";
+    EXPECT_EQ(sizeof(kFoo), num_bytes);
+    EXPECT_STREQ(kFoo, buffer);
+
+    // 11. Wait on |mp1| (which should eventually fail) and then close it.
+    EXPECT_EQ(
+        MOJO_RESULT_FAILED_PRECONDITION,
+        MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp1));
+  }
+
+  EXPECT_TRUE(test::Shutdown());
+}
+
+// TODO(vtl): Test immediate write & close.
+// TODO(vtl): Test broken-connection cases.
+
+}  // namespace
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_pair.cc b/mojo/edk/embedder/platform_channel_pair.cc
new file mode 100644
index 0000000..4e88bc9
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair.cc
@@ -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.
+
+#include "mojo/edk/embedder/platform_channel_pair.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] =
+    "mojo-platform-channel-handle";
+
+PlatformChannelPair::~PlatformChannelPair() {
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassServerHandle() {
+  return server_handle_.Pass();
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassClientHandle() {
+  return client_handle_.Pass();
+}
+
+void PlatformChannelPair::ChildProcessLaunched() {
+  DCHECK(client_handle_.is_valid());
+  client_handle_.reset();
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h
new file mode 100644
index 0000000..d77c100
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/launch.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace mojo {
+namespace embedder {
+
+// It would be nice to refactor base/process/launch.h to have a more platform-
+// independent way of representing handles that are passed to child processes.
+#if defined(OS_WIN)
+typedef base::HandlesToInheritVector HandlePassingInformation;
+#elif defined(OS_POSIX)
+typedef base::FileHandleMappingVector HandlePassingInformation;
+#else
+#error "Unsupported."
+#endif
+
+// This is used to create a pair of |PlatformHandle|s that are connected by a
+// suitable (platform-specific) bidirectional "pipe" (e.g., socket on POSIX,
+// named pipe on Windows). The resulting handles can then be used in the same
+// process (e.g., in tests) or between processes. (The "server" handle is the
+// one that will be used in the process that created the pair, whereas the
+// "client" handle is the one that will be used in a different process.)
+//
+// This class provides facilities for passing the client handle to a child
+// process. The parent should call |PrepareToPassClientHandlelToChildProcess()|
+// to get the data needed to do this, spawn the child using that data, and then
+// call |ChildProcessLaunched()|. Note that on Windows this facility (will) only
+// work on Vista and later (TODO(vtl)).
+//
+// Note: |PlatformChannelPair()|, |PassClientHandleFromParentProcess()| and
+// |PrepareToPassClientHandleToChildProcess()| have platform-specific
+// implementations.
+//
+// Note: On POSIX platforms, to write to the "pipe", use
+// |PlatformChannel{Write,Writev}()| (from platform_channel_utils_posix.h)
+// instead of |write()|, |writev()|, etc. Otherwise, you have to worry about
+// platform differences in suppressing |SIGPIPE|.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformChannelPair {
+ public:
+  PlatformChannelPair();
+  ~PlatformChannelPair();
+
+  ScopedPlatformHandle PassServerHandle();
+
+  // For in-process use (e.g., in tests or to pass over another channel).
+  ScopedPlatformHandle PassClientHandle();
+
+  // To be called in the child process, after the parent process called
+  // |PrepareToPassClientHandleToChildProcess()| and launched the child (using
+  // the provided data), to create a client handle connected to the server
+  // handle (in the parent process).
+  static ScopedPlatformHandle PassClientHandleFromParentProcess(
+      const base::CommandLine& command_line);
+
+  // Prepares to pass the client channel to a new child process, to be launched
+  // using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
+  // |*handle_passing_info| as needed.
+  // Note: For Windows, this method only works on Vista and later.
+  void PrepareToPassClientHandleToChildProcess(
+      base::CommandLine* command_line,
+      HandlePassingInformation* handle_passing_info) const;
+
+  // To be called once the child process has been successfully launched, to do
+  // any cleanup necessary.
+  void ChildProcessLaunched();
+
+ private:
+  static const char kMojoPlatformChannelHandleSwitch[];
+
+  ScopedPlatformHandle server_handle_;
+  ScopedPlatformHandle client_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
diff --git a/mojo/edk/embedder/platform_channel_pair_posix.cc b/mojo/edk/embedder/platform_channel_pair_posix.cc
new file mode 100644
index 0000000..f9886b5
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_posix.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium 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/edk/embedder/platform_channel_pair.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+namespace mojo {
+namespace embedder {
+
+namespace {
+
+bool IsTargetDescriptorUsed(
+    const base::FileHandleMappingVector& file_handle_mapping,
+    int target_fd) {
+  for (size_t i = 0; i < file_handle_mapping.size(); i++) {
+    if (file_handle_mapping[i].second == target_fd)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+PlatformChannelPair::PlatformChannelPair() {
+  // Create the Unix domain socket and set the ends to nonblocking.
+  int fds[2];
+  // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
+  PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
+  PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
+  PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
+
+#if defined(OS_MACOSX)
+  // This turns off |SIGPIPE| when writing to a closed socket (causing it to
+  // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
+  // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
+  int no_sigpipe = 1;
+  PCHECK(
+      setsockopt(
+          fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(no_sigpipe)) ==
+      0);
+  PCHECK(
+      setsockopt(
+          fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(no_sigpipe)) ==
+      0);
+#endif  // defined(OS_MACOSX)
+
+  server_handle_.reset(PlatformHandle(fds[0]));
+  DCHECK(server_handle_.is_valid());
+  client_handle_.reset(PlatformHandle(fds[1]));
+  DCHECK(client_handle_.is_valid());
+}
+
+// static
+ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
+    const base::CommandLine& command_line) {
+  std::string client_fd_string =
+      command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+  int client_fd = -1;
+  if (client_fd_string.empty() ||
+      !base::StringToInt(client_fd_string, &client_fd) ||
+      client_fd < base::GlobalDescriptors::kBaseDescriptor) {
+    LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
+    return ScopedPlatformHandle();
+  }
+
+  return ScopedPlatformHandle(PlatformHandle(client_fd));
+}
+
+void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+    base::CommandLine* command_line,
+    base::FileHandleMappingVector* handle_passing_info) const {
+  DCHECK(command_line);
+  DCHECK(handle_passing_info);
+  // This is an arbitrary sanity check. (Note that this guarantees that the loop
+  // below will terminate sanely.)
+  CHECK_LT(handle_passing_info->size(), 1000u);
+
+  DCHECK(client_handle_.is_valid());
+
+  // Find a suitable FD to map our client handle to in the child process.
+  // This has quadratic time complexity in the size of |*handle_passing_info|,
+  // but |*handle_passing_info| should be very small (usually/often empty).
+  int target_fd = base::GlobalDescriptors::kBaseDescriptor;
+  while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
+    target_fd++;
+
+  handle_passing_info->push_back(
+      std::pair<int, int>(client_handle_.get().fd, target_fd));
+  // Log a warning if the command line already has the switch, but "clobber" it
+  // anyway, since it's reasonably likely that all the switches were just copied
+  // from the parent.
+  LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
+      << "Child command line already has switch --"
+      << kMojoPlatformChannelHandleSwitch << "="
+      << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+  // (Any existing switch won't actually be removed from the command line, but
+  // the last one appended takes precedence.)
+  command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch,
+                                  base::IntToString(target_fd));
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
new file mode 100644
index 0000000..355e573
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium 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/edk/embedder/platform_channel_pair.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <deque>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace embedder {
+namespace {
+
+void WaitReadable(PlatformHandle h) {
+  struct pollfd pfds = {};
+  pfds.fd = h.fd;
+  pfds.events = POLLIN;
+  CHECK_EQ(poll(&pfds, 1, -1), 1);
+}
+
+class PlatformChannelPairPosixTest : public testing::Test {
+ public:
+  PlatformChannelPairPosixTest() {}
+  virtual ~PlatformChannelPairPosixTest() {}
+
+  virtual void SetUp() override {
+    // Make sure |SIGPIPE| isn't being ignored.
+    struct sigaction action = {};
+    action.sa_handler = SIG_DFL;
+    ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_));
+  }
+
+  virtual void TearDown() override {
+    // Restore the |SIGPIPE| handler.
+    ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, nullptr));
+  }
+
+ private:
+  struct sigaction old_action_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
+};
+
+TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
+  PlatformChannelPair channel_pair;
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+  // Write to the client.
+  static const char kHello[] = "hello";
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+            write(client_handle.get().fd, kHello, sizeof(kHello)));
+
+  // Close the client.
+  client_handle.reset();
+
+  // Read from the server; this should be okay.
+  char buffer[100] = {};
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+            read(server_handle.get().fd, buffer, sizeof(buffer)));
+  EXPECT_STREQ(kHello, buffer);
+
+  // Try reading again.
+  ssize_t result = read(server_handle.get().fd, buffer, sizeof(buffer));
+  // We should probably get zero (for "end of file"), but -1 would also be okay.
+  EXPECT_TRUE(result == 0 || result == -1);
+  if (result == -1)
+    PLOG(WARNING) << "read (expected 0 for EOF)";
+
+  // Test our replacement for |write()|/|send()|.
+  result = PlatformChannelWrite(server_handle.get(), kHello, sizeof(kHello));
+  EXPECT_EQ(-1, result);
+  if (errno != EPIPE)
+    PLOG(WARNING) << "write (expected EPIPE)";
+
+  // Test our replacement for |writev()|/|sendv()|.
+  struct iovec iov[2] = {{const_cast<char*>(kHello), sizeof(kHello)},
+                         {const_cast<char*>(kHello), sizeof(kHello)}};
+  result = PlatformChannelWritev(server_handle.get(), iov, 2);
+  EXPECT_EQ(-1, result);
+  if (errno != EPIPE)
+    PLOG(WARNING) << "write (expected EPIPE)";
+}
+
+TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
+  PlatformChannelPair channel_pair;
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+  for (size_t i = 0; i < 10; i++) {
+    std::string send_string(1 << i, 'A' + i);
+
+    EXPECT_EQ(static_cast<ssize_t>(send_string.size()),
+              PlatformChannelWrite(
+                  server_handle.get(), send_string.data(), send_string.size()));
+
+    WaitReadable(client_handle.get());
+
+    char buf[10000] = {};
+    std::deque<PlatformHandle> received_handles;
+    ssize_t result = PlatformChannelRecvmsg(
+        client_handle.get(), buf, sizeof(buf), &received_handles);
+    EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result);
+    EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result)));
+    EXPECT_TRUE(received_handles.empty());
+  }
+}
+
+TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static const char kHello[] = "hello";
+
+  PlatformChannelPair channel_pair;
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+  for (size_t i = 1; i < kPlatformChannelMaxNumHandles; i++) {
+    // Make |i| files, with the j-th file consisting of j copies of the digit i.
+    PlatformHandleVector platform_handles;
+    for (size_t j = 1; j <= i; j++) {
+      base::FilePath unused;
+      base::ScopedFILE fp(
+          base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+      ASSERT_TRUE(fp);
+      ASSERT_EQ(j, fwrite(std::string(j, '0' + i).data(), 1, j, fp.get()));
+      platform_handles.push_back(
+          test::PlatformHandleFromFILE(fp.Pass()).release());
+      ASSERT_TRUE(platform_handles.back().is_valid());
+    }
+
+    // Send the FDs (+ "hello").
+    struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
+    // We assume that the |sendmsg()| actually sends all the data.
+    EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+              PlatformChannelSendmsgWithHandles(server_handle.get(),
+                                                &iov,
+                                                1,
+                                                &platform_handles[0],
+                                                platform_handles.size()));
+
+    WaitReadable(client_handle.get());
+
+    char buf[100] = {};
+    std::deque<PlatformHandle> received_handles;
+    // We assume that the |recvmsg()| actually reads all the data.
+    EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+              PlatformChannelRecvmsg(
+                  client_handle.get(), buf, sizeof(buf), &received_handles));
+    EXPECT_STREQ(kHello, buf);
+    EXPECT_EQ(i, received_handles.size());
+
+    for (size_t j = 0; !received_handles.empty(); j++) {
+      base::ScopedFILE fp(test::FILEFromPlatformHandle(
+          ScopedPlatformHandle(received_handles.front()), "rb"));
+      received_handles.pop_front();
+      ASSERT_TRUE(fp);
+      rewind(fp.get());
+      char read_buf[100];
+      size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
+      EXPECT_EQ(j + 1, bytes_read);
+      EXPECT_EQ(std::string(j + 1, '0' + i), std::string(read_buf, bytes_read));
+    }
+  }
+}
+
+TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static const char kHello[] = "hello";
+
+  PlatformChannelPair channel_pair;
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+  const std::string file_contents("hello world");
+
+  {
+    base::FilePath unused;
+    base::ScopedFILE fp(
+        base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+    ASSERT_TRUE(fp);
+    ASSERT_EQ(file_contents.size(),
+              fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
+    PlatformHandleVector platform_handles;
+    platform_handles.push_back(
+        test::PlatformHandleFromFILE(fp.Pass()).release());
+    ASSERT_TRUE(platform_handles.back().is_valid());
+
+    // Send the FD (+ "hello").
+    struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
+    // We assume that the |sendmsg()| actually sends all the data.
+    EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+              PlatformChannelSendmsgWithHandles(server_handle.get(),
+                                                &iov,
+                                                1,
+                                                &platform_handles[0],
+                                                platform_handles.size()));
+  }
+
+  WaitReadable(client_handle.get());
+
+  // Start with an invalid handle in the deque.
+  std::deque<PlatformHandle> received_handles;
+  received_handles.push_back(PlatformHandle());
+
+  char buf[100] = {};
+  // We assume that the |recvmsg()| actually reads all the data.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+            PlatformChannelRecvmsg(
+                client_handle.get(), buf, sizeof(buf), &received_handles));
+  EXPECT_STREQ(kHello, buf);
+  ASSERT_EQ(2u, received_handles.size());
+  EXPECT_FALSE(received_handles[0].is_valid());
+  EXPECT_TRUE(received_handles[1].is_valid());
+
+  {
+    base::ScopedFILE fp(test::FILEFromPlatformHandle(
+        ScopedPlatformHandle(received_handles[1]), "rb"));
+    received_handles[1] = PlatformHandle();
+    ASSERT_TRUE(fp);
+    rewind(fp.get());
+    char read_buf[100];
+    size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
+    EXPECT_EQ(file_contents.size(), bytes_read);
+    EXPECT_EQ(file_contents, std::string(read_buf, bytes_read));
+  }
+}
+
+}  // namespace
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_pair_win.cc b/mojo/edk/embedder/platform_channel_pair_win.cc
new file mode 100644
index 0000000..1cafc4f
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_win.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 "mojo/edk/embedder/platform_channel_pair.h"
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/windows_version.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+namespace mojo {
+namespace embedder {
+
+namespace {
+
+std::wstring GeneratePipeName() {
+  return base::StringPrintf(L"\\\\.\\pipe\\mojo.%u.%u.%I64u",
+                            GetCurrentProcessId(),
+                            GetCurrentThreadId(),
+                            base::RandUint64());
+}
+
+}  // namespace
+
+PlatformChannelPair::PlatformChannelPair() {
+  std::wstring pipe_name = GeneratePipeName();
+
+  const DWORD kOpenMode =
+      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
+  const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
+  server_handle_.reset(PlatformHandle(
+      CreateNamedPipeW(pipe_name.c_str(),
+                       kOpenMode,
+                       kPipeMode,
+                       1,           // Max instances.
+                       4096,        // Out buffer size.
+                       4096,        // In buffer size.
+                       5000,        // Timeout in milliseconds.
+                       nullptr)));  // Default security descriptor.
+  PCHECK(server_handle_.is_valid());
+
+  const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+  // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
+  // the client.
+  const DWORD kFlags =
+      SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
+  // Allow the handle to be inherited by child processes.
+  SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
+                                             nullptr, TRUE};
+  client_handle_.reset(
+      PlatformHandle(CreateFileW(pipe_name.c_str(),
+                                 kDesiredAccess,
+                                 0,  // No sharing.
+                                 &security_attributes,
+                                 OPEN_EXISTING,
+                                 kFlags,
+                                 nullptr)));  // No template file.
+  PCHECK(client_handle_.is_valid());
+
+  // Since a client has connected, ConnectNamedPipe() should return zero and
+  // GetLastError() should return ERROR_PIPE_CONNECTED.
+  CHECK(!ConnectNamedPipe(server_handle_.get().handle, nullptr));
+  PCHECK(GetLastError() == ERROR_PIPE_CONNECTED);
+}
+
+// static
+ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
+    const base::CommandLine& command_line) {
+  std::string client_handle_string =
+      command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+
+  int client_handle_value = 0;
+  if (client_handle_string.empty() ||
+      !base::StringToInt(client_handle_string, &client_handle_value)) {
+    LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
+    return ScopedPlatformHandle();
+  }
+
+  return ScopedPlatformHandle(
+      PlatformHandle(LongToHandle(client_handle_value)));
+}
+
+void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+    base::CommandLine* command_line,
+    base::HandlesToInheritVector* handle_passing_info) const {
+  DCHECK(command_line);
+  DCHECK(handle_passing_info);
+  DCHECK(client_handle_.is_valid());
+
+  CHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA);
+
+  handle_passing_info->push_back(client_handle_.get().handle);
+
+  // Log a warning if the command line already has the switch, but "clobber" it
+  // anyway, since it's reasonably likely that all the switches were just copied
+  // from the parent.
+  LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
+      << "Child command line already has switch --"
+      << kMojoPlatformChannelHandleSwitch << "="
+      << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+  // (Any existing switch won't actually be removed from the command line, but
+  // the last one appended takes precedence.)
+  command_line->AppendSwitchASCII(
+      kMojoPlatformChannelHandleSwitch,
+      base::IntToString(HandleToLong(client_handle_.get().handle)));
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils_posix.cc
new file mode 100644
index 0000000..61b573b
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_utils_posix.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 The Chromium 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/edk/embedder/platform_channel_utils_posix.h"
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace embedder {
+
+// On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to
+// |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on
+// |write()|/|writev().) On Mac, |SIGPIPE| is suppressed by setting the
+// |SO_NOSIGPIPE| option on the socket.
+//
+// Performance notes:
+//  - On Linux, we have to use |send()|/|sendmsg()| rather than
+//    |write()|/|writev()| in order to suppress |SIGPIPE|. This is okay, since
+//    |send()| is (slightly) faster than |write()| (!), while |sendmsg()| is
+//    quite comparable to |writev()|.
+//  - On Mac, we may use |write()|/|writev()|. Here, |write()| is considerably
+//    faster than |send()|, whereas |sendmsg()| is quite comparable to
+//    |writev()|.
+//  - On both platforms, an appropriate |sendmsg()|/|writev()| is considerably
+//    faster than two |send()|s/|write()|s.
+//  - Relative numbers (minimum real times from 10 runs) for one |write()| of
+//    1032 bytes, one |send()| of 1032 bytes, one |writev()| of 32+1000 bytes,
+//    one |sendmsg()| of 32+1000 bytes, two |write()|s of 32 and 1000 bytes, two
+//    |send()|s of 32 and 1000 bytes:
+//    - Linux: 0.81 s, 0.77 s, 0.87 s, 0.89 s, 1.31 s, 1.22 s
+//    - Mac: 2.21 s, 2.91 s, 2.98 s, 3.08 s, 3.59 s, 4.74 s
+
+// Flags to use with calling |send()| or |sendmsg()| (see above).
+#if defined(OS_MACOSX)
+const int kSendFlags = 0;
+#else
+const int kSendFlags = MSG_NOSIGNAL;
+#endif
+
+ssize_t PlatformChannelWrite(PlatformHandle h,
+                             const void* bytes,
+                             size_t num_bytes) {
+  DCHECK(h.is_valid());
+  DCHECK(bytes);
+  DCHECK_GT(num_bytes, 0u);
+
+#if defined(OS_MACOSX)
+  return HANDLE_EINTR(write(h.fd, bytes, num_bytes));
+#else
+  return send(h.fd, bytes, num_bytes, kSendFlags);
+#endif
+}
+
+ssize_t PlatformChannelWritev(PlatformHandle h,
+                              struct iovec* iov,
+                              size_t num_iov) {
+  DCHECK(h.is_valid());
+  DCHECK(iov);
+  DCHECK_GT(num_iov, 0u);
+
+#if defined(OS_MACOSX)
+  return HANDLE_EINTR(writev(h.fd, iov, static_cast<int>(num_iov)));
+#else
+  struct msghdr msg = {};
+  msg.msg_iov = iov;
+  msg.msg_iovlen = num_iov;
+  return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+#endif
+}
+
+ssize_t PlatformChannelSendmsgWithHandles(PlatformHandle h,
+                                          struct iovec* iov,
+                                          size_t num_iov,
+                                          PlatformHandle* platform_handles,
+                                          size_t num_platform_handles) {
+  DCHECK(iov);
+  DCHECK_GT(num_iov, 0u);
+  DCHECK(platform_handles);
+  DCHECK_GT(num_platform_handles, 0u);
+  DCHECK_LE(num_platform_handles, kPlatformChannelMaxNumHandles);
+
+  char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+  struct msghdr msg = {};
+  msg.msg_iov = iov;
+  msg.msg_iovlen = num_iov;
+  msg.msg_control = cmsg_buf;
+  msg.msg_controllen = CMSG_LEN(num_platform_handles * sizeof(int));
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(num_platform_handles * sizeof(int));
+  for (size_t i = 0; i < num_platform_handles; i++) {
+    DCHECK(platform_handles[i].is_valid());
+    reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = platform_handles[i].fd;
+  }
+
+  return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+}
+
+bool PlatformChannelSendHandles(PlatformHandle h,
+                                PlatformHandle* handles,
+                                size_t num_handles) {
+  DCHECK(handles);
+  DCHECK_GT(num_handles, 0u);
+  DCHECK_LE(num_handles, kPlatformChannelMaxNumHandles);
+
+  // Note: |sendmsg()| fails on Mac if we don't write at least one character.
+  struct iovec iov = {const_cast<char*>(""), 1};
+  char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+  struct msghdr msg = {};
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsg_buf;
+  msg.msg_controllen = CMSG_LEN(num_handles * sizeof(int));
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(num_handles * sizeof(int));
+  for (size_t i = 0; i < num_handles; i++) {
+    DCHECK(handles[i].is_valid());
+    reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].fd;
+  }
+
+  ssize_t result = HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+  if (result < 1) {
+    DCHECK_EQ(result, -1);
+    return false;
+  }
+
+  for (size_t i = 0; i < num_handles; i++)
+    handles[i].CloseIfNecessary();
+  return true;
+}
+
+ssize_t PlatformChannelRecvmsg(PlatformHandle h,
+                               void* buf,
+                               size_t num_bytes,
+                               std::deque<PlatformHandle>* platform_handles) {
+  DCHECK(buf);
+  DCHECK_GT(num_bytes, 0u);
+  DCHECK(platform_handles);
+
+  struct iovec iov = {buf, num_bytes};
+  char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+  struct msghdr msg = {};
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsg_buf;
+  msg.msg_controllen = sizeof(cmsg_buf);
+
+  ssize_t result = HANDLE_EINTR(recvmsg(h.fd, &msg, MSG_DONTWAIT));
+  if (result < 0)
+    return result;
+
+  // Success; no control messages.
+  if (msg.msg_controllen == 0)
+    return result;
+
+  DCHECK(!(msg.msg_flags & MSG_CTRUNC));
+
+  for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+      size_t payload_length = cmsg->cmsg_len - CMSG_LEN(0);
+      DCHECK_EQ(payload_length % sizeof(int), 0u);
+      size_t num_fds = payload_length / sizeof(int);
+      const int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+      for (size_t i = 0; i < num_fds; i++) {
+        platform_handles->push_back(PlatformHandle(fds[i]));
+        DCHECK(platform_handles->back().is_valid());
+      }
+    }
+  }
+
+  return result;
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.h b/mojo/edk/embedder/platform_channel_utils_posix.h
new file mode 100644
index 0000000..87176fb
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_utils_posix.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_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+
+#include <stddef.h>
+#include <sys/types.h>  // For |ssize_t|.
+
+#include <deque>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+struct iovec;  // Declared in <sys/uio.h>.
+
+namespace mojo {
+namespace embedder {
+
+// The maximum number of handles that can be sent "at once" using
+// |PlatformChannelSendmsgWithHandles()|.
+// TODO(vtl): This number is taken from ipc/file_descriptor_set_posix.h:
+// |FileDescriptorSet::kMaxDescriptorsPerMessage|. Where does it come from?
+const size_t kPlatformChannelMaxNumHandles = 7;
+
+// Use these to write to a socket created using |PlatformChannelPair| (or
+// equivalent). These are like |write()| and |writev()|, but handle |EINTR| and
+// never raise |SIGPIPE|. (Note: On Mac, the suppression of |SIGPIPE| is set up
+// by |PlatformChannelPair|.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t
+    PlatformChannelWrite(PlatformHandle h, const void* bytes, size_t num_bytes);
+MOJO_SYSTEM_IMPL_EXPORT ssize_t
+    PlatformChannelWritev(PlatformHandle h, struct iovec* iov, size_t num_iov);
+
+// Writes data, and the given set of |PlatformHandle|s (i.e., file descriptors)
+// over the Unix domain socket given by |h| (e.g., created using
+// |PlatformChannelPair()|). All the handles must be valid, and there must be at
+// least one and at most |kPlatformChannelMaxNumHandles| handles. The return
+// value is as for |sendmsg()|, namely -1 on failure and otherwise the number of
+// bytes of data sent on success (note that this may not be all the data
+// specified by |iov|). (The handles are not closed, regardless of success or
+// failure.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t
+    PlatformChannelSendmsgWithHandles(PlatformHandle h,
+                                      struct iovec* iov,
+                                      size_t num_iov,
+                                      PlatformHandle* platform_handles,
+                                      size_t num_platform_handles);
+
+// TODO(vtl): Remove this once I've switched things over to
+// |PlatformChannelSendmsgWithHandles()|.
+// Sends |PlatformHandle|s (i.e., file descriptors) over the Unix domain socket
+// (e.g., created using PlatformChannelPair|). (These will be sent in a single
+// message having one null byte of data and one control message header with all
+// the file descriptors.) All of the handles must be valid, and there must be at
+// most |kPlatformChannelMaxNumHandles| (and at least one handle). Returns true
+// on success, in which case it closes all the handles.
+MOJO_SYSTEM_IMPL_EXPORT bool PlatformChannelSendHandles(PlatformHandle h,
+                                                        PlatformHandle* handles,
+                                                        size_t num_handles);
+
+// Wrapper around |recvmsg()|, which will extract any attached file descriptors
+// (in the control message) to |PlatformHandle|s (and append them to
+// |platform_handles|). (This also handles |EINTR|.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t
+    PlatformChannelRecvmsg(PlatformHandle h,
+                           void* buf,
+                           size_t num_bytes,
+                           std::deque<PlatformHandle>* platform_handles);
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
diff --git a/mojo/edk/embedder/platform_handle.cc b/mojo/edk/embedder/platform_handle.cc
new file mode 100644
index 0000000..4675714
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle.cc
@@ -0,0 +1,40 @@
+// 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/edk/embedder/platform_handle.h"
+
+#include "build/build_config.h"
+#if defined(OS_POSIX)
+#include <unistd.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#else
+#error "Platform not yet supported."
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+void PlatformHandle::CloseIfNecessary() {
+  if (!is_valid())
+    return;
+
+#if defined(OS_POSIX)
+  bool success = (close(fd) == 0);
+  DPCHECK(success);
+  fd = -1;
+#elif defined(OS_WIN)
+  bool success = !!CloseHandle(handle);
+  DPCHECK(success);
+  handle = INVALID_HANDLE_VALUE;
+#else
+#error "Platform not yet supported."
+#endif
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
new file mode 100644
index 0000000..346301a
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle.h
@@ -0,0 +1,47 @@
+// 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_EDK_EMBEDDER_PLATFORM_HANDLE_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_H_
+
+#include "build/build_config.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace mojo {
+namespace embedder {
+
+#if defined(OS_POSIX)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+  PlatformHandle() : fd(-1) {}
+  explicit PlatformHandle(int fd) : fd(fd) {}
+
+  void CloseIfNecessary();
+
+  bool is_valid() const { return fd != -1; }
+
+  int fd;
+};
+#elif defined(OS_WIN)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+  PlatformHandle() : handle(INVALID_HANDLE_VALUE) {}
+  explicit PlatformHandle(HANDLE handle) : handle(handle) {}
+
+  void CloseIfNecessary();
+
+  bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
+
+  HANDLE handle;
+};
+#else
+#error "Platform not yet supported."
+#endif
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/platform_handle_utils.h b/mojo/edk/embedder/platform_handle_utils.h
new file mode 100644
index 0000000..f594e21
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_utils.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_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
+
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+// Closes all the |PlatformHandle|s in the given container.
+template <typename PlatformHandleContainer>
+MOJO_SYSTEM_IMPL_EXPORT inline void CloseAllPlatformHandles(
+    PlatformHandleContainer* platform_handles) {
+  for (typename PlatformHandleContainer::iterator it =
+           platform_handles->begin();
+       it != platform_handles->end();
+       ++it)
+    it->CloseIfNecessary();
+}
+
+// Duplicates the given |PlatformHandle| (which must be valid). (Returns an
+// invalid |ScopedPlatformHandle| on failure.)
+MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle
+    DuplicatePlatformHandle(PlatformHandle platform_handle);
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
diff --git a/mojo/edk/embedder/platform_handle_utils_posix.cc b/mojo/edk/embedder/platform_handle_utils_posix.cc
new file mode 100644
index 0000000..b3be93f
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_utils_posix.cc
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/embedder/platform_handle_utils.h"
+
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) {
+  DCHECK(platform_handle.is_valid());
+  // Note that |dup()| returns -1 on error (which is exactly the value we use
+  // for invalid |PlatformHandle| FDs).
+  return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.fd)));
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_handle_utils_win.cc b/mojo/edk/embedder/platform_handle_utils_win.cc
new file mode 100644
index 0000000..12745d0
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_utils_win.cc
@@ -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.
+
+#include "mojo/edk/embedder/platform_handle_utils.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) {
+  DCHECK(platform_handle.is_valid());
+
+  HANDLE new_handle;
+  if (!DuplicateHandle(GetCurrentProcess(),
+                       platform_handle.handle,
+                       GetCurrentProcess(),
+                       &new_handle,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS))
+    return ScopedPlatformHandle();
+  DCHECK_NE(new_handle, INVALID_HANDLE_VALUE);
+  return ScopedPlatformHandle(PlatformHandle(new_handle));
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_handle_vector.h b/mojo/edk/embedder/platform_handle_vector.h
new file mode 100644
index 0000000..96356b7
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_vector.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_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_utils.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+typedef std::vector<PlatformHandle> PlatformHandleVector;
+
+// A deleter (for use with |scoped_ptr|) which closes all handles and then
+// |delete|s the |PlatformHandleVector|.
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandleVectorDeleter {
+  void operator()(PlatformHandleVector* platform_handles) const {
+    CloseAllPlatformHandles(platform_handles);
+    delete platform_handles;
+  }
+};
+
+typedef scoped_ptr<PlatformHandleVector, PlatformHandleVectorDeleter>
+    ScopedPlatformHandleVectorPtr;
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h
new file mode 100644
index 0000000..07f8b90
--- /dev/null
+++ b/mojo/edk/embedder/platform_shared_buffer.h
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+class PlatformSharedBufferMapping;
+
+// |PlatformSharedBuffer| is an interface for a thread-safe, ref-counted wrapper
+// around OS-specific shared memory. It has the following features:
+//   - A |PlatformSharedBuffer| simply represents a piece of shared memory that
+//     *may* be mapped and *may* be shared to another process.
+//   - A single |PlatformSharedBuffer| may be mapped multiple times. The
+//     lifetime of the mapping (owned by |PlatformSharedBufferMapping|) is
+//     separate from the lifetime of the |PlatformSharedBuffer|.
+//   - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not
+//     restricted by page size. However, more memory may actually be mapped than
+//     requested.
+//
+// It currently does NOT support the following:
+//   - Sharing read-only. (This will probably eventually be supported.)
+//
+// TODO(vtl): Rectify this with |base::SharedMemory|.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBuffer
+    : public base::RefCountedThreadSafe<PlatformSharedBuffer> {
+ public:
+  // Gets the size of shared buffer (in number of bytes).
+  virtual size_t GetNumBytes() const = 0;
+
+  // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|]
+  // must be contained in [0, |num_bytes|], and |length| must be at least 1.
+  // Returns null on failure.
+  virtual scoped_ptr<PlatformSharedBufferMapping> Map(size_t offset,
+                                                      size_t length) = 0;
+
+  // Checks if |offset| and |length| are valid arguments.
+  virtual bool IsValidMap(size_t offset, size_t length) = 0;
+
+  // Like |Map()|, but doesn't check its arguments (which should have been
+  // preflighted using |IsValidMap()|).
+  virtual scoped_ptr<PlatformSharedBufferMapping> MapNoCheck(size_t offset,
+                                                             size_t length) = 0;
+
+  // Duplicates the underlying platform handle and passes it to the caller.
+  // TODO(vtl): On POSIX, we'll need two FDs to support sharing read-only.
+  virtual ScopedPlatformHandle DuplicatePlatformHandle() = 0;
+
+  // Passes the underlying platform handle to the caller. This should only be
+  // called if there's a unique reference to this object (owned by the caller).
+  // After calling this, this object should no longer be used, but should only
+  // be disposed of.
+  virtual ScopedPlatformHandle PassPlatformHandle() = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<PlatformSharedBuffer>;
+
+  PlatformSharedBuffer() {}
+  virtual ~PlatformSharedBuffer() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PlatformSharedBuffer);
+};
+
+// An interface for a mapping of a |PlatformSharedBuffer| (compararable to a
+// "file view" in Windows); see above. Created by (implementations of)
+// |PlatformSharedBuffer::Map()|. Automatically unmaps memory on destruction.
+//
+// Mappings are NOT thread-safe.
+//
+// Note: This is an entirely separate class (instead of
+// |PlatformSharedBuffer::Mapping|) so that it can be forward-declared.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBufferMapping {
+ public:
+  // IMPORTANT: Implementations must implement a destructor that unmaps memory.
+  virtual ~PlatformSharedBufferMapping() {}
+
+  virtual void* GetBase() const = 0;
+  virtual size_t GetLength() const = 0;
+
+ protected:
+  PlatformSharedBufferMapping() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PlatformSharedBufferMapping);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
diff --git a/mojo/edk/embedder/platform_support.h b/mojo/edk/embedder/platform_support.h
new file mode 100644
index 0000000..4556ee3
--- /dev/null
+++ b/mojo/edk/embedder/platform_support.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_EDK_EMBEDDER_PLATFORM_SUPPORT_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_SUPPORT_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+class PlatformSharedBuffer;
+
+// This class is provided by the embedder to implement (typically
+// platform-dependent) things needed by the Mojo system implementation.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformSupport {
+ public:
+  virtual ~PlatformSupport() {}
+
+  virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) = 0;
+  virtual PlatformSharedBuffer* CreateSharedBufferFromHandle(
+      size_t num_bytes,
+      ScopedPlatformHandle platform_handle) = 0;
+
+ protected:
+  PlatformSupport() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PlatformSupport);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_SUPPORT_H_
diff --git a/mojo/edk/embedder/scoped_platform_handle.h b/mojo/edk/embedder/scoped_platform_handle.h
new file mode 100644
index 0000000..2919b04
--- /dev/null
+++ b/mojo/edk/embedder/scoped_platform_handle.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium 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_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
+#define MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
+
+#include "base/compiler_specific.h"
+#include "base/move.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+class MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle {
+  MOVE_ONLY_TYPE_FOR_CPP_03(ScopedPlatformHandle, RValue)
+
+ public:
+  ScopedPlatformHandle() {}
+  explicit ScopedPlatformHandle(PlatformHandle handle) : handle_(handle) {}
+  ~ScopedPlatformHandle() { handle_.CloseIfNecessary(); }
+
+  // Move-only constructor and operator=.
+  ScopedPlatformHandle(RValue other) : handle_(other.object->release()) {}
+  ScopedPlatformHandle& operator=(RValue other) {
+    handle_ = other.object->release();
+    return *this;
+  }
+
+  const PlatformHandle& get() const { return handle_; }
+
+  void swap(ScopedPlatformHandle& other) {
+    PlatformHandle temp = handle_;
+    handle_ = other.handle_;
+    other.handle_ = temp;
+  }
+
+  PlatformHandle release() WARN_UNUSED_RESULT {
+    PlatformHandle rv = handle_;
+    handle_ = PlatformHandle();
+    return rv;
+  }
+
+  void reset(PlatformHandle handle = PlatformHandle()) {
+    handle_.CloseIfNecessary();
+    handle_ = handle;
+  }
+
+  bool is_valid() const { return handle_.is_valid(); }
+
+ private:
+  PlatformHandle handle_;
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.cc b/mojo/edk/embedder/simple_platform_shared_buffer.cc
new file mode 100644
index 0000000..866250c
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_shared_buffer.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/platform_handle_utils.h"
+
+namespace mojo {
+namespace embedder {
+
+// static
+SimplePlatformSharedBuffer* SimplePlatformSharedBuffer::Create(
+    size_t num_bytes) {
+  DCHECK_GT(num_bytes, 0u);
+
+  SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes);
+  if (!rv->Init()) {
+    // We can't just delete it directly, due to the "in destructor" (debug)
+    // check.
+    scoped_refptr<SimplePlatformSharedBuffer> deleter(rv);
+    return nullptr;
+  }
+
+  return rv;
+}
+
+// static
+SimplePlatformSharedBuffer*
+SimplePlatformSharedBuffer::CreateFromPlatformHandle(
+    size_t num_bytes,
+    ScopedPlatformHandle platform_handle) {
+  DCHECK_GT(num_bytes, 0u);
+
+  SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes);
+  if (!rv->InitFromPlatformHandle(platform_handle.Pass())) {
+    // We can't just delete it directly, due to the "in destructor" (debug)
+    // check.
+    scoped_refptr<SimplePlatformSharedBuffer> deleter(rv);
+    return nullptr;
+  }
+
+  return rv;
+}
+
+size_t SimplePlatformSharedBuffer::GetNumBytes() const {
+  return num_bytes_;
+}
+
+scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::Map(
+    size_t offset,
+    size_t length) {
+  if (!IsValidMap(offset, length))
+    return nullptr;
+
+  return MapNoCheck(offset, length);
+}
+
+bool SimplePlatformSharedBuffer::IsValidMap(size_t offset, size_t length) {
+  if (offset > num_bytes_ || length == 0)
+    return false;
+
+  // Note: This is an overflow-safe check of |offset + length > num_bytes_|
+  // (that |num_bytes >= offset| is verified above).
+  if (length > num_bytes_ - offset)
+    return false;
+
+  return true;
+}
+
+scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapNoCheck(
+    size_t offset,
+    size_t length) {
+  DCHECK(IsValidMap(offset, length));
+  return MapImpl(offset, length);
+}
+
+ScopedPlatformHandle SimplePlatformSharedBuffer::DuplicatePlatformHandle() {
+  return mojo::embedder::DuplicatePlatformHandle(handle_.get());
+}
+
+ScopedPlatformHandle SimplePlatformSharedBuffer::PassPlatformHandle() {
+  DCHECK(HasOneRef());
+  return handle_.Pass();
+}
+
+SimplePlatformSharedBuffer::SimplePlatformSharedBuffer(size_t num_bytes)
+    : num_bytes_(num_bytes) {
+}
+
+SimplePlatformSharedBuffer::~SimplePlatformSharedBuffer() {
+}
+
+SimplePlatformSharedBufferMapping::~SimplePlatformSharedBufferMapping() {
+  Unmap();
+}
+
+void* SimplePlatformSharedBufferMapping::GetBase() const {
+  return base_;
+}
+
+size_t SimplePlatformSharedBufferMapping::GetLength() const {
+  return length_;
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.h b/mojo/edk/embedder/simple_platform_shared_buffer.h
new file mode 100644
index 0000000..110637e
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_shared_buffer.h
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_
+#define MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+// A simple implementation of |PlatformSharedBuffer|.
+class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBuffer
+    : public PlatformSharedBuffer {
+ public:
+  // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled).
+  // |num_bytes| must be nonzero. Returns null on failure.
+  static SimplePlatformSharedBuffer* Create(size_t num_bytes);
+
+  static SimplePlatformSharedBuffer* CreateFromPlatformHandle(
+      size_t num_bytes,
+      ScopedPlatformHandle platform_handle);
+
+  // |PlatformSharedBuffer| implementation:
+  virtual size_t GetNumBytes() const override;
+  virtual scoped_ptr<PlatformSharedBufferMapping> Map(size_t offset,
+                                                      size_t length) override;
+  virtual bool IsValidMap(size_t offset, size_t length) override;
+  virtual scoped_ptr<PlatformSharedBufferMapping> MapNoCheck(
+      size_t offset,
+      size_t length) override;
+  virtual ScopedPlatformHandle DuplicatePlatformHandle() override;
+  virtual ScopedPlatformHandle PassPlatformHandle() override;
+
+ private:
+  explicit SimplePlatformSharedBuffer(size_t num_bytes);
+  virtual ~SimplePlatformSharedBuffer();
+
+  // Implemented in simple_platform_shared_buffer_{posix,win}.cc:
+
+  // This is called by |Create()| before this object is given to anyone.
+  bool Init();
+
+  // This is like |Init()|, but for |CreateFromPlatformHandle()|. (Note: It
+  // should verify that |platform_handle| is an appropriate handle for the
+  // claimed |num_bytes_|.)
+  bool InitFromPlatformHandle(ScopedPlatformHandle platform_handle);
+
+  // The platform-dependent part of |Map()|; doesn't check arguments.
+  scoped_ptr<PlatformSharedBufferMapping> MapImpl(size_t offset, size_t length);
+
+  const size_t num_bytes_;
+
+  // This is set in |Init()|/|InitFromPlatformHandle()| and never modified
+  // (except by |PassPlatformHandle()|; see the comments above its declaration),
+  // hence does not need to be protected by a lock.
+  ScopedPlatformHandle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBuffer);
+};
+
+// An implementation of |PlatformSharedBufferMapping|, produced by
+// |SimplePlatformSharedBuffer|.
+class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBufferMapping
+    : public PlatformSharedBufferMapping {
+ public:
+  virtual ~SimplePlatformSharedBufferMapping();
+
+  virtual void* GetBase() const override;
+  virtual size_t GetLength() const override;
+
+ private:
+  friend class SimplePlatformSharedBuffer;
+
+  SimplePlatformSharedBufferMapping(void* base,
+                                    size_t length,
+                                    void* real_base,
+                                    size_t real_length)
+      : base_(base),
+        length_(length),
+        real_base_(real_base),
+        real_length_(real_length) {}
+  void Unmap();
+
+  void* const base_;
+  const size_t length_;
+
+  void* const real_base_;
+  const size_t real_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBufferMapping);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc b/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc
new file mode 100644
index 0000000..ebb55ee
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
+
+#include <stdint.h>
+#include <stdio.h>     // For |fileno()|.
+#include <sys/mman.h>  // For |mmap()|/|munmap()|.
+#include <sys/stat.h>
+#include <sys/types.h>  // For |off_t|.
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
+// |uint64_t|.
+static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t too big");
+static_assert(sizeof(off_t) <= sizeof(uint64_t), "off_t too big");
+
+namespace mojo {
+namespace embedder {
+
+// SimplePlatformSharedBuffer --------------------------------------------------
+
+bool SimplePlatformSharedBuffer::Init() {
+  DCHECK(!handle_.is_valid());
+
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  if (static_cast<uint64_t>(num_bytes_) >
+      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+    return false;
+  }
+
+  // TODO(vtl): This is stupid. The implementation of
+  // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
+  // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
+  // can own. (base/memory/shared_memory_posix.cc does this too, with more
+  // |fstat()|s thrown in for good measure.)
+  base::FilePath shared_buffer_dir;
+  if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
+    LOG(ERROR) << "Failed to get temporary directory for shared memory";
+    return false;
+  }
+  base::FilePath shared_buffer_file;
+  base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
+      shared_buffer_dir, &shared_buffer_file));
+  if (!fp) {
+    LOG(ERROR) << "Failed to create/open temporary file for shared memory";
+    return false;
+  }
+  // Note: |unlink()| is not interruptible.
+  if (unlink(shared_buffer_file.value().c_str()) != 0) {
+    PLOG(WARNING) << "unlink";
+    // This isn't "fatal" (e.g., someone else may have unlinked the file first),
+    // so we may as well continue.
+  }
+
+  // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
+  base::ScopedFD fd(dup(fileno(fp.get())));
+  if (!fd.is_valid()) {
+    PLOG(ERROR) << "dup";
+    return false;
+  }
+
+  if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) {
+    PLOG(ERROR) << "ftruncate";
+    return false;
+  }
+
+  handle_.reset(PlatformHandle(fd.release()));
+  return true;
+}
+
+bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
+    ScopedPlatformHandle platform_handle) {
+  DCHECK(!handle_.is_valid());
+
+  if (static_cast<uint64_t>(num_bytes_) >
+      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+    return false;
+  }
+
+  struct stat sb = {};
+  // Note: |fstat()| isn't interruptible.
+  if (fstat(platform_handle.get().fd, &sb) != 0) {
+    PLOG(ERROR) << "fstat";
+    return false;
+  }
+
+  if (!S_ISREG(sb.st_mode)) {
+    LOG(ERROR) << "Platform handle not to a regular file";
+    return false;
+  }
+
+  if (sb.st_size != static_cast<off_t>(num_bytes_)) {
+    LOG(ERROR) << "Shared memory file has the wrong size";
+    return false;
+  }
+
+  // TODO(vtl): More checks?
+
+  handle_ = platform_handle.Pass();
+  return true;
+}
+
+scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl(
+    size_t offset,
+    size_t length) {
+  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+  size_t real_offset = offset - offset_rounding;
+  size_t real_length = length + offset_rounding;
+
+  // This should hold (since we checked |num_bytes| versus the maximum value of
+  // |off_t| on creation, but it never hurts to be paranoid.
+  DCHECK_LE(static_cast<uint64_t>(real_offset),
+            static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
+
+  void* real_base = mmap(nullptr,
+                         real_length,
+                         PROT_READ | PROT_WRITE,
+                         MAP_SHARED,
+                         handle_.get().fd,
+                         static_cast<off_t>(real_offset));
+  // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
+  // return null either.
+  if (real_base == MAP_FAILED || !real_base) {
+    PLOG(ERROR) << "mmap";
+    return nullptr;
+  }
+
+  void* base = static_cast<char*>(real_base) + offset_rounding;
+  return make_scoped_ptr(new SimplePlatformSharedBufferMapping(
+      base, length, real_base, real_length));
+}
+
+// SimplePlatformSharedBufferMapping -------------------------------------------
+
+void SimplePlatformSharedBufferMapping::Unmap() {
+  int result = munmap(real_base_, real_length_);
+  PLOG_IF(ERROR, result != 0) << "munmap";
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc
new file mode 100644
index 0000000..45d27b9
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright 2014 The Chromium 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/edk/embedder/simple_platform_shared_buffer.h"
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace embedder {
+namespace {
+
+TEST(SimplePlatformSharedBufferTest, Basic) {
+  const size_t kNumInts = 100;
+  const size_t kNumBytes = kNumInts * sizeof(int);
+  // A fudge so that we're not just writing zero bytes 75% of the time.
+  const int kFudge = 1234567890;
+
+  // Make some memory.
+  scoped_refptr<SimplePlatformSharedBuffer> buffer(
+      SimplePlatformSharedBuffer::Create(kNumBytes));
+  ASSERT_TRUE(buffer.get());
+
+  // Map it all, scribble some stuff, and then unmap it.
+  {
+    EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes));
+    scoped_ptr<PlatformSharedBufferMapping> mapping(buffer->Map(0, kNumBytes));
+    ASSERT_TRUE(mapping);
+    ASSERT_TRUE(mapping->GetBase());
+    int* stuff = static_cast<int*>(mapping->GetBase());
+    for (size_t i = 0; i < kNumInts; i++)
+      stuff[i] = static_cast<int>(i) + kFudge;
+  }
+
+  // Map it all again, check that our scribbling is still there, then do a
+  // partial mapping and scribble on that, check that everything is coherent,
+  // unmap the first mapping, scribble on some of the second mapping, and then
+  // unmap it.
+  {
+    ASSERT_TRUE(buffer->IsValidMap(0, kNumBytes));
+    // Use |MapNoCheck()| this time.
+    scoped_ptr<PlatformSharedBufferMapping> mapping1(
+        buffer->MapNoCheck(0, kNumBytes));
+    ASSERT_TRUE(mapping1);
+    ASSERT_TRUE(mapping1->GetBase());
+    int* stuff1 = static_cast<int*>(mapping1->GetBase());
+    for (size_t i = 0; i < kNumInts; i++)
+      EXPECT_EQ(static_cast<int>(i) + kFudge, stuff1[i]) << i;
+
+    scoped_ptr<PlatformSharedBufferMapping> mapping2(
+        buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int)));
+    ASSERT_TRUE(mapping2);
+    ASSERT_TRUE(mapping2->GetBase());
+    int* stuff2 = static_cast<int*>(mapping2->GetBase());
+    EXPECT_EQ(static_cast<int>(kNumInts / 2) + kFudge, stuff2[0]);
+    EXPECT_EQ(static_cast<int>(kNumInts / 2) + 1 + kFudge, stuff2[1]);
+
+    stuff2[0] = 123;
+    stuff2[1] = 456;
+    EXPECT_EQ(123, stuff1[kNumInts / 2]);
+    EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]);
+
+    mapping1.reset();
+
+    EXPECT_EQ(123, stuff2[0]);
+    EXPECT_EQ(456, stuff2[1]);
+    stuff2[1] = 789;
+  }
+
+  // Do another partial mapping and check that everything is the way we expect
+  // it to be.
+  {
+    EXPECT_TRUE(buffer->IsValidMap(sizeof(int), kNumBytes - sizeof(int)));
+    scoped_ptr<PlatformSharedBufferMapping> mapping(
+        buffer->Map(sizeof(int), kNumBytes - sizeof(int)));
+    ASSERT_TRUE(mapping);
+    ASSERT_TRUE(mapping->GetBase());
+    int* stuff = static_cast<int*>(mapping->GetBase());
+
+    for (size_t j = 0; j < kNumInts - 1; j++) {
+      int i = static_cast<int>(j) + 1;
+      if (i == kNumInts / 2) {
+        EXPECT_EQ(123, stuff[j]);
+      } else if (i == kNumInts / 2 + 1) {
+        EXPECT_EQ(789, stuff[j]);
+      } else {
+        EXPECT_EQ(i + kFudge, stuff[j]) << i;
+      }
+    }
+  }
+}
+
+// TODO(vtl): Bigger buffers.
+
+TEST(SimplePlatformSharedBufferTest, InvalidMappings) {
+  scoped_refptr<SimplePlatformSharedBuffer> buffer(
+      SimplePlatformSharedBuffer::Create(100));
+  ASSERT_TRUE(buffer.get());
+
+  // Zero length not allowed.
+  EXPECT_FALSE(buffer->Map(0, 0));
+  EXPECT_FALSE(buffer->IsValidMap(0, 0));
+
+  // Okay:
+  EXPECT_TRUE(buffer->Map(0, 100));
+  EXPECT_TRUE(buffer->IsValidMap(0, 100));
+  // Offset + length too big.
+  EXPECT_FALSE(buffer->Map(0, 101));
+  EXPECT_FALSE(buffer->IsValidMap(0, 101));
+  EXPECT_FALSE(buffer->Map(1, 100));
+  EXPECT_FALSE(buffer->IsValidMap(1, 100));
+
+  // Okay:
+  EXPECT_TRUE(buffer->Map(50, 50));
+  EXPECT_TRUE(buffer->IsValidMap(50, 50));
+  // Offset + length too big.
+  EXPECT_FALSE(buffer->Map(50, 51));
+  EXPECT_FALSE(buffer->IsValidMap(50, 51));
+  EXPECT_FALSE(buffer->Map(51, 50));
+  EXPECT_FALSE(buffer->IsValidMap(51, 50));
+}
+
+TEST(SimplePlatformSharedBufferTest, TooBig) {
+  // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds
+  // (since it only involves creating a 4 GB file).
+  const size_t kMaxSizeT = std::numeric_limits<size_t>::max();
+  scoped_refptr<SimplePlatformSharedBuffer> buffer(
+      SimplePlatformSharedBuffer::Create(kMaxSizeT));
+  // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should
+  // always fail.
+  if (buffer.get())
+    EXPECT_FALSE(buffer->Map(0, kMaxSizeT));
+}
+
+// Tests that separate mappings get distinct addresses.
+// Note: It's not inconceivable that the OS could ref-count identical mappings
+// and reuse the same address, in which case we'd have to be more careful about
+// using the address as the key for unmapping.
+TEST(SimplePlatformSharedBufferTest, MappingsDistinct) {
+  scoped_refptr<SimplePlatformSharedBuffer> buffer(
+      SimplePlatformSharedBuffer::Create(100));
+  scoped_ptr<PlatformSharedBufferMapping> mapping1(buffer->Map(0, 100));
+  scoped_ptr<PlatformSharedBufferMapping> mapping2(buffer->Map(0, 100));
+  EXPECT_NE(mapping1->GetBase(), mapping2->GetBase());
+}
+
+TEST(SimplePlatformSharedBufferTest, BufferZeroInitialized) {
+  static const size_t kSizes[] = {10, 100, 1000, 10000, 100000};
+  for (size_t i = 0; i < arraysize(kSizes); i++) {
+    scoped_refptr<SimplePlatformSharedBuffer> buffer(
+        SimplePlatformSharedBuffer::Create(kSizes[i]));
+    scoped_ptr<PlatformSharedBufferMapping> mapping(buffer->Map(0, kSizes[i]));
+    for (size_t j = 0; j < kSizes[i]; j++) {
+      // "Assert" instead of "expect" so we don't spam the output with thousands
+      // of failures if we fail.
+      ASSERT_EQ('\0', static_cast<char*>(mapping->GetBase())[j])
+          << "size " << kSizes[i] << ", offset " << j;
+    }
+  }
+}
+
+TEST(SimplePlatformSharedBufferTest, MappingsOutliveBuffer) {
+  scoped_ptr<PlatformSharedBufferMapping> mapping1;
+  scoped_ptr<PlatformSharedBufferMapping> mapping2;
+
+  {
+    scoped_refptr<SimplePlatformSharedBuffer> buffer(
+        SimplePlatformSharedBuffer::Create(100));
+    mapping1 = buffer->Map(0, 100).Pass();
+    mapping2 = buffer->Map(50, 50).Pass();
+    static_cast<char*>(mapping1->GetBase())[50] = 'x';
+  }
+
+  EXPECT_EQ('x', static_cast<char*>(mapping2->GetBase())[0]);
+
+  static_cast<char*>(mapping2->GetBase())[1] = 'y';
+  EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[51]);
+}
+
+}  // namespace
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_win.cc b/mojo/edk/embedder/simple_platform_shared_buffer_win.cc
new file mode 100644
index 0000000..fd3de83
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_shared_buffer_win.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
+
+#include <windows.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/sys_info.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace embedder {
+
+// SimplePlatformSharedBuffer --------------------------------------------------
+
+bool SimplePlatformSharedBuffer::Init() {
+  DCHECK(!handle_.is_valid());
+
+  // TODO(vtl): Currently, we only support mapping up to 2^32-1 bytes.
+  if (static_cast<uint64_t>(num_bytes_) >
+      static_cast<uint64_t>(std::numeric_limits<DWORD>::max())) {
+    return false;
+  }
+
+  // IMPORTANT NOTE: Unnamed objects are NOT SECURABLE. Thus if we ever want to
+  // share read-only to other processes, we'll have to name our file mapping
+  // object.
+  // TODO(vtl): Unlike |base::SharedMemory|, we don't round up the size (to a
+  // multiple of 64 KB). This may cause problems with NaCl. Cross this bridge
+  // when we get there. crbug.com/210609
+  handle_.reset(PlatformHandle(CreateFileMapping(INVALID_HANDLE_VALUE,
+                                                 nullptr,
+                                                 PAGE_READWRITE,
+                                                 0,
+                                                 static_cast<DWORD>(num_bytes_),
+                                                 nullptr)));
+  if (!handle_.is_valid()) {
+    PLOG(ERROR) << "CreateFileMapping";
+    return false;
+  }
+
+  return true;
+}
+
+bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
+    ScopedPlatformHandle platform_handle) {
+  DCHECK(!handle_.is_valid());
+
+  // TODO(vtl): Implement.
+  NOTIMPLEMENTED();
+  return false;
+}
+
+scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl(
+    size_t offset,
+    size_t length) {
+  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+  size_t real_offset = offset - offset_rounding;
+  size_t real_length = length + offset_rounding;
+
+  // This should hold (since we checked |num_bytes| versus the maximum value of
+  // |off_t| on creation, but it never hurts to be paranoid.
+  DCHECK_LE(static_cast<uint64_t>(real_offset),
+            static_cast<uint64_t>(std::numeric_limits<DWORD>::max()));
+
+  void* real_base = MapViewOfFile(handle_.get().handle,
+                                  FILE_MAP_READ | FILE_MAP_WRITE,
+                                  0,
+                                  static_cast<DWORD>(real_offset),
+                                  real_length);
+  if (!real_base) {
+    PLOG(ERROR) << "MapViewOfFile";
+    return nullptr;
+  }
+
+  void* base = static_cast<char*>(real_base) + offset_rounding;
+  return make_scoped_ptr(new SimplePlatformSharedBufferMapping(
+      base, length, real_base, real_length));
+}
+
+// SimplePlatformSharedBufferMapping -------------------------------------------
+
+void SimplePlatformSharedBufferMapping::Unmap() {
+  BOOL result = UnmapViewOfFile(real_base_);
+  PLOG_IF(ERROR, !result) << "UnmapViewOfFile";
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_support.cc b/mojo/edk/embedder/simple_platform_support.cc
new file mode 100644
index 0000000..c59cfb3
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_support.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/edk/embedder/simple_platform_support.h"
+
+#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
+
+namespace mojo {
+namespace embedder {
+
+PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBuffer(
+    size_t num_bytes) {
+  return SimplePlatformSharedBuffer::Create(num_bytes);
+}
+
+PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBufferFromHandle(
+    size_t num_bytes,
+    ScopedPlatformHandle platform_handle) {
+  return SimplePlatformSharedBuffer::CreateFromPlatformHandle(
+      num_bytes, platform_handle.Pass());
+}
+
+}  // namespace embedder
+}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_support.h b/mojo/edk/embedder/simple_platform_support.h
new file mode 100644
index 0000000..fc5371d
--- /dev/null
+++ b/mojo/edk/embedder/simple_platform_support.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_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_
+#define MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_support.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+// A simple implementation of |PlatformSupport|, when sandboxing and
+// multiprocess support are not issues (e.g., in most tests). Note: This class
+// has no state, and different instances of |SimplePlatformSupport| are mutually
+// compatible (i.e., you don't need to use a single instance of it everywhere --
+// you may simply create one whenever/wherever you need it).
+class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSupport : public PlatformSupport {
+ public:
+  SimplePlatformSupport() {}
+  virtual ~SimplePlatformSupport() {}
+
+  virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) override;
+  virtual PlatformSharedBuffer* CreateSharedBufferFromHandle(
+      size_t num_bytes,
+      ScopedPlatformHandle platform_handle) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SimplePlatformSupport);
+};
+
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_
diff --git a/mojo/edk/embedder/test_embedder.cc b/mojo/edk/embedder/test_embedder.cc
new file mode 100644
index 0000000..95d3be6
--- /dev/null
+++ b/mojo/edk/embedder/test_embedder.cc
@@ -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.
+
+#include "mojo/edk/embedder/test_embedder.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/entrypoints.h"
+#include "mojo/edk/system/handle_table.h"
+
+namespace mojo {
+
+namespace system {
+namespace internal {
+
+bool ShutdownCheckNoLeaks(Core* core_impl) {
+  // No point in taking the lock.
+  const HandleTable::HandleToEntryMap& handle_to_entry_map =
+      core_impl->handle_table_.handle_to_entry_map_;
+
+  if (handle_to_entry_map.empty())
+    return true;
+
+  for (HandleTable::HandleToEntryMap::const_iterator it =
+           handle_to_entry_map.begin();
+       it != handle_to_entry_map.end();
+       ++it) {
+    LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << (*it).first;
+  }
+  return false;
+}
+
+}  // namespace internal
+}  // namespace system
+
+namespace embedder {
+namespace test {
+
+void InitWithSimplePlatformSupport() {
+  Init(make_scoped_ptr(new SimplePlatformSupport()));
+}
+
+bool Shutdown() {
+  system::Core* core = system::entrypoints::GetCore();
+  CHECK(core);
+  system::entrypoints::SetCore(nullptr);
+
+  bool rv = system::internal::ShutdownCheckNoLeaks(core);
+  delete core;
+  return rv;
+}
+
+}  // namespace test
+}  // namespace embedder
+
+}  // namespace mojo
diff --git a/mojo/edk/embedder/test_embedder.h b/mojo/edk/embedder/test_embedder.h
new file mode 100644
index 0000000..b6203b4
--- /dev/null
+++ b/mojo/edk/embedder/test_embedder.h
@@ -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.
+
+#ifndef MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_
+#define MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_
+
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+namespace test {
+
+// Calls |Init()| with a |SimplePlatformSupport| (use this in tests if, e.g.,
+// you don't care about sandboxing, etc.).
+MOJO_SYSTEM_IMPL_EXPORT void InitWithSimplePlatformSupport();
+
+// This shuts down the global, singleton instance. (Note: "Real" embedders are
+// not expected to ever shut down this instance. This |Shutdown()| function will
+// do more work to ensure that tests don't leak, etc.) Returns true if there
+// were no problems, false if there were leaks -- i.e., handles still open -- or
+// any other problems.
+MOJO_SYSTEM_IMPL_EXPORT bool Shutdown();
+
+}  // namespace test
+}  // namespace embedder
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
new file mode 100644
index 0000000..549af7d
--- /dev/null
+++ b/mojo/edk/system/BUILD.gn
@@ -0,0 +1,147 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("system_config") {
+  defines = [
+    # Ensures that dependent projects import the core functions on Windows.
+    "MOJO_USE_SYSTEM_IMPL",
+  ]
+}
+
+component("system") {
+  output_name = "mojo_system_impl"
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//mojo/edk/embedder",
+  ]
+
+  defines = [
+    "MOJO_SYSTEM_IMPL_IMPLEMENTATION",
+    "MOJO_SYSTEM_IMPLEMENTATION",
+  ]
+
+  all_dependent_configs = [ ":system_config" ]
+
+  sources = [
+    "channel.cc",
+    "channel.h",
+    "channel_endpoint.cc",
+    "channel_endpoint.h",
+    "constants.h",
+    "core.cc",
+    "core.h",
+    "data_pipe.cc",
+    "data_pipe.h",
+    "data_pipe_consumer_dispatcher.cc",
+    "data_pipe_consumer_dispatcher.h",
+    "data_pipe_producer_dispatcher.cc",
+    "data_pipe_producer_dispatcher.h",
+    "dispatcher.cc",
+    "dispatcher.h",
+    "entrypoints.cc",
+    "handle_signals_state.h",
+    "handle_table.cc",
+    "handle_table.h",
+    "local_data_pipe.cc",
+    "local_data_pipe.h",
+    "local_message_pipe_endpoint.cc",
+    "local_message_pipe_endpoint.h",
+    "mapping_table.cc",
+    "mapping_table.h",
+    "memory.cc",
+    "memory.h",
+    "message_in_transit.cc",
+    "message_in_transit.h",
+    "message_in_transit_queue.cc",
+    "message_in_transit_queue.h",
+    "message_pipe.cc",
+    "message_pipe.h",
+    "message_pipe_dispatcher.cc",
+    "message_pipe_dispatcher.h",
+    "message_pipe_endpoint.cc",
+    "message_pipe_endpoint.h",
+    "options_validation.h",
+    "platform_handle_dispatcher.cc",
+    "platform_handle_dispatcher.h",
+    "proxy_message_pipe_endpoint.cc",
+    "proxy_message_pipe_endpoint.h",
+    "raw_channel.cc",
+    "raw_channel.h",
+    "raw_channel_posix.cc",
+    "raw_channel_win.cc",
+    "shared_buffer_dispatcher.cc",
+    "shared_buffer_dispatcher.h",
+    "simple_dispatcher.cc",
+    "simple_dispatcher.h",
+    "transport_data.cc",
+    "transport_data.h",
+    "waiter.cc",
+    "waiter.h",
+    "waiter_list.cc",
+    "waiter_list.h",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_system_unittests
+test("mojo_system_unittests") {
+  deps = [
+    ":system",
+    "//base",
+    "//mojo/edk/embedder:embedder_unittests",
+    "//mojo/edk/test:test_support",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "../test/multiprocess_test_helper_unittest.cc",
+    "channel_unittest.cc",
+    "core_test_base.cc",
+    "core_test_base.h",
+    "core_unittest.cc",
+    "data_pipe_unittest.cc",
+    "dispatcher_unittest.cc",
+    "local_data_pipe_unittest.cc",
+    "memory_unittest.cc",
+    "message_pipe_dispatcher_unittest.cc",
+    "message_pipe_test_utils.cc",
+    "message_pipe_test_utils.h",
+    "message_pipe_unittest.cc",
+    "multiprocess_message_pipe_unittest.cc",
+    "options_validation_unittest.cc",
+    "platform_handle_dispatcher_unittest.cc",
+    "raw_channel_unittest.cc",
+    "remote_message_pipe_unittest.cc",
+    "run_all_unittests.cc",
+    "shared_buffer_dispatcher_unittest.cc",
+    "simple_dispatcher_unittest.cc",
+    "test_utils.cc",
+    "test_utils.h",
+    "waiter_list_unittest.cc",
+    "waiter_test_utils.cc",
+    "waiter_test_utils.h",
+    "waiter_unittest.cc",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_message_pipe_perftests
+test("mojo_message_pipe_perftests") {
+  deps = [
+    ":system",
+    "//base",
+    "//base/test:test_support",
+    "//base/test:test_support_perf",
+    "//mojo/edk/test:test_support",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "message_pipe_perftest.cc",
+    "message_pipe_test_utils.h",
+    "message_pipe_test_utils.cc",
+    "test_utils.cc",
+    "test_utils.h",
+  ]
+}
diff --git a/mojo/edk/system/PRESUBMIT.py b/mojo/edk/system/PRESUBMIT.py
new file mode 100644
index 0000000..2827571
--- /dev/null
+++ b/mojo/edk/system/PRESUBMIT.py
@@ -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.
+
+"""Presubmit script for mojo/edk/system.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
+                                                              output_api)
+  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
+  return results
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
new file mode 100644
index 0000000..2cc99b6
--- /dev/null
+++ b/mojo/edk/system/channel.cc
@@ -0,0 +1,531 @@
+// 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/edk/system/channel.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/message_pipe_endpoint.h"
+#include "mojo/edk/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+static_assert(Channel::kBootstrapEndpointId !=
+                  MessageInTransit::kInvalidEndpointId,
+              "kBootstrapEndpointId is invalid");
+
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
+    Channel::kBootstrapEndpointId;
+
+Channel::Channel(embedder::PlatformSupport* platform_support)
+    : platform_support_(platform_support),
+      is_running_(false),
+      is_shutting_down_(false),
+      next_local_id_(kBootstrapEndpointId) {
+}
+
+bool Channel::Init(scoped_ptr<RawChannel> raw_channel) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+  DCHECK(raw_channel);
+
+  // No need to take |lock_|, since this must be called before this object
+  // becomes thread-safe.
+  DCHECK(!is_running_);
+  raw_channel_ = raw_channel.Pass();
+
+  if (!raw_channel_->Init(this)) {
+    raw_channel_.reset();
+    return false;
+  }
+
+  is_running_ = true;
+  return true;
+}
+
+void Channel::Shutdown() {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+  IdToEndpointMap to_destroy;
+  {
+    base::AutoLock locker(lock_);
+    if (!is_running_)
+      return;
+
+    // Note: Don't reset |raw_channel_|, in case we're being called from within
+    // |OnReadMessage()| or |OnError()|.
+    raw_channel_->Shutdown();
+    is_running_ = false;
+
+    // We need to deal with it outside the lock.
+    std::swap(to_destroy, local_id_to_endpoint_map_);
+  }
+
+  size_t num_live = 0;
+  size_t num_zombies = 0;
+  for (IdToEndpointMap::iterator it = to_destroy.begin();
+       it != to_destroy.end();
+       ++it) {
+    if (it->second->state_ == ChannelEndpoint::STATE_NORMAL) {
+      it->second->OnDisconnect();
+      num_live++;
+    } else {
+      num_zombies++;
+    }
+    it->second->DetachFromChannel();
+  }
+  DVLOG_IF(2, num_live || num_zombies) << "Shut down Channel with " << num_live
+                                       << " live endpoints and " << num_zombies
+                                       << " zombies";
+}
+
+void Channel::WillShutdownSoon() {
+  base::AutoLock locker(lock_);
+  is_shutting_down_ = true;
+}
+
+// Note: |endpoint| being a |scoped_refptr| makes this function safe, since it
+// keeps the endpoint alive even after the lock is released. Otherwise, there's
+// the temptation to simply pass the result of |new ChannelEndpoint(...)|
+// directly to this function, which wouldn't be sufficient for safety.
+MessageInTransit::EndpointId Channel::AttachEndpoint(
+    scoped_refptr<ChannelEndpoint> endpoint) {
+  DCHECK(endpoint.get());
+
+  MessageInTransit::EndpointId local_id;
+  {
+    base::AutoLock locker(lock_);
+
+    DLOG_IF(WARNING, is_shutting_down_)
+        << "AttachEndpoint() while shutting down";
+
+    while (next_local_id_ == MessageInTransit::kInvalidEndpointId ||
+           local_id_to_endpoint_map_.find(next_local_id_) !=
+               local_id_to_endpoint_map_.end())
+      next_local_id_++;
+
+    local_id = next_local_id_;
+    next_local_id_++;
+    local_id_to_endpoint_map_[local_id] = endpoint;
+  }
+
+  endpoint->AttachToChannel(this, local_id);
+  return local_id;
+}
+
+// TODO(vtl): This function is currently slightly absurd, but we'll eventually
+// get rid of it and merge it with |AttachEndpoint()|.
+void Channel::RunEndpoint(scoped_refptr<ChannelEndpoint> endpoint,
+                          MessageInTransit::EndpointId remote_id) {
+  {
+    base::AutoLock locker(lock_);
+
+    DLOG_IF(WARNING, is_shutting_down_)
+        << "RunMessagePipeEndpoint() while shutting down";
+
+    // Absurdity: |endpoint->state_| is protected by our lock.
+    if (endpoint->state_ != ChannelEndpoint::STATE_NORMAL) {
+      DVLOG(2) << "Ignoring run message pipe endpoint for zombie endpoint";
+      return;
+    }
+  }
+
+  // TODO(vtl): FIXME -- We need to handle the case that message pipe is already
+  // running when we're here due to |kSubtypeChannelRunMessagePipeEndpoint|).
+  endpoint->Run(remote_id);
+}
+
+void Channel::RunRemoteMessagePipeEndpoint(
+    MessageInTransit::EndpointId local_id,
+    MessageInTransit::EndpointId remote_id) {
+#if DCHECK_IS_ON
+  {
+    base::AutoLock locker(lock_);
+    DCHECK(local_id_to_endpoint_map_.find(local_id) !=
+           local_id_to_endpoint_map_.end());
+  }
+#endif
+
+  if (!SendControlMessage(
+          MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint,
+          local_id,
+          remote_id)) {
+    HandleLocalError(base::StringPrintf(
+        "Failed to send message to run remote message pipe endpoint (local ID "
+        "%u, remote ID %u)",
+        static_cast<unsigned>(local_id),
+        static_cast<unsigned>(remote_id)));
+  }
+}
+
+bool Channel::WriteMessage(scoped_ptr<MessageInTransit> message) {
+  base::AutoLock locker(lock_);
+  if (!is_running_) {
+    // TODO(vtl): I think this is probably not an error condition, but I should
+    // think about it (and the shutdown sequence) more carefully.
+    LOG(WARNING) << "WriteMessage() after shutdown";
+    return false;
+  }
+
+  DLOG_IF(WARNING, is_shutting_down_) << "WriteMessage() while shutting down";
+  return raw_channel_->WriteMessage(message.Pass());
+}
+
+bool Channel::IsWriteBufferEmpty() {
+  base::AutoLock locker(lock_);
+  if (!is_running_)
+    return true;
+  return raw_channel_->IsWriteBufferEmpty();
+}
+
+void Channel::DetachMessagePipeEndpoint(
+    MessageInTransit::EndpointId local_id,
+    MessageInTransit::EndpointId remote_id) {
+  DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
+
+  // If this is non-null after the locked block, the endpoint should be detached
+  // (and no remove message sent).
+  scoped_refptr<ChannelEndpoint> endpoint_to_detach;
+  {
+    base::AutoLock locker_(lock_);
+    if (!is_running_)
+      return;
+
+    IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id);
+    DCHECK(it != local_id_to_endpoint_map_.end());
+
+    switch (it->second->state_) {
+      case ChannelEndpoint::STATE_NORMAL:
+        it->second->state_ = ChannelEndpoint::STATE_WAIT_REMOTE_REMOVE_ACK;
+        if (remote_id == MessageInTransit::kInvalidEndpointId)
+          return;
+        // We have to send a remove message (outside the lock).
+        break;
+      case ChannelEndpoint::STATE_WAIT_LOCAL_DETACH:
+        endpoint_to_detach = it->second;
+        local_id_to_endpoint_map_.erase(it);
+        // We have to detach (outside the lock).
+        break;
+      case ChannelEndpoint::STATE_WAIT_REMOTE_REMOVE_ACK:
+        NOTREACHED();
+        return;
+    }
+  }
+  if (endpoint_to_detach.get()) {
+    endpoint_to_detach->DetachFromChannel();
+    return;
+  }
+
+  if (!SendControlMessage(
+          MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint,
+          local_id,
+          remote_id)) {
+    HandleLocalError(base::StringPrintf(
+        "Failed to send message to remove remote message pipe endpoint (local "
+        "ID %u, remote ID %u)",
+        static_cast<unsigned>(local_id),
+        static_cast<unsigned>(remote_id)));
+  }
+}
+
+size_t Channel::GetSerializedPlatformHandleSize() const {
+  return raw_channel_->GetSerializedPlatformHandleSize();
+}
+
+Channel::~Channel() {
+  // The channel should have been shut down first.
+  DCHECK(!is_running_);
+}
+
+void Channel::OnReadMessage(
+    const MessageInTransit::View& message_view,
+    embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+  switch (message_view.type()) {
+    case MessageInTransit::kTypeMessagePipeEndpoint:
+    case MessageInTransit::kTypeMessagePipe:
+      OnReadMessageForDownstream(message_view, platform_handles.Pass());
+      break;
+    case MessageInTransit::kTypeChannel:
+      OnReadMessageForChannel(message_view, platform_handles.Pass());
+      break;
+    default:
+      HandleRemoteError(
+          base::StringPrintf("Received message of invalid type %u",
+                             static_cast<unsigned>(message_view.type())));
+      break;
+  }
+}
+
+void Channel::OnError(Error error) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+  switch (error) {
+    case ERROR_READ_SHUTDOWN:
+      // The other side was cleanly closed, so this isn't actually an error.
+      DVLOG(1) << "RawChannel read error (shutdown)";
+      break;
+    case ERROR_READ_BROKEN: {
+      base::AutoLock locker(lock_);
+      LOG_IF(ERROR, !is_shutting_down_)
+          << "RawChannel read error (connection broken)";
+      break;
+    }
+    case ERROR_READ_BAD_MESSAGE:
+      // Receiving a bad message means either a bug, data corruption, or
+      // malicious attack (probably due to some other bug).
+      LOG(ERROR) << "RawChannel read error (received bad message)";
+      break;
+    case ERROR_READ_UNKNOWN:
+      LOG(ERROR) << "RawChannel read error (unknown)";
+      break;
+    case ERROR_WRITE:
+      // Write errors are slightly notable: they probably shouldn't happen under
+      // normal operation (but maybe the other side crashed).
+      LOG(WARNING) << "RawChannel write error";
+      break;
+  }
+  Shutdown();
+}
+
+void Channel::OnReadMessageForDownstream(
+    const MessageInTransit::View& message_view,
+    embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+  DCHECK(message_view.type() == MessageInTransit::kTypeMessagePipeEndpoint ||
+         message_view.type() == MessageInTransit::kTypeMessagePipe);
+
+  MessageInTransit::EndpointId local_id = message_view.destination_id();
+  if (local_id == MessageInTransit::kInvalidEndpointId) {
+    HandleRemoteError("Received message with no destination ID");
+    return;
+  }
+
+  scoped_refptr<ChannelEndpoint> endpoint;
+  ChannelEndpoint::State state = ChannelEndpoint::STATE_NORMAL;
+  {
+    base::AutoLock locker(lock_);
+
+    // Since we own |raw_channel_|, and this method and |Shutdown()| should only
+    // be called from the creation thread, |raw_channel_| should never be null
+    // here.
+    DCHECK(is_running_);
+
+    IdToEndpointMap::const_iterator it =
+        local_id_to_endpoint_map_.find(local_id);
+    if (it != local_id_to_endpoint_map_.end()) {
+      endpoint = it->second;
+      state = it->second->state_;
+    }
+  }
+  if (!endpoint.get()) {
+    HandleRemoteError(base::StringPrintf(
+        "Received a message for nonexistent local destination ID %u",
+        static_cast<unsigned>(local_id)));
+    // This is strongly indicative of some problem. However, it's not a fatal
+    // error, since it may indicate a buggy (or hostile) remote process. Don't
+    // die even for Debug builds, since handling this properly needs to be
+    // tested (TODO(vtl)).
+    DLOG(ERROR) << "This should not happen under normal operation.";
+    return;
+  }
+
+  // Ignore messages for zombie endpoints (not an error).
+  if (state != ChannelEndpoint::STATE_NORMAL) {
+    DVLOG(2) << "Ignoring downstream message for zombie endpoint (local ID = "
+             << local_id << ", remote ID = " << message_view.source_id() << ")";
+    return;
+  }
+
+  if (!endpoint->OnReadMessage(message_view, platform_handles.Pass())) {
+    HandleLocalError(
+        base::StringPrintf("Failed to enqueue message to local ID %u",
+                           static_cast<unsigned>(local_id)));
+    return;
+  }
+}
+
+void Channel::OnReadMessageForChannel(
+    const MessageInTransit::View& message_view,
+    embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(message_view.type(), MessageInTransit::kTypeChannel);
+
+  // Currently, no channel messages take platform handles.
+  if (platform_handles) {
+    HandleRemoteError(
+        "Received invalid channel message (has platform handles)");
+    NOTREACHED();
+    return;
+  }
+
+  switch (message_view.subtype()) {
+    case MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint:
+      DVLOG(2) << "Handling channel message to run message pipe (local ID "
+               << message_view.destination_id() << ", remote ID "
+               << message_view.source_id() << ")";
+      if (!OnRunMessagePipeEndpoint(message_view.destination_id(),
+                                    message_view.source_id())) {
+        HandleRemoteError(
+            "Received invalid channel message to run message pipe");
+      }
+      break;
+    case MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint:
+      DVLOG(2) << "Handling channel message to remove message pipe (local ID "
+               << message_view.destination_id() << ", remote ID "
+               << message_view.source_id() << ")";
+      if (!OnRemoveMessagePipeEndpoint(message_view.destination_id(),
+                                       message_view.source_id())) {
+        HandleRemoteError(
+            "Received invalid channel message to remove message pipe");
+      }
+      break;
+    case MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck:
+      DVLOG(2) << "Handling channel message to ack remove message pipe (local "
+                  "ID " << message_view.destination_id() << ", remote ID "
+               << message_view.source_id() << ")";
+      if (!OnRemoveMessagePipeEndpointAck(message_view.destination_id())) {
+        HandleRemoteError(
+            "Received invalid channel message to ack remove message pipe");
+      }
+      break;
+    default:
+      HandleRemoteError("Received invalid channel message");
+      NOTREACHED();
+      break;
+  }
+}
+
+bool Channel::OnRunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+                                       MessageInTransit::EndpointId remote_id) {
+  scoped_refptr<ChannelEndpoint> endpoint;
+  {
+    base::AutoLock locker(lock_);
+
+    IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id);
+    if (it == local_id_to_endpoint_map_.end())
+      return false;
+
+    endpoint = it->second;
+  }
+
+  RunEndpoint(endpoint, remote_id);
+  return true;
+}
+
+bool Channel::OnRemoveMessagePipeEndpoint(
+    MessageInTransit::EndpointId local_id,
+    MessageInTransit::EndpointId remote_id) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+  scoped_refptr<ChannelEndpoint> endpoint;
+  {
+    base::AutoLock locker(lock_);
+
+    IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id);
+    if (it == local_id_to_endpoint_map_.end()) {
+      DVLOG(2) << "Remove message pipe endpoint error: not found";
+      return false;
+    }
+
+    switch (it->second->state_) {
+      case ChannelEndpoint::STATE_NORMAL:
+        // This is the normal case; we'll proceed on to "wait local detach".
+        break;
+
+      case ChannelEndpoint::STATE_WAIT_LOCAL_DETACH:
+        // We can only be in this state because we got a "remove" already, so
+        // getting another such message is invalid.
+        DVLOG(2) << "Remove message pipe endpoint error: wrong state";
+        return false;
+
+      case ChannelEndpoint::STATE_WAIT_REMOTE_REMOVE_ACK:
+        // Remove messages "crossed"; we have to wait for the ack.
+        return true;
+    }
+
+    it->second->state_ = ChannelEndpoint::STATE_WAIT_LOCAL_DETACH;
+    endpoint = it->second;
+    // Send the remove ack message outside the lock.
+  }
+
+  if (!SendControlMessage(
+          MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck,
+          local_id,
+          remote_id)) {
+    HandleLocalError(base::StringPrintf(
+        "Failed to send message to remove remote message pipe endpoint ack "
+        "(local ID %u, remote ID %u)",
+        static_cast<unsigned>(local_id),
+        static_cast<unsigned>(remote_id)));
+  }
+
+  endpoint->OnDisconnect();
+  return true;
+}
+
+bool Channel::OnRemoveMessagePipeEndpointAck(
+    MessageInTransit::EndpointId local_id) {
+  DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+  scoped_refptr<ChannelEndpoint> endpoint;
+  {
+    base::AutoLock locker(lock_);
+
+    IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id);
+    if (it == local_id_to_endpoint_map_.end()) {
+      DVLOG(2) << "Remove message pipe endpoint ack error: not found";
+      return false;
+    }
+
+    if (it->second->state_ != ChannelEndpoint::STATE_WAIT_REMOTE_REMOVE_ACK) {
+      DVLOG(2) << "Remove message pipe endpoint ack error: wrong state";
+      return false;
+    }
+
+    endpoint = it->second;
+    local_id_to_endpoint_map_.erase(it);
+    // Detach the endpoint outside the lock.
+  }
+
+  endpoint->DetachFromChannel();
+  return true;
+}
+
+bool Channel::SendControlMessage(MessageInTransit::Subtype subtype,
+                                 MessageInTransit::EndpointId local_id,
+                                 MessageInTransit::EndpointId remote_id) {
+  DVLOG(2) << "Sending channel control message: subtype " << subtype
+           << ", local ID " << local_id << ", remote ID " << remote_id;
+  scoped_ptr<MessageInTransit> message(new MessageInTransit(
+      MessageInTransit::kTypeChannel, subtype, 0, nullptr));
+  message->set_source_id(local_id);
+  message->set_destination_id(remote_id);
+  return WriteMessage(message.Pass());
+}
+
+void Channel::HandleRemoteError(const base::StringPiece& error_message) {
+  // TODO(vtl): Is this how we really want to handle this? Probably we want to
+  // terminate the connection, since it's spewing invalid stuff.
+  LOG(WARNING) << error_message;
+}
+
+void Channel::HandleLocalError(const base::StringPiece& error_message) {
+  // TODO(vtl): Is this how we really want to handle this?
+  // Sometimes we'll want to propagate the error back to the message pipe
+  // (endpoint), and notify it that the remote is (effectively) closed.
+  // Sometimes we'll want to kill the channel (and notify all the endpoints that
+  // their remotes are dead.
+  LOG(WARNING) << error_message;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
new file mode 100644
index 0000000..7088cbd
--- /dev/null
+++ b/mojo/edk/system/channel.h
@@ -0,0 +1,193 @@
+// 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_EDK_SYSTEM_CHANNEL_H_
+#define MOJO_EDK_SYSTEM_CHANNEL_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformSupport;
+}
+
+namespace system {
+
+class ChannelEndpoint;
+
+// This class is mostly thread-safe. It must be created on an I/O thread.
+// |Init()| must be called on that same thread before it becomes thread-safe (in
+// particular, before references are given to any other thread) and |Shutdown()|
+// must be called on that same thread before destruction. Its public methods are
+// otherwise thread-safe. (Many private methods are restricted to the creation
+// thread.) It may be destroyed on any thread, in the sense that the last
+// reference to it may be released on any thread, with the proviso that
+// |Shutdown()| must have been called first (so the pattern is that a "main"
+// reference is kept on its creation thread and is released after |Shutdown()|
+// is called, but other threads may have temporarily "dangling" references).
+//
+// Note the lock order (in order of allowable acquisition): |MessagePipe|,
+// |ChannelEndpoint|, |Channel|. Thus |Channel| may not call into
+// |ChannelEndpoint| with |Channel|'s lock held.
+class MOJO_SYSTEM_IMPL_EXPORT Channel
+    : public base::RefCountedThreadSafe<Channel>,
+      public RawChannel::Delegate {
+ public:
+  // The first message pipe endpoint attached will have this as its local ID.
+  static const MessageInTransit::EndpointId kBootstrapEndpointId = 1;
+
+  // |platform_support| (typically owned by |Core|) must remain alive until
+  // after |Shutdown()| is called.
+  explicit Channel(embedder::PlatformSupport* platform_support);
+
+  // This must be called on the creation thread before any other methods are
+  // called, and before references to this object are given to any other
+  // threads. |raw_channel| should be uninitialized. Returns true on success. On
+  // failure, no other methods should be called (including |Shutdown()|).
+  bool Init(scoped_ptr<RawChannel> raw_channel);
+
+  // This must be called on the creation thread before destruction (which can
+  // happen on any thread).
+  void Shutdown();
+
+  // Signals that |Shutdown()| will be called soon (this may be called from any
+  // thread, unlike |Shutdown()|). Warnings will be issued if, e.g., messages
+  // are written after this is called; other warnings may be suppressed. (This
+  // may be called multiple times, or not at all.)
+  void WillShutdownSoon();
+
+  // Attaches the given endpoint to this channel. This assigns it a local ID,
+  // which it returns. The first endpoint attached will always have
+  // |kBootstrapEndpointId| as its local ID. (For bootstrapping, this occurs on
+  // both sides, so one should use |kBootstrapEndpointId| for the remote ID for
+  // the first message pipe across a channel.) Returns |kInvalidEndpointId| on
+  // failure.
+  // TODO(vtl): This should be combined with "run", and it should take a
+  // |ChannelEndpoint| instead.
+  // TODO(vtl): Maybe limit the number of attached message pipes.
+  MessageInTransit::EndpointId AttachEndpoint(
+      scoped_refptr<ChannelEndpoint> endpoint);
+
+  // Runs the given endpoint (which must have been attached to this |Channel|,
+  // and not detached), assigning it the specified |remote_id|.
+  void RunEndpoint(scoped_refptr<ChannelEndpoint> endpoint,
+                   MessageInTransit::EndpointId remote_id);
+
+  // Tells the other side of the channel to run a message pipe endpoint (which
+  // must already be attached); |local_id| and |remote_id| are relative to this
+  // channel (i.e., |local_id| is the other side's remote ID and |remote_id| is
+  // its local ID).
+  // TODO(vtl): Maybe we should just have a flag argument to
+  // |RunMessagePipeEndpoint()| that tells it to do this.
+  void RunRemoteMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+                                    MessageInTransit::EndpointId remote_id);
+
+  // This forwards |message| verbatim to |raw_channel_|.
+  bool WriteMessage(scoped_ptr<MessageInTransit> message);
+
+  // See |RawChannel::IsWriteBufferEmpty()|.
+  // TODO(vtl): Maybe we shouldn't expose this, and instead have a
+  // |FlushWriteBufferAndShutdown()| or something like that.
+  bool IsWriteBufferEmpty();
+
+  // This removes the message pipe/port's endpoint (with the given local ID and
+  // given remote ID, which should be |kInvalidEndpointId| if not yet running),
+  // returned by |AttachEndpoint()| from this channel. After this is called,
+  // |local_id| may be reused for another message pipe.
+  void DetachMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+                                 MessageInTransit::EndpointId remote_id);
+
+  // See |RawChannel::GetSerializedPlatformHandleSize()|.
+  size_t GetSerializedPlatformHandleSize() const;
+
+  embedder::PlatformSupport* platform_support() const {
+    return platform_support_;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<Channel>;
+  virtual ~Channel();
+
+  // |RawChannel::Delegate| implementation (only called on the creation thread):
+  virtual void OnReadMessage(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles) override;
+  virtual void OnError(Error error) override;
+
+  // Helpers for |OnReadMessage| (only called on the creation thread):
+  void OnReadMessageForDownstream(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles);
+  void OnReadMessageForChannel(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles);
+
+  // Handles "run message pipe endpoint" messages.
+  bool OnRunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+                                MessageInTransit::EndpointId remote_id);
+  // Handles "remove message pipe endpoint" messages.
+  bool OnRemoveMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+                                   MessageInTransit::EndpointId remote_id);
+  // Handles "remove message pipe endpoint ack" messages.
+  bool OnRemoveMessagePipeEndpointAck(MessageInTransit::EndpointId local_id);
+
+  // Handles errors (e.g., invalid messages) from the remote side. Callable from
+  // any thread.
+  void HandleRemoteError(const base::StringPiece& error_message);
+  // Handles internal errors/failures from the local side. Callable from any
+  // thread.
+  void HandleLocalError(const base::StringPiece& error_message);
+
+  // Helper to send channel control messages. Returns true on success. Should be
+  // called *without* |lock_| held. Callable from any thread.
+  bool SendControlMessage(MessageInTransit::Subtype subtype,
+                          MessageInTransit::EndpointId source_id,
+                          MessageInTransit::EndpointId destination_id);
+
+  base::ThreadChecker creation_thread_checker_;
+
+  embedder::PlatformSupport* const platform_support_;
+
+  // Note: |MessagePipe|s MUST NOT be used under |lock_|. I.e., |lock_| can only
+  // be acquired after |MessagePipe::lock_|, never before. Thus to call into a
+  // |MessagePipe|, a reference to the |MessagePipe| should be acquired from
+  // |local_id_to_endpoint_map_| under |lock_| and then the lock released.
+  base::Lock lock_;  // Protects the members below.
+
+  scoped_ptr<RawChannel> raw_channel_;
+  bool is_running_;
+  // Set when |WillShutdownSoon()| is called.
+  bool is_shutting_down_;
+
+  typedef base::hash_map<MessageInTransit::EndpointId,
+                         scoped_refptr<ChannelEndpoint>> IdToEndpointMap;
+  IdToEndpointMap local_id_to_endpoint_map_;
+  // The next local ID to try (when allocating new local IDs). Note: It should
+  // be checked for existence before use.
+  MessageInTransit::EndpointId next_local_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(Channel);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CHANNEL_H_
diff --git a/mojo/edk/system/channel_endpoint.cc b/mojo/edk/system/channel_endpoint.cc
new file mode 100644
index 0000000..40cb184
--- /dev/null
+++ b/mojo/edk/system/channel_endpoint.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium 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/edk/system/channel_endpoint.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+ChannelEndpoint::ChannelEndpoint(MessagePipe* message_pipe, unsigned port)
+    : state_(STATE_NORMAL),
+      message_pipe_(message_pipe),
+      port_(port),
+      channel_(),
+      local_id_(MessageInTransit::kInvalidEndpointId),
+      remote_id_(MessageInTransit::kInvalidEndpointId) {
+  DCHECK(message_pipe_.get());
+  DCHECK(port_ == 0 || port_ == 1);
+}
+
+void ChannelEndpoint::TakeMessages(MessageInTransitQueue* message_queue) {
+  DCHECK(paused_message_queue_.IsEmpty());
+  paused_message_queue_.Swap(message_queue);
+}
+
+bool ChannelEndpoint::EnqueueMessage(scoped_ptr<MessageInTransit> message) {
+  DCHECK(message);
+
+  base::AutoLock locker(lock_);
+
+  if (!channel_ || remote_id_ == MessageInTransit::kInvalidEndpointId) {
+    // We may reach here if we haven't been attached or run yet.
+    // TODO(vtl): We may also reach here if the channel is shut down early for
+    // some reason (with live message pipes on it). We can't check |state_| yet,
+    // until it's protected under lock, but in this case we should return false
+    // (and not enqueue any messages).
+    paused_message_queue_.AddMessage(message.Pass());
+    return true;
+  }
+
+  // TODO(vtl): Currently, this only works in the "running" case.
+  DCHECK_NE(remote_id_, MessageInTransit::kInvalidEndpointId);
+
+  return WriteMessageNoLock(message.Pass());
+}
+
+void ChannelEndpoint::DetachFromMessagePipe() {
+  // TODO(vtl): Once |message_pipe_| is under |lock_|, we should null it out
+  // here. For now, get the channel to do so for us.
+
+  scoped_refptr<Channel> channel;
+  {
+    base::AutoLock locker(lock_);
+    DCHECK(message_pipe_.get());
+    message_pipe_ = nullptr;
+
+    if (!channel_)
+      return;
+    DCHECK_NE(local_id_, MessageInTransit::kInvalidEndpointId);
+    // TODO(vtl): Once we combine "run" into "attach", |remote_id_| should valid
+    // here as well.
+    channel = channel_;
+  }
+  // Don't call this under |lock_|, since it'll call us back.
+  // TODO(vtl): This seems pretty suboptimal.
+  channel->DetachMessagePipeEndpoint(local_id_, remote_id_);
+}
+
+void ChannelEndpoint::AttachToChannel(Channel* channel,
+                                      MessageInTransit::EndpointId local_id) {
+  DCHECK(channel);
+  DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
+
+  base::AutoLock locker(lock_);
+  DCHECK(!channel_);
+  DCHECK_EQ(local_id_, MessageInTransit::kInvalidEndpointId);
+  channel_ = channel;
+  local_id_ = local_id;
+}
+
+void ChannelEndpoint::Run(MessageInTransit::EndpointId remote_id) {
+  DCHECK_NE(remote_id, MessageInTransit::kInvalidEndpointId);
+
+  base::AutoLock locker(lock_);
+  DCHECK(channel_);
+  DCHECK_EQ(remote_id_, MessageInTransit::kInvalidEndpointId);
+  remote_id_ = remote_id;
+
+  while (!paused_message_queue_.IsEmpty()) {
+    LOG_IF(WARNING, !WriteMessageNoLock(paused_message_queue_.GetMessage()))
+        << "Failed to write enqueue message to channel";
+  }
+}
+
+bool ChannelEndpoint::OnReadMessage(
+    const MessageInTransit::View& message_view,
+    embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+  scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view));
+  scoped_refptr<MessagePipe> message_pipe;
+  unsigned port;
+  {
+    base::AutoLock locker(lock_);
+    DCHECK(channel_);
+    if (!message_pipe_.get()) {
+      // This isn't a failure per se. (It just means that, e.g., the other end
+      // of the message point closed first.)
+      return true;
+    }
+
+    if (message_view.transport_data_buffer_size() > 0) {
+      DCHECK(message_view.transport_data_buffer());
+      message->SetDispatchers(TransportData::DeserializeDispatchers(
+          message_view.transport_data_buffer(),
+          message_view.transport_data_buffer_size(),
+          platform_handles.Pass(),
+          channel_));
+    }
+
+    // Take a ref, and call |EnqueueMessage()| outside the lock.
+    message_pipe = message_pipe_;
+    port = port_;
+  }
+
+  MojoResult result = message_pipe->EnqueueMessage(
+      MessagePipe::GetPeerPort(port), message.Pass());
+  return (result == MOJO_RESULT_OK);
+}
+
+void ChannelEndpoint::OnDisconnect() {
+  scoped_refptr<MessagePipe> message_pipe;
+  unsigned port;
+  {
+    base::AutoLock locker(lock_);
+    if (!message_pipe_.get())
+      return;
+
+    // Take a ref, and call |Close()| outside the lock.
+    message_pipe = message_pipe_;
+    port = port_;
+  }
+  message_pipe->Close(port);
+}
+
+void ChannelEndpoint::DetachFromChannel() {
+  base::AutoLock locker(lock_);
+  DCHECK(channel_);
+  DCHECK_NE(local_id_, MessageInTransit::kInvalidEndpointId);
+  // TODO(vtl): Once we combine "run" into "attach", |remote_id_| should valid
+  // here as well.
+  channel_ = nullptr;
+  local_id_ = MessageInTransit::kInvalidEndpointId;
+  remote_id_ = MessageInTransit::kInvalidEndpointId;
+}
+
+ChannelEndpoint::~ChannelEndpoint() {
+  DCHECK(!message_pipe_.get());
+  DCHECK(!channel_);
+  DCHECK_EQ(local_id_, MessageInTransit::kInvalidEndpointId);
+  DCHECK_EQ(remote_id_, MessageInTransit::kInvalidEndpointId);
+}
+
+bool ChannelEndpoint::WriteMessageNoLock(scoped_ptr<MessageInTransit> message) {
+  DCHECK(message);
+
+  lock_.AssertAcquired();
+
+  DCHECK(channel_);
+  DCHECK_NE(local_id_, MessageInTransit::kInvalidEndpointId);
+  DCHECK_NE(remote_id_, MessageInTransit::kInvalidEndpointId);
+
+  message->SerializeAndCloseDispatchers(channel_);
+  message->set_source_id(local_id_);
+  message->set_destination_id(remote_id_);
+  return channel_->WriteMessage(message.Pass());
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/channel_endpoint.h b/mojo/edk/system/channel_endpoint.h
new file mode 100644
index 0000000..0ba7aaf
--- /dev/null
+++ b/mojo/edk/system/channel_endpoint.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_
+#define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_in_transit_queue.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class MessagePipe;
+
+// TODO(vtl): The plan:
+//   - (Done.) Move |Channel::Endpoint| to |ChannelEndpoint|. Make it
+//     refcounted, and not copyable. Make |Channel| a friend. Make things work.
+//   - (Done.) Give |ChannelEndpoint| a lock. The lock order (in order of
+//     allowable acquisition) is: |MessagePipe|, |ChannelEndpoint|, |Channel|.
+//   - Stop having |Channel| as a friend.
+//   - Move logic from |ProxyMessagePipeEndpoint| into |ChannelEndpoint|. Right
+//     now, we have to go through lots of contortions to manipulate state owned
+//     by |ProxyMessagePipeEndpoint| (in particular, |Channel::Endpoint| doesn't
+//     know about the remote ID; the local ID is duplicated in two places).
+//     Hollow out |ProxyMessagePipeEndpoint|, and have it just own a reference
+//     to |ChannelEndpoint| (hence the refcounting).
+//   - In essence, |ChannelEndpoint| becomes the thing that knows about
+//     channel-specific aspects of an endpoint (notably local and remote IDs,
+//     and knowledge about handshaking), and mediates between the |Channel| and
+//     the |MessagePipe|.
+//   - In the end state, |Channel| should no longer need to know about
+//     |MessagePipe| and ports (but only |ChannelEndpoint|) and
+//     |ProxyMessagePipeEndpoint| should no longer need to know about |Channel|
+//     (ditto).
+//
+// Things as they are now, before I change everything (TODO(vtl): update this
+// comment appropriately):
+//
+// Terminology:
+//   - "Message pipe endpoint": In the implementation, a |MessagePipe| owns
+//     two |MessagePipeEndpoint| objects, one for each port. The
+//     |MessagePipeEndpoint| objects are only accessed via the |MessagePipe|
+//     (which has the lock), with the additional information of the port
+//     number. So as far as the channel is concerned, a message pipe endpoint
+//     is a pointer to a |MessagePipe| together with the port number.
+//       - The value of |port| in |EndpointInfo| refers to the
+//         |ProxyMessagePipeEndpoint| (i.e., the endpoint that is logically on
+//         the other side). Messages received by a channel for a message pipe
+//         are thus written to the *peer* of this port.
+//   - "Attached"/"detached": A message pipe endpoint is attached to a channel
+//     if it has a pointer to it. It must be detached before the channel gives
+//     up its pointer to it in order to break a reference cycle. (This cycle
+//     is needed to allow a channel to be shut down cleanly, without shutting
+//     down everything else first.)
+//   - "Running" (message pipe endpoint): A message pipe endpoint is running
+//     if messages written to it (via some |MessagePipeDispatcher|, to which
+//     some |MojoHandle| is assigned) are being transmitted through the
+//     channel.
+//       - Before a message pipe endpoint is run, it will queue messages.
+//       - When a message pipe endpoint is detached from a channel, it is also
+//         taken out of the running state. After that point, messages should
+//         no longer be written to it.
+//   - "Normal" message pipe endpoint (state): The channel itself does not
+//     have knowledge of whether a message pipe endpoint has started running
+//     yet. It will *receive* messages for a message pipe in either state (but
+//     the message pipe endpoint won't *send* messages to the channel if it
+//     has not started running).
+//   - "Zombie" message pipe endpoint (state): A message pipe endpoint is a
+//     zombie if it is still in |local_id_to_endpoint_info_map_|, but the
+//     channel is no longer forwarding messages to it (even if it may still be
+//     receiving messages for it).
+//       - There are various types of zombies, depending on the reason the
+//         message pipe endpoint cannot yet be removed.
+//       - If the remote side is closed, it will send a "remove" control
+//         message. After the channel receives that message (to which it
+//         responds with a "remove ack" control message), it knows that it
+//         shouldn't receive any more messages for that message pipe endpoint
+//         (local ID), but it must wait for the endpoint to detach. (It can't
+//         do so without a race, since it can't call into the message pipe
+//         under |lock_|.) [TODO(vtl): When I add remotely-allocated IDs,
+//         we'll have to remove the |EndpointInfo| from
+//         |local_id_to_endpoint_info_map_| -- i.e., remove the local ID,
+//         since it's no longer valid and may be reused by the remote side --
+//         and keep the |EndpointInfo| alive in some other way.]
+//       - If the local side is closed and the message pipe endpoint was
+//         already running (so there are no queued messages left to send), it
+//         will detach the endpoint, and send a "remove" control message.
+//         However, the channel may still receive messages for that endpoint
+//         until it receives a "remove ack" control message.
+//       - If the local side is closed but the message pipe endpoint was not
+//         yet running , the detaching is delayed until after it is run and
+//         all the queued messages are sent to the channel. On being detached,
+//         things proceed as in one of the above cases. The endpoint is *not*
+//         a zombie until it is detached (or a "remove" message is received).
+//         [TODO(vtl): Maybe we can get rid of this case? It'd only not yet be
+//         running since under the current scheme it wouldn't have a remote ID
+//         yet.]
+//       - Note that even if the local side is closed, it may still receive a
+//         "remove" message from the other side (if the other side is closed
+//         simultaneously, and both sides send "remove" messages). In that
+//         case, it must still remain alive until it receives the "remove
+//         ack" (and it must ack the "remove" message that it received).
+class MOJO_SYSTEM_IMPL_EXPORT ChannelEndpoint
+    : public base::RefCountedThreadSafe<ChannelEndpoint> {
+ public:
+  // TODO(vtl): More comments....
+  ChannelEndpoint(MessagePipe* message_pipe, unsigned port);
+
+  // Takes messages from the given |MessageInTransitQueue|. This must be called
+  // before this object is attached to a channel, and before anyone has a chance
+  // to enqueue any messages.
+  void TakeMessages(MessageInTransitQueue* message_queue);
+
+  // Methods called by |MessagePipe| (via |ProxyMessagePipeEndpoint|):
+
+  // TODO(vtl): This currently only works if we're "running". We'll move the
+  // "paused message queue" here (will this be needed when we have
+  // locally-allocated remote IDs?).
+  bool EnqueueMessage(scoped_ptr<MessageInTransit> message);
+
+  void DetachFromMessagePipe();
+
+  // Methods called by |Channel|:
+
+  // Called by |Channel| when it takes a reference to this object.
+  void AttachToChannel(Channel* channel, MessageInTransit::EndpointId local_id);
+
+  // TODO(vtl): Combine this with |AttachToChannel()|.
+  void Run(MessageInTransit::EndpointId remote_id);
+
+  // Called by |Channel| when it receives a message for the message pipe.
+  bool OnReadMessage(const MessageInTransit::View& message_view,
+                     embedder::ScopedPlatformHandleVectorPtr platform_handles);
+
+  // Called by |Channel| to notify that it'll no longer receive messages for the
+  // message pipe (i.e., |OnReadMessage()| will no longer be called).
+  // TODO(vtl): After more simplification, we might be able to get rid of this
+  // (and merge it with |DetachFromChannel()|).
+  void OnDisconnect();
+
+  // Called by |Channel| before it gives up its reference to this object.
+  void DetachFromChannel();
+
+ private:
+  enum State {
+    // Attached, possibly running or not.
+    STATE_NORMAL,
+    // "Zombie" states:
+    // Waiting for |DetachMessagePipeEndpoint()| before removing.
+    STATE_WAIT_LOCAL_DETACH,
+    // Waiting for a |kSubtypeChannelRemoveMessagePipeEndpointAck| before
+    // removing.
+    STATE_WAIT_REMOTE_REMOVE_ACK,
+  };
+
+  // TODO(vtl): This is totally temporary, until this becomes a proper
+  // self-contained class. See the plan above.
+  friend class Channel;
+
+  friend class base::RefCountedThreadSafe<ChannelEndpoint>;
+  ~ChannelEndpoint();
+
+  // Must be called with |lock_| held.
+  bool WriteMessageNoLock(scoped_ptr<MessageInTransit> message);
+
+  // TODO(vtl): Move these under lock.
+  State state_;
+
+  // TODO(vtl): Move the things above under lock.
+  // Protects the members below.
+  base::Lock lock_;
+
+  // |message_pipe_| must be valid whenever it is non-null. Before
+  // |*message_pipe_| gives up its reference to this object, it must call
+  // |DetachFromMessagePipe()|.
+  // NOTE: This is a |scoped_refptr<>|, rather than a raw pointer, since the
+  // |Channel| needs to keep the |MessagePipe| alive for the "proxy-proxy" case.
+  // Possibly we'll be able to eliminate that case when we have full
+  // multiprocess support.
+  // WARNING: |MessagePipe| methods must not be called under |lock_|. Thus to
+  // make such a call, a reference must first be taken under |lock_| and the
+  // lock released.
+  scoped_refptr<MessagePipe> message_pipe_;
+  unsigned port_;
+
+  // |channel_| must be valid whenever it is non-null. Before |*channel_| gives
+  // up its reference to this object, it must call |DetachFromChannel()|.
+  Channel* channel_;
+  MessageInTransit::EndpointId local_id_;
+  MessageInTransit::EndpointId remote_id_;
+
+  // This queue is used before we're running on a channel and ready to send
+  // messages.
+  MessageInTransitQueue paused_message_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelEndpoint);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_
diff --git a/mojo/edk/system/channel_unittest.cc b/mojo/edk/system/channel_unittest.cc
new file mode 100644
index 0000000..0c5d596
--- /dev/null
+++ b/mojo/edk/system/channel_unittest.cc
@@ -0,0 +1,320 @@
+// Copyright 2014 The Chromium 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/edk/system/channel.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/test_io_thread.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+enum Tristate { TRISTATE_UNKNOWN = -1, TRISTATE_FALSE = 0, TRISTATE_TRUE = 1 };
+
+Tristate BoolToTristate(bool b) {
+  return b ? TRISTATE_TRUE : TRISTATE_FALSE;
+}
+
+class ChannelTest : public testing::Test {
+ public:
+  ChannelTest()
+      : io_thread_(base::TestIOThread::kAutoStart),
+        init_result_(TRISTATE_UNKNOWN) {}
+  virtual ~ChannelTest() {}
+
+  virtual void SetUp() override {
+    io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&ChannelTest::SetUpOnIOThread, base::Unretained(this)));
+  }
+
+  void CreateChannelOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+    channel_ = new Channel(&platform_support_);
+  }
+
+  void InitChannelOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    CHECK(raw_channel_);
+    CHECK(channel_.get());
+    CHECK_EQ(init_result_, TRISTATE_UNKNOWN);
+
+    init_result_ = BoolToTristate(channel_->Init(raw_channel_.Pass()));
+  }
+
+  void ShutdownChannelOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    CHECK(channel_.get());
+    channel_->Shutdown();
+  }
+
+  base::TestIOThread* io_thread() { return &io_thread_; }
+  RawChannel* raw_channel() { return raw_channel_.get(); }
+  scoped_ptr<RawChannel>* mutable_raw_channel() { return &raw_channel_; }
+  Channel* channel() { return channel_.get(); }
+  scoped_refptr<Channel>* mutable_channel() { return &channel_; }
+  Tristate init_result() const { return init_result_; }
+
+ private:
+  void SetUpOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    embedder::PlatformChannelPair channel_pair;
+    raw_channel_ = RawChannel::Create(channel_pair.PassServerHandle()).Pass();
+    other_platform_handle_ = channel_pair.PassClientHandle();
+  }
+
+  embedder::SimplePlatformSupport platform_support_;
+  base::TestIOThread io_thread_;
+  scoped_ptr<RawChannel> raw_channel_;
+  embedder::ScopedPlatformHandle other_platform_handle_;
+  scoped_refptr<Channel> channel_;
+
+  Tristate init_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelTest);
+};
+
+// ChannelTest.InitShutdown ----------------------------------------------------
+
+TEST_F(ChannelTest, InitShutdown) {
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&ChannelTest::CreateChannelOnIOThread,
+                                          base::Unretained(this)));
+  ASSERT_TRUE(channel());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this)));
+  EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+                 base::Unretained(this)));
+
+  // Okay to destroy |Channel| on not-the-I/O-thread.
+  EXPECT_TRUE(channel()->HasOneRef());
+  *mutable_channel() = nullptr;
+}
+
+// ChannelTest.InitFails -------------------------------------------------------
+
+class MockRawChannelOnInitFails : public RawChannel {
+ public:
+  MockRawChannelOnInitFails() : on_init_called_(false) {}
+  virtual ~MockRawChannelOnInitFails() {}
+
+  // |RawChannel| public methods:
+  virtual size_t GetSerializedPlatformHandleSize() const override { return 0; }
+
+ private:
+  // |RawChannel| protected methods:
+  virtual IOResult Read(size_t*) override {
+    CHECK(false);
+    return IO_FAILED_UNKNOWN;
+  }
+  virtual IOResult ScheduleRead() override {
+    CHECK(false);
+    return IO_FAILED_UNKNOWN;
+  }
+  virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+      size_t,
+      const void*) override {
+    CHECK(false);
+    return embedder::ScopedPlatformHandleVectorPtr();
+  }
+  virtual IOResult WriteNoLock(size_t*, size_t*) override {
+    CHECK(false);
+    return IO_FAILED_UNKNOWN;
+  }
+  virtual IOResult ScheduleWriteNoLock() override {
+    CHECK(false);
+    return IO_FAILED_UNKNOWN;
+  }
+  virtual bool OnInit() override {
+    EXPECT_FALSE(on_init_called_);
+    on_init_called_ = true;
+    return false;
+  }
+  virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer>,
+                                scoped_ptr<WriteBuffer>) override {
+    CHECK(false);
+  }
+
+  bool on_init_called_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockRawChannelOnInitFails);
+};
+
+TEST_F(ChannelTest, InitFails) {
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&ChannelTest::CreateChannelOnIOThread,
+                                          base::Unretained(this)));
+  ASSERT_TRUE(channel());
+
+  ASSERT_TRUE(raw_channel());
+  mutable_raw_channel()->reset(new MockRawChannelOnInitFails());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this)));
+  EXPECT_EQ(TRISTATE_FALSE, init_result());
+
+  // Should destroy |Channel| with no |Shutdown()| (on not-the-I/O-thread).
+  EXPECT_TRUE(channel()->HasOneRef());
+  *mutable_channel() = nullptr;
+}
+
+// ChannelTest.CloseBeforeRun --------------------------------------------------
+
+TEST_F(ChannelTest, CloseBeforeRun) {
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&ChannelTest::CreateChannelOnIOThread,
+                                          base::Unretained(this)));
+  ASSERT_TRUE(channel());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this)));
+  EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+  scoped_refptr<ChannelEndpoint> channel_endpoint;
+  scoped_refptr<MessagePipe> mp(
+      MessagePipe::CreateLocalProxy(&channel_endpoint));
+
+  MessageInTransit::EndpointId local_id =
+      channel()->AttachEndpoint(channel_endpoint);
+  EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+  mp->Close(0);
+
+  // TODO(vtl): Currently, the |Close()| above won't detach (since it thinks
+  // we're still expecting a "run" message from the other side), so the
+  // |RunMessagePipeEndpoint()| below will return true. We need to refactor
+  // |AttachEndpoint()| to indicate whether |Run...()| will necessarily be
+  // called or not. (Then, in the case that it may not be called, this will
+  // return false.)
+  channel()->RunEndpoint(channel_endpoint, Channel::kBootstrapEndpointId);
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+                 base::Unretained(this)));
+
+  EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// ChannelTest.ShutdownAfterAttachAndRun ---------------------------------------
+
+TEST_F(ChannelTest, ShutdownAfterAttach) {
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&ChannelTest::CreateChannelOnIOThread,
+                                          base::Unretained(this)));
+  ASSERT_TRUE(channel());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this)));
+  EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+  scoped_refptr<ChannelEndpoint> channel_endpoint;
+  scoped_refptr<MessagePipe> mp(
+      MessagePipe::CreateLocalProxy(&channel_endpoint));
+
+  MessageInTransit::EndpointId local_id =
+      channel()->AttachEndpoint(channel_endpoint);
+  EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+  // TODO(vtl): Currently, we always "expect" a |RunMessagePipeEndpoint()| after
+  // an |AttachEndpoint()| (which is actually incorrect). We need to refactor
+  // |AttachEndpoint()| to indicate whether |Run...()| will necessarily be
+  // called or not. (Then, in the case that it may not be called, we should test
+  // a |Shutdown()| without the |Run...()|.)
+  channel()->RunEndpoint(channel_endpoint, Channel::kBootstrapEndpointId);
+
+  Waiter waiter;
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Don't wait for the shutdown to run ...
+  io_thread()->PostTask(FROM_HERE,
+                        base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+                                   base::Unretained(this)));
+
+  // ... since this |Wait()| should fail once the channel is shut down.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            waiter.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+  HandleSignalsState hss;
+  mp->RemoveWaiter(0, &waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(0);
+
+  EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// ChannelTest.WaitAfterAttachRunAndShutdown -----------------------------------
+
+TEST_F(ChannelTest, WaitAfterAttachRunAndShutdown) {
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&ChannelTest::CreateChannelOnIOThread,
+                                          base::Unretained(this)));
+  ASSERT_TRUE(channel());
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this)));
+  EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+  scoped_refptr<ChannelEndpoint> channel_endpoint;
+  scoped_refptr<MessagePipe> mp(
+      MessagePipe::CreateLocalProxy(&channel_endpoint));
+
+  MessageInTransit::EndpointId local_id =
+      channel()->AttachEndpoint(channel_endpoint);
+  EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+  channel()->RunEndpoint(channel_endpoint, Channel::kBootstrapEndpointId);
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+                 base::Unretained(this)));
+
+  Waiter waiter;
+  waiter.Init();
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(0);
+
+  EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// TODO(vtl): More. ------------------------------------------------------------
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/constants.h b/mojo/edk/system/constants.h
new file mode 100644
index 0000000..23f35d8
--- /dev/null
+++ b/mojo/edk/system/constants.h
@@ -0,0 +1,48 @@
+// 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_EDK_SYSTEM_CONSTANTS_H_
+#define MOJO_EDK_SYSTEM_CONSTANTS_H_
+
+#include <stddef.h>
+
+namespace mojo {
+namespace system {
+
+// Maximum number of open (Mojo) handles.
+// TODO(vtl): This doesn't count "live" handles, some of which may live in
+// messages.
+const size_t kMaxHandleTableSize = 1000000;
+
+// Maximum number of active memory mappings.
+const size_t kMaxMappingTableSize = 1000000;
+
+const size_t kMaxWaitManyNumHandles = kMaxHandleTableSize;
+
+const size_t kMaxMessageNumBytes = 4 * 1024 * 1024;
+
+const size_t kMaxMessageNumHandles = 10000;
+
+// Maximum capacity of a data pipe, in bytes. This value must fit into a
+// |uint32_t|.
+// WARNING: If you bump it closer to 2^32, you must audit all the code to check
+// that we don't overflow (2^31 would definitely be risky; up to 2^30 is
+// probably okay).
+const size_t kMaxDataPipeCapacityBytes = 256 * 1024 * 1024;  // 256 MB.
+
+const size_t kDefaultDataPipeCapacityBytes = 1024 * 1024;  // 1 MB.
+
+// Alignment for the "start" of the data buffer used by data pipes. (The
+// alignment of elements will depend on this and the element size.)
+const size_t kDataPipeBufferAlignmentBytes = 16;
+
+// TODO(vtl): Set this hard limit appropriately (e.g., higher on 64-bit). (This
+// will also entail some auditing to make sure I'm not messing up my checks
+// anywhere.)
+const size_t kMaxSharedMemoryNumBytes = 1024 * 1024 * 1024;  // 1 GB.
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CONSTANTS_H_
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
new file mode 100644
index 0000000..c67a626
--- /dev/null
+++ b/mojo/edk/system/core.cc
@@ -0,0 +1,606 @@
+// 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/edk/system/core.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/platform_support.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/data_pipe.h"
+#include "mojo/edk/system/data_pipe_consumer_dispatcher.h"
+#include "mojo/edk/system/data_pipe_producer_dispatcher.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/local_data_pipe.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/shared_buffer_dispatcher.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace system {
+
+// Implementation notes
+//
+// Mojo primitives are implemented by the singleton |Core| object. Most calls
+// are for a "primary" handle (the first argument). |Core::GetDispatcher()| is
+// used to look up a |Dispatcher| object for a given handle. That object
+// implements most primitives for that object. The wait primitives are not
+// attached to objects and are implemented by |Core| itself.
+//
+// Some objects have multiple handles associated to them, e.g., message pipes
+// (which have two). In such a case, there is still a |Dispatcher| (e.g.,
+// |MessagePipeDispatcher|) for each handle, with each handle having a strong
+// reference to the common "secondary" object (e.g., |MessagePipe|). This
+// secondary object does NOT have any references to the |Dispatcher|s (even if
+// it did, it wouldn't be able to do anything with them due to lock order
+// requirements -- see below).
+//
+// Waiting is implemented by having the thread that wants to wait call the
+// |Dispatcher|s for the handles that it wants to wait on with a |Waiter|
+// object; this |Waiter| object may be created on the stack of that thread or be
+// kept in thread local storage for that thread (TODO(vtl): future improvement).
+// The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned
+// by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g.,
+// |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a
+// |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|.
+
+// Thread-safety notes
+//
+// Mojo primitives calls are thread-safe. We achieve this with relatively
+// fine-grained locking. There is a global handle table lock. This lock should
+// be held as briefly as possible (TODO(vtl): a future improvement would be to
+// switch it to a reader-writer lock). Each |Dispatcher| object then has a lock
+// (which subclasses can use to protect their data).
+//
+// The lock ordering is as follows:
+//   1. global handle table lock, global mapping table lock
+//   2. |Dispatcher| locks
+//   3. secondary object locks
+//   ...
+//   INF. |Waiter| locks
+//
+// Notes:
+//    - While holding a |Dispatcher| lock, you may not unconditionally attempt
+//      to take another |Dispatcher| lock. (This has consequences on the
+//      concurrency semantics of |MojoWriteMessage()| when passing handles.)
+//      Doing so would lead to deadlock.
+//    - Locks at the "INF" level may not have any locks taken while they are
+//      held.
+
+// TODO(vtl): This should take a |scoped_ptr<PlatformSupport>| as a parameter.
+Core::Core(scoped_ptr<embedder::PlatformSupport> platform_support)
+    : platform_support_(platform_support.Pass()) {
+}
+
+Core::~Core() {
+}
+
+MojoHandle Core::AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher) {
+  base::AutoLock locker(handle_table_lock_);
+  return handle_table_.AddDispatcher(dispatcher);
+}
+
+scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) {
+  if (handle == MOJO_HANDLE_INVALID)
+    return nullptr;
+
+  base::AutoLock locker(handle_table_lock_);
+  return handle_table_.GetDispatcher(handle);
+}
+
+MojoTimeTicks Core::GetTimeTicksNow() {
+  return base::TimeTicks::Now().ToInternalValue();
+}
+
+MojoResult Core::Close(MojoHandle handle) {
+  if (handle == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> dispatcher;
+  {
+    base::AutoLock locker(handle_table_lock_);
+    MojoResult result =
+        handle_table_.GetAndRemoveDispatcher(handle, &dispatcher);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  // The dispatcher doesn't have a say in being closed, but gets notified of it.
+  // Note: This is done outside of |handle_table_lock_|. As a result, there's a
+  // race condition that the dispatcher must handle; see the comment in
+  // |Dispatcher| in dispatcher.h.
+  return dispatcher->Close();
+}
+
+MojoResult Core::Wait(MojoHandle handle,
+                      MojoHandleSignals signals,
+                      MojoDeadline deadline,
+                      UserPointer<MojoHandleSignalsState> signals_state) {
+  uint32_t unused = static_cast<uint32_t>(-1);
+  HandleSignalsState hss;
+  MojoResult rv = WaitManyInternal(&handle,
+                                   &signals,
+                                   1,
+                                   deadline,
+                                   &unused,
+                                   signals_state.IsNull() ? nullptr : &hss);
+  if (rv != MOJO_RESULT_INVALID_ARGUMENT && !signals_state.IsNull())
+    signals_state.Put(hss);
+  return rv;
+}
+
+MojoResult Core::WaitMany(UserPointer<const MojoHandle> handles,
+                          UserPointer<const MojoHandleSignals> signals,
+                          uint32_t num_handles,
+                          MojoDeadline deadline,
+                          UserPointer<uint32_t> result_index,
+                          UserPointer<MojoHandleSignalsState> signals_states) {
+  if (num_handles < 1)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (num_handles > kMaxWaitManyNumHandles)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  UserPointer<const MojoHandle>::Reader handles_reader(handles, num_handles);
+  UserPointer<const MojoHandleSignals>::Reader signals_reader(signals,
+                                                              num_handles);
+  uint32_t index = static_cast<uint32_t>(-1);
+  MojoResult rv;
+  if (signals_states.IsNull()) {
+    rv = WaitManyInternal(handles_reader.GetPointer(),
+                          signals_reader.GetPointer(),
+                          num_handles,
+                          deadline,
+                          &index,
+                          nullptr);
+  } else {
+    UserPointer<MojoHandleSignalsState>::Writer signals_states_writer(
+        signals_states, num_handles);
+    // Note: The |reinterpret_cast| is safe, since |HandleSignalsState| is a
+    // subclass of |MojoHandleSignalsState| that doesn't add any data members.
+    rv = WaitManyInternal(handles_reader.GetPointer(),
+                          signals_reader.GetPointer(),
+                          num_handles,
+                          deadline,
+                          &index,
+                          reinterpret_cast<HandleSignalsState*>(
+                              signals_states_writer.GetPointer()));
+    if (rv != MOJO_RESULT_INVALID_ARGUMENT)
+      signals_states_writer.Commit();
+  }
+  if (index != static_cast<uint32_t>(-1) && !result_index.IsNull())
+    result_index.Put(index);
+  return rv;
+}
+
+MojoResult Core::CreateMessagePipe(
+    UserPointer<const MojoCreateMessagePipeOptions> options,
+    UserPointer<MojoHandle> message_pipe_handle0,
+    UserPointer<MojoHandle> message_pipe_handle1) {
+  MojoCreateMessagePipeOptions validated_options = {};
+  MojoResult result =
+      MessagePipeDispatcher::ValidateCreateOptions(options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  scoped_refptr<MessagePipeDispatcher> dispatcher0(
+      new MessagePipeDispatcher(validated_options));
+  scoped_refptr<MessagePipeDispatcher> dispatcher1(
+      new MessagePipeDispatcher(validated_options));
+
+  std::pair<MojoHandle, MojoHandle> handle_pair;
+  {
+    base::AutoLock locker(handle_table_lock_);
+    handle_pair = handle_table_.AddDispatcherPair(dispatcher0, dispatcher1);
+  }
+  if (handle_pair.first == MOJO_HANDLE_INVALID) {
+    DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID);
+    LOG(ERROR) << "Handle table full";
+    dispatcher0->Close();
+    dispatcher1->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  scoped_refptr<MessagePipe> message_pipe(MessagePipe::CreateLocalLocal());
+  dispatcher0->Init(message_pipe, 0);
+  dispatcher1->Init(message_pipe, 1);
+
+  message_pipe_handle0.Put(handle_pair.first);
+  message_pipe_handle1.Put(handle_pair.second);
+  return MOJO_RESULT_OK;
+}
+
+// Implementation note: To properly cancel waiters and avoid other races, this
+// does not transfer dispatchers from one handle to another, even when sending a
+// message in-process. Instead, it must transfer the "contents" of the
+// dispatcher to a new dispatcher, and then close the old dispatcher. If this
+// isn't done, in the in-process case, calls on the old handle may complete
+// after the the message has been received and a new handle created (and
+// possibly even after calls have been made on the new handle).
+MojoResult Core::WriteMessage(MojoHandle message_pipe_handle,
+                              UserPointer<const void> bytes,
+                              uint32_t num_bytes,
+                              UserPointer<const MojoHandle> handles,
+                              uint32_t num_handles,
+                              MojoWriteMessageFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  // Easy case: not sending any handles.
+  if (num_handles == 0)
+    return dispatcher->WriteMessage(bytes, num_bytes, nullptr, flags);
+
+  // We have to handle |handles| here, since we have to mark them busy in the
+  // global handle table. We can't delegate this to the dispatcher, since the
+  // handle table lock must be acquired before the dispatcher lock.
+  //
+  // (This leads to an oddity: |handles|/|num_handles| are always verified for
+  // validity, even for dispatchers that don't support |WriteMessage()| and will
+  // simply return failure unconditionally. It also breaks the usual
+  // left-to-right verification order of arguments.)
+  if (num_handles > kMaxMessageNumHandles)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  UserPointer<const MojoHandle>::Reader handles_reader(handles, num_handles);
+
+  // We'll need to hold on to the dispatchers so that we can pass them on to
+  // |WriteMessage()| and also so that we can unlock their locks afterwards
+  // without accessing the handle table. These can be dumb pointers, since their
+  // entries in the handle table won't get removed (since they'll be marked as
+  // busy).
+  std::vector<DispatcherTransport> transports(num_handles);
+
+  // When we pass handles, we have to try to take all their dispatchers' locks
+  // and mark the handles as busy. If the call succeeds, we then remove the
+  // handles from the handle table.
+  {
+    base::AutoLock locker(handle_table_lock_);
+    MojoResult result =
+        handle_table_.MarkBusyAndStartTransport(message_pipe_handle,
+                                                handles_reader.GetPointer(),
+                                                num_handles,
+                                                &transports);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  MojoResult rv =
+      dispatcher->WriteMessage(bytes, num_bytes, &transports, flags);
+
+  // We need to release the dispatcher locks before we take the handle table
+  // lock.
+  for (uint32_t i = 0; i < num_handles; i++)
+    transports[i].End();
+
+  {
+    base::AutoLock locker(handle_table_lock_);
+    if (rv == MOJO_RESULT_OK) {
+      handle_table_.RemoveBusyHandles(handles_reader.GetPointer(), num_handles);
+    } else {
+      handle_table_.RestoreBusyHandles(handles_reader.GetPointer(),
+                                       num_handles);
+    }
+  }
+
+  return rv;
+}
+
+MojoResult Core::ReadMessage(MojoHandle message_pipe_handle,
+                             UserPointer<void> bytes,
+                             UserPointer<uint32_t> num_bytes,
+                             UserPointer<MojoHandle> handles,
+                             UserPointer<uint32_t> num_handles,
+                             MojoReadMessageFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  uint32_t num_handles_value = num_handles.IsNull() ? 0 : num_handles.Get();
+
+  MojoResult rv;
+  if (num_handles_value == 0) {
+    // Easy case: won't receive any handles.
+    rv = dispatcher->ReadMessage(
+        bytes, num_bytes, nullptr, &num_handles_value, flags);
+  } else {
+    DispatcherVector dispatchers;
+    rv = dispatcher->ReadMessage(
+        bytes, num_bytes, &dispatchers, &num_handles_value, flags);
+    if (!dispatchers.empty()) {
+      DCHECK_EQ(rv, MOJO_RESULT_OK);
+      DCHECK(!num_handles.IsNull());
+      DCHECK_LE(dispatchers.size(), static_cast<size_t>(num_handles_value));
+
+      bool success;
+      UserPointer<MojoHandle>::Writer handles_writer(handles,
+                                                     dispatchers.size());
+      {
+        base::AutoLock locker(handle_table_lock_);
+        success = handle_table_.AddDispatcherVector(
+            dispatchers, handles_writer.GetPointer());
+      }
+      if (success) {
+        handles_writer.Commit();
+      } else {
+        LOG(ERROR) << "Received message with " << dispatchers.size()
+                   << " handles, but handle table full";
+        // Close dispatchers (outside the lock).
+        for (size_t i = 0; i < dispatchers.size(); i++) {
+          if (dispatchers[i].get())
+            dispatchers[i]->Close();
+        }
+        if (rv == MOJO_RESULT_OK)
+          rv = MOJO_RESULT_RESOURCE_EXHAUSTED;
+      }
+    }
+  }
+
+  if (!num_handles.IsNull())
+    num_handles.Put(num_handles_value);
+  return rv;
+}
+
+MojoResult Core::CreateDataPipe(
+    UserPointer<const MojoCreateDataPipeOptions> options,
+    UserPointer<MojoHandle> data_pipe_producer_handle,
+    UserPointer<MojoHandle> data_pipe_consumer_handle) {
+  MojoCreateDataPipeOptions validated_options = {};
+  MojoResult result =
+      DataPipe::ValidateCreateOptions(options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  scoped_refptr<DataPipeProducerDispatcher> producer_dispatcher(
+      new DataPipeProducerDispatcher());
+  scoped_refptr<DataPipeConsumerDispatcher> consumer_dispatcher(
+      new DataPipeConsumerDispatcher());
+
+  std::pair<MojoHandle, MojoHandle> handle_pair;
+  {
+    base::AutoLock locker(handle_table_lock_);
+    handle_pair = handle_table_.AddDispatcherPair(producer_dispatcher,
+                                                  consumer_dispatcher);
+  }
+  if (handle_pair.first == MOJO_HANDLE_INVALID) {
+    DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID);
+    LOG(ERROR) << "Handle table full";
+    producer_dispatcher->Close();
+    consumer_dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+  DCHECK_NE(handle_pair.second, MOJO_HANDLE_INVALID);
+
+  scoped_refptr<DataPipe> data_pipe(new LocalDataPipe(validated_options));
+  producer_dispatcher->Init(data_pipe);
+  consumer_dispatcher->Init(data_pipe);
+
+  data_pipe_producer_handle.Put(handle_pair.first);
+  data_pipe_consumer_handle.Put(handle_pair.second);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle,
+                           UserPointer<const void> elements,
+                           UserPointer<uint32_t> num_bytes,
+                           MojoWriteDataFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->WriteData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle,
+                                UserPointer<void*> buffer,
+                                UserPointer<uint32_t> buffer_num_bytes,
+                                MojoWriteDataFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Core::EndWriteData(MojoHandle data_pipe_producer_handle,
+                              uint32_t num_bytes_written) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->EndWriteData(num_bytes_written);
+}
+
+MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle,
+                          UserPointer<void> elements,
+                          UserPointer<uint32_t> num_bytes,
+                          MojoReadDataFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->ReadData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle,
+                               UserPointer<const void*> buffer,
+                               UserPointer<uint32_t> buffer_num_bytes,
+                               MojoReadDataFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->BeginReadData(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Core::EndReadData(MojoHandle data_pipe_consumer_handle,
+                             uint32_t num_bytes_read) {
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->EndReadData(num_bytes_read);
+}
+
+MojoResult Core::CreateSharedBuffer(
+    UserPointer<const MojoCreateSharedBufferOptions> options,
+    uint64_t num_bytes,
+    UserPointer<MojoHandle> shared_buffer_handle) {
+  MojoCreateSharedBufferOptions validated_options = {};
+  MojoResult result = SharedBufferDispatcher::ValidateCreateOptions(
+      options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  result = SharedBufferDispatcher::Create(
+      platform_support(), validated_options, num_bytes, &dispatcher);
+  if (result != MOJO_RESULT_OK) {
+    DCHECK(!dispatcher.get());
+    return result;
+  }
+
+  MojoHandle h = AddDispatcher(dispatcher);
+  if (h == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  shared_buffer_handle.Put(h);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::DuplicateBufferHandle(
+    MojoHandle buffer_handle,
+    UserPointer<const MojoDuplicateBufferHandleOptions> options,
+    UserPointer<MojoHandle> new_buffer_handle) {
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  // Don't verify |options| here; that's the dispatcher's job.
+  scoped_refptr<Dispatcher> new_dispatcher;
+  MojoResult result =
+      dispatcher->DuplicateBufferHandle(options, &new_dispatcher);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  MojoHandle new_handle = AddDispatcher(new_dispatcher);
+  if (new_handle == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  new_buffer_handle.Put(new_handle);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::MapBuffer(MojoHandle buffer_handle,
+                           uint64_t offset,
+                           uint64_t num_bytes,
+                           UserPointer<void*> buffer,
+                           MojoMapBufferFlags flags) {
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+  if (!dispatcher.get())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
+  MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  DCHECK(mapping);
+  void* address = mapping->GetBase();
+  {
+    base::AutoLock locker(mapping_table_lock_);
+    result = mapping_table_.AddMapping(mapping.Pass());
+  }
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  buffer.Put(address);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::UnmapBuffer(UserPointer<void> buffer) {
+  base::AutoLock locker(mapping_table_lock_);
+  return mapping_table_.RemoveMapping(buffer.GetPointerValue());
+}
+
+// Note: We allow |handles| to repeat the same handle multiple times, since
+// different flags may be specified.
+// TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this
+// more carefully and address it if necessary.
+MojoResult Core::WaitManyInternal(const MojoHandle* handles,
+                                  const MojoHandleSignals* signals,
+                                  uint32_t num_handles,
+                                  MojoDeadline deadline,
+                                  uint32_t* result_index,
+                                  HandleSignalsState* signals_states) {
+  DCHECK_GT(num_handles, 0u);
+  DCHECK_EQ(*result_index, static_cast<uint32_t>(-1));
+
+  DispatcherVector dispatchers;
+  dispatchers.reserve(num_handles);
+  for (uint32_t i = 0; i < num_handles; i++) {
+    scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]);
+    if (!dispatcher.get()) {
+      *result_index = i;
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    }
+    dispatchers.push_back(dispatcher);
+  }
+
+  // TODO(vtl): Should make the waiter live (permanently) in TLS.
+  Waiter waiter;
+  waiter.Init();
+
+  uint32_t i;
+  MojoResult rv = MOJO_RESULT_OK;
+  for (i = 0; i < num_handles; i++) {
+    rv = dispatchers[i]->AddWaiter(
+        &waiter, signals[i], i, signals_states ? &signals_states[i] : nullptr);
+    if (rv != MOJO_RESULT_OK) {
+      *result_index = i;
+      break;
+    }
+  }
+  uint32_t num_added = i;
+
+  if (rv == MOJO_RESULT_ALREADY_EXISTS)
+    rv = MOJO_RESULT_OK;  // The i-th one is already "triggered".
+  else if (rv == MOJO_RESULT_OK)
+    rv = waiter.Wait(deadline, result_index);
+
+  // Make sure no other dispatchers try to wake |waiter| for the current
+  // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be
+  // destroyed, but this would still be required if the waiter were in TLS.)
+  for (i = 0; i < num_added; i++) {
+    dispatchers[i]->RemoveWaiter(&waiter,
+                                 signals_states ? &signals_states[i] : nullptr);
+  }
+  if (signals_states) {
+    for (; i < num_handles; i++)
+      signals_states[i] = dispatchers[i]->GetHandleSignalsState();
+  }
+
+  return rv;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
new file mode 100644
index 0000000..5a3c27b
--- /dev/null
+++ b/mojo/edk/system/core.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_CORE_H_
+#define MOJO_EDK_SYSTEM_CORE_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/handle_table.h"
+#include "mojo/edk/system/mapping_table.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformSupport;
+}
+
+namespace system {
+
+class Dispatcher;
+struct HandleSignalsState;
+
+// |Core| is an object that implements the Mojo system calls. All public methods
+// are thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT Core {
+ public:
+  // ---------------------------------------------------------------------------
+
+  // These methods are only to be used by via the embedder API (and internally):
+  explicit Core(scoped_ptr<embedder::PlatformSupport> platform_support);
+  virtual ~Core();
+
+  // Adds |dispatcher| to the handle table, returning the handle for it. Returns
+  // |MOJO_HANDLE_INVALID| on failure, namely if the handle table is full.
+  MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher);
+
+  // Looks up the dispatcher for the given handle. Returns null if the handle is
+  // invalid.
+  scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
+
+  embedder::PlatformSupport* platform_support() const {
+    return platform_support_.get();
+  }
+
+  // ---------------------------------------------------------------------------
+
+  // System calls implementation:
+  MojoTimeTicks GetTimeTicksNow();
+  MojoResult Close(MojoHandle handle);
+  MojoResult Wait(MojoHandle handle,
+                  MojoHandleSignals signals,
+                  MojoDeadline deadline,
+                  UserPointer<MojoHandleSignalsState> signals_state);
+  MojoResult WaitMany(UserPointer<const MojoHandle> handles,
+                      UserPointer<const MojoHandleSignals> signals,
+                      uint32_t num_handles,
+                      MojoDeadline deadline,
+                      UserPointer<uint32_t> result_index,
+                      UserPointer<MojoHandleSignalsState> signals_states);
+  MojoResult CreateMessagePipe(
+      UserPointer<const MojoCreateMessagePipeOptions> options,
+      UserPointer<MojoHandle> message_pipe_handle0,
+      UserPointer<MojoHandle> message_pipe_handle1);
+  MojoResult WriteMessage(MojoHandle message_pipe_handle,
+                          UserPointer<const void> bytes,
+                          uint32_t num_bytes,
+                          UserPointer<const MojoHandle> handles,
+                          uint32_t num_handles,
+                          MojoWriteMessageFlags flags);
+  MojoResult ReadMessage(MojoHandle message_pipe_handle,
+                         UserPointer<void> bytes,
+                         UserPointer<uint32_t> num_bytes,
+                         UserPointer<MojoHandle> handles,
+                         UserPointer<uint32_t> num_handles,
+                         MojoReadMessageFlags flags);
+  MojoResult CreateDataPipe(
+      UserPointer<const MojoCreateDataPipeOptions> options,
+      UserPointer<MojoHandle> data_pipe_producer_handle,
+      UserPointer<MojoHandle> data_pipe_consumer_handle);
+  MojoResult WriteData(MojoHandle data_pipe_producer_handle,
+                       UserPointer<const void> elements,
+                       UserPointer<uint32_t> num_bytes,
+                       MojoWriteDataFlags flags);
+  MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle,
+                            UserPointer<void*> buffer,
+                            UserPointer<uint32_t> buffer_num_bytes,
+                            MojoWriteDataFlags flags);
+  MojoResult EndWriteData(MojoHandle data_pipe_producer_handle,
+                          uint32_t num_bytes_written);
+  MojoResult ReadData(MojoHandle data_pipe_consumer_handle,
+                      UserPointer<void> elements,
+                      UserPointer<uint32_t> num_bytes,
+                      MojoReadDataFlags flags);
+  MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle,
+                           UserPointer<const void*> buffer,
+                           UserPointer<uint32_t> buffer_num_bytes,
+                           MojoReadDataFlags flags);
+  MojoResult EndReadData(MojoHandle data_pipe_consumer_handle,
+                         uint32_t num_bytes_read);
+  MojoResult CreateSharedBuffer(
+      UserPointer<const MojoCreateSharedBufferOptions> options,
+      uint64_t num_bytes,
+      UserPointer<MojoHandle> shared_buffer_handle);
+  MojoResult DuplicateBufferHandle(
+      MojoHandle buffer_handle,
+      UserPointer<const MojoDuplicateBufferHandleOptions> options,
+      UserPointer<MojoHandle> new_buffer_handle);
+  MojoResult MapBuffer(MojoHandle buffer_handle,
+                       uint64_t offset,
+                       uint64_t num_bytes,
+                       UserPointer<void*> buffer,
+                       MojoMapBufferFlags flags);
+  MojoResult UnmapBuffer(UserPointer<void> buffer);
+
+ private:
+  friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+  // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic
+  // validation of arguments. |*result_index| is only set if the result (whether
+  // success or failure) applies to a specific handle, so its value should be
+  // preinitialized to |static_cast<uint32_t>(-1)|.
+  MojoResult WaitManyInternal(const MojoHandle* handles,
+                              const MojoHandleSignals* signals,
+                              uint32_t num_handles,
+                              MojoDeadline deadline,
+                              uint32_t* result_index,
+                              HandleSignalsState* signals_states);
+
+  const scoped_ptr<embedder::PlatformSupport> platform_support_;
+
+  // TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we
+  // had them).
+  base::Lock handle_table_lock_;  // Protects |handle_table_|.
+  HandleTable handle_table_;
+
+  base::Lock mapping_table_lock_;  // Protects |mapping_table_|.
+  MappingTable mapping_table_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CORE_H_
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
new file mode 100644
index 0000000..0f2c7cc
--- /dev/null
+++ b/mojo/edk/system/core_test_base.cc
@@ -0,0 +1,356 @@
+// 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/edk/system/core_test_base.h"
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/memory.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+namespace {
+
+// MockDispatcher --------------------------------------------------------------
+
+class MockDispatcher : public Dispatcher {
+ public:
+  explicit MockDispatcher(CoreTestBase::MockHandleInfo* info) : info_(info) {
+    CHECK(info_);
+    info_->IncrementCtorCallCount();
+  }
+
+  // |Dispatcher| private methods:
+  virtual Type GetType() const override { return kTypeUnknown; }
+
+ private:
+  virtual ~MockDispatcher() { info_->IncrementDtorCallCount(); }
+
+  // |Dispatcher| protected methods:
+  virtual void CloseImplNoLock() override {
+    info_->IncrementCloseCallCount();
+    lock().AssertAcquired();
+  }
+
+  virtual MojoResult WriteMessageImplNoLock(
+      UserPointer<const void> bytes,
+      uint32_t num_bytes,
+      std::vector<DispatcherTransport>* transports,
+      MojoWriteMessageFlags /*flags*/) override {
+    info_->IncrementWriteMessageCallCount();
+    lock().AssertAcquired();
+
+    if (num_bytes > kMaxMessageNumBytes)
+      return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+    if (transports)
+      return MOJO_RESULT_UNIMPLEMENTED;
+
+    return MOJO_RESULT_OK;
+  }
+
+  virtual MojoResult ReadMessageImplNoLock(
+      UserPointer<void> bytes,
+      UserPointer<uint32_t> num_bytes,
+      DispatcherVector* dispatchers,
+      uint32_t* num_dispatchers,
+      MojoReadMessageFlags /*flags*/) override {
+    info_->IncrementReadMessageCallCount();
+    lock().AssertAcquired();
+
+    if (num_dispatchers) {
+      *num_dispatchers = 1;
+      if (dispatchers) {
+        // Okay to leave an invalid dispatcher.
+        dispatchers->resize(1);
+      }
+    }
+
+    return MOJO_RESULT_OK;
+  }
+
+  virtual MojoResult WriteDataImplNoLock(
+      UserPointer<const void> /*elements*/,
+      UserPointer<uint32_t> /*num_bytes*/,
+      MojoWriteDataFlags /*flags*/) override {
+    info_->IncrementWriteDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult BeginWriteDataImplNoLock(
+      UserPointer<void*> /*buffer*/,
+      UserPointer<uint32_t> /*buffer_num_bytes*/,
+      MojoWriteDataFlags /*flags*/) override {
+    info_->IncrementBeginWriteDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult EndWriteDataImplNoLock(
+      uint32_t /*num_bytes_written*/) override {
+    info_->IncrementEndWriteDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult ReadDataImplNoLock(UserPointer<void> /*elements*/,
+                                        UserPointer<uint32_t> /*num_bytes*/,
+                                        MojoReadDataFlags /*flags*/) override {
+    info_->IncrementReadDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult BeginReadDataImplNoLock(
+      UserPointer<const void*> /*buffer*/,
+      UserPointer<uint32_t> /*buffer_num_bytes*/,
+      MojoReadDataFlags /*flags*/) override {
+    info_->IncrementBeginReadDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult EndReadDataImplNoLock(
+      uint32_t /*num_bytes_read*/) override {
+    info_->IncrementEndReadDataCallCount();
+    lock().AssertAcquired();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  virtual MojoResult AddWaiterImplNoLock(
+      Waiter* /*waiter*/,
+      MojoHandleSignals /*signals*/,
+      uint32_t /*context*/,
+      HandleSignalsState* signals_state) override {
+    info_->IncrementAddWaiterCallCount();
+    lock().AssertAcquired();
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  virtual void RemoveWaiterImplNoLock(
+      Waiter* /*waiter*/,
+      HandleSignalsState* signals_state) override {
+    info_->IncrementRemoveWaiterCallCount();
+    lock().AssertAcquired();
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+  }
+
+  virtual void CancelAllWaitersNoLock() override {
+    info_->IncrementCancelAllWaitersCallCount();
+    lock().AssertAcquired();
+  }
+
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override {
+    return scoped_refptr<Dispatcher>(new MockDispatcher(info_));
+  }
+
+  CoreTestBase::MockHandleInfo* const info_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDispatcher);
+};
+
+}  // namespace
+
+// CoreTestBase ----------------------------------------------------------------
+
+CoreTestBase::CoreTestBase() {
+}
+
+CoreTestBase::~CoreTestBase() {
+}
+
+void CoreTestBase::SetUp() {
+  core_ = new Core(make_scoped_ptr(new embedder::SimplePlatformSupport()));
+}
+
+void CoreTestBase::TearDown() {
+  delete core_;
+  core_ = nullptr;
+}
+
+MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) {
+  CHECK(core_);
+  scoped_refptr<MockDispatcher> dispatcher(new MockDispatcher(info));
+  return core_->AddDispatcher(dispatcher);
+}
+
+// CoreTestBase_MockHandleInfo -------------------------------------------------
+
+CoreTestBase_MockHandleInfo::CoreTestBase_MockHandleInfo()
+    : ctor_call_count_(0),
+      dtor_call_count_(0),
+      close_call_count_(0),
+      write_message_call_count_(0),
+      read_message_call_count_(0),
+      write_data_call_count_(0),
+      begin_write_data_call_count_(0),
+      end_write_data_call_count_(0),
+      read_data_call_count_(0),
+      begin_read_data_call_count_(0),
+      end_read_data_call_count_(0),
+      add_waiter_call_count_(0),
+      remove_waiter_call_count_(0),
+      cancel_all_waiters_call_count_(0) {
+}
+
+CoreTestBase_MockHandleInfo::~CoreTestBase_MockHandleInfo() {
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCtorCallCount() const {
+  base::AutoLock locker(lock_);
+  return ctor_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetDtorCallCount() const {
+  base::AutoLock locker(lock_);
+  return dtor_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCloseCallCount() const {
+  base::AutoLock locker(lock_);
+  return close_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetWriteMessageCallCount() const {
+  base::AutoLock locker(lock_);
+  return write_message_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetReadMessageCallCount() const {
+  base::AutoLock locker(lock_);
+  return read_message_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetWriteDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetBeginWriteDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return begin_write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetEndWriteDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return end_write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetReadDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetBeginReadDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return begin_read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetEndReadDataCallCount() const {
+  base::AutoLock locker(lock_);
+  return end_read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetAddWaiterCallCount() const {
+  base::AutoLock locker(lock_);
+  return add_waiter_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetRemoveWaiterCallCount() const {
+  base::AutoLock locker(lock_);
+  return remove_waiter_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCancelAllWaitersCallCount() const {
+  base::AutoLock locker(lock_);
+  return cancel_all_waiters_call_count_;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCtorCallCount() {
+  base::AutoLock locker(lock_);
+  ctor_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementDtorCallCount() {
+  base::AutoLock locker(lock_);
+  dtor_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCloseCallCount() {
+  base::AutoLock locker(lock_);
+  close_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementWriteMessageCallCount() {
+  base::AutoLock locker(lock_);
+  write_message_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementReadMessageCallCount() {
+  base::AutoLock locker(lock_);
+  read_message_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementWriteDataCallCount() {
+  base::AutoLock locker(lock_);
+  write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementBeginWriteDataCallCount() {
+  base::AutoLock locker(lock_);
+  begin_write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementEndWriteDataCallCount() {
+  base::AutoLock locker(lock_);
+  end_write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementReadDataCallCount() {
+  base::AutoLock locker(lock_);
+  read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementBeginReadDataCallCount() {
+  base::AutoLock locker(lock_);
+  begin_read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementEndReadDataCallCount() {
+  base::AutoLock locker(lock_);
+  end_read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementAddWaiterCallCount() {
+  base::AutoLock locker(lock_);
+  add_waiter_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementRemoveWaiterCallCount() {
+  base::AutoLock locker(lock_);
+  remove_waiter_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCancelAllWaitersCallCount() {
+  base::AutoLock locker(lock_);
+  cancel_all_waiters_call_count_++;
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/core_test_base.h b/mojo/edk/system/core_test_base.h
new file mode 100644
index 0000000..409f7e1
--- /dev/null
+++ b/mojo/edk/system/core_test_base.h
@@ -0,0 +1,105 @@
+// 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_EDK_SYSTEM_CORE_TEST_BASE_H_
+#define MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+
+class Core;
+
+namespace test {
+
+class CoreTestBase_MockHandleInfo;
+
+class CoreTestBase : public testing::Test {
+ public:
+  typedef CoreTestBase_MockHandleInfo MockHandleInfo;
+
+  CoreTestBase();
+  virtual ~CoreTestBase();
+
+  virtual void SetUp() override;
+  virtual void TearDown() override;
+
+ protected:
+  // |info| must remain alive until the returned handle is closed.
+  MojoHandle CreateMockHandle(MockHandleInfo* info);
+
+  Core* core() { return core_; }
+
+ private:
+  Core* core_;
+
+  DISALLOW_COPY_AND_ASSIGN(CoreTestBase);
+};
+
+class CoreTestBase_MockHandleInfo {
+ public:
+  CoreTestBase_MockHandleInfo();
+  ~CoreTestBase_MockHandleInfo();
+
+  unsigned GetCtorCallCount() const;
+  unsigned GetDtorCallCount() const;
+  unsigned GetCloseCallCount() const;
+  unsigned GetWriteMessageCallCount() const;
+  unsigned GetReadMessageCallCount() const;
+  unsigned GetWriteDataCallCount() const;
+  unsigned GetBeginWriteDataCallCount() const;
+  unsigned GetEndWriteDataCallCount() const;
+  unsigned GetReadDataCallCount() const;
+  unsigned GetBeginReadDataCallCount() const;
+  unsigned GetEndReadDataCallCount() const;
+  unsigned GetAddWaiterCallCount() const;
+  unsigned GetRemoveWaiterCallCount() const;
+  unsigned GetCancelAllWaitersCallCount() const;
+
+  // For use by |MockDispatcher|:
+  void IncrementCtorCallCount();
+  void IncrementDtorCallCount();
+  void IncrementCloseCallCount();
+  void IncrementWriteMessageCallCount();
+  void IncrementReadMessageCallCount();
+  void IncrementWriteDataCallCount();
+  void IncrementBeginWriteDataCallCount();
+  void IncrementEndWriteDataCallCount();
+  void IncrementReadDataCallCount();
+  void IncrementBeginReadDataCallCount();
+  void IncrementEndReadDataCallCount();
+  void IncrementAddWaiterCallCount();
+  void IncrementRemoveWaiterCallCount();
+  void IncrementCancelAllWaitersCallCount();
+
+ private:
+  mutable base::Lock lock_;  // Protects the following members.
+  unsigned ctor_call_count_;
+  unsigned dtor_call_count_;
+  unsigned close_call_count_;
+  unsigned write_message_call_count_;
+  unsigned read_message_call_count_;
+  unsigned write_data_call_count_;
+  unsigned begin_write_data_call_count_;
+  unsigned end_write_data_call_count_;
+  unsigned read_data_call_count_;
+  unsigned begin_read_data_call_count_;
+  unsigned end_read_data_call_count_;
+  unsigned add_waiter_call_count_;
+  unsigned remove_waiter_call_count_;
+  unsigned cancel_all_waiters_call_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(CoreTestBase_MockHandleInfo);
+};
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
new file mode 100644
index 0000000..9adde19
--- /dev/null
+++ b/mojo/edk/system/core_unittest.cc
@@ -0,0 +1,1486 @@
+// 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/edk/system/core.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "mojo/edk/system/core_test_base.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const MojoHandleSignalsState kEmptyMojoHandleSignalsState = {0u, 0u};
+const MojoHandleSignalsState kFullMojoHandleSignalsState = {~0u, ~0u};
+
+typedef test::CoreTestBase CoreTest;
+
+TEST_F(CoreTest, GetTimeTicksNow) {
+  const MojoTimeTicks start = core()->GetTimeTicksNow();
+  EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+      << "GetTimeTicksNow should return nonzero value";
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(15));
+  const MojoTimeTicks finish = core()->GetTimeTicksNow();
+  // Allow for some fuzz in sleep.
+  EXPECT_GE((finish - start), static_cast<MojoTimeTicks>(8000))
+      << "Sleeping should result in increasing time ticks";
+}
+
+TEST_F(CoreTest, Basic) {
+  MockHandleInfo info;
+
+  EXPECT_EQ(0u, info.GetCtorCallCount());
+  MojoHandle h = CreateMockHandle(&info);
+  EXPECT_EQ(1u, info.GetCtorCallCount());
+  EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+  EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h,
+                                 NullUserPointer(),
+                                 0,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+  EXPECT_EQ(0u, info.GetReadMessageCallCount());
+  uint32_t num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h,
+                                NullUserPointer(),
+                                MakeUserPointer(&num_bytes),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetReadMessageCallCount());
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h,
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(2u, info.GetReadMessageCallCount());
+
+  EXPECT_EQ(0u, info.GetWriteDataCallCount());
+  EXPECT_EQ(
+      MOJO_RESULT_UNIMPLEMENTED,
+      core()->WriteData(
+          h, NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetWriteDataCallCount());
+
+  EXPECT_EQ(0u, info.GetBeginWriteDataCallCount());
+  EXPECT_EQ(
+      MOJO_RESULT_UNIMPLEMENTED,
+      core()->BeginWriteData(
+          h, NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetBeginWriteDataCallCount());
+
+  EXPECT_EQ(0u, info.GetEndWriteDataCallCount());
+  EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndWriteData(h, 0));
+  EXPECT_EQ(1u, info.GetEndWriteDataCallCount());
+
+  EXPECT_EQ(0u, info.GetReadDataCallCount());
+  EXPECT_EQ(
+      MOJO_RESULT_UNIMPLEMENTED,
+      core()->ReadData(
+          h, NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetReadDataCallCount());
+
+  EXPECT_EQ(0u, info.GetBeginReadDataCallCount());
+  EXPECT_EQ(
+      MOJO_RESULT_UNIMPLEMENTED,
+      core()->BeginReadData(
+          h, NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, info.GetBeginReadDataCallCount());
+
+  EXPECT_EQ(0u, info.GetEndReadDataCallCount());
+  EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndReadData(h, 0));
+  EXPECT_EQ(1u, info.GetEndReadDataCallCount());
+
+  EXPECT_EQ(0u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h,
+                         ~MOJO_HANDLE_SIGNAL_NONE,
+                         MOJO_DEADLINE_INDEFINITE,
+                         NullUserPointer()));
+  EXPECT_EQ(1u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0, NullUserPointer()));
+  EXPECT_EQ(2u, info.GetAddWaiterCallCount());
+  MojoHandleSignalsState hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h,
+                         ~MOJO_HANDLE_SIGNAL_NONE,
+                         MOJO_DEADLINE_INDEFINITE,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(3u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, NullUserPointer()));
+  EXPECT_EQ(4u, info.GetAddWaiterCallCount());
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(
+                h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, MakeUserPointer(&hss)));
+  EXPECT_EQ(5u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  MojoHandleSignals handle_signals = ~MOJO_HANDLE_SIGNAL_NONE;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WaitMany(MakeUserPointer(&h),
+                             MakeUserPointer(&handle_signals),
+                             1,
+                             MOJO_DEADLINE_INDEFINITE,
+                             NullUserPointer(),
+                             NullUserPointer()));
+  EXPECT_EQ(6u, info.GetAddWaiterCallCount());
+  uint32_t result_index = static_cast<uint32_t>(-1);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WaitMany(MakeUserPointer(&h),
+                             MakeUserPointer(&handle_signals),
+                             1,
+                             MOJO_DEADLINE_INDEFINITE,
+                             MakeUserPointer(&result_index),
+                             NullUserPointer()));
+  EXPECT_EQ(7u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(0u, result_index);
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WaitMany(MakeUserPointer(&h),
+                             MakeUserPointer(&handle_signals),
+                             1,
+                             MOJO_DEADLINE_INDEFINITE,
+                             NullUserPointer(),
+                             MakeUserPointer(&hss)));
+  EXPECT_EQ(8u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  result_index = static_cast<uint32_t>(-1);
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WaitMany(MakeUserPointer(&h),
+                             MakeUserPointer(&handle_signals),
+                             1,
+                             MOJO_DEADLINE_INDEFINITE,
+                             MakeUserPointer(&result_index),
+                             MakeUserPointer(&hss)));
+  EXPECT_EQ(9u, info.GetAddWaiterCallCount());
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  EXPECT_EQ(0u, info.GetDtorCallCount());
+  EXPECT_EQ(0u, info.GetCloseCallCount());
+  EXPECT_EQ(0u, info.GetCancelAllWaitersCallCount());
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  EXPECT_EQ(1u, info.GetCancelAllWaitersCallCount());
+  EXPECT_EQ(1u, info.GetCloseCallCount());
+  EXPECT_EQ(1u, info.GetDtorCallCount());
+
+  // No waiters should ever have ever been added.
+  EXPECT_EQ(0u, info.GetRemoveWaiterCallCount());
+}
+
+TEST_F(CoreTest, InvalidArguments) {
+  // |Close()|:
+  {
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000));
+
+    // Test a double-close.
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+    EXPECT_EQ(1u, info.GetCloseCallCount());
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h));
+    EXPECT_EQ(1u, info.GetCloseCallCount());
+  }
+
+  // |Wait()|:
+  {
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(MOJO_HANDLE_INVALID,
+                           ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE,
+                           NullUserPointer()));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(10,
+                           ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE,
+                           NullUserPointer()));
+
+    MojoHandleSignalsState hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(MOJO_HANDLE_INVALID,
+                           ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE,
+                           MakeUserPointer(&hss)));
+    // On invalid argument, it shouldn't modify the handle signals state.
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+    hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(10,
+                           ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE,
+                           MakeUserPointer(&hss)));
+    // On invalid argument, it shouldn't modify the handle signals state.
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+  }
+
+  // |WaitMany()|:
+  {
+    MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
+    MojoHandleSignals signals[2] = {~MOJO_HANDLE_SIGNAL_NONE,
+                                    ~MOJO_HANDLE_SIGNAL_NONE};
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               0,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(NullUserPointer(),
+                               MakeUserPointer(signals),
+                               0,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+    // If |num_handles| is invalid, it should leave |result_index| and
+    // |signals_states| alone.
+    // (We use -1 internally; make sure that doesn't leak.)
+    uint32_t result_index = 123;
+    MojoHandleSignalsState hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(NullUserPointer(),
+                               MakeUserPointer(signals),
+                               0,
+                               MOJO_DEADLINE_INDEFINITE,
+                               MakeUserPointer(&result_index),
+                               MakeUserPointer(&hss)));
+    EXPECT_EQ(123u, result_index);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               NullUserPointer(),
+                               0,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               1,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+    // But if a handle is bad, then it should set |result_index| but still leave
+    // |signals_states| alone.
+    result_index = static_cast<uint32_t>(-1);
+    hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               1,
+                               MOJO_DEADLINE_INDEFINITE,
+                               MakeUserPointer(&result_index),
+                               MakeUserPointer(&hss)));
+    EXPECT_EQ(0u, result_index);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+
+    MockHandleInfo info[2];
+    handles[0] = CreateMockHandle(&info[0]);
+
+    result_index = static_cast<uint32_t>(-1);
+    hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               1,
+                               MOJO_DEADLINE_INDEFINITE,
+                               MakeUserPointer(&result_index),
+                               MakeUserPointer(&hss)));
+    EXPECT_EQ(0u, result_index);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    // On invalid argument, it'll leave |signals_states| alone.
+    result_index = static_cast<uint32_t>(-1);
+    hss = kFullMojoHandleSignalsState;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               2,
+                               MOJO_DEADLINE_INDEFINITE,
+                               MakeUserPointer(&result_index),
+                               MakeUserPointer(&hss)));
+    EXPECT_EQ(1u, result_index);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+    handles[1] = handles[0] + 1;  // Invalid handle.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               2,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+    handles[1] = CreateMockHandle(&info[1]);
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              core()->WaitMany(MakeUserPointer(handles),
+                               MakeUserPointer(signals),
+                               2,
+                               MOJO_DEADLINE_INDEFINITE,
+                               NullUserPointer(),
+                               NullUserPointer()));
+
+    // TODO(vtl): Test one where we get "failed precondition" only for the
+    // second handle (and the first one is valid to wait on).
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0]));
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1]));
+  }
+
+  // |CreateMessagePipe()|: Nothing to check (apart from things that cause
+  // death).
+
+  // |WriteMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(MOJO_HANDLE_INVALID,
+                                   NullUserPointer(),
+                                   0,
+                                   NullUserPointer(),
+                                   0,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+    MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
+
+    // Huge handle count (implausibly big on some systems -- more than can be
+    // stored in a 32-bit address space).
+    // Note: This may return either |MOJO_RESULT_INVALID_ARGUMENT| or
+    // |MOJO_RESULT_RESOURCE_EXHAUSTED|, depending on whether it's plausible or
+    // not.
+    EXPECT_NE(MOJO_RESULT_OK,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   std::numeric_limits<uint32_t>::max(),
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Huge handle count (plausibly big).
+    EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+              core()->WriteMessage(
+                  h,
+                  NullUserPointer(),
+                  0,
+                  MakeUserPointer(handles),
+                  std::numeric_limits<uint32_t>::max() / sizeof(handles[0]),
+                  MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Invalid handle in |handles|.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   1,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Two invalid handles in |handles|.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   2,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Can't send a handle over itself.
+    handles[0] = h;
+    EXPECT_EQ(MOJO_RESULT_BUSY,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   1,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+    MockHandleInfo info2;
+    MojoHandle h2 = CreateMockHandle(&info2);
+
+    // This is "okay", but |MockDispatcher| doesn't implement it.
+    handles[0] = h2;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   1,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // One of the |handles| is still invalid.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   2,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // One of the |handles| is the same as |handle|.
+    handles[1] = h;
+    EXPECT_EQ(MOJO_RESULT_BUSY,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   2,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // Can't send a handle twice in the same message.
+    handles[1] = h2;
+    EXPECT_EQ(MOJO_RESULT_BUSY,
+              core()->WriteMessage(h,
+                                   NullUserPointer(),
+                                   0,
+                                   MakeUserPointer(handles),
+                                   2,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // Note: Since we never successfully sent anything with it, |h2| should
+    // still be valid.
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h2));
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+
+  // |ReadMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->ReadMessage(MOJO_HANDLE_INVALID,
+                                  NullUserPointer(),
+                                  NullUserPointer(),
+                                  NullUserPointer(),
+                                  NullUserPointer(),
+                                  MOJO_READ_MESSAGE_FLAG_NONE));
+
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+
+    // Okay.
+    uint32_t handle_count = 0;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->ReadMessage(h,
+                                  NullUserPointer(),
+                                  NullUserPointer(),
+                                  NullUserPointer(),
+                                  MakeUserPointer(&handle_count),
+                                  MOJO_READ_MESSAGE_FLAG_NONE));
+    // Checked by |Core|, shouldn't go through to the dispatcher.
+    EXPECT_EQ(1u, info.GetReadMessageCallCount());
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+}
+
+// These test invalid arguments that should cause death if we're being paranoid
+// about checking arguments (which we would want to do if, e.g., we were in a
+// true "kernel" situation, but we might not want to do otherwise for
+// performance reasons). Probably blatant errors like passing in null pointers
+// (for required pointer arguments) will still cause death, but perhaps not
+// predictably.
+TEST_F(CoreTest, InvalidArgumentsDeath) {
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+
+  // |WaitMany()|:
+  {
+    MojoHandle handle = MOJO_HANDLE_INVALID;
+    MojoHandleSignals signals = ~MOJO_HANDLE_SIGNAL_NONE;
+    EXPECT_DEATH_IF_SUPPORTED(core()->WaitMany(NullUserPointer(),
+                                               MakeUserPointer(&signals),
+                                               1,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               NullUserPointer(),
+                                               NullUserPointer()),
+                              kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(core()->WaitMany(MakeUserPointer(&handle),
+                                               NullUserPointer(),
+                                               1,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               NullUserPointer(),
+                                               NullUserPointer()),
+                              kMemoryCheckFailedRegex);
+    // TODO(vtl): |result_index| and |signals_states| are optional. Test them
+    // with non-null invalid pointers?
+  }
+
+  // |CreateMessagePipe()|:
+  {
+    MojoHandle h;
+    EXPECT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(
+            NullUserPointer(), NullUserPointer(), NullUserPointer()),
+        kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(
+            NullUserPointer(), MakeUserPointer(&h), NullUserPointer()),
+        kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(
+            NullUserPointer(), NullUserPointer(), MakeUserPointer(&h)),
+        kMemoryCheckFailedRegex);
+  }
+
+  // |WriteMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+
+    // Null |handles| with nonzero |num_handles|.
+    EXPECT_DEATH_IF_SUPPORTED(
+        core()->WriteMessage(h,
+                             NullUserPointer(),
+                             0,
+                             NullUserPointer(),
+                             1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE),
+        kMemoryCheckFailedRegex);
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+
+  // |ReadMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+
+    uint32_t handle_count = 1;
+    EXPECT_DEATH_IF_SUPPORTED(
+        core()->ReadMessage(h,
+                            NullUserPointer(),
+                            NullUserPointer(),
+                            NullUserPointer(),
+                            MakeUserPointer(&handle_count),
+                            MOJO_READ_MESSAGE_FLAG_NONE),
+        kMemoryCheckFailedRegex);
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+}
+
+// TODO(vtl): test |Wait()| and |WaitMany()| properly
+//  - including |WaitMany()| with the same handle more than once (with
+//    same/different signals)
+
+TEST_F(CoreTest, MessagePipe) {
+  MojoHandle h[2];
+  MojoHandleSignalsState hss[2];
+  uint32_t result_index;
+
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      core()->CreateMessagePipe(
+          NullUserPointer(), MakeUserPointer(&h[0]), MakeUserPointer(&h[1])));
+  // Should get two distinct, valid handles.
+  EXPECT_NE(h[0], MOJO_HANDLE_INVALID);
+  EXPECT_NE(h[1], MOJO_HANDLE_INVALID);
+  EXPECT_NE(h[0], h[1]);
+
+  // Neither should be readable.
+  MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
+                                  MOJO_HANDLE_SIGNAL_READABLE};
+  result_index = static_cast<uint32_t>(-1);
+  hss[0] = kEmptyMojoHandleSignalsState;
+  hss[1] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            core()->WaitMany(MakeUserPointer(h),
+                             MakeUserPointer(signals),
+                             2,
+                             0,
+                             MakeUserPointer(&result_index),
+                             MakeUserPointer(hss)));
+  EXPECT_EQ(static_cast<uint32_t>(-1), result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[1].satisfiable_signals);
+
+  // Try to read anyway.
+  char buffer[1] = {'a'};
+  uint32_t buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            core()->ReadMessage(h[0],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&buffer_size),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  // Check that it left its inputs alone.
+  EXPECT_EQ('a', buffer[0]);
+  EXPECT_EQ(1u, buffer_size);
+
+  // Both should be writable.
+  hss[0] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h[0],
+                         MOJO_HANDLE_SIGNAL_WRITABLE,
+                         1000000000,
+                         MakeUserPointer(&hss[0])));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+  hss[0] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h[1],
+                         MOJO_HANDLE_SIGNAL_WRITABLE,
+                         1000000000,
+                         MakeUserPointer(&hss[0])));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+
+  // Also check that |h[1]| is writable using |WaitMany()|.
+  signals[0] = MOJO_HANDLE_SIGNAL_READABLE;
+  signals[1] = MOJO_HANDLE_SIGNAL_WRITABLE;
+  result_index = static_cast<uint32_t>(-1);
+  hss[0] = kEmptyMojoHandleSignalsState;
+  hss[1] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WaitMany(MakeUserPointer(h),
+                             MakeUserPointer(signals),
+                             2,
+                             MOJO_DEADLINE_INDEFINITE,
+                             MakeUserPointer(&result_index),
+                             MakeUserPointer(hss)));
+  EXPECT_EQ(1u, result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[1].satisfiable_signals);
+
+  // Write to |h[1]|.
+  buffer[0] = 'b';
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h[1],
+                                 UserPointer<const void>(buffer),
+                                 1,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Check that |h[0]| is now readable.
+  signals[0] = MOJO_HANDLE_SIGNAL_READABLE;
+  signals[1] = MOJO_HANDLE_SIGNAL_READABLE;
+  result_index = static_cast<uint32_t>(-1);
+  hss[0] = kEmptyMojoHandleSignalsState;
+  hss[1] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WaitMany(MakeUserPointer(h),
+                             MakeUserPointer(signals),
+                             2,
+                             MOJO_DEADLINE_INDEFINITE,
+                             MakeUserPointer(&result_index),
+                             MakeUserPointer(hss)));
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[1].satisfiable_signals);
+
+  // Read from |h[0]|.
+  // First, get only the size.
+  buffer_size = 0;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            core()->ReadMessage(h[0],
+                                NullUserPointer(),
+                                MakeUserPointer(&buffer_size),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(1u, buffer_size);
+  // Then actually read it.
+  buffer[0] = 'c';
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h[0],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&buffer_size),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ('b', buffer[0]);
+  EXPECT_EQ(1u, buffer_size);
+
+  // |h[0]| should no longer be readable.
+  hss[0] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->Wait(
+          h[0], MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss[0])));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfiable_signals);
+
+  // Write to |h[0]|.
+  buffer[0] = 'd';
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h[0],
+                                 UserPointer<const void>(buffer),
+                                 1,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Close |h[0]|.
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[0]));
+
+  // Check that |h[1]| is no longer writable (and will never be).
+  hss[0] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h[1],
+                         MOJO_HANDLE_SIGNAL_WRITABLE,
+                         1000000000,
+                         MakeUserPointer(&hss[0])));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfiable_signals);
+
+  // Check that |h[1]| is still readable (for the moment).
+  hss[0] = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss[0])));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfiable_signals);
+
+  // Discard a message from |h[1]|.
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            core()->ReadMessage(h[1],
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                NullUserPointer(),
+                                MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // |h[1]| is no longer readable (and will never be).
+  hss[0] = kFullMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss[0])));
+  EXPECT_EQ(0u, hss[0].satisfied_signals);
+  EXPECT_EQ(0u, hss[0].satisfiable_signals);
+
+  // Try writing to |h[1]|.
+  buffer[0] = 'e';
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WriteMessage(h[1],
+                                 UserPointer<const void>(buffer),
+                                 1,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[1]));
+}
+
+// Tests passing a message pipe handle.
+TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) {
+  const char kHello[] = "hello";
+  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+  const char kWorld[] = "world!!!";
+  const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+  char buffer[100];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t num_bytes;
+  MojoHandle handles[10];
+  uint32_t num_handles;
+  MojoHandleSignalsState hss;
+  MojoHandle h_received;
+
+  MojoHandle h_passing[2];
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(NullUserPointer(),
+                                      MakeUserPointer(&h_passing[0]),
+                                      MakeUserPointer(&h_passing[1])));
+
+  // Make sure that |h_passing[]| work properly.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+  EXPECT_EQ(0u, num_handles);
+
+  // Make sure that you can't pass either of the message pipe's handles over
+  // itself.
+  EXPECT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&h_passing[0]),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&h_passing[1]),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  MojoHandle h_passed[2];
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(NullUserPointer(),
+                                      MakeUserPointer(&h_passed[0]),
+                                      MakeUserPointer(&h_passed[1])));
+
+  // Make sure that |h_passed[]| work properly.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passed[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passed[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passed[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+  EXPECT_EQ(0u, num_handles);
+
+  // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kWorld),
+                                 kWorldSize,
+                                 MakeUserPointer(&h_passed[1]),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kWorldSize, num_bytes);
+  EXPECT_STREQ(kWorld, buffer);
+  EXPECT_EQ(1u, num_handles);
+  h_received = handles[0];
+  EXPECT_NE(h_received, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h_received, h_passing[0]);
+  EXPECT_NE(h_received, h_passing[1]);
+  EXPECT_NE(h_received, h_passed[0]);
+
+  // Note: We rely on the Mojo system not re-using handle values very often.
+  EXPECT_NE(h_received, h_passed[1]);
+
+  // |h_passed[1]| should no longer be valid; check that trying to close it
+  // fails. See above note.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1]));
+
+  // Write to |h_passed[0]|. Should receive on |h_received|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passed[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 NullUserPointer(),
+                                 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_received,
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_received,
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+  EXPECT_EQ(0u, num_handles);
+
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0]));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_received));
+}
+
+TEST_F(CoreTest, DataPipe) {
+  MojoHandle ph, ch;  // p is for producer and c is for consumer.
+  MojoHandleSignalsState hss;
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->CreateDataPipe(
+                NullUserPointer(), MakeUserPointer(&ph), MakeUserPointer(&ch)));
+  // Should get two distinct, valid handles.
+  EXPECT_NE(ph, MOJO_HANDLE_INVALID);
+  EXPECT_NE(ch, MOJO_HANDLE_INVALID);
+  EXPECT_NE(ph, ch);
+
+  // Producer should be never-readable, but already writable.
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Consumer should be never-writable, and not yet readable.
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Write.
+  char elements[2] = {'A', 'B'};
+  uint32_t num_bytes = 2u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph,
+                              UserPointer<const void>(elements),
+                              MakeUserPointer(&num_bytes),
+                              MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(2u, num_bytes);
+
+  // Consumer should now be readable.
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Read one character.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = 1u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch,
+                             UserPointer<void>(elements),
+                             MakeUserPointer(&num_bytes),
+                             MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ('A', elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Two-phase write.
+  void* write_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginWriteData(ph,
+                                   MakeUserPointer(&write_ptr),
+                                   MakeUserPointer(&num_bytes),
+                                   MOJO_WRITE_DATA_FLAG_NONE));
+  // We count on the default options providing a decent buffer size.
+  ASSERT_GE(num_bytes, 3u);
+
+  // Trying to do a normal write during a two-phase write should fail.
+  elements[0] = 'X';
+  num_bytes = 1u;
+  EXPECT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteData(ph,
+                              UserPointer<const void>(elements),
+                              MakeUserPointer(&num_bytes),
+                              MOJO_WRITE_DATA_FLAG_NONE));
+
+  // Actually write the data, and complete it now.
+  static_cast<char*>(write_ptr)[0] = 'C';
+  static_cast<char*>(write_ptr)[1] = 'D';
+  static_cast<char*>(write_ptr)[2] = 'E';
+  EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u));
+
+  // Query how much data we have.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch,
+                             NullUserPointer(),
+                             MakeUserPointer(&num_bytes),
+                             MOJO_READ_DATA_FLAG_QUERY));
+  EXPECT_EQ(4u, num_bytes);
+
+  // Try to discard ten characters, in all-or-none mode. Should fail.
+  num_bytes = 10;
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            core()->ReadData(
+                ch,
+                NullUserPointer(),
+                MakeUserPointer(&num_bytes),
+                MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+  // Discard two characters.
+  num_bytes = 2;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(
+                ch,
+                NullUserPointer(),
+                MakeUserPointer(&num_bytes),
+                MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+  // Read the remaining two characters, in two-phase mode (all-or-none).
+  const void* read_ptr = nullptr;
+  num_bytes = 2;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginReadData(ch,
+                                  MakeUserPointer(&read_ptr),
+                                  MakeUserPointer(&num_bytes),
+                                  MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+  // Note: Count on still being able to do the contiguous read here.
+  ASSERT_EQ(2u, num_bytes);
+
+  // Discarding right now should fail.
+  num_bytes = 1;
+  EXPECT_EQ(MOJO_RESULT_BUSY,
+            core()->ReadData(ch,
+                             NullUserPointer(),
+                             MakeUserPointer(&num_bytes),
+                             MOJO_READ_DATA_FLAG_DISCARD));
+
+  // Actually check our data and end the two-phase read.
+  EXPECT_EQ('D', static_cast<const char*>(read_ptr)[0]);
+  EXPECT_EQ('E', static_cast<const char*>(read_ptr)[1]);
+  EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u));
+
+  // Consumer should now be no longer readable.
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // TODO(vtl): More.
+
+  // Close the producer.
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+
+  // The consumer should now be never-readable.
+  hss = kFullMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss)));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch));
+}
+
+// Tests passing data pipe producer and consumer handles.
+TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) {
+  const char kHello[] = "hello";
+  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+  const char kWorld[] = "world!!!";
+  const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+  char buffer[100];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t num_bytes;
+  MojoHandle handles[10];
+  uint32_t num_handles;
+  MojoHandleSignalsState hss;
+
+  MojoHandle h_passing[2];
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(NullUserPointer(),
+                                      MakeUserPointer(&h_passing[0]),
+                                      MakeUserPointer(&h_passing[1])));
+
+  MojoHandle ph, ch;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->CreateDataPipe(
+                NullUserPointer(), MakeUserPointer(&ph), MakeUserPointer(&ch)));
+
+  // Send |ch| from |h_passing[0]| to |h_passing[1]|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&ch),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+  EXPECT_EQ(1u, num_handles);
+  MojoHandle ch_received = handles[0];
+  EXPECT_NE(ch_received, MOJO_HANDLE_INVALID);
+  EXPECT_NE(ch_received, h_passing[0]);
+  EXPECT_NE(ch_received, h_passing[1]);
+  EXPECT_NE(ch_received, ph);
+
+  // Note: We rely on the Mojo system not re-using handle values very often.
+  EXPECT_NE(ch_received, ch);
+
+  // |ch| should no longer be valid; check that trying to close it fails. See
+  // above note.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch));
+
+  // Write to |ph|. Should receive on |ch_received|.
+  num_bytes = kWorldSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph,
+                              UserPointer<const void>(kWorld),
+                              MakeUserPointer(&num_bytes),
+                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch_received,
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch_received,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&num_bytes),
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kWorldSize, num_bytes);
+  EXPECT_STREQ(kWorld, buffer);
+
+  // Now pass |ph| in the same direction.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kWorld),
+                                 kWorldSize,
+                                 MakeUserPointer(&ph),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kWorldSize, num_bytes);
+  EXPECT_STREQ(kWorld, buffer);
+  EXPECT_EQ(1u, num_handles);
+  MojoHandle ph_received = handles[0];
+  EXPECT_NE(ph_received, MOJO_HANDLE_INVALID);
+  EXPECT_NE(ph_received, h_passing[0]);
+  EXPECT_NE(ph_received, h_passing[1]);
+  EXPECT_NE(ph_received, ch_received);
+
+  // Again, rely on the Mojo system not re-using handle values very often.
+  EXPECT_NE(ph_received, ph);
+
+  // |ph| should no longer be valid; check that trying to close it fails. See
+  // above note.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph));
+
+  // Write to |ph_received|. Should receive on |ch_received|.
+  num_bytes = kHelloSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph_received,
+                              UserPointer<const void>(kHello),
+                              MakeUserPointer(&num_bytes),
+                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch_received,
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch_received,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&num_bytes),
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+
+  ph = ph_received;
+  ph_received = MOJO_HANDLE_INVALID;
+  ch = ch_received;
+  ch_received = MOJO_HANDLE_INVALID;
+
+  // Make sure that |ph| can't be sent if it's in a two-phase write.
+  void* write_ptr = nullptr;
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginWriteData(ph,
+                                   MakeUserPointer(&write_ptr),
+                                   MakeUserPointer(&num_bytes),
+                                   MOJO_WRITE_DATA_FLAG_NONE));
+  ASSERT_GE(num_bytes, 1u);
+  EXPECT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&ph),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // But |ch| can, even if |ph| is in a two-phase write.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&ch),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ch = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         NullUserPointer()));
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, num_bytes);
+  EXPECT_STREQ(kHello, buffer);
+  EXPECT_EQ(1u, num_handles);
+  ch = handles[0];
+  EXPECT_NE(ch, MOJO_HANDLE_INVALID);
+
+  // Complete the two-phase write.
+  static_cast<char*>(write_ptr)[0] = 'x';
+  EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1));
+
+  // Wait for |ch| to be readable.
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      core()->Wait(
+          ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Make sure that |ch| can't be sent if it's in a two-phase read.
+  const void* read_ptr = nullptr;
+  num_bytes = 1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginReadData(ch,
+                                  MakeUserPointer(&read_ptr),
+                                  MakeUserPointer(&num_bytes),
+                                  MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+  EXPECT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kHello),
+                                 kHelloSize,
+                                 MakeUserPointer(&ch),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // But |ph| can, even if |ch| is in a two-phase read.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0],
+                                 UserPointer<const void>(kWorld),
+                                 kWorldSize,
+                                 MakeUserPointer(&ph),
+                                 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ph = MOJO_HANDLE_INVALID;
+  hss = kEmptyMojoHandleSignalsState;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1],
+                         MOJO_HANDLE_SIGNAL_READABLE,
+                         1000000000,
+                         MakeUserPointer(&hss)));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h_passing[1],
+                                UserPointer<void>(buffer),
+                                MakeUserPointer(&num_bytes),
+                                MakeUserPointer(handles),
+                                MakeUserPointer(&num_handles),
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kWorldSize, num_bytes);
+  EXPECT_STREQ(kWorld, buffer);
+  EXPECT_EQ(1u, num_handles);
+  ph = handles[0];
+  EXPECT_NE(ph, MOJO_HANDLE_INVALID);
+
+  // Complete the two-phase read.
+  EXPECT_EQ('x', static_cast<const char*>(read_ptr)[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1));
+
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch));
+}
+
+// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|.
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/data_pipe.cc b/mojo/edk/system/data_pipe.cc
new file mode 100644
index 0000000..3aca67a
--- /dev/null
+++ b/mojo/edk/system/data_pipe.cc
@@ -0,0 +1,473 @@
+// 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/edk/system/data_pipe.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/options_validation.h"
+#include "mojo/edk/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+// static
+const MojoCreateDataPipeOptions DataPipe::kDefaultCreateOptions = {
+    static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)),
+    MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1u,
+    static_cast<uint32_t>(kDefaultDataPipeCapacityBytes)};
+
+// static
+MojoResult DataPipe::ValidateCreateOptions(
+    UserPointer<const MojoCreateDataPipeOptions> in_options,
+    MojoCreateDataPipeOptions* out_options) {
+  const MojoCreateDataPipeOptionsFlags kKnownFlags =
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD;
+
+  *out_options = kDefaultCreateOptions;
+  if (in_options.IsNull())
+    return MOJO_RESULT_OK;
+
+  UserOptionsReader<MojoCreateDataPipeOptions> reader(in_options);
+  if (!reader.is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateDataPipeOptions, flags, reader))
+    return MOJO_RESULT_OK;
+  if ((reader.options().flags & ~kKnownFlags))
+    return MOJO_RESULT_UNIMPLEMENTED;
+  out_options->flags = reader.options().flags;
+
+  // Checks for fields beyond |flags|:
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(
+          MojoCreateDataPipeOptions, element_num_bytes, reader))
+    return MOJO_RESULT_OK;
+  if (reader.options().element_num_bytes == 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  out_options->element_num_bytes = reader.options().element_num_bytes;
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(
+          MojoCreateDataPipeOptions, capacity_num_bytes, reader) ||
+      reader.options().capacity_num_bytes == 0) {
+    // Round the default capacity down to a multiple of the element size (but at
+    // least one element).
+    out_options->capacity_num_bytes =
+        std::max(static_cast<uint32_t>(kDefaultDataPipeCapacityBytes -
+                                       (kDefaultDataPipeCapacityBytes %
+                                        out_options->element_num_bytes)),
+                 out_options->element_num_bytes);
+    return MOJO_RESULT_OK;
+  }
+  if (reader.options().capacity_num_bytes % out_options->element_num_bytes != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (reader.options().capacity_num_bytes > kMaxDataPipeCapacityBytes)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  out_options->capacity_num_bytes = reader.options().capacity_num_bytes;
+
+  return MOJO_RESULT_OK;
+}
+
+void DataPipe::ProducerCancelAllWaiters() {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+  producer_waiter_list_->CancelAllWaiters();
+}
+
+void DataPipe::ProducerClose() {
+  base::AutoLock locker(lock_);
+  DCHECK(producer_open_);
+  producer_open_ = false;
+  DCHECK(has_local_producer_no_lock());
+  producer_waiter_list_.reset();
+  // Not a bug, except possibly in "user" code.
+  DVLOG_IF(2, producer_in_two_phase_write_no_lock())
+      << "Producer closed with active two-phase write";
+  producer_two_phase_max_num_bytes_written_ = 0;
+  ProducerCloseImplNoLock();
+  AwakeConsumerWaitersForStateChangeNoLock(
+      ConsumerGetHandleSignalsStateImplNoLock());
+}
+
+MojoResult DataPipe::ProducerWriteData(UserPointer<const void> elements,
+                                       UserPointer<uint32_t> num_bytes,
+                                       bool all_or_none) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+
+  if (producer_in_two_phase_write_no_lock())
+    return MOJO_RESULT_BUSY;
+  if (!consumer_open_no_lock())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  // Returning "busy" takes priority over "invalid argument".
+  uint32_t max_num_bytes_to_write = num_bytes.Get();
+  if (max_num_bytes_to_write % element_num_bytes_ != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (max_num_bytes_to_write == 0)
+    return MOJO_RESULT_OK;  // Nothing to do.
+
+  uint32_t min_num_bytes_to_write = all_or_none ? max_num_bytes_to_write : 0;
+
+  HandleSignalsState old_consumer_state =
+      ConsumerGetHandleSignalsStateImplNoLock();
+  MojoResult rv = ProducerWriteDataImplNoLock(
+      elements, num_bytes, max_num_bytes_to_write, min_num_bytes_to_write);
+  HandleSignalsState new_consumer_state =
+      ConsumerGetHandleSignalsStateImplNoLock();
+  if (!new_consumer_state.equals(old_consumer_state))
+    AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+  return rv;
+}
+
+MojoResult DataPipe::ProducerBeginWriteData(
+    UserPointer<void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    bool all_or_none) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+
+  if (producer_in_two_phase_write_no_lock())
+    return MOJO_RESULT_BUSY;
+  if (!consumer_open_no_lock())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  uint32_t min_num_bytes_to_write = 0;
+  if (all_or_none) {
+    min_num_bytes_to_write = buffer_num_bytes.Get();
+    if (min_num_bytes_to_write % element_num_bytes_ != 0)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  MojoResult rv = ProducerBeginWriteDataImplNoLock(
+      buffer, buffer_num_bytes, min_num_bytes_to_write);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+  // Note: No need to awake producer waiters, even though we're going from
+  // writable to non-writable (since you can't wait on non-writability).
+  // Similarly, though this may have discarded data (in "may discard" mode),
+  // making it non-readable, there's still no need to awake consumer waiters.
+  DCHECK(producer_in_two_phase_write_no_lock());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipe::ProducerEndWriteData(uint32_t num_bytes_written) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+
+  if (!producer_in_two_phase_write_no_lock())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  // Note: Allow successful completion of the two-phase write even if the
+  // consumer has been closed.
+
+  HandleSignalsState old_consumer_state =
+      ConsumerGetHandleSignalsStateImplNoLock();
+  MojoResult rv;
+  if (num_bytes_written > producer_two_phase_max_num_bytes_written_ ||
+      num_bytes_written % element_num_bytes_ != 0) {
+    rv = MOJO_RESULT_INVALID_ARGUMENT;
+    producer_two_phase_max_num_bytes_written_ = 0;
+  } else {
+    rv = ProducerEndWriteDataImplNoLock(num_bytes_written);
+  }
+  // Two-phase write ended even on failure.
+  DCHECK(!producer_in_two_phase_write_no_lock());
+  // If we're now writable, we *became* writable (since we weren't writable
+  // during the two-phase write), so awake producer waiters.
+  HandleSignalsState new_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  if (new_producer_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE))
+    AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+  HandleSignalsState new_consumer_state =
+      ConsumerGetHandleSignalsStateImplNoLock();
+  if (!new_consumer_state.equals(old_consumer_state))
+    AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+  return rv;
+}
+
+HandleSignalsState DataPipe::ProducerGetHandleSignalsState() {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+  return ProducerGetHandleSignalsStateImplNoLock();
+}
+
+MojoResult DataPipe::ProducerAddWaiter(Waiter* waiter,
+                                       MojoHandleSignals signals,
+                                       uint32_t context,
+                                       HandleSignalsState* signals_state) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+
+  HandleSignalsState producer_state = ProducerGetHandleSignalsStateImplNoLock();
+  if (producer_state.satisfies(signals)) {
+    if (signals_state)
+      *signals_state = producer_state;
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+  if (!producer_state.can_satisfy(signals)) {
+    if (signals_state)
+      *signals_state = producer_state;
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  producer_waiter_list_->AddWaiter(waiter, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void DataPipe::ProducerRemoveWaiter(Waiter* waiter,
+                                    HandleSignalsState* signals_state) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_producer_no_lock());
+  producer_waiter_list_->RemoveWaiter(waiter);
+  if (signals_state)
+    *signals_state = ProducerGetHandleSignalsStateImplNoLock();
+}
+
+bool DataPipe::ProducerIsBusy() const {
+  base::AutoLock locker(lock_);
+  return producer_in_two_phase_write_no_lock();
+}
+
+void DataPipe::ConsumerCancelAllWaiters() {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+  consumer_waiter_list_->CancelAllWaiters();
+}
+
+void DataPipe::ConsumerClose() {
+  base::AutoLock locker(lock_);
+  DCHECK(consumer_open_);
+  consumer_open_ = false;
+  DCHECK(has_local_consumer_no_lock());
+  consumer_waiter_list_.reset();
+  // Not a bug, except possibly in "user" code.
+  DVLOG_IF(2, consumer_in_two_phase_read_no_lock())
+      << "Consumer closed with active two-phase read";
+  consumer_two_phase_max_num_bytes_read_ = 0;
+  ConsumerCloseImplNoLock();
+  AwakeProducerWaitersForStateChangeNoLock(
+      ProducerGetHandleSignalsStateImplNoLock());
+}
+
+MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements,
+                                      UserPointer<uint32_t> num_bytes,
+                                      bool all_or_none) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  if (consumer_in_two_phase_read_no_lock())
+    return MOJO_RESULT_BUSY;
+
+  uint32_t max_num_bytes_to_read = num_bytes.Get();
+  if (max_num_bytes_to_read % element_num_bytes_ != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (max_num_bytes_to_read == 0)
+    return MOJO_RESULT_OK;  // Nothing to do.
+
+  uint32_t min_num_bytes_to_read = all_or_none ? max_num_bytes_to_read : 0;
+
+  HandleSignalsState old_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  MojoResult rv = ConsumerReadDataImplNoLock(
+      elements, num_bytes, max_num_bytes_to_read, min_num_bytes_to_read);
+  HandleSignalsState new_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  if (!new_producer_state.equals(old_producer_state))
+    AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+  return rv;
+}
+
+MojoResult DataPipe::ConsumerDiscardData(UserPointer<uint32_t> num_bytes,
+                                         bool all_or_none) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  if (consumer_in_two_phase_read_no_lock())
+    return MOJO_RESULT_BUSY;
+
+  uint32_t max_num_bytes_to_discard = num_bytes.Get();
+  if (max_num_bytes_to_discard % element_num_bytes_ != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (max_num_bytes_to_discard == 0)
+    return MOJO_RESULT_OK;  // Nothing to do.
+
+  uint32_t min_num_bytes_to_discard =
+      all_or_none ? max_num_bytes_to_discard : 0;
+
+  HandleSignalsState old_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  MojoResult rv = ConsumerDiscardDataImplNoLock(
+      num_bytes, max_num_bytes_to_discard, min_num_bytes_to_discard);
+  HandleSignalsState new_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  if (!new_producer_state.equals(old_producer_state))
+    AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+  return rv;
+}
+
+MojoResult DataPipe::ConsumerQueryData(UserPointer<uint32_t> num_bytes) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  if (consumer_in_two_phase_read_no_lock())
+    return MOJO_RESULT_BUSY;
+
+  // Note: Don't need to validate |*num_bytes| for query.
+  return ConsumerQueryDataImplNoLock(num_bytes);
+}
+
+MojoResult DataPipe::ConsumerBeginReadData(
+    UserPointer<const void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    bool all_or_none) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  if (consumer_in_two_phase_read_no_lock())
+    return MOJO_RESULT_BUSY;
+
+  uint32_t min_num_bytes_to_read = 0;
+  if (all_or_none) {
+    min_num_bytes_to_read = buffer_num_bytes.Get();
+    if (min_num_bytes_to_read % element_num_bytes_ != 0)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  MojoResult rv = ConsumerBeginReadDataImplNoLock(
+      buffer, buffer_num_bytes, min_num_bytes_to_read);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+  DCHECK(consumer_in_two_phase_read_no_lock());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  if (!consumer_in_two_phase_read_no_lock())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  HandleSignalsState old_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  MojoResult rv;
+  if (num_bytes_read > consumer_two_phase_max_num_bytes_read_ ||
+      num_bytes_read % element_num_bytes_ != 0) {
+    rv = MOJO_RESULT_INVALID_ARGUMENT;
+    consumer_two_phase_max_num_bytes_read_ = 0;
+  } else {
+    rv = ConsumerEndReadDataImplNoLock(num_bytes_read);
+  }
+  // Two-phase read ended even on failure.
+  DCHECK(!consumer_in_two_phase_read_no_lock());
+  // If we're now readable, we *became* readable (since we weren't readable
+  // during the two-phase read), so awake consumer waiters.
+  HandleSignalsState new_consumer_state =
+      ConsumerGetHandleSignalsStateImplNoLock();
+  if (new_consumer_state.satisfies(MOJO_HANDLE_SIGNAL_READABLE))
+    AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+  HandleSignalsState new_producer_state =
+      ProducerGetHandleSignalsStateImplNoLock();
+  if (!new_producer_state.equals(old_producer_state))
+    AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+  return rv;
+}
+
+HandleSignalsState DataPipe::ConsumerGetHandleSignalsState() {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+  return ConsumerGetHandleSignalsStateImplNoLock();
+}
+
+MojoResult DataPipe::ConsumerAddWaiter(Waiter* waiter,
+                                       MojoHandleSignals signals,
+                                       uint32_t context,
+                                       HandleSignalsState* signals_state) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+
+  HandleSignalsState consumer_state = ConsumerGetHandleSignalsStateImplNoLock();
+  if (consumer_state.satisfies(signals)) {
+    if (signals_state)
+      *signals_state = consumer_state;
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+  if (!consumer_state.can_satisfy(signals)) {
+    if (signals_state)
+      *signals_state = consumer_state;
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  consumer_waiter_list_->AddWaiter(waiter, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void DataPipe::ConsumerRemoveWaiter(Waiter* waiter,
+                                    HandleSignalsState* signals_state) {
+  base::AutoLock locker(lock_);
+  DCHECK(has_local_consumer_no_lock());
+  consumer_waiter_list_->RemoveWaiter(waiter);
+  if (signals_state)
+    *signals_state = ConsumerGetHandleSignalsStateImplNoLock();
+}
+
+bool DataPipe::ConsumerIsBusy() const {
+  base::AutoLock locker(lock_);
+  return consumer_in_two_phase_read_no_lock();
+}
+
+DataPipe::DataPipe(bool has_local_producer,
+                   bool has_local_consumer,
+                   const MojoCreateDataPipeOptions& validated_options)
+    : may_discard_((validated_options.flags &
+                    MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)),
+      element_num_bytes_(validated_options.element_num_bytes),
+      capacity_num_bytes_(validated_options.capacity_num_bytes),
+      producer_open_(true),
+      consumer_open_(true),
+      producer_waiter_list_(has_local_producer ? new WaiterList() : nullptr),
+      consumer_waiter_list_(has_local_consumer ? new WaiterList() : nullptr),
+      producer_two_phase_max_num_bytes_written_(0),
+      consumer_two_phase_max_num_bytes_read_(0) {
+  // Check that the passed in options actually are validated.
+  MojoCreateDataPipeOptions unused ALLOW_UNUSED = {0};
+  DCHECK_EQ(ValidateCreateOptions(MakeUserPointer(&validated_options), &unused),
+            MOJO_RESULT_OK);
+}
+
+DataPipe::~DataPipe() {
+  DCHECK(!producer_open_);
+  DCHECK(!consumer_open_);
+  DCHECK(!producer_waiter_list_);
+  DCHECK(!consumer_waiter_list_);
+}
+
+void DataPipe::AwakeProducerWaitersForStateChangeNoLock(
+    const HandleSignalsState& new_producer_state) {
+  lock_.AssertAcquired();
+  if (!has_local_producer_no_lock())
+    return;
+  producer_waiter_list_->AwakeWaitersForStateChange(new_producer_state);
+}
+
+void DataPipe::AwakeConsumerWaitersForStateChangeNoLock(
+    const HandleSignalsState& new_consumer_state) {
+  lock_.AssertAcquired();
+  if (!has_local_consumer_no_lock())
+    return;
+  consumer_waiter_list_->AwakeWaitersForStateChange(new_consumer_state);
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h
new file mode 100644
index 0000000..64b18e2
--- /dev/null
+++ b/mojo/edk/system/data_pipe.h
@@ -0,0 +1,216 @@
+// 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_EDK_SYSTEM_DATA_PIPE_H_
+#define MOJO_EDK_SYSTEM_DATA_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+class Waiter;
+class WaiterList;
+
+// |DataPipe| is a base class for secondary objects implementing data pipes,
+// similar to |MessagePipe| (see the explanatory comment in core.cc). It is
+// typically owned by the dispatcher(s) corresponding to the local endpoints.
+// Its subclasses implement the three cases: local producer and consumer, local
+// producer and remote consumer, and remote producer and local consumer. This
+// class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipe
+    : public base::RefCountedThreadSafe<DataPipe> {
+ public:
+  // The default options for |MojoCreateDataPipe()|. (Real uses should obtain
+  // this via |ValidateCreateOptions()| with a null |in_options|; this is
+  // exposed directly for testing convenience.)
+  static const MojoCreateDataPipeOptions kDefaultCreateOptions;
+
+  // Validates and/or sets default options for |MojoCreateDataPipeOptions|. If
+  // non-null, |in_options| must point to a struct of at least
+  // |in_options->struct_size| bytes. |out_options| must point to a (current)
+  // |MojoCreateDataPipeOptions| and will be entirely overwritten on success (it
+  // may be partly overwritten on failure).
+  static MojoResult ValidateCreateOptions(
+      UserPointer<const MojoCreateDataPipeOptions> in_options,
+      MojoCreateDataPipeOptions* out_options);
+
+  // These are called by the producer dispatcher to implement its methods of
+  // corresponding names.
+  void ProducerCancelAllWaiters();
+  void ProducerClose();
+  MojoResult ProducerWriteData(UserPointer<const void> elements,
+                               UserPointer<uint32_t> num_bytes,
+                               bool all_or_none);
+  MojoResult ProducerBeginWriteData(UserPointer<void*> buffer,
+                                    UserPointer<uint32_t> buffer_num_bytes,
+                                    bool all_or_none);
+  MojoResult ProducerEndWriteData(uint32_t num_bytes_written);
+  HandleSignalsState ProducerGetHandleSignalsState();
+  MojoResult ProducerAddWaiter(Waiter* waiter,
+                               MojoHandleSignals signals,
+                               uint32_t context,
+                               HandleSignalsState* signals_state);
+  void ProducerRemoveWaiter(Waiter* waiter, HandleSignalsState* signals_state);
+  bool ProducerIsBusy() const;
+
+  // These are called by the consumer dispatcher to implement its methods of
+  // corresponding names.
+  void ConsumerCancelAllWaiters();
+  void ConsumerClose();
+  // This does not validate its arguments, except to check that |*num_bytes| is
+  // a multiple of |element_num_bytes_|.
+  MojoResult ConsumerReadData(UserPointer<void> elements,
+                              UserPointer<uint32_t> num_bytes,
+                              bool all_or_none);
+  MojoResult ConsumerDiscardData(UserPointer<uint32_t> num_bytes,
+                                 bool all_or_none);
+  MojoResult ConsumerQueryData(UserPointer<uint32_t> num_bytes);
+  MojoResult ConsumerBeginReadData(UserPointer<const void*> buffer,
+                                   UserPointer<uint32_t> buffer_num_bytes,
+                                   bool all_or_none);
+  MojoResult ConsumerEndReadData(uint32_t num_bytes_read);
+  HandleSignalsState ConsumerGetHandleSignalsState();
+  MojoResult ConsumerAddWaiter(Waiter* waiter,
+                               MojoHandleSignals signals,
+                               uint32_t context,
+                               HandleSignalsState* signals_state);
+  void ConsumerRemoveWaiter(Waiter* waiter, HandleSignalsState* signals_state);
+  bool ConsumerIsBusy() const;
+
+ protected:
+  DataPipe(bool has_local_producer,
+           bool has_local_consumer,
+           const MojoCreateDataPipeOptions& validated_options);
+
+  friend class base::RefCountedThreadSafe<DataPipe>;
+  virtual ~DataPipe();
+
+  virtual void ProducerCloseImplNoLock() = 0;
+  // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes_|.
+  virtual MojoResult ProducerWriteDataImplNoLock(
+      UserPointer<const void> elements,
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_write,
+      uint32_t min_num_bytes_to_write) = 0;
+  virtual MojoResult ProducerBeginWriteDataImplNoLock(
+      UserPointer<void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      uint32_t min_num_bytes_to_write) = 0;
+  virtual MojoResult ProducerEndWriteDataImplNoLock(
+      uint32_t num_bytes_written) = 0;
+  // Note: A producer should not be writable during a two-phase write.
+  virtual HandleSignalsState ProducerGetHandleSignalsStateImplNoLock()
+      const = 0;
+
+  virtual void ConsumerCloseImplNoLock() = 0;
+  // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|.
+  virtual MojoResult ConsumerReadDataImplNoLock(
+      UserPointer<void> elements,
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_read,
+      uint32_t min_num_bytes_to_read) = 0;
+  virtual MojoResult ConsumerDiscardDataImplNoLock(
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_discard,
+      uint32_t min_num_bytes_to_discard) = 0;
+  // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|.
+  virtual MojoResult ConsumerQueryDataImplNoLock(
+      UserPointer<uint32_t> num_bytes) = 0;
+  virtual MojoResult ConsumerBeginReadDataImplNoLock(
+      UserPointer<const void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      uint32_t min_num_bytes_to_read) = 0;
+  virtual MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) = 0;
+  // Note: A consumer should not be writable during a two-phase read.
+  virtual HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock()
+      const = 0;
+
+  // Thread-safe and fast (they don't take the lock):
+  bool may_discard() const { return may_discard_; }
+  size_t element_num_bytes() const { return element_num_bytes_; }
+  size_t capacity_num_bytes() const { return capacity_num_bytes_; }
+
+  // Must be called under lock.
+  bool producer_open_no_lock() const {
+    lock_.AssertAcquired();
+    return producer_open_;
+  }
+  bool consumer_open_no_lock() const {
+    lock_.AssertAcquired();
+    return consumer_open_;
+  }
+  uint32_t producer_two_phase_max_num_bytes_written_no_lock() const {
+    lock_.AssertAcquired();
+    return producer_two_phase_max_num_bytes_written_;
+  }
+  uint32_t consumer_two_phase_max_num_bytes_read_no_lock() const {
+    lock_.AssertAcquired();
+    return consumer_two_phase_max_num_bytes_read_;
+  }
+  void set_producer_two_phase_max_num_bytes_written_no_lock(
+      uint32_t num_bytes) {
+    lock_.AssertAcquired();
+    producer_two_phase_max_num_bytes_written_ = num_bytes;
+  }
+  void set_consumer_two_phase_max_num_bytes_read_no_lock(uint32_t num_bytes) {
+    lock_.AssertAcquired();
+    consumer_two_phase_max_num_bytes_read_ = num_bytes;
+  }
+  bool producer_in_two_phase_write_no_lock() const {
+    lock_.AssertAcquired();
+    return producer_two_phase_max_num_bytes_written_ > 0;
+  }
+  bool consumer_in_two_phase_read_no_lock() const {
+    lock_.AssertAcquired();
+    return consumer_two_phase_max_num_bytes_read_ > 0;
+  }
+
+ private:
+  void AwakeProducerWaitersForStateChangeNoLock(
+      const HandleSignalsState& new_producer_state);
+  void AwakeConsumerWaitersForStateChangeNoLock(
+      const HandleSignalsState& new_consumer_state);
+
+  bool has_local_producer_no_lock() const {
+    lock_.AssertAcquired();
+    return !!producer_waiter_list_;
+  }
+  bool has_local_consumer_no_lock() const {
+    lock_.AssertAcquired();
+    return !!consumer_waiter_list_;
+  }
+
+  const bool may_discard_;
+  const size_t element_num_bytes_;
+  const size_t capacity_num_bytes_;
+
+  mutable base::Lock lock_;  // Protects the following members.
+  // *Known* state of producer or consumer.
+  bool producer_open_;
+  bool consumer_open_;
+  // Non-null only if the producer or consumer, respectively, is local.
+  scoped_ptr<WaiterList> producer_waiter_list_;
+  scoped_ptr<WaiterList> consumer_waiter_list_;
+  // These are nonzero if and only if a two-phase write/read is in progress.
+  uint32_t producer_two_phase_max_num_bytes_written_;
+  uint32_t consumer_two_phase_max_num_bytes_read_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipe);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
new file mode 100644
index 0000000..2b3f514
--- /dev/null
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -0,0 +1,130 @@
+// 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/edk/system/data_pipe_consumer_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/data_pipe.h"
+#include "mojo/edk/system/memory.h"
+
+namespace mojo {
+namespace system {
+
+DataPipeConsumerDispatcher::DataPipeConsumerDispatcher() {
+}
+
+void DataPipeConsumerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) {
+  DCHECK(data_pipe.get());
+  data_pipe_ = data_pipe;
+}
+
+Dispatcher::Type DataPipeConsumerDispatcher::GetType() const {
+  return kTypeDataPipeConsumer;
+}
+
+DataPipeConsumerDispatcher::~DataPipeConsumerDispatcher() {
+  // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+  DCHECK(!data_pipe_.get());
+}
+
+void DataPipeConsumerDispatcher::CancelAllWaitersNoLock() {
+  lock().AssertAcquired();
+  data_pipe_->ConsumerCancelAllWaiters();
+}
+
+void DataPipeConsumerDispatcher::CloseImplNoLock() {
+  lock().AssertAcquired();
+  data_pipe_->ConsumerClose();
+  data_pipe_ = nullptr;
+}
+
+scoped_refptr<Dispatcher>
+DataPipeConsumerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+  lock().AssertAcquired();
+
+  scoped_refptr<DataPipeConsumerDispatcher> rv =
+      new DataPipeConsumerDispatcher();
+  rv->Init(data_pipe_);
+  data_pipe_ = nullptr;
+  return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult DataPipeConsumerDispatcher::ReadDataImplNoLock(
+    UserPointer<void> elements,
+    UserPointer<uint32_t> num_bytes,
+    MojoReadDataFlags flags) {
+  lock().AssertAcquired();
+
+  if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) {
+    // These flags are mutally exclusive.
+    if ((flags & MOJO_READ_DATA_FLAG_QUERY))
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    DVLOG_IF(2, !elements.IsNull())
+        << "Discard mode: ignoring non-null |elements|";
+    return data_pipe_->ConsumerDiscardData(
+        num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+  }
+
+  if ((flags & MOJO_READ_DATA_FLAG_QUERY)) {
+    DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD));  // Handled above.
+    DVLOG_IF(2, !elements.IsNull())
+        << "Query mode: ignoring non-null |elements|";
+    return data_pipe_->ConsumerQueryData(num_bytes);
+  }
+
+  return data_pipe_->ConsumerReadData(
+      elements, num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeConsumerDispatcher::BeginReadDataImplNoLock(
+    UserPointer<const void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    MojoReadDataFlags flags) {
+  lock().AssertAcquired();
+
+  // These flags may not be used in two-phase mode.
+  if ((flags & MOJO_READ_DATA_FLAG_DISCARD) ||
+      (flags & MOJO_READ_DATA_FLAG_QUERY))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return data_pipe_->ConsumerBeginReadData(
+      buffer, buffer_num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeConsumerDispatcher::EndReadDataImplNoLock(
+    uint32_t num_bytes_read) {
+  lock().AssertAcquired();
+
+  return data_pipe_->ConsumerEndReadData(num_bytes_read);
+}
+
+HandleSignalsState DataPipeConsumerDispatcher::GetHandleSignalsStateImplNoLock()
+    const {
+  lock().AssertAcquired();
+  return data_pipe_->ConsumerGetHandleSignalsState();
+}
+
+MojoResult DataPipeConsumerDispatcher::AddWaiterImplNoLock(
+    Waiter* waiter,
+    MojoHandleSignals signals,
+    uint32_t context,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  return data_pipe_->ConsumerAddWaiter(waiter, signals, context, signals_state);
+}
+
+void DataPipeConsumerDispatcher::RemoveWaiterImplNoLock(
+    Waiter* waiter,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  data_pipe_->ConsumerRemoveWaiter(waiter, signals_state);
+}
+
+bool DataPipeConsumerDispatcher::IsBusyNoLock() const {
+  lock().AssertAcquired();
+  return data_pipe_->ConsumerIsBusy();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
new file mode 100644
index 0000000..59c1239
--- /dev/null
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -0,0 +1,67 @@
+// 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_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class DataPipe;
+
+// This is the |Dispatcher| implementation for the consumer handle for data
+// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is
+// thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher : public Dispatcher {
+ public:
+  DataPipeConsumerDispatcher();
+
+  // Must be called before any other methods.
+  void Init(scoped_refptr<DataPipe> data_pipe);
+
+  // |Dispatcher| public methods:
+  virtual Type GetType() const override;
+
+ private:
+  virtual ~DataPipeConsumerDispatcher();
+
+  // |Dispatcher| protected methods:
+  virtual void CancelAllWaitersNoLock() override;
+  virtual void CloseImplNoLock() override;
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  virtual MojoResult ReadDataImplNoLock(UserPointer<void> elements,
+                                        UserPointer<uint32_t> num_bytes,
+                                        MojoReadDataFlags flags) override;
+  virtual MojoResult BeginReadDataImplNoLock(
+      UserPointer<const void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      MojoReadDataFlags flags) override;
+  virtual MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read) override;
+  virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const override;
+  virtual MojoResult AddWaiterImplNoLock(
+      Waiter* waiter,
+      MojoHandleSignals signals,
+      uint32_t context,
+      HandleSignalsState* signals_state) override;
+  virtual void RemoveWaiterImplNoLock(
+      Waiter* waiter,
+      HandleSignalsState* signals_state) override;
+  virtual bool IsBusyNoLock() const override;
+
+  // Protected by |lock()|:
+  scoped_refptr<DataPipe> data_pipe_;  // This will be null if closed.
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
new file mode 100644
index 0000000..0a82c9a
--- /dev/null
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -0,0 +1,107 @@
+// 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/edk/system/data_pipe_producer_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/data_pipe.h"
+#include "mojo/edk/system/memory.h"
+
+namespace mojo {
+namespace system {
+
+DataPipeProducerDispatcher::DataPipeProducerDispatcher() {
+}
+
+void DataPipeProducerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) {
+  DCHECK(data_pipe.get());
+  data_pipe_ = data_pipe;
+}
+
+Dispatcher::Type DataPipeProducerDispatcher::GetType() const {
+  return kTypeDataPipeProducer;
+}
+
+DataPipeProducerDispatcher::~DataPipeProducerDispatcher() {
+  // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+  DCHECK(!data_pipe_.get());
+}
+
+void DataPipeProducerDispatcher::CancelAllWaitersNoLock() {
+  lock().AssertAcquired();
+  data_pipe_->ProducerCancelAllWaiters();
+}
+
+void DataPipeProducerDispatcher::CloseImplNoLock() {
+  lock().AssertAcquired();
+  data_pipe_->ProducerClose();
+  data_pipe_ = nullptr;
+}
+
+scoped_refptr<Dispatcher>
+DataPipeProducerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+  lock().AssertAcquired();
+
+  scoped_refptr<DataPipeProducerDispatcher> rv =
+      new DataPipeProducerDispatcher();
+  rv->Init(data_pipe_);
+  data_pipe_ = nullptr;
+  return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult DataPipeProducerDispatcher::WriteDataImplNoLock(
+    UserPointer<const void> elements,
+    UserPointer<uint32_t> num_bytes,
+    MojoWriteDataFlags flags) {
+  lock().AssertAcquired();
+  return data_pipe_->ProducerWriteData(
+      elements, num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeProducerDispatcher::BeginWriteDataImplNoLock(
+    UserPointer<void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    MojoWriteDataFlags flags) {
+  lock().AssertAcquired();
+
+  return data_pipe_->ProducerBeginWriteData(
+      buffer, buffer_num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeProducerDispatcher::EndWriteDataImplNoLock(
+    uint32_t num_bytes_written) {
+  lock().AssertAcquired();
+
+  return data_pipe_->ProducerEndWriteData(num_bytes_written);
+}
+
+HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsStateImplNoLock()
+    const {
+  lock().AssertAcquired();
+  return data_pipe_->ProducerGetHandleSignalsState();
+}
+
+MojoResult DataPipeProducerDispatcher::AddWaiterImplNoLock(
+    Waiter* waiter,
+    MojoHandleSignals signals,
+    uint32_t context,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  return data_pipe_->ProducerAddWaiter(waiter, signals, context, signals_state);
+}
+
+void DataPipeProducerDispatcher::RemoveWaiterImplNoLock(
+    Waiter* waiter,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  data_pipe_->ProducerRemoveWaiter(waiter, signals_state);
+}
+
+bool DataPipeProducerDispatcher::IsBusyNoLock() const {
+  lock().AssertAcquired();
+  return data_pipe_->ProducerIsBusy();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h
new file mode 100644
index 0000000..a53357a
--- /dev/null
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -0,0 +1,68 @@
+// 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_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class DataPipe;
+
+// This is the |Dispatcher| implementation for the producer handle for data
+// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is
+// thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher : public Dispatcher {
+ public:
+  DataPipeProducerDispatcher();
+
+  // Must be called before any other methods.
+  void Init(scoped_refptr<DataPipe> data_pipe);
+
+  // |Dispatcher| public methods:
+  virtual Type GetType() const override;
+
+ private:
+  virtual ~DataPipeProducerDispatcher();
+
+  // |Dispatcher| protected methods:
+  virtual void CancelAllWaitersNoLock() override;
+  virtual void CloseImplNoLock() override;
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  virtual MojoResult WriteDataImplNoLock(UserPointer<const void> elements,
+                                         UserPointer<uint32_t> num_bytes,
+                                         MojoWriteDataFlags flags) override;
+  virtual MojoResult BeginWriteDataImplNoLock(
+      UserPointer<void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      MojoWriteDataFlags flags) override;
+  virtual MojoResult EndWriteDataImplNoLock(
+      uint32_t num_bytes_written) override;
+  virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const override;
+  virtual MojoResult AddWaiterImplNoLock(
+      Waiter* waiter,
+      MojoHandleSignals signals,
+      uint32_t context,
+      HandleSignalsState* signals_state) override;
+  virtual void RemoveWaiterImplNoLock(
+      Waiter* waiter,
+      HandleSignalsState* signals_state) override;
+  virtual bool IsBusyNoLock() const override;
+
+  // Protected by |lock()|:
+  scoped_refptr<DataPipe> data_pipe_;  // This will be null if closed.
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
new file mode 100644
index 0000000..8d2e4eb
--- /dev/null
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -0,0 +1,377 @@
+// 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/edk/system/data_pipe.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/edk/system/constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const uint32_t kSizeOfCreateOptions =
+    static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions));
+
+// Does a cursory sanity check of |validated_options|. Calls
+// |ValidateCreateOptions()| on already-validated options. The validated options
+// should be valid, and the revalidated copy should be the same.
+void RevalidateCreateOptions(
+    const MojoCreateDataPipeOptions& validated_options) {
+  EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size);
+  // Nothing to check for flags.
+  EXPECT_GT(validated_options.element_num_bytes, 0u);
+  EXPECT_GT(validated_options.capacity_num_bytes, 0u);
+  EXPECT_EQ(0u,
+            validated_options.capacity_num_bytes %
+                validated_options.element_num_bytes);
+
+  MojoCreateDataPipeOptions revalidated_options = {};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&validated_options),
+                                            &revalidated_options));
+  EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size);
+  EXPECT_EQ(validated_options.element_num_bytes,
+            revalidated_options.element_num_bytes);
+  EXPECT_EQ(validated_options.capacity_num_bytes,
+            revalidated_options.capacity_num_bytes);
+  EXPECT_EQ(validated_options.flags, revalidated_options.flags);
+}
+
+// Checks that a default-computed capacity is correct. (Does not duplicate the
+// checks done by |RevalidateCreateOptions()|.)
+void CheckDefaultCapacity(const MojoCreateDataPipeOptions& validated_options) {
+  EXPECT_LE(validated_options.capacity_num_bytes,
+            kDefaultDataPipeCapacityBytes);
+  EXPECT_GT(validated_options.capacity_num_bytes +
+                validated_options.element_num_bytes,
+            kDefaultDataPipeCapacityBytes);
+}
+
+// Tests valid inputs to |ValidateCreateOptions()|.
+TEST(DataPipeTest, ValidateCreateOptionsValid) {
+  // Default options.
+  {
+    MojoCreateDataPipeOptions validated_options = {};
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        DataPipe::ValidateCreateOptions(NullUserPointer(), &validated_options));
+    RevalidateCreateOptions(validated_options);
+    CheckDefaultCapacity(validated_options);
+  }
+
+  // Size member, but nothing beyond.
+  {
+    MojoCreateDataPipeOptions options = {
+        offsetof(MojoCreateDataPipeOptions, flags)  // |struct_size|.
+    };
+    MojoCreateDataPipeOptions validated_options = {};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                              &validated_options));
+    RevalidateCreateOptions(validated_options);
+    CheckDefaultCapacity(validated_options);
+  }
+
+  // Different flags.
+  MojoCreateDataPipeOptionsFlags flags_values[] = {
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD};
+  for (size_t i = 0; i < arraysize(flags_values); i++) {
+    const MojoCreateDataPipeOptionsFlags flags = flags_values[i];
+
+    // Flags member, but nothing beyond.
+    {
+      MojoCreateDataPipeOptions options = {
+          // |struct_size|.
+          offsetof(MojoCreateDataPipeOptions, element_num_bytes),
+          flags  // |flags|.
+      };
+      MojoCreateDataPipeOptions validated_options = {};
+      EXPECT_EQ(MOJO_RESULT_OK,
+                DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                &validated_options));
+      RevalidateCreateOptions(validated_options);
+      EXPECT_EQ(options.flags, validated_options.flags);
+      CheckDefaultCapacity(validated_options);
+    }
+
+    // Different capacities (size 1).
+    for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) {
+      MojoCreateDataPipeOptions options = {
+          kSizeOfCreateOptions,  // |struct_size|.
+          flags,                 // |flags|.
+          1,                     // |element_num_bytes|.
+          capacity               // |capacity_num_bytes|.
+      };
+      MojoCreateDataPipeOptions validated_options = {};
+      EXPECT_EQ(MOJO_RESULT_OK,
+                DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                &validated_options))
+          << capacity;
+      RevalidateCreateOptions(validated_options);
+      EXPECT_EQ(options.flags, validated_options.flags);
+      EXPECT_EQ(options.element_num_bytes, validated_options.element_num_bytes);
+      EXPECT_EQ(options.capacity_num_bytes,
+                validated_options.capacity_num_bytes);
+    }
+
+    // Small sizes.
+    for (uint32_t size = 1; size < 100; size++) {
+      // Different capacities.
+      for (uint32_t elements = 1; elements <= 1000 * 1000; elements *= 10) {
+        MojoCreateDataPipeOptions options = {
+            kSizeOfCreateOptions,  // |struct_size|.
+            flags,                 // |flags|.
+            size,                  // |element_num_bytes|.
+            size * elements        // |capacity_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size << ", " << elements;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        EXPECT_EQ(options.capacity_num_bytes,
+                  validated_options.capacity_num_bytes);
+      }
+
+      // Default capacity.
+      {
+        MojoCreateDataPipeOptions options = {
+            kSizeOfCreateOptions,  // |struct_size|.
+            flags,                 // |flags|.
+            size,                  // |element_num_bytes|.
+            0                      // |capacity_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        CheckDefaultCapacity(validated_options);
+      }
+
+      // No capacity field.
+      {
+        MojoCreateDataPipeOptions options = {
+            // |struct_size|.
+            offsetof(MojoCreateDataPipeOptions, capacity_num_bytes),
+            flags,  // |flags|.
+            size    // |element_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        CheckDefaultCapacity(validated_options);
+      }
+    }
+
+    // Larger sizes.
+    for (uint32_t size = 100; size <= 100 * 1000; size *= 10) {
+      // Capacity of 1000 elements.
+      {
+        MojoCreateDataPipeOptions options = {
+            kSizeOfCreateOptions,  // |struct_size|.
+            flags,                 // |flags|.
+            size,                  // |element_num_bytes|.
+            1000 * size            // |capacity_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        EXPECT_EQ(options.capacity_num_bytes,
+                  validated_options.capacity_num_bytes);
+      }
+
+      // Default capacity.
+      {
+        MojoCreateDataPipeOptions options = {
+            kSizeOfCreateOptions,  // |struct_size|.
+            flags,                 // |flags|.
+            size,                  // |element_num_bytes|.
+            0                      // |capacity_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        CheckDefaultCapacity(validated_options);
+      }
+
+      // No capacity field.
+      {
+        MojoCreateDataPipeOptions options = {
+            // |struct_size|.
+            offsetof(MojoCreateDataPipeOptions, capacity_num_bytes),
+            flags,  // |flags|.
+            size    // |element_num_bytes|.
+        };
+        MojoCreateDataPipeOptions validated_options = {};
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                                  &validated_options))
+            << size;
+        RevalidateCreateOptions(validated_options);
+        EXPECT_EQ(options.flags, validated_options.flags);
+        EXPECT_EQ(options.element_num_bytes,
+                  validated_options.element_num_bytes);
+        CheckDefaultCapacity(validated_options);
+      }
+    }
+  }
+}
+
+TEST(DataPipeTest, ValidateCreateOptionsInvalid) {
+  // Invalid |struct_size|.
+  {
+    MojoCreateDataPipeOptions options = {
+        1,                                        // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        1,                                        // |element_num_bytes|.
+        0                                         // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+
+  // Unknown |flags|.
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,  // |struct_size|.
+        ~0u,                   // |flags|.
+        1,                     // |element_num_bytes|.
+        0                      // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_UNIMPLEMENTED,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+
+  // Invalid |element_num_bytes|.
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        0,                                        // |element_num_bytes|.
+        1000                                      // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+  // |element_num_bytes| too big.
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        std::numeric_limits<uint32_t>::max(),     // |element_num_bytes|.
+        std::numeric_limits<uint32_t>::max()      // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_RESOURCE_EXHAUSTED,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                         // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,      // |flags|.
+        std::numeric_limits<uint32_t>::max() - 1000,  // |element_num_bytes|.
+        std::numeric_limits<uint32_t>::max() - 1000   // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_RESOURCE_EXHAUSTED,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+
+  // Invalid |capacity_num_bytes|.
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        2,                                        // |element_num_bytes|.
+        1                                         // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        2,                                        // |element_num_bytes|.
+        111                                       // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        5,                                        // |element_num_bytes|.
+        104                                       // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+  // |capacity_num_bytes| too big.
+  {
+    MojoCreateDataPipeOptions options = {
+        kSizeOfCreateOptions,                     // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        8,                                        // |element_num_bytes|.
+        0xffff0000                                // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions unused;
+    EXPECT_EQ(
+        MOJO_RESULT_RESOURCE_EXHAUSTED,
+        DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused));
+  }
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
new file mode 100644
index 0000000..5607125
--- /dev/null
+++ b/mojo/edk/system/dispatcher.cc
@@ -0,0 +1,489 @@
+// 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/edk/system/dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+#include "mojo/edk/system/shared_buffer_dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+namespace test {
+
+// TODO(vtl): Maybe this should be defined in a test-only file instead.
+DispatcherTransport DispatcherTryStartTransport(Dispatcher* dispatcher) {
+  return Dispatcher::HandleTableAccess::TryStartTransport(dispatcher);
+}
+
+}  // namespace test
+
+// Dispatcher ------------------------------------------------------------------
+
+// static
+DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport(
+    Dispatcher* dispatcher) {
+  DCHECK(dispatcher);
+
+  if (!dispatcher->lock_.Try())
+    return DispatcherTransport();
+
+  // We shouldn't race with things that close dispatchers, since closing can
+  // only take place either under |handle_table_lock_| or when the handle is
+  // marked as busy.
+  DCHECK(!dispatcher->is_closed_);
+
+  return DispatcherTransport(dispatcher);
+}
+
+// static
+void Dispatcher::TransportDataAccess::StartSerialize(
+    Dispatcher* dispatcher,
+    Channel* channel,
+    size_t* max_size,
+    size_t* max_platform_handles) {
+  DCHECK(dispatcher);
+  dispatcher->StartSerialize(channel, max_size, max_platform_handles);
+}
+
+// static
+bool Dispatcher::TransportDataAccess::EndSerializeAndClose(
+    Dispatcher* dispatcher,
+    Channel* channel,
+    void* destination,
+    size_t* actual_size,
+    embedder::PlatformHandleVector* platform_handles) {
+  DCHECK(dispatcher);
+  return dispatcher->EndSerializeAndClose(
+      channel, destination, actual_size, platform_handles);
+}
+
+// static
+scoped_refptr<Dispatcher> Dispatcher::TransportDataAccess::Deserialize(
+    Channel* channel,
+    int32_t type,
+    const void* source,
+    size_t size,
+    embedder::PlatformHandleVector* platform_handles) {
+  switch (static_cast<int32_t>(type)) {
+    case kTypeUnknown:
+      DVLOG(2) << "Deserializing invalid handle";
+      return scoped_refptr<Dispatcher>();
+    case kTypeMessagePipe:
+      return scoped_refptr<Dispatcher>(
+          MessagePipeDispatcher::Deserialize(channel, source, size));
+    case kTypeDataPipeProducer:
+    case kTypeDataPipeConsumer:
+      // TODO(vtl): Implement.
+      LOG(WARNING) << "Deserialization of dispatcher type " << type
+                   << " not supported";
+      return scoped_refptr<Dispatcher>();
+    case kTypeSharedBuffer:
+      return scoped_refptr<Dispatcher>(SharedBufferDispatcher::Deserialize(
+          channel, source, size, platform_handles));
+    case kTypePlatformHandle:
+      return scoped_refptr<Dispatcher>(PlatformHandleDispatcher::Deserialize(
+          channel, source, size, platform_handles));
+  }
+  LOG(WARNING) << "Unknown dispatcher type " << type;
+  return scoped_refptr<Dispatcher>();
+}
+
+MojoResult Dispatcher::Close() {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  CloseNoLock();
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Dispatcher::WriteMessage(
+    UserPointer<const void> bytes,
+    uint32_t num_bytes,
+    std::vector<DispatcherTransport>* transports,
+    MojoWriteMessageFlags flags) {
+  DCHECK(!transports || (transports->size() > 0 &&
+                         transports->size() < kMaxMessageNumHandles));
+
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return WriteMessageImplNoLock(bytes, num_bytes, transports, flags);
+}
+
+MojoResult Dispatcher::ReadMessage(UserPointer<void> bytes,
+                                   UserPointer<uint32_t> num_bytes,
+                                   DispatcherVector* dispatchers,
+                                   uint32_t* num_dispatchers,
+                                   MojoReadMessageFlags flags) {
+  DCHECK(!num_dispatchers || *num_dispatchers == 0 ||
+         (dispatchers && dispatchers->empty()));
+
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return ReadMessageImplNoLock(
+      bytes, num_bytes, dispatchers, num_dispatchers, flags);
+}
+
+MojoResult Dispatcher::WriteData(UserPointer<const void> elements,
+                                 UserPointer<uint32_t> num_bytes,
+                                 MojoWriteDataFlags flags) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return WriteDataImplNoLock(elements, num_bytes, flags);
+}
+
+MojoResult Dispatcher::BeginWriteData(UserPointer<void*> buffer,
+                                      UserPointer<uint32_t> buffer_num_bytes,
+                                      MojoWriteDataFlags flags) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return BeginWriteDataImplNoLock(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Dispatcher::EndWriteData(uint32_t num_bytes_written) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return EndWriteDataImplNoLock(num_bytes_written);
+}
+
+MojoResult Dispatcher::ReadData(UserPointer<void> elements,
+                                UserPointer<uint32_t> num_bytes,
+                                MojoReadDataFlags flags) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return ReadDataImplNoLock(elements, num_bytes, flags);
+}
+
+MojoResult Dispatcher::BeginReadData(UserPointer<const void*> buffer,
+                                     UserPointer<uint32_t> buffer_num_bytes,
+                                     MojoReadDataFlags flags) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return BeginReadDataImplNoLock(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Dispatcher::EndReadData(uint32_t num_bytes_read) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return EndReadDataImplNoLock(num_bytes_read);
+}
+
+MojoResult Dispatcher::DuplicateBufferHandle(
+    UserPointer<const MojoDuplicateBufferHandleOptions> options,
+    scoped_refptr<Dispatcher>* new_dispatcher) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return DuplicateBufferHandleImplNoLock(options, new_dispatcher);
+}
+
+MojoResult Dispatcher::MapBuffer(
+    uint64_t offset,
+    uint64_t num_bytes,
+    MojoMapBufferFlags flags,
+    scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return MapBufferImplNoLock(offset, num_bytes, flags, mapping);
+}
+
+HandleSignalsState Dispatcher::GetHandleSignalsState() const {
+  base::AutoLock locker(lock_);
+  if (is_closed_)
+    return HandleSignalsState();
+
+  return GetHandleSignalsStateImplNoLock();
+}
+
+MojoResult Dispatcher::AddWaiter(Waiter* waiter,
+                                 MojoHandleSignals signals,
+                                 uint32_t context,
+                                 HandleSignalsState* signals_state) {
+  base::AutoLock locker(lock_);
+  if (is_closed_) {
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  return AddWaiterImplNoLock(waiter, signals, context, signals_state);
+}
+
+void Dispatcher::RemoveWaiter(Waiter* waiter,
+                              HandleSignalsState* handle_signals_state) {
+  base::AutoLock locker(lock_);
+  if (is_closed_) {
+    if (handle_signals_state)
+      *handle_signals_state = HandleSignalsState();
+    return;
+  }
+  RemoveWaiterImplNoLock(waiter, handle_signals_state);
+}
+
+Dispatcher::Dispatcher() : is_closed_(false) {
+}
+
+Dispatcher::~Dispatcher() {
+  // Make sure that |Close()| was called.
+  DCHECK(is_closed_);
+}
+
+void Dispatcher::CancelAllWaitersNoLock() {
+  lock_.AssertAcquired();
+  DCHECK(is_closed_);
+  // By default, waiting isn't supported. Only dispatchers that can be waited on
+  // will do something nontrivial.
+}
+
+void Dispatcher::CloseImplNoLock() {
+  lock_.AssertAcquired();
+  DCHECK(is_closed_);
+  // This may not need to do anything. Dispatchers should override this to do
+  // any actual close-time cleanup necessary.
+}
+
+MojoResult Dispatcher::WriteMessageImplNoLock(
+    UserPointer<const void> /*bytes*/,
+    uint32_t /*num_bytes*/,
+    std::vector<DispatcherTransport>* /*transports*/,
+    MojoWriteMessageFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for message pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadMessageImplNoLock(
+    UserPointer<void> /*bytes*/,
+    UserPointer<uint32_t> /*num_bytes*/,
+    DispatcherVector* /*dispatchers*/,
+    uint32_t* /*num_dispatchers*/,
+    MojoReadMessageFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for message pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::WriteDataImplNoLock(UserPointer<const void> /*elements*/,
+                                           UserPointer<uint32_t> /*num_bytes*/,
+                                           MojoWriteDataFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginWriteDataImplNoLock(
+    UserPointer<void*> /*buffer*/,
+    UserPointer<uint32_t> /*buffer_num_bytes*/,
+    MojoWriteDataFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndWriteDataImplNoLock(uint32_t /*num_bytes_written*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadDataImplNoLock(UserPointer<void> /*elements*/,
+                                          UserPointer<uint32_t> /*num_bytes*/,
+                                          MojoReadDataFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginReadDataImplNoLock(
+    UserPointer<const void*> /*buffer*/,
+    UserPointer<uint32_t> /*buffer_num_bytes*/,
+    MojoReadDataFlags /*flags*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndReadDataImplNoLock(uint32_t /*num_bytes_read*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for data pipe dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::DuplicateBufferHandleImplNoLock(
+    UserPointer<const MojoDuplicateBufferHandleOptions> /*options*/,
+    scoped_refptr<Dispatcher>* /*new_dispatcher*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for buffer dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::MapBufferImplNoLock(
+    uint64_t /*offset*/,
+    uint64_t /*num_bytes*/,
+    MojoMapBufferFlags /*flags*/,
+    scoped_ptr<embedder::PlatformSharedBufferMapping>* /*mapping*/) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, not supported. Only needed for buffer dispatchers.
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+HandleSignalsState Dispatcher::GetHandleSignalsStateImplNoLock() const {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, waiting isn't supported. Only dispatchers that can be waited on
+  // will do something nontrivial.
+  return HandleSignalsState();
+}
+
+MojoResult Dispatcher::AddWaiterImplNoLock(Waiter* /*waiter*/,
+                                           MojoHandleSignals /*signals*/,
+                                           uint32_t /*context*/,
+                                           HandleSignalsState* signals_state) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, waiting isn't supported. Only dispatchers that can be waited on
+  // will do something nontrivial.
+  if (signals_state)
+    *signals_state = HandleSignalsState();
+  return MOJO_RESULT_FAILED_PRECONDITION;
+}
+
+void Dispatcher::RemoveWaiterImplNoLock(Waiter* /*waiter*/,
+                                        HandleSignalsState* signals_state) {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // By default, waiting isn't supported. Only dispatchers that can be waited on
+  // will do something nontrivial.
+  if (signals_state)
+    *signals_state = HandleSignalsState();
+}
+
+void Dispatcher::StartSerializeImplNoLock(Channel* /*channel*/,
+                                          size_t* max_size,
+                                          size_t* max_platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  DCHECK(!is_closed_);
+  *max_size = 0;
+  *max_platform_handles = 0;
+}
+
+bool Dispatcher::EndSerializeAndCloseImplNoLock(
+    Channel* /*channel*/,
+    void* /*destination*/,
+    size_t* /*actual_size*/,
+    embedder::PlatformHandleVector* /*platform_handles*/) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  DCHECK(is_closed_);
+  // By default, serializing isn't supported, so just close.
+  CloseImplNoLock();
+  return false;
+}
+
+bool Dispatcher::IsBusyNoLock() const {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+  // Most dispatchers support only "atomic" operations, so they are never busy
+  // (in this sense).
+  return false;
+}
+
+void Dispatcher::CloseNoLock() {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+
+  is_closed_ = true;
+  CancelAllWaitersNoLock();
+  CloseImplNoLock();
+}
+
+scoped_refptr<Dispatcher>
+Dispatcher::CreateEquivalentDispatcherAndCloseNoLock() {
+  lock_.AssertAcquired();
+  DCHECK(!is_closed_);
+
+  is_closed_ = true;
+  CancelAllWaitersNoLock();
+  return CreateEquivalentDispatcherAndCloseImplNoLock();
+}
+
+void Dispatcher::StartSerialize(Channel* channel,
+                                size_t* max_size,
+                                size_t* max_platform_handles) {
+  DCHECK(channel);
+  DCHECK(max_size);
+  DCHECK(max_platform_handles);
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  DCHECK(!is_closed_);
+  StartSerializeImplNoLock(channel, max_size, max_platform_handles);
+}
+
+bool Dispatcher::EndSerializeAndClose(
+    Channel* channel,
+    void* destination,
+    size_t* actual_size,
+    embedder::PlatformHandleVector* platform_handles) {
+  DCHECK(channel);
+  DCHECK(actual_size);
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  DCHECK(!is_closed_);
+
+  // Like other |...Close()| methods, we mark ourselves as closed before calling
+  // the impl. But there's no need to cancel waiters: we shouldn't have any (and
+  // shouldn't be in |Core|'s handle table.
+  is_closed_ = true;
+
+#if !defined(NDEBUG)
+  // See the comment above |EndSerializeAndCloseImplNoLock()|. In brief: Locking
+  // isn't actually needed, but we need to satisfy assertions (which we don't
+  // want to remove or weaken).
+  base::AutoLock locker(lock_);
+#endif
+
+  return EndSerializeAndCloseImplNoLock(
+      channel, destination, actual_size, platform_handles);
+}
+
+// DispatcherTransport ---------------------------------------------------------
+
+void DispatcherTransport::End() {
+  DCHECK(dispatcher_);
+  dispatcher_->lock_.Release();
+  dispatcher_ = nullptr;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
new file mode 100644
index 0000000..3e2230a
--- /dev/null
+++ b/mojo/edk/system/dispatcher.h
@@ -0,0 +1,405 @@
+// 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_EDK_SYSTEM_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_DISPATCHER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformSharedBufferMapping;
+}
+
+namespace system {
+
+class Channel;
+class Core;
+class Dispatcher;
+class DispatcherTransport;
+class HandleTable;
+class LocalMessagePipeEndpoint;
+class ProxyMessagePipeEndpoint;
+class TransportData;
+class Waiter;
+
+typedef std::vector<scoped_refptr<Dispatcher>> DispatcherVector;
+
+namespace test {
+
+// Test helper. We need to declare it here so we can friend it.
+MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport
+    DispatcherTryStartTransport(Dispatcher* dispatcher);
+
+}  // namespace test
+
+// A |Dispatcher| implements Mojo primitives that are "attached" to a particular
+// handle. This includes most (all?) primitives except for |MojoWait...()|. This
+// object is thread-safe, with its state being protected by a single lock
+// |lock_|, which is also made available to implementation subclasses (via the
+// |lock()| method).
+class MOJO_SYSTEM_IMPL_EXPORT Dispatcher
+    : public base::RefCountedThreadSafe<Dispatcher> {
+ public:
+  enum Type {
+    kTypeUnknown = 0,
+    kTypeMessagePipe,
+    kTypeDataPipeProducer,
+    kTypeDataPipeConsumer,
+    kTypeSharedBuffer,
+
+    // "Private" types (not exposed via the public interface):
+    kTypePlatformHandle = -1
+  };
+  virtual Type GetType() const = 0;
+
+  // These methods implement the various primitives named |Mojo...()|. These
+  // take |lock_| and handle races with |Close()|. Then they call out to
+  // subclasses' |...ImplNoLock()| methods (still under |lock_|), which actually
+  // implement the primitives.
+  // NOTE(vtl): This puts a big lock around each dispatcher (i.e., handle), and
+  // prevents the various |...ImplNoLock()|s from releasing the lock as soon as
+  // possible. If this becomes an issue, we can rethink this.
+  MojoResult Close();
+
+  // |transports| may be non-null if and only if there are handles to be
+  // written; not that |this| must not be in |transports|. On success, all the
+  // dispatchers in |transports| must have been moved to a closed state; on
+  // failure, they should remain in their original state.
+  MojoResult WriteMessage(UserPointer<const void> bytes,
+                          uint32_t num_bytes,
+                          std::vector<DispatcherTransport>* transports,
+                          MojoWriteMessageFlags flags);
+  // |dispatchers| must be non-null but empty, if |num_dispatchers| is non-null
+  // and nonzero. On success, it will be set to the dispatchers to be received
+  // (and assigned handles) as part of the message.
+  MojoResult ReadMessage(UserPointer<void> bytes,
+                         UserPointer<uint32_t> num_bytes,
+                         DispatcherVector* dispatchers,
+                         uint32_t* num_dispatchers,
+                         MojoReadMessageFlags flags);
+  MojoResult WriteData(UserPointer<const void> elements,
+                       UserPointer<uint32_t> elements_num_bytes,
+                       MojoWriteDataFlags flags);
+  MojoResult BeginWriteData(UserPointer<void*> buffer,
+                            UserPointer<uint32_t> buffer_num_bytes,
+                            MojoWriteDataFlags flags);
+  MojoResult EndWriteData(uint32_t num_bytes_written);
+  MojoResult ReadData(UserPointer<void> elements,
+                      UserPointer<uint32_t> num_bytes,
+                      MojoReadDataFlags flags);
+  MojoResult BeginReadData(UserPointer<const void*> buffer,
+                           UserPointer<uint32_t> buffer_num_bytes,
+                           MojoReadDataFlags flags);
+  MojoResult EndReadData(uint32_t num_bytes_read);
+  // |options| may be null. |new_dispatcher| must not be null, but
+  // |*new_dispatcher| should be null (and will contain the dispatcher for the
+  // new handle on success).
+  MojoResult DuplicateBufferHandle(
+      UserPointer<const MojoDuplicateBufferHandleOptions> options,
+      scoped_refptr<Dispatcher>* new_dispatcher);
+  MojoResult MapBuffer(
+      uint64_t offset,
+      uint64_t num_bytes,
+      MojoMapBufferFlags flags,
+      scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping);
+
+  // Gets the current handle signals state. (The default implementation simply
+  // returns a default-constructed |HandleSignalsState|, i.e., no signals
+  // satisfied or satisfiable.) Note: The state is subject to change from other
+  // threads.
+  HandleSignalsState GetHandleSignalsState() const;
+
+  // Adds a waiter to this dispatcher. The waiter will be woken up when this
+  // object changes state to satisfy |signals| with context |context|. It will
+  // also be woken up when it becomes impossible for the object to ever satisfy
+  // |signals| with a suitable error status.
+  //
+  // If |signals_state| is non-null, on *failure* |*signals_state| will be set
+  // to the current handle signals state (on success, it is left untouched).
+  //
+  // Returns:
+  //  - |MOJO_RESULT_OK| if the waiter was added;
+  //  - |MOJO_RESULT_ALREADY_EXISTS| if |signals| is already satisfied;
+  //  - |MOJO_RESULT_INVALID_ARGUMENT| if the dispatcher has been closed; and
+  //  - |MOJO_RESULT_FAILED_PRECONDITION| if it is not (or no longer) possible
+  //    that |signals| will ever be satisfied.
+  MojoResult AddWaiter(Waiter* waiter,
+                       MojoHandleSignals signals,
+                       uint32_t context,
+                       HandleSignalsState* signals_state);
+  // Removes a waiter from this dispatcher. (It is valid to call this multiple
+  // times for the same |waiter| on the same object, so long as |AddWaiter()|
+  // was called at most once.) If |signals_state| is non-null, |*signals_state|
+  // will be set to the current handle signals state.
+  void RemoveWaiter(Waiter* waiter, HandleSignalsState* signals_state);
+
+  // A dispatcher must be put into a special state in order to be sent across a
+  // message pipe. Outside of tests, only |HandleTableAccess| is allowed to do
+  // this, since there are requirements on the handle table (see below).
+  //
+  // In this special state, only a restricted set of operations is allowed.
+  // These are the ones available as |DispatcherTransport| methods. Other
+  // |Dispatcher| methods must not be called until |DispatcherTransport::End()|
+  // has been called.
+  class HandleTableAccess {
+   private:
+    friend class Core;
+    friend class HandleTable;
+    // Tests also need this, to avoid needing |Core|.
+    friend DispatcherTransport test::DispatcherTryStartTransport(Dispatcher*);
+
+    // This must be called under the handle table lock and only if the handle
+    // table entry is not marked busy. The caller must maintain a reference to
+    // |dispatcher| until |DispatcherTransport::End()| is called.
+    static DispatcherTransport TryStartTransport(Dispatcher* dispatcher);
+  };
+
+  // A |TransportData| may serialize dispatchers that are given to it (and which
+  // were previously attached to the |MessageInTransit| that is creating it) to
+  // a given |Channel| and then (probably in a different process) deserialize.
+  // Note that the |MessageInTransit| "owns" (i.e., has the only ref to) these
+  // dispatchers, so there are no locking issues. (There's no lock ordering
+  // issue, and in fact no need to take dispatcher locks at all.)
+  // TODO(vtl): Consider making another wrapper similar to |DispatcherTransport|
+  // (but with an owning, unique reference), and having
+  // |CreateEquivalentDispatcherAndCloseImplNoLock()| return that wrapper (and
+  // |MessageInTransit|, etc. only holding on to such wrappers).
+  class TransportDataAccess {
+   private:
+    friend class TransportData;
+
+    // Serialization API. These functions may only be called on such
+    // dispatchers. (|channel| is the |Channel| to which the dispatcher is to be
+    // serialized.) See the |Dispatcher| methods of the same names for more
+    // details.
+    static void StartSerialize(Dispatcher* dispatcher,
+                               Channel* channel,
+                               size_t* max_size,
+                               size_t* max_platform_handles);
+    static bool EndSerializeAndClose(
+        Dispatcher* dispatcher,
+        Channel* channel,
+        void* destination,
+        size_t* actual_size,
+        embedder::PlatformHandleVector* platform_handles);
+
+    // Deserialization API.
+    // Note: This "clears" (i.e., reset to the invalid handle) any platform
+    // handles that it takes ownership of.
+    static scoped_refptr<Dispatcher> Deserialize(
+        Channel* channel,
+        int32_t type,
+        const void* source,
+        size_t size,
+        embedder::PlatformHandleVector* platform_handles);
+  };
+
+ protected:
+  friend class base::RefCountedThreadSafe<Dispatcher>;
+
+  Dispatcher();
+  virtual ~Dispatcher();
+
+  // These are to be overridden by subclasses (if necessary). They are called
+  // exactly once -- first |CancelAllWaitersNoLock()|, then |CloseImplNoLock()|,
+  // when the dispatcher is being closed. They are called under |lock_|.
+  virtual void CancelAllWaitersNoLock();
+  virtual void CloseImplNoLock();
+  virtual scoped_refptr<Dispatcher>
+      CreateEquivalentDispatcherAndCloseImplNoLock() = 0;
+
+  // These are to be overridden by subclasses (if necessary). They are never
+  // called after the dispatcher has been closed. They are called under |lock_|.
+  // See the descriptions of the methods without the "ImplNoLock" for more
+  // information.
+  virtual MojoResult WriteMessageImplNoLock(
+      UserPointer<const void> bytes,
+      uint32_t num_bytes,
+      std::vector<DispatcherTransport>* transports,
+      MojoWriteMessageFlags flags);
+  virtual MojoResult ReadMessageImplNoLock(UserPointer<void> bytes,
+                                           UserPointer<uint32_t> num_bytes,
+                                           DispatcherVector* dispatchers,
+                                           uint32_t* num_dispatchers,
+                                           MojoReadMessageFlags flags);
+  virtual MojoResult WriteDataImplNoLock(UserPointer<const void> elements,
+                                         UserPointer<uint32_t> num_bytes,
+                                         MojoWriteDataFlags flags);
+  virtual MojoResult BeginWriteDataImplNoLock(
+      UserPointer<void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      MojoWriteDataFlags flags);
+  virtual MojoResult EndWriteDataImplNoLock(uint32_t num_bytes_written);
+  virtual MojoResult ReadDataImplNoLock(UserPointer<void> elements,
+                                        UserPointer<uint32_t> num_bytes,
+                                        MojoReadDataFlags flags);
+  virtual MojoResult BeginReadDataImplNoLock(
+      UserPointer<const void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      MojoReadDataFlags flags);
+  virtual MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read);
+  virtual MojoResult DuplicateBufferHandleImplNoLock(
+      UserPointer<const MojoDuplicateBufferHandleOptions> options,
+      scoped_refptr<Dispatcher>* new_dispatcher);
+  virtual MojoResult MapBufferImplNoLock(
+      uint64_t offset,
+      uint64_t num_bytes,
+      MojoMapBufferFlags flags,
+      scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping);
+  virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const;
+  virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+                                         MojoHandleSignals signals,
+                                         uint32_t context,
+                                         HandleSignalsState* signals_state);
+  virtual void RemoveWaiterImplNoLock(Waiter* waiter,
+                                      HandleSignalsState* signals_state);
+
+  // These implement the API used to serialize dispatchers to a |Channel|
+  // (described below). They will only be called on a dispatcher that's attached
+  // to and "owned" by a |MessageInTransit|. See the non-"impl" versions for
+  // more information.
+  //
+  // Note: |StartSerializeImplNoLock()| is actually called with |lock_| NOT
+  // held, since the dispatcher should only be accessible to the calling thread.
+  // On Debug builds, |EndSerializeAndCloseImplNoLock()| is called with |lock_|
+  // held, to satisfy any |lock_.AssertAcquired()| (e.g., in |CloseImplNoLock()|
+  // -- and anything it calls); disentangling those assertions is
+  // difficult/fragile, and would weaken our general checking of invariants.
+  //
+  // TODO(vtl): Consider making these pure virtual once most things support
+  // being passed over a message pipe.
+  virtual void StartSerializeImplNoLock(Channel* channel,
+                                        size_t* max_size,
+                                        size_t* max_platform_handles);
+  virtual bool EndSerializeAndCloseImplNoLock(
+      Channel* channel,
+      void* destination,
+      size_t* actual_size,
+      embedder::PlatformHandleVector* platform_handles);
+
+  // Available to subclasses. (Note: Returns a non-const reference, just like
+  // |base::AutoLock|'s constructor takes a non-const reference.)
+  base::Lock& lock() const { return lock_; }
+
+ private:
+  friend class DispatcherTransport;
+
+  // This should be overridden to return true if/when there's an ongoing
+  // operation (e.g., two-phase read/writes on data pipes) that should prevent a
+  // handle from being sent over a message pipe (with status "busy").
+  virtual bool IsBusyNoLock() const;
+
+  // Closes the dispatcher. This must be done under lock, and unlike |Close()|,
+  // the dispatcher must not be closed already. (This is the "equivalent" of
+  // |CreateEquivalentDispatcherAndCloseNoLock()|, for situations where the
+  // dispatcher must be disposed of instead of "transferred".)
+  void CloseNoLock();
+
+  // Creates an equivalent dispatcher -- representing the same resource as this
+  // dispatcher -- and close (i.e., disable) this dispatcher. I.e., this
+  // dispatcher will look as though it was closed, but the resource it
+  // represents will be assigned to the new dispatcher. This must be called
+  // under the dispatcher's lock.
+  scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock();
+
+  // API to serialize dispatchers to a |Channel|, exposed to only
+  // |TransportData| (via |TransportData|). They may only be called on a
+  // dispatcher attached to a |MessageInTransit| (and in particular not in
+  // |CoreImpl|'s handle table).
+  //
+  // Starts the serialization. Returns (via the two "out" parameters) the
+  // maximum amount of space that may be needed to serialize this dispatcher to
+  // the given |Channel| (no more than
+  // |TransportData::kMaxSerializedDispatcherSize|) and the maximum number of
+  // |PlatformHandle|s that may need to be attached (no more than
+  // |TransportData::kMaxSerializedDispatcherPlatformHandles|). If this
+  // dispatcher cannot be serialized to the given |Channel|, |*max_size| and
+  // |*max_platform_handles| should be set to zero. A call to this method will
+  // ALWAYS be followed by a call to |EndSerializeAndClose()| (even if this
+  // dispatcher cannot be serialized to the given |Channel|).
+  void StartSerialize(Channel* channel,
+                      size_t* max_size,
+                      size_t* max_platform_handles);
+  // Completes the serialization of this dispatcher to the given |Channel| and
+  // closes it. (This call will always follow an earlier call to
+  // |StartSerialize()|, with the same |Channel|.) This does so by writing to
+  // |destination| and appending any |PlatformHandle|s needed to
+  // |platform_handles| (which may be null if no platform handles were indicated
+  // to be required to |StartSerialize()|). This may write no more than the
+  // amount indicated by |StartSerialize()|. (WARNING: Beware of races, e.g., if
+  // something can be mutated between the two calls!) Returns true on success,
+  // in which case |*actual_size| is set to the amount it actually wrote to
+  // |destination|. On failure, |*actual_size| should not be modified; however,
+  // the dispatcher will still be closed.
+  bool EndSerializeAndClose(Channel* channel,
+                            void* destination,
+                            size_t* actual_size,
+                            embedder::PlatformHandleVector* platform_handles);
+
+  // This protects the following members as well as any state added by
+  // subclasses.
+  mutable base::Lock lock_;
+  bool is_closed_;
+
+  DISALLOW_COPY_AND_ASSIGN(Dispatcher);
+};
+
+// Wrapper around a |Dispatcher| pointer, while it's being processed to be
+// passed in a message pipe. See the comment about
+// |Dispatcher::HandleTableAccess| for more details.
+//
+// Note: This class is deliberately "thin" -- no more expensive than a
+// |Dispatcher*|.
+class MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport {
+ public:
+  DispatcherTransport() : dispatcher_(nullptr) {}
+
+  void End();
+
+  Dispatcher::Type GetType() const { return dispatcher_->GetType(); }
+  bool IsBusy() const { return dispatcher_->IsBusyNoLock(); }
+  void Close() { dispatcher_->CloseNoLock(); }
+  scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndClose() {
+    return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock();
+  }
+
+  bool is_valid() const { return !!dispatcher_; }
+
+ protected:
+  Dispatcher* dispatcher() { return dispatcher_; }
+
+ private:
+  friend class Dispatcher::HandleTableAccess;
+
+  explicit DispatcherTransport(Dispatcher* dispatcher)
+      : dispatcher_(dispatcher) {}
+
+  Dispatcher* dispatcher_;
+
+  // Copy and assign allowed.
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DISPATCHER_H_
diff --git a/mojo/edk/system/dispatcher_unittest.cc b/mojo/edk/system/dispatcher_unittest.cc
new file mode 100644
index 0000000..d9d526d
--- /dev/null
+++ b/mojo/edk/system/dispatcher_unittest.cc
@@ -0,0 +1,326 @@
+// 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/edk/system/dispatcher.h"
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Trivial subclass that makes the constructor public.
+class TrivialDispatcher : public Dispatcher {
+ public:
+  TrivialDispatcher() {}
+
+  virtual Type GetType() const override { return kTypeUnknown; }
+
+ private:
+  friend class base::RefCountedThreadSafe<TrivialDispatcher>;
+  virtual ~TrivialDispatcher() {}
+
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override {
+    lock().AssertAcquired();
+    return scoped_refptr<Dispatcher>(new TrivialDispatcher());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TrivialDispatcher);
+};
+
+TEST(DispatcherTest, Basic) {
+  scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+  EXPECT_EQ(Dispatcher::kTypeUnknown, d->GetType());
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->WriteMessage(
+                NullUserPointer(), 0, nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->ReadMessage(NullUserPointer(),
+                           NullUserPointer(),
+                           nullptr,
+                           nullptr,
+                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->WriteData(
+          NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->BeginWriteData(
+          NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->ReadData(
+          NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->BeginReadData(
+          NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0));
+  Waiter w;
+  w.Init();
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            d->AddWaiter(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  // Okay to remove even if it wasn't added (or was already removed).
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->WriteMessage(
+                NullUserPointer(), 0, nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->ReadMessage(NullUserPointer(),
+                           NullUserPointer(),
+                           nullptr,
+                           nullptr,
+                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->WriteData(
+          NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->BeginWriteData(
+          NullUserPointer(), NullUserPointer(), MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->ReadData(
+          NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      d->BeginReadData(
+          NullUserPointer(), NullUserPointer(), MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0));
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->AddWaiter(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+}
+
+class ThreadSafetyStressThread : public base::SimpleThread {
+ public:
+  enum DispatcherOp {
+    CLOSE = 0,
+    WRITE_MESSAGE,
+    READ_MESSAGE,
+    WRITE_DATA,
+    BEGIN_WRITE_DATA,
+    END_WRITE_DATA,
+    READ_DATA,
+    BEGIN_READ_DATA,
+    END_READ_DATA,
+    DUPLICATE_BUFFER_HANDLE,
+    MAP_BUFFER,
+    ADD_WAITER,
+    REMOVE_WAITER,
+    DISPATCHER_OP_COUNT
+  };
+
+  ThreadSafetyStressThread(base::WaitableEvent* event,
+                           scoped_refptr<Dispatcher> dispatcher,
+                           DispatcherOp op)
+      : base::SimpleThread("thread_safety_stress_thread"),
+        event_(event),
+        dispatcher_(dispatcher),
+        op_(op) {
+    CHECK_LE(0, op_);
+    CHECK_LT(op_, DISPATCHER_OP_COUNT);
+  }
+
+  virtual ~ThreadSafetyStressThread() { Join(); }
+
+ private:
+  virtual void Run() override {
+    event_->Wait();
+
+    waiter_.Init();
+    switch (op_) {
+      case CLOSE: {
+        MojoResult r = dispatcher_->Close();
+        EXPECT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_INVALID_ARGUMENT)
+            << "Result: " << r;
+        break;
+      }
+      case WRITE_MESSAGE:
+        EXPECT_EQ(
+            MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher_->WriteMessage(
+                NullUserPointer(), 0, nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
+        break;
+      case READ_MESSAGE:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                  dispatcher_->ReadMessage(NullUserPointer(),
+                                           NullUserPointer(),
+                                           nullptr,
+                                           nullptr,
+                                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+        break;
+      case WRITE_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                  dispatcher_->WriteData(NullUserPointer(),
+                                         NullUserPointer(),
+                                         MOJO_WRITE_DATA_FLAG_NONE));
+        break;
+      case BEGIN_WRITE_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                  dispatcher_->BeginWriteData(NullUserPointer(),
+                                              NullUserPointer(),
+                                              MOJO_WRITE_DATA_FLAG_NONE));
+        break;
+      case END_WRITE_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndWriteData(0));
+        break;
+      case READ_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                  dispatcher_->ReadData(NullUserPointer(),
+                                        NullUserPointer(),
+                                        MOJO_READ_DATA_FLAG_NONE));
+        break;
+      case BEGIN_READ_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                  dispatcher_->BeginReadData(NullUserPointer(),
+                                             NullUserPointer(),
+                                             MOJO_READ_DATA_FLAG_NONE));
+        break;
+      case END_READ_DATA:
+        EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndReadData(0));
+        break;
+      case DUPLICATE_BUFFER_HANDLE: {
+        scoped_refptr<Dispatcher> unused;
+        EXPECT_EQ(
+            MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher_->DuplicateBufferHandle(NullUserPointer(), &unused));
+        break;
+      }
+      case MAP_BUFFER: {
+        scoped_ptr<embedder::PlatformSharedBufferMapping> unused;
+        EXPECT_EQ(
+            MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher_->MapBuffer(0u, 0u, MOJO_MAP_BUFFER_FLAG_NONE, &unused));
+        break;
+      }
+      case ADD_WAITER: {
+        HandleSignalsState hss;
+        MojoResult r =
+            dispatcher_->AddWaiter(&waiter_, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss);
+        EXPECT_TRUE(r == MOJO_RESULT_FAILED_PRECONDITION ||
+                    r == MOJO_RESULT_INVALID_ARGUMENT);
+        EXPECT_EQ(0u, hss.satisfied_signals);
+        EXPECT_EQ(0u, hss.satisfiable_signals);
+        break;
+      }
+      case REMOVE_WAITER: {
+        HandleSignalsState hss;
+        dispatcher_->RemoveWaiter(&waiter_, &hss);
+        EXPECT_EQ(0u, hss.satisfied_signals);
+        EXPECT_EQ(0u, hss.satisfiable_signals);
+        break;
+      }
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    // Always try to remove the waiter, in case we added it.
+    HandleSignalsState hss;
+    dispatcher_->RemoveWaiter(&waiter_, &hss);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+  }
+
+  base::WaitableEvent* const event_;
+  const scoped_refptr<Dispatcher> dispatcher_;
+  const DispatcherOp op_;
+
+  Waiter waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadSafetyStressThread);
+};
+
+TEST(DispatcherTest, ThreadSafetyStress) {
+  static const size_t kRepeatCount = 20;
+  static const size_t kNumThreads = 100;
+
+  for (size_t i = 0; i < kRepeatCount; i++) {
+    // Manual reset, not initially signalled.
+    base::WaitableEvent event(true, false);
+    scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+    {
+      ScopedVector<ThreadSafetyStressThread> threads;
+      for (size_t j = 0; j < kNumThreads; j++) {
+        ThreadSafetyStressThread::DispatcherOp op =
+            static_cast<ThreadSafetyStressThread::DispatcherOp>(
+                (i + j) % ThreadSafetyStressThread::DISPATCHER_OP_COUNT);
+        threads.push_back(new ThreadSafetyStressThread(&event, d, op));
+        threads.back()->Start();
+      }
+      // Kicks off real work on the threads:
+      event.Signal();
+    }  // Joins all the threads.
+
+    // One of the threads should already have closed the dispatcher.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->Close());
+  }
+}
+
+TEST(DispatcherTest, ThreadSafetyStressNoClose) {
+  static const size_t kRepeatCount = 20;
+  static const size_t kNumThreads = 100;
+
+  for (size_t i = 0; i < kRepeatCount; i++) {
+    // Manual reset, not initially signalled.
+    base::WaitableEvent event(true, false);
+    scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+    {
+      ScopedVector<ThreadSafetyStressThread> threads;
+      for (size_t j = 0; j < kNumThreads; j++) {
+        ThreadSafetyStressThread::DispatcherOp op =
+            static_cast<ThreadSafetyStressThread::DispatcherOp>(
+                (i + j) % (ThreadSafetyStressThread::DISPATCHER_OP_COUNT - 1) +
+                1);
+        threads.push_back(new ThreadSafetyStressThread(&event, d, op));
+        threads.back()->Start();
+      }
+      // Kicks off real work on the threads:
+      event.Signal();
+    }  // Joins all the threads.
+
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/entrypoints.cc b/mojo/edk/system/entrypoints.cc
new file mode 100644
index 0000000..7ec5bb4
--- /dev/null
+++ b/mojo/edk/system/entrypoints.cc
@@ -0,0 +1,191 @@
+// Copyright 2014 The Chromium 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/edk/system/entrypoints.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/message_pipe.h"
+
+static mojo::system::Core* g_core = nullptr;
+
+using mojo::system::MakeUserPointer;
+
+namespace mojo {
+namespace system {
+namespace entrypoints {
+
+void SetCore(Core* core) {
+  g_core = core;
+}
+
+Core* GetCore() {
+  return g_core;
+}
+
+}  // namespace entrypoints
+}  // namepace system
+}  // namespace mojo
+
+// Definitions of the system functions.
+extern "C" {
+MojoTimeTicks MojoGetTimeTicksNow() {
+  return g_core->GetTimeTicksNow();
+}
+
+MojoResult MojoClose(MojoHandle handle) {
+  return g_core->Close(handle);
+}
+
+MojoResult MojoWait(MojoHandle handle,
+                    MojoHandleSignals signals,
+                    MojoDeadline deadline) {
+  return g_core->Wait(
+      handle, signals, deadline, mojo::system::NullUserPointer());
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+                        const MojoHandleSignals* signals,
+                        uint32_t num_handles,
+                        MojoDeadline deadline) {
+  uint32_t result_index = static_cast<uint32_t>(-1);
+  MojoResult result = g_core->WaitMany(MakeUserPointer(handles),
+                                       MakeUserPointer(signals),
+                                       num_handles,
+                                       deadline,
+                                       MakeUserPointer(&result_index),
+                                       mojo::system::NullUserPointer());
+  return (result == MOJO_RESULT_OK) ? static_cast<MojoResult>(result_index)
+                                    : result;
+}
+
+MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+                                 MojoHandle* message_pipe_handle0,
+                                 MojoHandle* message_pipe_handle1) {
+  return g_core->CreateMessagePipe(MakeUserPointer(options),
+                                   MakeUserPointer(message_pipe_handle0),
+                                   MakeUserPointer(message_pipe_handle1));
+}
+
+MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+                            const void* bytes,
+                            uint32_t num_bytes,
+                            const MojoHandle* handles,
+                            uint32_t num_handles,
+                            MojoWriteMessageFlags flags) {
+  return g_core->WriteMessage(message_pipe_handle,
+                              MakeUserPointer(bytes),
+                              num_bytes,
+                              MakeUserPointer(handles),
+                              num_handles,
+                              flags);
+}
+
+MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+                           void* bytes,
+                           uint32_t* num_bytes,
+                           MojoHandle* handles,
+                           uint32_t* num_handles,
+                           MojoReadMessageFlags flags) {
+  return g_core->ReadMessage(message_pipe_handle,
+                             MakeUserPointer(bytes),
+                             MakeUserPointer(num_bytes),
+                             MakeUserPointer(handles),
+                             MakeUserPointer(num_handles),
+                             flags);
+}
+
+MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
+                              MojoHandle* data_pipe_producer_handle,
+                              MojoHandle* data_pipe_consumer_handle) {
+  return g_core->CreateDataPipe(MakeUserPointer(options),
+                                MakeUserPointer(data_pipe_producer_handle),
+                                MakeUserPointer(data_pipe_consumer_handle));
+}
+
+MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
+                         const void* elements,
+                         uint32_t* num_elements,
+                         MojoWriteDataFlags flags) {
+  return g_core->WriteData(data_pipe_producer_handle,
+                           MakeUserPointer(elements),
+                           MakeUserPointer(num_elements),
+                           flags);
+}
+
+MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+                              void** buffer,
+                              uint32_t* buffer_num_elements,
+                              MojoWriteDataFlags flags) {
+  return g_core->BeginWriteData(data_pipe_producer_handle,
+                                MakeUserPointer(buffer),
+                                MakeUserPointer(buffer_num_elements),
+                                flags);
+}
+
+MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+                            uint32_t num_elements_written) {
+  return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+                        void* elements,
+                        uint32_t* num_elements,
+                        MojoReadDataFlags flags) {
+  return g_core->ReadData(data_pipe_consumer_handle,
+                          MakeUserPointer(elements),
+                          MakeUserPointer(num_elements),
+                          flags);
+}
+
+MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+                             const void** buffer,
+                             uint32_t* buffer_num_elements,
+                             MojoReadDataFlags flags) {
+  return g_core->BeginReadData(data_pipe_consumer_handle,
+                               MakeUserPointer(buffer),
+                               MakeUserPointer(buffer_num_elements),
+                               flags);
+}
+
+MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+                           uint32_t num_elements_read) {
+  return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBuffer(
+    const struct MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  return g_core->CreateSharedBuffer(MakeUserPointer(options),
+                                    num_bytes,
+                                    MakeUserPointer(shared_buffer_handle));
+}
+
+MojoResult MojoDuplicateBufferHandle(
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  return g_core->DuplicateBufferHandle(buffer_handle,
+                                       MakeUserPointer(options),
+                                       MakeUserPointer(new_buffer_handle));
+}
+
+MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+                         uint64_t offset,
+                         uint64_t num_bytes,
+                         void** buffer,
+                         MojoMapBufferFlags flags) {
+  return g_core->MapBuffer(
+      buffer_handle, offset, num_bytes, MakeUserPointer(buffer), flags);
+}
+
+MojoResult MojoUnmapBuffer(void* buffer) {
+  return g_core->UnmapBuffer(MakeUserPointer(buffer));
+}
+
+}  // extern "C"
diff --git a/mojo/edk/system/entrypoints.h b/mojo/edk/system/entrypoints.h
new file mode 100644
index 0000000..46c41b4
--- /dev/null
+++ b/mojo/edk/system/entrypoints.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_EDK_SYSTEM_ENTRYPOINTS_H_
+#define MOJO_EDK_SYSTEM_ENTRYPOINTS_H_
+
+namespace mojo {
+namespace system {
+
+class Core;
+
+namespace entrypoints {
+
+// Sets the instance of Core to be used by system functions.
+void SetCore(Core* core);
+// Gets the instance of Core to be used by system functions.
+Core* GetCore();
+
+}  // namespace entrypoints
+}  // namepace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_ENTRYPOINTS_H_
diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h
new file mode 100644
index 0000000..3391ea0
--- /dev/null
+++ b/mojo/edk/system/handle_signals_state.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_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
+#define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
+
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+// Just "add" some constructors and methods to the C struct
+// |MojoHandleSignalsState| (for convenience). This should add no overhead.
+struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState
+    : public MojoHandleSignalsState {
+  HandleSignalsState() {
+    satisfied_signals = MOJO_HANDLE_SIGNAL_NONE;
+    satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE;
+  }
+  HandleSignalsState(MojoHandleSignals satisfied,
+                     MojoHandleSignals satisfiable) {
+    satisfied_signals = satisfied;
+    satisfiable_signals = satisfiable;
+  }
+
+  bool equals(const HandleSignalsState& other) const {
+    return satisfied_signals == other.satisfied_signals &&
+           satisfiable_signals == other.satisfiable_signals;
+  }
+
+  bool satisfies(MojoHandleSignals signals) const {
+    return !!(satisfied_signals & signals);
+  }
+
+  bool can_satisfy(MojoHandleSignals signals) const {
+    return !!(satisfiable_signals & signals);
+  }
+
+  // (Copy and assignment allowed.)
+};
+static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState),
+              "HandleSignalsState should add no overhead");
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc
new file mode 100644
index 0000000..9c01230
--- /dev/null
+++ b/mojo/edk/system/handle_table.cc
@@ -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.
+
+#include "mojo/edk/system/handle_table.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+HandleTable::Entry::Entry() : busy(false) {
+}
+
+HandleTable::Entry::Entry(const scoped_refptr<Dispatcher>& dispatcher)
+    : dispatcher(dispatcher), busy(false) {
+}
+
+HandleTable::Entry::~Entry() {
+  DCHECK(!busy);
+}
+
+HandleTable::HandleTable() : next_handle_(MOJO_HANDLE_INVALID + 1) {
+}
+
+HandleTable::~HandleTable() {
+  // This should usually not be reached (the only instance should be owned by
+  // the singleton |Core|, which lives forever), except in tests.
+}
+
+Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) {
+  DCHECK_NE(handle, MOJO_HANDLE_INVALID);
+
+  HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
+  if (it == handle_to_entry_map_.end())
+    return nullptr;
+  return it->second.dispatcher.get();
+}
+
+MojoResult HandleTable::GetAndRemoveDispatcher(
+    MojoHandle handle,
+    scoped_refptr<Dispatcher>* dispatcher) {
+  DCHECK_NE(handle, MOJO_HANDLE_INVALID);
+  DCHECK(dispatcher);
+
+  HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
+  if (it == handle_to_entry_map_.end())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (it->second.busy)
+    return MOJO_RESULT_BUSY;
+  *dispatcher = it->second.dispatcher;
+  handle_to_entry_map_.erase(it);
+
+  return MOJO_RESULT_OK;
+}
+
+MojoHandle HandleTable::AddDispatcher(
+    const scoped_refptr<Dispatcher>& dispatcher) {
+  if (handle_to_entry_map_.size() >= kMaxHandleTableSize)
+    return MOJO_HANDLE_INVALID;
+  return AddDispatcherNoSizeCheck(dispatcher);
+}
+
+std::pair<MojoHandle, MojoHandle> HandleTable::AddDispatcherPair(
+    const scoped_refptr<Dispatcher>& dispatcher0,
+    const scoped_refptr<Dispatcher>& dispatcher1) {
+  if (handle_to_entry_map_.size() + 1 >= kMaxHandleTableSize)
+    return std::make_pair(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID);
+  return std::make_pair(AddDispatcherNoSizeCheck(dispatcher0),
+                        AddDispatcherNoSizeCheck(dispatcher1));
+}
+
+bool HandleTable::AddDispatcherVector(const DispatcherVector& dispatchers,
+                                      MojoHandle* handles) {
+  DCHECK_LE(dispatchers.size(), kMaxMessageNumHandles);
+  DCHECK(handles);
+  // TODO(vtl): |std::numeric_limits<size_t>::max()| isn't a compile-time
+  // expression in C++03.
+  static_assert(
+      static_cast<uint64_t>(kMaxHandleTableSize) + kMaxMessageNumHandles <
+          (sizeof(size_t) == 8 ? kuint64max
+                               : static_cast<uint64_t>(kuint32max)),
+      "Addition may overflow");
+
+  if (handle_to_entry_map_.size() + dispatchers.size() > kMaxHandleTableSize)
+    return false;
+
+  for (size_t i = 0; i < dispatchers.size(); i++) {
+    if (dispatchers[i].get()) {
+      handles[i] = AddDispatcherNoSizeCheck(dispatchers[i]);
+    } else {
+      LOG(WARNING) << "Invalid dispatcher at index " << i;
+      handles[i] = MOJO_HANDLE_INVALID;
+    }
+  }
+  return true;
+}
+
+MojoResult HandleTable::MarkBusyAndStartTransport(
+    MojoHandle disallowed_handle,
+    const MojoHandle* handles,
+    uint32_t num_handles,
+    std::vector<DispatcherTransport>* transports) {
+  DCHECK_NE(disallowed_handle, MOJO_HANDLE_INVALID);
+  DCHECK(handles);
+  DCHECK_LE(num_handles, kMaxMessageNumHandles);
+  DCHECK(transports);
+  DCHECK_EQ(transports->size(), num_handles);
+
+  std::vector<Entry*> entries(num_handles);
+
+  // First verify all the handles and get their dispatchers.
+  uint32_t i;
+  MojoResult error_result = MOJO_RESULT_INTERNAL;
+  for (i = 0; i < num_handles; i++) {
+    // Sending your own handle is not allowed (and, for consistency, returns
+    // "busy").
+    if (handles[i] == disallowed_handle) {
+      error_result = MOJO_RESULT_BUSY;
+      break;
+    }
+
+    HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+    if (it == handle_to_entry_map_.end()) {
+      error_result = MOJO_RESULT_INVALID_ARGUMENT;
+      break;
+    }
+
+    entries[i] = &it->second;
+    if (entries[i]->busy) {
+      error_result = MOJO_RESULT_BUSY;
+      break;
+    }
+    // Note: By marking the handle as busy here, we're also preventing the
+    // same handle from being sent multiple times in the same message.
+    entries[i]->busy = true;
+
+    // Try to start the transport.
+    DispatcherTransport transport =
+        Dispatcher::HandleTableAccess::TryStartTransport(
+            entries[i]->dispatcher.get());
+    if (!transport.is_valid()) {
+      // Only log for Debug builds, since this is not a problem with the system
+      // code, but with user code.
+      DLOG(WARNING) << "Likely race condition in user code detected: attempt "
+                       "to transfer handle " << handles[i]
+                    << " while it is in use on a different thread";
+
+      // Unset the busy flag (since it won't be unset below).
+      entries[i]->busy = false;
+      error_result = MOJO_RESULT_BUSY;
+      break;
+    }
+
+    // Check if the dispatcher is busy (e.g., in a two-phase read/write).
+    // (Note that this must be done after the dispatcher's lock is acquired.)
+    if (transport.IsBusy()) {
+      // Unset the busy flag and end the transport (since it won't be done
+      // below).
+      entries[i]->busy = false;
+      transport.End();
+      error_result = MOJO_RESULT_BUSY;
+      break;
+    }
+
+    // Hang on to the transport (which we'll need to end the transport).
+    (*transports)[i] = transport;
+  }
+  if (i < num_handles) {
+    DCHECK_NE(error_result, MOJO_RESULT_INTERNAL);
+
+    // Unset the busy flags and release the locks.
+    for (uint32_t j = 0; j < i; j++) {
+      DCHECK(entries[j]->busy);
+      entries[j]->busy = false;
+      (*transports)[j].End();
+    }
+    return error_result;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoHandle HandleTable::AddDispatcherNoSizeCheck(
+    const scoped_refptr<Dispatcher>& dispatcher) {
+  DCHECK(dispatcher.get());
+  DCHECK_LT(handle_to_entry_map_.size(), kMaxHandleTableSize);
+  DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID);
+
+  // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try
+  // assigning randomly?)
+  while (handle_to_entry_map_.find(next_handle_) !=
+         handle_to_entry_map_.end()) {
+    next_handle_++;
+    if (next_handle_ == MOJO_HANDLE_INVALID)
+      next_handle_++;
+  }
+
+  MojoHandle new_handle = next_handle_;
+  handle_to_entry_map_[new_handle] = Entry(dispatcher);
+
+  next_handle_++;
+  if (next_handle_ == MOJO_HANDLE_INVALID)
+    next_handle_++;
+
+  return new_handle;
+}
+
+void HandleTable::RemoveBusyHandles(const MojoHandle* handles,
+                                    uint32_t num_handles) {
+  DCHECK(handles);
+  DCHECK_LE(num_handles, kMaxMessageNumHandles);
+
+  for (uint32_t i = 0; i < num_handles; i++) {
+    HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+    DCHECK(it != handle_to_entry_map_.end());
+    DCHECK(it->second.busy);
+    it->second.busy = false;  // For the sake of a |DCHECK()|.
+    handle_to_entry_map_.erase(it);
+  }
+}
+
+void HandleTable::RestoreBusyHandles(const MojoHandle* handles,
+                                     uint32_t num_handles) {
+  DCHECK(handles);
+  DCHECK_LE(num_handles, kMaxMessageNumHandles);
+
+  for (uint32_t i = 0; i < num_handles; i++) {
+    HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+    DCHECK(it != handle_to_entry_map_.end());
+    DCHECK(it->second.busy);
+    it->second.busy = false;
+  }
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h
new file mode 100644
index 0000000..9385643
--- /dev/null
+++ b/mojo/edk/system/handle_table.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_HANDLE_TABLE_H_
+#define MOJO_EDK_SYSTEM_HANDLE_TABLE_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+class Core;
+class Dispatcher;
+class DispatcherTransport;
+
+typedef std::vector<scoped_refptr<Dispatcher>> DispatcherVector;
+
+// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
+// so it can be friended.
+namespace internal {
+bool ShutdownCheckNoLeaks(Core*);
+}
+
+// This class provides the (global) handle table (owned by |Core|), which maps
+// (valid) |MojoHandle|s to |Dispatcher|s. This is abstracted so that, e.g.,
+// caching may be added.
+//
+// This class is NOT thread-safe; locking is left to |Core| (since it may need
+// to make several changes -- "atomically" or in rapid successsion, in which
+// case the extra locking/unlocking would be unnecessary overhead).
+
+class MOJO_SYSTEM_IMPL_EXPORT HandleTable {
+ public:
+  HandleTable();
+  ~HandleTable();
+
+  // Gets the dispatcher for a given handle (which should not be
+  // |MOJO_HANDLE_INVALID|). Returns null if there's no dispatcher for the given
+  // handle.
+  // WARNING: For efficiency, this returns a dumb pointer. If you're going to
+  // use the result outside |Core|'s lock, you MUST take a reference (e.g., by
+  // storing the result inside a |scoped_refptr|).
+  Dispatcher* GetDispatcher(MojoHandle handle);
+
+  // On success, gets the dispatcher for a given handle (which should not be
+  // |MOJO_HANDLE_INVALID|) and removes it. (On failure, returns an appropriate
+  // result (and leaves |dispatcher| alone), namely
+  // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
+  // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
+  MojoResult GetAndRemoveDispatcher(MojoHandle handle,
+                                    scoped_refptr<Dispatcher>* dispatcher);
+
+  // Adds a dispatcher (which must be valid), returning the handle for it.
+  // Returns |MOJO_HANDLE_INVALID| on failure (if the handle table is full).
+  MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher);
+
+  // Adds a pair of dispatchers (which must be valid), return a pair of handles
+  // for them. On failure (if the handle table is full), the first (and second)
+  // handles will be |MOJO_HANDLE_INVALID|, and neither dispatcher will be
+  // added.
+  std::pair<MojoHandle, MojoHandle> AddDispatcherPair(
+      const scoped_refptr<Dispatcher>& dispatcher0,
+      const scoped_refptr<Dispatcher>& dispatcher1);
+
+  // Adds the given vector of dispatchers (of size at most
+  // |kMaxMessageNumHandles|). |handles| must point to an array of size at least
+  // |dispatchers.size()|. Unlike the other |AddDispatcher...()| functions, some
+  // of the dispatchers may be invalid (null). Returns true on success and false
+  // on failure (if the handle table is full), in which case it leaves
+  // |handles[...]| untouched (and all dispatchers unadded).
+  bool AddDispatcherVector(const DispatcherVector& dispatchers,
+                           MojoHandle* handles);
+
+  // Tries to mark the given handles as busy and start transport on them (i.e.,
+  // take their dispatcher locks); |transports| must be sized to contain
+  // |num_handles| elements. On failure, returns them to their original
+  // (non-busy, unlocked state).
+  MojoResult MarkBusyAndStartTransport(
+      MojoHandle disallowed_handle,
+      const MojoHandle* handles,
+      uint32_t num_handles,
+      std::vector<DispatcherTransport>* transports);
+
+  // Remove the given handles, which must all be present and which should have
+  // previously been marked busy by |MarkBusyAndStartTransport()|.
+  void RemoveBusyHandles(const MojoHandle* handles, uint32_t num_handles);
+
+  // Restores the given handles, which must all be present and which should have
+  // previously been marked busy by |MarkBusyAndStartTransport()|, to a non-busy
+  // state.
+  void RestoreBusyHandles(const MojoHandle* handles, uint32_t num_handles);
+
+ private:
+  friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+  // The |busy| member is used only to deal with functions (in particular
+  // |Core::WriteMessage()|) that want to hold on to a dispatcher and later
+  // remove it from the handle table, without holding on to the handle table
+  // lock.
+  //
+  // For example, if |Core::WriteMessage()| is called with a handle to be sent,
+  // (under the handle table lock) it must first check that that handle is not
+  // busy (if it is busy, then it fails with |MOJO_RESULT_BUSY|) and then marks
+  // it as busy. To avoid deadlock, it should also try to acquire the locks for
+  // all the dispatchers for the handles that it is sending (and fail with
+  // |MOJO_RESULT_BUSY| if the attempt fails). At this point, it can release the
+  // handle table lock.
+  //
+  // If |Core::Close()| is simultaneously called on that handle, it too checks
+  // if the handle is marked busy. If it is, it fails (with |MOJO_RESULT_BUSY|).
+  // This prevents |Core::WriteMessage()| from sending a handle that has been
+  // closed (or learning about this too late).
+  struct Entry {
+    Entry();
+    explicit Entry(const scoped_refptr<Dispatcher>& dispatcher);
+    ~Entry();
+
+    scoped_refptr<Dispatcher> dispatcher;
+    bool busy;
+  };
+  typedef base::hash_map<MojoHandle, Entry> HandleToEntryMap;
+
+  // Adds the given dispatcher to the handle table, not doing any size checks.
+  MojoHandle AddDispatcherNoSizeCheck(
+      const scoped_refptr<Dispatcher>& dispatcher);
+
+  HandleToEntryMap handle_to_entry_map_;
+  MojoHandle next_handle_;  // Invariant: never |MOJO_HANDLE_INVALID|.
+
+  DISALLOW_COPY_AND_ASSIGN(HandleTable);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_HANDLE_TABLE_H_
diff --git a/mojo/edk/system/local_data_pipe.cc b/mojo/edk/system/local_data_pipe.cc
new file mode 100644
index 0000000..8ee696c
--- /dev/null
+++ b/mojo/edk/system/local_data_pipe.cc
@@ -0,0 +1,341 @@
+// 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.
+
+// TODO(vtl): I currently potentially overflow in doing index calculations.
+// E.g., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but
+// their sum may not. This is bad and poses a security risk. (We're currently
+// saved by the limit on capacity -- the maximum size of the buffer, checked in
+// |DataPipe::ValidateOptions()|, is currently sufficiently small.)
+
+#include "mojo/edk/system/local_data_pipe.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/edk/system/constants.h"
+
+namespace mojo {
+namespace system {
+
+LocalDataPipe::LocalDataPipe(const MojoCreateDataPipeOptions& options)
+    : DataPipe(true, true, options), start_index_(0), current_num_bytes_(0) {
+  // Note: |buffer_| is lazily allocated, since a common case will be that one
+  // of the handles is immediately passed off to another process.
+}
+
+LocalDataPipe::~LocalDataPipe() {
+}
+
+void LocalDataPipe::ProducerCloseImplNoLock() {
+  // If the consumer is still open and we still have data, we have to keep the
+  // buffer around. Currently, we won't free it even if it empties later. (We
+  // could do this -- requiring a check on every read -- but that seems to be
+  // optimizing for the uncommon case.)
+  if (!consumer_open_no_lock() || !current_num_bytes_) {
+    // Note: There can only be a two-phase *read* (by the consumer) if we still
+    // have data.
+    DCHECK(!consumer_in_two_phase_read_no_lock());
+    DestroyBufferNoLock();
+  }
+}
+
+MojoResult LocalDataPipe::ProducerWriteDataImplNoLock(
+    UserPointer<const void> elements,
+    UserPointer<uint32_t> num_bytes,
+    uint32_t max_num_bytes_to_write,
+    uint32_t min_num_bytes_to_write) {
+  DCHECK_EQ(max_num_bytes_to_write % element_num_bytes(), 0u);
+  DCHECK_EQ(min_num_bytes_to_write % element_num_bytes(), 0u);
+  DCHECK_GT(max_num_bytes_to_write, 0u);
+  DCHECK(consumer_open_no_lock());
+
+  size_t num_bytes_to_write = 0;
+  if (may_discard()) {
+    if (min_num_bytes_to_write > capacity_num_bytes())
+      return MOJO_RESULT_OUT_OF_RANGE;
+
+    num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write),
+                                  capacity_num_bytes());
+    if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
+      // Discard as much as needed (discard oldest first).
+      MarkDataAsConsumedNoLock(num_bytes_to_write -
+                               (capacity_num_bytes() - current_num_bytes_));
+      // No need to wake up write waiters, since we're definitely going to leave
+      // the buffer full.
+    }
+  } else {
+    if (min_num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
+      // Don't return "should wait" since you can't wait for a specified amount
+      // of data.
+      return MOJO_RESULT_OUT_OF_RANGE;
+    }
+
+    num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write),
+                                  capacity_num_bytes() - current_num_bytes_);
+  }
+  if (num_bytes_to_write == 0)
+    return MOJO_RESULT_SHOULD_WAIT;
+
+  // The amount we can write in our first |memcpy()|.
+  size_t num_bytes_to_write_first =
+      std::min(num_bytes_to_write, GetMaxNumBytesToWriteNoLock());
+  // Do the first (and possibly only) |memcpy()|.
+  size_t first_write_index =
+      (start_index_ + current_num_bytes_) % capacity_num_bytes();
+  EnsureBufferNoLock();
+  elements.GetArray(buffer_.get() + first_write_index,
+                    num_bytes_to_write_first);
+
+  if (num_bytes_to_write_first < num_bytes_to_write) {
+    // The "second write index" is zero.
+    elements.At(num_bytes_to_write_first)
+        .GetArray(buffer_.get(), num_bytes_to_write - num_bytes_to_write_first);
+  }
+
+  current_num_bytes_ += num_bytes_to_write;
+  DCHECK_LE(current_num_bytes_, capacity_num_bytes());
+  num_bytes.Put(static_cast<uint32_t>(num_bytes_to_write));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock(
+    UserPointer<void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    uint32_t min_num_bytes_to_write) {
+  DCHECK(consumer_open_no_lock());
+
+  // The index we need to start writing at.
+  size_t write_index =
+      (start_index_ + current_num_bytes_) % capacity_num_bytes();
+
+  size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock();
+  if (min_num_bytes_to_write > max_num_bytes_to_write) {
+    // In "may discard" mode, we can always write from the write index to the
+    // end of the buffer.
+    if (may_discard() &&
+        min_num_bytes_to_write <= capacity_num_bytes() - write_index) {
+      // To do so, we need to discard an appropriate amount of data.
+      // We should only reach here if the start index is after the write index!
+      DCHECK_GE(start_index_, write_index);
+      DCHECK_GT(min_num_bytes_to_write - max_num_bytes_to_write, 0u);
+      MarkDataAsConsumedNoLock(min_num_bytes_to_write - max_num_bytes_to_write);
+      max_num_bytes_to_write = min_num_bytes_to_write;
+    } else {
+      // Don't return "should wait" since you can't wait for a specified amount
+      // of data.
+      return MOJO_RESULT_OUT_OF_RANGE;
+    }
+  }
+
+  // Don't go into a two-phase write if there's no room.
+  if (max_num_bytes_to_write == 0)
+    return MOJO_RESULT_SHOULD_WAIT;
+
+  EnsureBufferNoLock();
+  buffer.Put(buffer_.get() + write_index);
+  buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_write));
+  set_producer_two_phase_max_num_bytes_written_no_lock(
+      static_cast<uint32_t>(max_num_bytes_to_write));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock(
+    uint32_t num_bytes_written) {
+  DCHECK_LE(num_bytes_written,
+            producer_two_phase_max_num_bytes_written_no_lock());
+  current_num_bytes_ += num_bytes_written;
+  DCHECK_LE(current_num_bytes_, capacity_num_bytes());
+  set_producer_two_phase_max_num_bytes_written_no_lock(0);
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateImplNoLock()
+    const {
+  HandleSignalsState rv;
+  if (consumer_open_no_lock()) {
+    if ((may_discard() || current_num_bytes_ < capacity_num_bytes()) &&
+        !producer_in_two_phase_write_no_lock())
+      rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+  }
+  return rv;
+}
+
+void LocalDataPipe::ConsumerCloseImplNoLock() {
+  // If the producer is around and in a two-phase write, we have to keep the
+  // buffer around. (We then don't free it until the producer is closed. This
+  // could be rectified, but again seems like optimizing for the uncommon case.)
+  if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock())
+    DestroyBufferNoLock();
+  current_num_bytes_ = 0;
+}
+
+MojoResult LocalDataPipe::ConsumerReadDataImplNoLock(
+    UserPointer<void> elements,
+    UserPointer<uint32_t> num_bytes,
+    uint32_t max_num_bytes_to_read,
+    uint32_t min_num_bytes_to_read) {
+  DCHECK_EQ(max_num_bytes_to_read % element_num_bytes(), 0u);
+  DCHECK_EQ(min_num_bytes_to_read % element_num_bytes(), 0u);
+  DCHECK_GT(max_num_bytes_to_read, 0u);
+
+  if (min_num_bytes_to_read > current_num_bytes_) {
+    // Don't return "should wait" since you can't wait for a specified amount of
+    // data.
+    return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  size_t num_bytes_to_read =
+      std::min(static_cast<size_t>(max_num_bytes_to_read), current_num_bytes_);
+  if (num_bytes_to_read == 0) {
+    return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  // The amount we can read in our first |memcpy()|.
+  size_t num_bytes_to_read_first =
+      std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock());
+  elements.PutArray(buffer_.get() + start_index_, num_bytes_to_read_first);
+
+  if (num_bytes_to_read_first < num_bytes_to_read) {
+    // The "second read index" is zero.
+    elements.At(num_bytes_to_read_first)
+        .PutArray(buffer_.get(), num_bytes_to_read - num_bytes_to_read_first);
+  }
+
+  MarkDataAsConsumedNoLock(num_bytes_to_read);
+  num_bytes.Put(static_cast<uint32_t>(num_bytes_to_read));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock(
+    UserPointer<uint32_t> num_bytes,
+    uint32_t max_num_bytes_to_discard,
+    uint32_t min_num_bytes_to_discard) {
+  DCHECK_EQ(max_num_bytes_to_discard % element_num_bytes(), 0u);
+  DCHECK_EQ(min_num_bytes_to_discard % element_num_bytes(), 0u);
+  DCHECK_GT(max_num_bytes_to_discard, 0u);
+
+  if (min_num_bytes_to_discard > current_num_bytes_) {
+    // Don't return "should wait" since you can't wait for a specified amount of
+    // data.
+    return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  // Be consistent with other operations; error if no data available.
+  if (current_num_bytes_ == 0) {
+    return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  size_t num_bytes_to_discard = std::min(
+      static_cast<size_t>(max_num_bytes_to_discard), current_num_bytes_);
+  MarkDataAsConsumedNoLock(num_bytes_to_discard);
+  num_bytes.Put(static_cast<uint32_t>(num_bytes_to_discard));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock(
+    UserPointer<uint32_t> num_bytes) {
+  // Note: This cast is safe, since the capacity fits into a |uint32_t|.
+  num_bytes.Put(static_cast<uint32_t>(current_num_bytes_));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock(
+    UserPointer<const void*> buffer,
+    UserPointer<uint32_t> buffer_num_bytes,
+    uint32_t min_num_bytes_to_read) {
+  size_t max_num_bytes_to_read = GetMaxNumBytesToReadNoLock();
+  if (min_num_bytes_to_read > max_num_bytes_to_read) {
+    // Don't return "should wait" since you can't wait for a specified amount of
+    // data.
+    return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  // Don't go into a two-phase read if there's no data.
+  if (max_num_bytes_to_read == 0) {
+    return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
+                                   : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  buffer.Put(buffer_.get() + start_index_);
+  buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_read));
+  set_consumer_two_phase_max_num_bytes_read_no_lock(
+      static_cast<uint32_t>(max_num_bytes_to_read));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock(
+    uint32_t num_bytes_read) {
+  DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read_no_lock());
+  DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes());
+  MarkDataAsConsumedNoLock(num_bytes_read);
+  set_consumer_two_phase_max_num_bytes_read_no_lock(0);
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState LocalDataPipe::ConsumerGetHandleSignalsStateImplNoLock()
+    const {
+  HandleSignalsState rv;
+  if (current_num_bytes_ > 0) {
+    if (!consumer_in_two_phase_read_no_lock())
+      rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  } else if (producer_open_no_lock()) {
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  }
+  return rv;
+}
+
+void LocalDataPipe::EnsureBufferNoLock() {
+  DCHECK(producer_open_no_lock());
+  if (buffer_)
+    return;
+  buffer_.reset(static_cast<char*>(
+      base::AlignedAlloc(capacity_num_bytes(), kDataPipeBufferAlignmentBytes)));
+}
+
+void LocalDataPipe::DestroyBufferNoLock() {
+#ifndef NDEBUG
+  // Scribble on the buffer to help detect use-after-frees. (This also helps the
+  // unit test detect certain bugs without needing ASAN or similar.)
+  if (buffer_)
+    memset(buffer_.get(), 0xcd, capacity_num_bytes());
+#endif
+  buffer_.reset();
+}
+
+size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() {
+  size_t next_index = start_index_ + current_num_bytes_;
+  if (next_index >= capacity_num_bytes()) {
+    next_index %= capacity_num_bytes();
+    DCHECK_GE(start_index_, next_index);
+    DCHECK_EQ(start_index_ - next_index,
+              capacity_num_bytes() - current_num_bytes_);
+    return start_index_ - next_index;
+  }
+  return capacity_num_bytes() - next_index;
+}
+
+size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() {
+  if (start_index_ + current_num_bytes_ > capacity_num_bytes())
+    return capacity_num_bytes() - start_index_;
+  return current_num_bytes_;
+}
+
+void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) {
+  DCHECK_LE(num_bytes, current_num_bytes_);
+  start_index_ += num_bytes;
+  start_index_ %= capacity_num_bytes();
+  current_num_bytes_ -= num_bytes;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/local_data_pipe.h b/mojo/edk/system/local_data_pipe.h
new file mode 100644
index 0000000..8d3b75b
--- /dev/null
+++ b/mojo/edk/system/local_data_pipe.h
@@ -0,0 +1,92 @@
+// 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_EDK_SYSTEM_LOCAL_DATA_PIPE_H_
+#define MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/data_pipe.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// |LocalDataPipe| is a subclass that "implements" |DataPipe| for data pipes
+// whose producer and consumer are both local. This class is thread-safe (with
+// protection provided by |DataPipe|'s |lock_|.
+class MOJO_SYSTEM_IMPL_EXPORT LocalDataPipe : public DataPipe {
+ public:
+  // |validated_options| should be the output of |DataPipe::ValidateOptions()|.
+  // In particular: |struct_size| is ignored (so |validated_options| must be the
+  // current version of the struct) and |capacity_num_bytes| must be nonzero.
+  explicit LocalDataPipe(const MojoCreateDataPipeOptions& validated_options);
+
+ private:
+  friend class base::RefCountedThreadSafe<LocalDataPipe>;
+  virtual ~LocalDataPipe();
+
+  // |DataPipe| implementation:
+  virtual void ProducerCloseImplNoLock() override;
+  virtual MojoResult ProducerWriteDataImplNoLock(
+      UserPointer<const void> elements,
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_write,
+      uint32_t min_num_bytes_to_write) override;
+  virtual MojoResult ProducerBeginWriteDataImplNoLock(
+      UserPointer<void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      uint32_t min_num_bytes_to_write) override;
+  virtual MojoResult ProducerEndWriteDataImplNoLock(
+      uint32_t num_bytes_written) override;
+  virtual HandleSignalsState ProducerGetHandleSignalsStateImplNoLock()
+      const override;
+  virtual void ConsumerCloseImplNoLock() override;
+  virtual MojoResult ConsumerReadDataImplNoLock(
+      UserPointer<void> elements,
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_read,
+      uint32_t min_num_bytes_to_read) override;
+  virtual MojoResult ConsumerDiscardDataImplNoLock(
+      UserPointer<uint32_t> num_bytes,
+      uint32_t max_num_bytes_to_discard,
+      uint32_t min_num_bytes_to_discard) override;
+  virtual MojoResult ConsumerQueryDataImplNoLock(
+      UserPointer<uint32_t> num_bytes) override;
+  virtual MojoResult ConsumerBeginReadDataImplNoLock(
+      UserPointer<const void*> buffer,
+      UserPointer<uint32_t> buffer_num_bytes,
+      uint32_t min_num_bytes_to_read) override;
+  virtual MojoResult ConsumerEndReadDataImplNoLock(
+      uint32_t num_bytes_read) override;
+  virtual HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock()
+      const override;
+
+  void EnsureBufferNoLock();
+  void DestroyBufferNoLock();
+
+  // Get the maximum (single) write/read size right now (in number of elements);
+  // result fits in a |uint32_t|.
+  size_t GetMaxNumBytesToWriteNoLock();
+  size_t GetMaxNumBytesToReadNoLock();
+
+  // Marks the given number of bytes as consumed/discarded. |num_bytes| must be
+  // greater than |current_num_bytes_|.
+  void MarkDataAsConsumedNoLock(size_t num_bytes);
+
+  // The members below are protected by |DataPipe|'s |lock_|:
+  scoped_ptr<char, base::AlignedFreeDeleter> buffer_;
+  // Circular buffer.
+  size_t start_index_;
+  size_t current_num_bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalDataPipe);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_
diff --git a/mojo/edk/system/local_data_pipe_unittest.cc b/mojo/edk/system/local_data_pipe_unittest.cc
new file mode 100644
index 0000000..979b4d2
--- /dev/null
+++ b/mojo/edk/system/local_data_pipe_unittest.cc
@@ -0,0 +1,2003 @@
+// 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/edk/system/local_data_pipe.h"
+
+#include <string.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/data_pipe.h"
+#include "mojo/edk/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const uint32_t kSizeOfOptions =
+    static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions));
+
+// Validate options.
+TEST(LocalDataPipeTest, Creation) {
+  // Create using default options.
+  {
+    // Get default options.
+    MojoCreateDataPipeOptions default_options = {0};
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        DataPipe::ValidateCreateOptions(NullUserPointer(), &default_options));
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(default_options));
+    dp->ProducerClose();
+    dp->ConsumerClose();
+  }
+
+  // Create using non-default options.
+  {
+    const MojoCreateDataPipeOptions options = {
+        kSizeOfOptions,                           // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        1,                                        // |element_num_bytes|.
+        1000                                      // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions validated_options = {0};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                              &validated_options));
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    dp->ProducerClose();
+    dp->ConsumerClose();
+  }
+  {
+    const MojoCreateDataPipeOptions options = {
+        kSizeOfOptions,                           // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+        4,                                        // |element_num_bytes|.
+        4000                                      // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions validated_options = {0};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                              &validated_options));
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    dp->ProducerClose();
+    dp->ConsumerClose();
+  }
+  {
+    const MojoCreateDataPipeOptions options = {
+        kSizeOfOptions,                                  // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+        7,                                               // |element_num_bytes|.
+        7000000  // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions validated_options = {0};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                              &validated_options));
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    dp->ProducerClose();
+    dp->ConsumerClose();
+  }
+  // Default capacity.
+  {
+    const MojoCreateDataPipeOptions options = {
+        kSizeOfOptions,                                  // |struct_size|.
+        MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+        100,                                             // |element_num_bytes|.
+        0  // |capacity_num_bytes|.
+    };
+    MojoCreateDataPipeOptions validated_options = {0};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                              &validated_options));
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    dp->ProducerClose();
+    dp->ConsumerClose();
+  }
+}
+
+TEST(LocalDataPipeTest, SimpleReadWrite) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      1000 * sizeof(int32_t)                    // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  int32_t elements[10] = {0};
+  uint32_t num_bytes = 0;
+
+  // Try reading; nothing there yet.
+  num_bytes = static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_SHOULD_WAIT,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+
+  // Query; nothing there yet.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Discard; nothing there yet.
+  num_bytes = static_cast<uint32_t>(5u * sizeof(elements[0]));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), false));
+
+  // Read with invalid |num_bytes|.
+  num_bytes = sizeof(elements[0]) + 1;
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+
+  // Write two elements.
+  elements[0] = 123;
+  elements[1] = 456;
+  num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  // It should have written everything (even without "all or none").
+  EXPECT_EQ(2u * sizeof(elements[0]), num_bytes);
+
+  // Query.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(2 * sizeof(elements[0]), num_bytes);
+
+  // Read one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(1u * sizeof(elements[0]), num_bytes);
+  EXPECT_EQ(123, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Query.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1 * sizeof(elements[0]), num_bytes);
+
+  // Try to read two elements, with "all or none".
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(-1, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Try to read two elements, without "all or none".
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(456, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Query.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+// Note: The "basic" waiting tests test that the "wait states" are correct in
+// various situations; they don't test that waiters are properly awoken on state
+// changes. (For that, we need to use multiple threads.)
+TEST(LocalDataPipeTest, BasicProducerWaiting) {
+  // Note: We take advantage of the fact that for |LocalDataPipe|, capacities
+  // are strict maximums. This is not guaranteed by the API.
+
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      2 * sizeof(int32_t)                       // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+  Waiter waiter;
+  uint32_t context = 0;
+  HandleSignalsState hss;
+
+  // Never readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Already writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 34, &hss));
+
+  // Write two elements.
+  int32_t elements[2] = {123, 456};
+  uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  true));
+  EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+
+  // Adding a waiter should now succeed.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 56, nullptr));
+  // And it shouldn't be writable yet.
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Do it again.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 78, nullptr));
+
+  // Read one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  EXPECT_EQ(123, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Waiting should now succeed.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+  EXPECT_EQ(78u, context);
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Try writing, using a two-phase write.
+  void* buffer = nullptr;
+  num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerBeginWriteData(
+                MakeUserPointer(&buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(buffer);
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+  static_cast<int32_t*>(buffer)[0] = 789;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerEndWriteData(
+                static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+  // Add a waiter.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 90, nullptr));
+
+  // Read one element, using a two-phase read.
+  const void* read_buffer = nullptr;
+  num_bytes = 0u;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(read_buffer);
+  // Since we only read one element (after having written three in all), the
+  // two-phase read should only allow us to read one. This checks an
+  // implementation detail!
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  EXPECT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerEndReadData(static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+  // Waiting should succeed.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+  EXPECT_EQ(90u, context);
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Write one element.
+  elements[0] = 123;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+  // Add a waiter.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12, nullptr));
+
+  // Close the consumer.
+  dp->ConsumerClose();
+
+  // It should now be never-writable.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context));
+  EXPECT_EQ(12u, context);
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  dp->ProducerClose();
+}
+
+TEST(LocalDataPipeTest, BasicConsumerWaiting) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      1000 * sizeof(int32_t)                    // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    Waiter waiter;
+    uint32_t context = 0;
+    HandleSignalsState hss;
+
+    // Never writable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_FAILED_PRECONDITION,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12, &hss));
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Not yet readable.
+    waiter.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerAddWaiter(
+                  &waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, nullptr));
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+    hss = HandleSignalsState();
+    dp->ConsumerRemoveWaiter(&waiter, &hss);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Write two elements.
+    int32_t elements[2] = {123, 456};
+    uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(elements),
+                                    MakeUserPointer(&num_bytes),
+                                    true));
+
+    // Should already be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 56, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Discard one element.
+    num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+    EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+    // Should still be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 78, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read one element.
+    elements[0] = -1;
+    elements[1] = -1;
+    num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerReadData(
+            UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+    EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+    EXPECT_EQ(456, elements[0]);
+    EXPECT_EQ(-1, elements[1]);
+
+    // Adding a waiter should now succeed.
+    waiter.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerAddWaiter(
+                  &waiter, MOJO_HANDLE_SIGNAL_READABLE, 90, nullptr));
+
+    // Write one element.
+    elements[0] = 789;
+    elements[1] = -1;
+    num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(elements),
+                                    MakeUserPointer(&num_bytes),
+                                    true));
+
+    // Waiting should now succeed.
+    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+    EXPECT_EQ(90u, context);
+    hss = HandleSignalsState();
+    dp->ConsumerRemoveWaiter(&waiter, &hss);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Close the producer.
+    dp->ProducerClose();
+
+    // Should still be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read one element.
+    elements[0] = -1;
+    elements[1] = -1;
+    num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerReadData(
+            UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+    EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+    EXPECT_EQ(789, elements[0]);
+    EXPECT_EQ(-1, elements[1]);
+
+    // Should be never-readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_FAILED_PRECONDITION,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, &hss));
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    dp->ConsumerClose();
+  }
+
+  // Test with two-phase APIs and closing the producer with an active consumer
+  // waiter.
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+    Waiter waiter;
+    uint32_t context = 0;
+    HandleSignalsState hss;
+
+    // Write two elements.
+    int32_t* elements = nullptr;
+    void* buffer = nullptr;
+    // Request room for three (but we'll only write two).
+    uint32_t num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerBeginWriteData(
+                  MakeUserPointer(&buffer), MakeUserPointer(&num_bytes), true));
+    EXPECT_TRUE(buffer);
+    EXPECT_GE(num_bytes, static_cast<uint32_t>(3u * sizeof(elements[0])));
+    elements = static_cast<int32_t*>(buffer);
+    elements[0] = 123;
+    elements[1] = 456;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerEndWriteData(
+                  static_cast<uint32_t>(2u * sizeof(elements[0]))));
+
+    // Should already be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read one element.
+    // Request two in all-or-none mode, but only read one.
+    const void* read_buffer = nullptr;
+    num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerBeginReadData(
+            MakeUserPointer(&read_buffer), MakeUserPointer(&num_bytes), true));
+    EXPECT_TRUE(read_buffer);
+    EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+    const int32_t* read_elements = static_cast<const int32_t*>(read_buffer);
+    EXPECT_EQ(123, read_elements[0]);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerEndReadData(
+                  static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+    // Should still be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read one element.
+    // Request three, but not in all-or-none mode.
+    read_buffer = nullptr;
+    num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerBeginReadData(
+            MakeUserPointer(&read_buffer), MakeUserPointer(&num_bytes), false));
+    EXPECT_TRUE(read_buffer);
+    EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+    read_elements = static_cast<const int32_t*>(read_buffer);
+    EXPECT_EQ(456, read_elements[0]);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerEndReadData(
+                  static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+    // Adding a waiter should now succeed.
+    waiter.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerAddWaiter(
+                  &waiter, MOJO_HANDLE_SIGNAL_READABLE, 56, nullptr));
+
+    // Close the producer.
+    dp->ProducerClose();
+
+    // Should be never-readable.
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context));
+    EXPECT_EQ(56u, context);
+    hss = HandleSignalsState();
+    dp->ConsumerRemoveWaiter(&waiter, &hss);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    dp->ConsumerClose();
+  }
+}
+
+// Tests that data pipes aren't writable/readable during two-phase writes/reads.
+TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      1000 * sizeof(int32_t)                    // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+  Waiter waiter;
+  HandleSignalsState hss;
+
+  // It should be writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  uint32_t num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+  void* write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(write_ptr);
+  EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+  // At this point, it shouldn't be writable.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 1, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // It shouldn't be readable yet either.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 2, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ConsumerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  static_cast<int32_t*>(write_ptr)[0] = 123;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerEndWriteData(static_cast<uint32_t>(1u * sizeof(int32_t))));
+
+  // It should be writable again.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 3, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // And readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 4, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Start another two-phase write and check that it's readable even in the
+  // middle of it.
+  num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(write_ptr);
+  EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+  // It should be readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // End the two-phase write without writing anything.
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u));
+
+  // Start a two-phase read.
+  num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+  const void* read_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(read_ptr);
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes);
+
+  // At this point, it should still be writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // But not readable.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ConsumerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // End the two-phase read without reading anything.
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u));
+
+  // It should be readable again.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 8, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+// Test that a "may discard" data pipe is writable even when it's full.
+TEST(LocalDataPipeTest, BasicMayDiscardWaiting) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                                  // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),          // |element_num_bytes|.
+      1 * sizeof(int32_t)                              // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+  Waiter waiter;
+  HandleSignalsState hss;
+
+  // Writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Not readable.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ConsumerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  uint32_t num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+  int32_t element = 123;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(&element),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+
+  // Still writable (even though it's full).
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 2, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // Now readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 3, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Overwrite that element.
+  num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+  element = 456;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(&element),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+
+  // Still writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // And still readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Read that element.
+  num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+  element = 0;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(&element), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+  EXPECT_EQ(456, element);
+
+  // Still writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
+  // No longer readable.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ConsumerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+void Seq(int32_t start, size_t count, int32_t* out) {
+  for (size_t i = 0; i < count; i++)
+    out[i] = start + static_cast<int32_t>(i);
+}
+
+TEST(LocalDataPipeTest, MayDiscard) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                                  // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),          // |element_num_bytes|.
+      10 * sizeof(int32_t)                             // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  int32_t buffer[100] = {0};
+  uint32_t num_bytes = 0;
+
+  num_bytes = 20u * sizeof(int32_t);
+  Seq(0, arraysize(buffer), buffer);
+  // Try writing more than capacity. (This test relies on the implementation
+  // enforcing the capacity strictly.)
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+  // Read half of what we wrote.
+  num_bytes = 5u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  int32_t expected_buffer[100];
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  Seq(0, 5u, expected_buffer);
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+  // Internally, a circular buffer would now look like:
+  //   -, -, -, -, -, 5, 6, 7, 8, 9
+
+  // Write a bit more than the space that's available.
+  num_bytes = 8u * sizeof(int32_t);
+  Seq(100, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(8u * sizeof(int32_t), num_bytes);
+  // Internally, a circular buffer would now look like:
+  //   100, 101, 102, 103, 104, 105, 106, 107, 8, 9
+
+  // Read half of what's available.
+  num_bytes = 5u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  expected_buffer[0] = 8;
+  expected_buffer[1] = 9;
+  expected_buffer[2] = 100;
+  expected_buffer[3] = 101;
+  expected_buffer[4] = 102;
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+  // Internally, a circular buffer would now look like:
+  //   -, -, -, 103, 104, 105, 106, 107, -, -
+
+  // Write one integer.
+  num_bytes = 1u * sizeof(int32_t);
+  Seq(200, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+  // Internally, a circular buffer would now look like:
+  //   -, -, -, 103, 104, 105, 106, 107, 200, -
+
+  // Write five more.
+  num_bytes = 5u * sizeof(int32_t);
+  Seq(300, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  // Internally, a circular buffer would now look like:
+  //   301, 302, 303, 304, 104, 105, 106, 107, 200, 300
+
+  // Read it all.
+  num_bytes = sizeof(buffer);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  expected_buffer[0] = 104;
+  expected_buffer[1] = 105;
+  expected_buffer[2] = 106;
+  expected_buffer[3] = 107;
+  expected_buffer[4] = 200;
+  expected_buffer[5] = 300;
+  expected_buffer[6] = 301;
+  expected_buffer[7] = 302;
+  expected_buffer[8] = 303;
+  expected_buffer[9] = 304;
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Test two-phase writes, including in all-or-none mode.
+  // Note: Again, the following depends on an implementation detail -- namely
+  // that the write pointer will point at the 5th element of the buffer (and the
+  // buffer has exactly the capacity requested).
+
+  num_bytes = 0u;
+  void* write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_TRUE(write_ptr);
+  EXPECT_EQ(6u * sizeof(int32_t), num_bytes);
+  Seq(400, 6, static_cast<int32_t*>(write_ptr));
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(6u * sizeof(int32_t)));
+  // Internally, a circular buffer would now look like:
+  //   -, -, -, -, 400, 401, 402, 403, 404, 405
+
+  // |ProducerBeginWriteData()| ignores |*num_bytes| except in "all-or-none"
+  // mode.
+  num_bytes = 6u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(4u * sizeof(int32_t), num_bytes);
+  static_cast<int32_t*>(write_ptr)[0] = 500;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(1u * sizeof(int32_t)));
+  // Internally, a circular buffer would now look like:
+  //   500, -, -, -, 400, 401, 402, 403, 404, 405
+
+  // Requesting a 10-element buffer in all-or-none mode fails at this point.
+  num_bytes = 10u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+
+  // But requesting, say, a 5-element (up to 9, really) buffer should be okay.
+  // It will discard two elements.
+  num_bytes = 5u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  // Only write 4 elements though.
+  Seq(600, 4, static_cast<int32_t*>(write_ptr));
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(4u * sizeof(int32_t)));
+  // Internally, a circular buffer would now look like:
+  //   500, 600, 601, 602, 603, -, 402, 403, 404, 405
+
+  // Do this again. Make sure we can get a buffer all the way out to the end of
+  // the internal buffer.
+  num_bytes = 5u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  // Only write 3 elements though.
+  Seq(700, 3, static_cast<int32_t*>(write_ptr));
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(3u * sizeof(int32_t)));
+  // Internally, a circular buffer would now look like:
+  //   500, 600, 601, 602, 603, 700, 701, 702, -, -
+
+  // Read everything.
+  num_bytes = sizeof(buffer);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(8u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  expected_buffer[0] = 500;
+  expected_buffer[1] = 600;
+  expected_buffer[2] = 601;
+  expected_buffer[3] = 602;
+  expected_buffer[4] = 603;
+  expected_buffer[5] = 700;
+  expected_buffer[6] = 701;
+  expected_buffer[7] = 702;
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, AllOrNone) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      10 * sizeof(int32_t)                      // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // Try writing way too much.
+  uint32_t num_bytes = 20u * sizeof(int32_t);
+  int32_t buffer[100];
+  Seq(0, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+
+  // Should still be empty.
+  num_bytes = ~0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Write some data.
+  num_bytes = 5u * sizeof(int32_t);
+  Seq(100, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  // Half full.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  // Too much.
+  num_bytes = 6u * sizeof(int32_t);
+  Seq(200, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+
+  // Try reading too much.
+  num_bytes = 11u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  int32_t expected_buffer[100];
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much.
+  num_bytes = 11u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+
+  // Just a little.
+  num_bytes = 2u * sizeof(int32_t);
+  Seq(300, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+
+  // Just right.
+  num_bytes = 3u * sizeof(int32_t);
+  Seq(400, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(3u * sizeof(int32_t), num_bytes);
+
+  // Exactly full.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+  // Read half.
+  num_bytes = 5u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  Seq(100, 5, expected_buffer);
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try reading too much again.
+  num_bytes = 6u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much again.
+  num_bytes = 6u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+
+  // Discard a little.
+  num_bytes = 2u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+
+  // Three left.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(3u * sizeof(int32_t), num_bytes);
+
+  // Close the producer, then test producer-closed cases.
+  dp->ProducerClose();
+
+  // Try reading too much; "failed precondition" since the producer is closed.
+  num_bytes = 4u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much; "failed precondition" again.
+  num_bytes = 4u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+
+  // Read a little.
+  num_bytes = 2u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  Seq(400, 2, expected_buffer);
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Discard the remaining element.
+  num_bytes = 1u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Empty again.
+  num_bytes = ~0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, AllOrNoneMayDiscard) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                                  // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),          // |element_num_bytes|.
+      10 * sizeof(int32_t)                             // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // Try writing way too much.
+  uint32_t num_bytes = 20u * sizeof(int32_t);
+  int32_t buffer[100];
+  Seq(0, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+
+  // Write some stuff.
+  num_bytes = 5u * sizeof(int32_t);
+  Seq(100, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  // Write lots of stuff (discarding all but "104").
+  num_bytes = 9u * sizeof(int32_t);
+  Seq(200, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(9u * sizeof(int32_t), num_bytes);
+
+  // Read one.
+  num_bytes = 1u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+  int32_t expected_buffer[100];
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  expected_buffer[0] = 104;
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try reading too many.
+  num_bytes = 10u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too many.
+  num_bytes = 10u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+
+  // Discard a bunch.
+  num_bytes = 4u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true));
+
+  // Half full.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  // Write as much as possible.
+  num_bytes = 10u * sizeof(int32_t);
+  Seq(300, arraysize(buffer), buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+  // Read everything.
+  num_bytes = 10u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerReadData(
+                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+  Seq(300, 10, expected_buffer);
+  EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Note: All-or-none two-phase writes on a "may discard" data pipe are tested
+  // in LocalDataPipeTest.MayDiscard.
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, TwoPhaseAllOrNone) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      10 * sizeof(int32_t)                      // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // Try writing way too much (two-phase).
+  uint32_t num_bytes = 20u * sizeof(int32_t);
+  void* write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Try writing an amount which isn't a multiple of the element size
+  // (two-phase).
+  static_assert(sizeof(int32_t) > 1u, "Wow! int32_t's have size 1");
+  num_bytes = 1u;
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Try reading way too much (two-phase).
+  num_bytes = 20u * sizeof(int32_t);
+  const void* read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Write half (two-phase).
+  num_bytes = 5u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+  // May provide more space than requested.
+  EXPECT_GE(num_bytes, 5u * sizeof(int32_t));
+  EXPECT_TRUE(write_ptr);
+  Seq(0, 5, static_cast<int32_t*>(write_ptr));
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(5u * sizeof(int32_t)));
+
+  // Try reading an amount which isn't a multiple of the element size
+  // (two-phase).
+  num_bytes = 1u;
+  read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Read one (two-phase).
+  num_bytes = 1u * sizeof(int32_t);
+  read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+  EXPECT_GE(num_bytes, 1u * sizeof(int32_t));
+  EXPECT_EQ(0, static_cast<const int32_t*>(read_ptr)[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(1u * sizeof(int32_t)));
+
+  // We should have four left, leaving room for six.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(4u * sizeof(int32_t), num_bytes);
+
+  // Assuming a tight circular buffer of the specified capacity, we can't do a
+  // two-phase write of six now.
+  num_bytes = 6u * sizeof(int32_t);
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OUT_OF_RANGE,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Write six elements (simple), filling the buffer.
+  num_bytes = 6u * sizeof(int32_t);
+  int32_t buffer[100];
+  Seq(100, 6, buffer);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerWriteData(
+          UserPointer<const void>(buffer), MakeUserPointer(&num_bytes), true));
+  EXPECT_EQ(6u * sizeof(int32_t), num_bytes);
+
+  // We have ten.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+  // But a two-phase read of ten should fail.
+  num_bytes = 10u * sizeof(int32_t);
+  read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+
+  // Close the producer.
+  dp->ProducerClose();
+
+  // A two-phase read of nine should work.
+  num_bytes = 9u * sizeof(int32_t);
+  read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+  EXPECT_GE(num_bytes, 9u * sizeof(int32_t));
+  EXPECT_EQ(1, static_cast<const int32_t*>(read_ptr)[0]);
+  EXPECT_EQ(2, static_cast<const int32_t*>(read_ptr)[1]);
+  EXPECT_EQ(3, static_cast<const int32_t*>(read_ptr)[2]);
+  EXPECT_EQ(4, static_cast<const int32_t*>(read_ptr)[3]);
+  EXPECT_EQ(100, static_cast<const int32_t*>(read_ptr)[4]);
+  EXPECT_EQ(101, static_cast<const int32_t*>(read_ptr)[5]);
+  EXPECT_EQ(102, static_cast<const int32_t*>(read_ptr)[6]);
+  EXPECT_EQ(103, static_cast<const int32_t*>(read_ptr)[7]);
+  EXPECT_EQ(104, static_cast<const int32_t*>(read_ptr)[8]);
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(9u * sizeof(int32_t)));
+
+  // A two-phase read of two should fail, with "failed precondition".
+  num_bytes = 2u * sizeof(int32_t);
+  read_ptr = nullptr;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            dp->ConsumerBeginReadData(
+                MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), true));
+
+  dp->ConsumerClose();
+}
+
+// Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads,
+// respectively, as much as possible, even if it has to "wrap around" the
+// internal circular buffer. (Note that the two-phase write and read do not do
+// this.)
+TEST(LocalDataPipeTest, WrapAround) {
+  unsigned char test_data[1000];
+  for (size_t i = 0; i < arraysize(test_data); i++)
+    test_data[i] = static_cast<unsigned char>(i);
+
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      1u,                                       // |element_num_bytes|.
+      100u                                      // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+  // This test won't be valid if |ValidateCreateOptions()| decides to give the
+  // pipe more space.
+  ASSERT_EQ(100u, validated_options.capacity_num_bytes);
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // Write 20 bytes.
+  uint32_t num_bytes = 20u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(&test_data[0]),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(20u, num_bytes);
+
+  // Read 10 bytes.
+  unsigned char read_buffer[1000] = {0};
+  num_bytes = 10u;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(read_buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(10u, num_bytes);
+  EXPECT_EQ(0, memcmp(read_buffer, &test_data[0], 10u));
+
+  // Check that a two-phase write can now only write (at most) 80 bytes. (This
+  // checks an implementation detail; this behavior is not guaranteed, but we
+  // need it for this test.)
+  void* write_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr),
+                                       MakeUserPointer(&num_bytes),
+                                       false));
+  EXPECT_TRUE(write_buffer_ptr);
+  EXPECT_EQ(80u, num_bytes);
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u));
+
+  // Write as much data as we can (using |ProducerWriteData()|). We should write
+  // 90 bytes.
+  num_bytes = 200u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(&test_data[20]),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(90u, num_bytes);
+
+  // Check that a two-phase read can now only read (at most) 90 bytes. (This
+  // checks an implementation detail; this behavior is not guaranteed, but we
+  // need it for this test.)
+  const void* read_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr),
+                                      MakeUserPointer(&num_bytes),
+                                      false));
+  EXPECT_TRUE(read_buffer_ptr);
+  EXPECT_EQ(90u, num_bytes);
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u));
+
+  // Read as much as possible (using |ConsumerReadData()|). We should read 100
+  // bytes.
+  num_bytes =
+      static_cast<uint32_t>(arraysize(read_buffer) * sizeof(read_buffer[0]));
+  memset(read_buffer, 0, num_bytes);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(
+          UserPointer<void>(read_buffer), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(100u, num_bytes);
+  EXPECT_EQ(0, memcmp(read_buffer, &test_data[10], 100u));
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+// Tests the behavior of closing the producer or consumer with respect to
+// writes and reads (simple and two-phase).
+TEST(LocalDataPipeTest, CloseWriteRead) {
+  const char kTestData[] = "hello world";
+  const uint32_t kTestDataSize = static_cast<uint32_t>(sizeof(kTestData));
+
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      1u,                                       // |element_num_bytes|.
+      1000u                                     // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  // Close producer first, then consumer.
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+    // Write some data, so we'll have something to read.
+    uint32_t num_bytes = kTestDataSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(kTestData),
+                                    MakeUserPointer(&num_bytes),
+                                    false));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Write it again, so we'll have something left over.
+    num_bytes = kTestDataSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(kTestData),
+                                    MakeUserPointer(&num_bytes),
+                                    false));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Start two-phase write.
+    void* write_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr),
+                                         MakeUserPointer(&num_bytes),
+                                         false));
+    EXPECT_TRUE(write_buffer_ptr);
+    EXPECT_GT(num_bytes, 0u);
+
+    // Start two-phase read.
+    const void* read_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr),
+                                        MakeUserPointer(&num_bytes),
+                                        false));
+    EXPECT_TRUE(read_buffer_ptr);
+    EXPECT_EQ(2u * kTestDataSize, num_bytes);
+
+    // Close the producer.
+    dp->ProducerClose();
+
+    // The consumer can finish its two-phase read.
+    EXPECT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize));
+    EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(kTestDataSize));
+
+    // And start another.
+    read_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr),
+                                        MakeUserPointer(&num_bytes),
+                                        false));
+    EXPECT_TRUE(read_buffer_ptr);
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Close the consumer, which cancels the two-phase read.
+    dp->ConsumerClose();
+  }
+
+  // Close consumer first, then producer.
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+    // Write some data, so we'll have something to read.
+    uint32_t num_bytes = kTestDataSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(kTestData),
+                                    MakeUserPointer(&num_bytes),
+                                    false));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Start two-phase write.
+    void* write_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr),
+                                         MakeUserPointer(&num_bytes),
+                                         false));
+    EXPECT_TRUE(write_buffer_ptr);
+    ASSERT_GT(num_bytes, kTestDataSize);
+
+    // Start two-phase read.
+    const void* read_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr),
+                                        MakeUserPointer(&num_bytes),
+                                        false));
+    EXPECT_TRUE(read_buffer_ptr);
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Close the consumer.
+    dp->ConsumerClose();
+
+    // Actually write some data. (Note: Premature freeing of the buffer would
+    // probably only be detected under ASAN or similar.)
+    memcpy(write_buffer_ptr, kTestData, kTestDataSize);
+    // Note: Even though the consumer has been closed, ending the two-phase
+    // write will report success.
+    EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(kTestDataSize));
+
+    // But trying to write should result in failure.
+    num_bytes = kTestDataSize;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              dp->ProducerWriteData(UserPointer<const void>(kTestData),
+                                    MakeUserPointer(&num_bytes),
+                                    false));
+
+    // As will trying to start another two-phase write.
+    write_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr),
+                                         MakeUserPointer(&num_bytes),
+                                         false));
+
+    dp->ProducerClose();
+  }
+
+  // Test closing the consumer first, then the producer, with an active
+  // two-phase write.
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+    // Start two-phase write.
+    void* write_buffer_ptr = nullptr;
+    uint32_t num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr),
+                                         MakeUserPointer(&num_bytes),
+                                         false));
+    EXPECT_TRUE(write_buffer_ptr);
+    ASSERT_GT(num_bytes, kTestDataSize);
+
+    dp->ConsumerClose();
+    dp->ProducerClose();
+  }
+
+  // Test closing the producer and then trying to read (with no data).
+  {
+    scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+    // Write some data, so we'll have something to read.
+    uint32_t num_bytes = kTestDataSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dp->ProducerWriteData(UserPointer<const void>(kTestData),
+                                    MakeUserPointer(&num_bytes),
+                                    false));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+
+    // Close the producer.
+    dp->ProducerClose();
+
+    // Read that data.
+    char buffer[1000];
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerReadData(
+            UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+    EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
+
+    // A second read should fail.
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(
+        MOJO_RESULT_FAILED_PRECONDITION,
+        dp->ConsumerReadData(
+            UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+
+    // A two-phase read should also fail.
+    const void* read_buffer_ptr = nullptr;
+    num_bytes = 0u;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr),
+                                        MakeUserPointer(&num_bytes),
+                                        false));
+
+    // Ditto for discard.
+    num_bytes = 10u;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), false));
+
+    dp->ConsumerClose();
+  }
+}
+
+TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      static_cast<uint32_t>(sizeof(int32_t)),   // |element_num_bytes|.
+      10 * sizeof(int32_t)                      // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // No data.
+  uint32_t num_bytes = 1000u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Try "ending" a two-phase write when one isn't active.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            dp->ProducerEndWriteData(1u * sizeof(int32_t)));
+
+  // Still no data.
+  num_bytes = 1000u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Try ending a two-phase write with an invalid amount (too much).
+  num_bytes = 0u;
+  void* write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dp->ProducerEndWriteData(num_bytes +
+                                     static_cast<uint32_t>(sizeof(int32_t))));
+
+  // But the two-phase write still ended.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u));
+
+  // Still no data.
+  num_bytes = 1000u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Try ending a two-phase write with an invalid amount (not a multiple of the
+  // element size).
+  num_bytes = 0u;
+  write_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerBeginWriteData(
+          MakeUserPointer(&write_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_GE(num_bytes, 1u);
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ProducerEndWriteData(1u));
+
+  // But the two-phase write still ended.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u));
+
+  // Still no data.
+  num_bytes = 1000u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(0u, num_bytes);
+
+  // Now write some data, so we'll be able to try reading.
+  int32_t element = 123;
+  num_bytes = 1u * sizeof(int32_t);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(&element),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+
+  // One element available.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Try "ending" a two-phase read when one isn't active.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            dp->ConsumerEndReadData(1u * sizeof(int32_t)));
+
+  // Still one element available.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Try ending a two-phase read with an invalid amount (too much).
+  num_bytes = 0u;
+  const void* read_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dp->ConsumerEndReadData(num_bytes +
+                                    static_cast<uint32_t>(sizeof(int32_t))));
+
+  // Still one element available.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Try ending a two-phase read with an invalid amount (not a multiple of the
+  // element size).
+  num_bytes = 0u;
+  read_ptr = nullptr;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+  EXPECT_EQ(123, static_cast<const int32_t*>(read_ptr)[0]);
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ConsumerEndReadData(1u));
+
+  // Still one element available.
+  num_bytes = 0u;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+// Tests that even with "may discard", the data won't change under a two-phase
+// read.
+// TODO(vtl): crbug.com/348644: We currently don't pass this. (There are two
+// related issues: First, we don't recognize that the data given to
+// |ConsumerBeginReadData()| isn't discardable until |ConsumerEndReadData()|,
+// and thus we erroneously allow |ProducerWriteData()| to succeed. Second, the
+// |ProducerWriteData()| then changes the data underneath the two-phase read.)
+TEST(LocalDataPipeTest, DISABLED_MayDiscardTwoPhaseConsistent) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                                  // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD,  // |flags|.
+      1,                                               // |element_num_bytes|.
+      2                                                // |capacity_num_bytes|.
+  };
+  MojoCreateDataPipeOptions validated_options = {0};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            DataPipe::ValidateCreateOptions(MakeUserPointer(&options),
+                                            &validated_options));
+
+  scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+  // Write some elements.
+  char elements[2] = {'a', 'b'};
+  uint32_t num_bytes = 2u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+  EXPECT_EQ(2u, num_bytes);
+
+  // Begin reading.
+  const void* read_ptr = nullptr;
+  num_bytes = 2u;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(2u, num_bytes);
+  EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]);
+  EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]);
+
+  // Try to write some more. But nothing should be discardable right now.
+  elements[0] = 'x';
+  elements[1] = 'y';
+  num_bytes = 2u;
+  // TODO(vtl): This should be:
+  //  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+  //            dp->ProducerWriteData(elements, &num_bytes, false));
+  // but we incorrectly think that the bytes being read are discardable. Letting
+  // this through reveals the significant consequence.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+
+  // Check that our read buffer hasn't changed underneath us.
+  EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]);
+  EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]);
+
+  // End reading.
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u));
+
+  // Now writing should succeed.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dp->ProducerWriteData(UserPointer<const void>(elements),
+                                  MakeUserPointer(&num_bytes),
+                                  false));
+
+  // And if we read, we should get the new values.
+  read_ptr = nullptr;
+  num_bytes = 2u;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerBeginReadData(
+          MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), false));
+  EXPECT_EQ(2u, num_bytes);
+  EXPECT_EQ('x', static_cast<const char*>(read_ptr)[0]);
+  EXPECT_EQ('y', static_cast<const char*>(read_ptr)[1]);
+
+  // End reading.
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u));
+
+  dp->ProducerClose();
+  dp->ConsumerClose();
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/local_message_pipe_endpoint.cc b/mojo/edk/system/local_message_pipe_endpoint.cc
new file mode 100644
index 0000000..124241e
--- /dev/null
+++ b/mojo/edk/system/local_message_pipe_endpoint.cc
@@ -0,0 +1,176 @@
+// 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/edk/system/local_message_pipe_endpoint.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/message_in_transit.h"
+
+namespace mojo {
+namespace system {
+
+LocalMessagePipeEndpoint::LocalMessagePipeEndpoint()
+    : is_open_(true), is_peer_open_(true) {
+}
+
+LocalMessagePipeEndpoint::~LocalMessagePipeEndpoint() {
+  DCHECK(!is_open_);
+  DCHECK(message_queue_.IsEmpty());  // Should be implied by not being open.
+}
+
+MessagePipeEndpoint::Type LocalMessagePipeEndpoint::GetType() const {
+  return kTypeLocal;
+}
+
+bool LocalMessagePipeEndpoint::OnPeerClose() {
+  DCHECK(is_open_);
+  DCHECK(is_peer_open_);
+
+  HandleSignalsState old_state = GetHandleSignalsState();
+  is_peer_open_ = false;
+  HandleSignalsState new_state = GetHandleSignalsState();
+
+  if (!new_state.equals(old_state))
+    waiter_list_.AwakeWaitersForStateChange(new_state);
+
+  return true;
+}
+
+void LocalMessagePipeEndpoint::EnqueueMessage(
+    scoped_ptr<MessageInTransit> message) {
+  DCHECK(is_open_);
+  DCHECK(is_peer_open_);
+
+  bool was_empty = message_queue_.IsEmpty();
+  message_queue_.AddMessage(message.Pass());
+  if (was_empty)
+    waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsState());
+}
+
+void LocalMessagePipeEndpoint::Close() {
+  DCHECK(is_open_);
+  is_open_ = false;
+  message_queue_.Clear();
+}
+
+void LocalMessagePipeEndpoint::CancelAllWaiters() {
+  DCHECK(is_open_);
+  waiter_list_.CancelAllWaiters();
+}
+
+MojoResult LocalMessagePipeEndpoint::ReadMessage(
+    UserPointer<void> bytes,
+    UserPointer<uint32_t> num_bytes,
+    DispatcherVector* dispatchers,
+    uint32_t* num_dispatchers,
+    MojoReadMessageFlags flags) {
+  DCHECK(is_open_);
+  DCHECK(!dispatchers || dispatchers->empty());
+
+  const uint32_t max_bytes = num_bytes.IsNull() ? 0 : num_bytes.Get();
+  const uint32_t max_num_dispatchers = num_dispatchers ? *num_dispatchers : 0;
+
+  if (message_queue_.IsEmpty()) {
+    return is_peer_open_ ? MOJO_RESULT_SHOULD_WAIT
+                         : MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  // TODO(vtl): If |flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD|, we could pop
+  // and release the lock immediately.
+  bool enough_space = true;
+  MessageInTransit* message = message_queue_.PeekMessage();
+  if (!num_bytes.IsNull())
+    num_bytes.Put(message->num_bytes());
+  if (message->num_bytes() <= max_bytes)
+    bytes.PutArray(message->bytes(), message->num_bytes());
+  else
+    enough_space = false;
+
+  if (DispatcherVector* queued_dispatchers = message->dispatchers()) {
+    if (num_dispatchers)
+      *num_dispatchers = static_cast<uint32_t>(queued_dispatchers->size());
+    if (enough_space) {
+      if (queued_dispatchers->empty()) {
+        // Nothing to do.
+      } else if (queued_dispatchers->size() <= max_num_dispatchers) {
+        DCHECK(dispatchers);
+        dispatchers->swap(*queued_dispatchers);
+      } else {
+        enough_space = false;
+      }
+    }
+  } else {
+    if (num_dispatchers)
+      *num_dispatchers = 0;
+  }
+
+  message = nullptr;
+
+  if (enough_space || (flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)) {
+    message_queue_.DiscardMessage();
+
+    // Now it's empty, thus no longer readable.
+    if (message_queue_.IsEmpty()) {
+      // It's currently not possible to wait for non-readability, but we should
+      // do the state change anyway.
+      waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsState());
+    }
+  }
+
+  if (!enough_space)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState LocalMessagePipeEndpoint::GetHandleSignalsState() const {
+  HandleSignalsState rv;
+  if (!message_queue_.IsEmpty()) {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  }
+  if (is_peer_open_) {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+    rv.satisfiable_signals |=
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+  }
+  return rv;
+}
+
+MojoResult LocalMessagePipeEndpoint::AddWaiter(
+    Waiter* waiter,
+    MojoHandleSignals signals,
+    uint32_t context,
+    HandleSignalsState* signals_state) {
+  DCHECK(is_open_);
+
+  HandleSignalsState state = GetHandleSignalsState();
+  if (state.satisfies(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+  if (!state.can_satisfy(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  waiter_list_.AddWaiter(waiter, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void LocalMessagePipeEndpoint::RemoveWaiter(Waiter* waiter,
+                                            HandleSignalsState* signals_state) {
+  DCHECK(is_open_);
+  waiter_list_.RemoveWaiter(waiter);
+  if (signals_state)
+    *signals_state = GetHandleSignalsState();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/local_message_pipe_endpoint.h b/mojo/edk/system/local_message_pipe_endpoint.h
new file mode 100644
index 0000000..ccff26d
--- /dev/null
+++ b/mojo/edk/system/local_message_pipe_endpoint.h
@@ -0,0 +1,64 @@
+// 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_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/message_in_transit_queue.h"
+#include "mojo/edk/system/message_pipe_endpoint.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+class MOJO_SYSTEM_IMPL_EXPORT LocalMessagePipeEndpoint
+    : public MessagePipeEndpoint {
+ public:
+  LocalMessagePipeEndpoint();
+  virtual ~LocalMessagePipeEndpoint();
+
+  // |MessagePipeEndpoint| implementation:
+  virtual Type GetType() const override;
+  virtual bool OnPeerClose() override;
+  virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) override;
+
+  // There's a dispatcher for |LocalMessagePipeEndpoint|s, so we have to
+  // implement/override these:
+  virtual void Close() override;
+  virtual void CancelAllWaiters() override;
+  virtual MojoResult ReadMessage(UserPointer<void> bytes,
+                                 UserPointer<uint32_t> num_bytes,
+                                 DispatcherVector* dispatchers,
+                                 uint32_t* num_dispatchers,
+                                 MojoReadMessageFlags flags) override;
+  virtual HandleSignalsState GetHandleSignalsState() const override;
+  virtual MojoResult AddWaiter(Waiter* waiter,
+                               MojoHandleSignals signals,
+                               uint32_t context,
+                               HandleSignalsState* signals_state) override;
+  virtual void RemoveWaiter(Waiter* waiter,
+                            HandleSignalsState* signals_state) override;
+
+  // This is only to be used by |MessagePipe|:
+  MessageInTransitQueue* message_queue() { return &message_queue_; }
+
+ private:
+  bool is_open_;
+  bool is_peer_open_;
+
+  // Queue of incoming messages.
+  MessageInTransitQueue message_queue_;
+  WaiterList waiter_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalMessagePipeEndpoint);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/mojo/edk/system/mapping_table.cc b/mojo/edk/system/mapping_table.cc
new file mode 100644
index 0000000..0a28a1d
--- /dev/null
+++ b/mojo/edk/system/mapping_table.cc
@@ -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.
+
+#include "mojo/edk/system/mapping_table.h"
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/constants.h"
+
+namespace mojo {
+namespace system {
+
+MappingTable::MappingTable() {
+}
+
+MappingTable::~MappingTable() {
+  // This should usually not be reached (the only instance should be owned by
+  // the singleton |Core|, which lives forever), except in tests.
+}
+
+MojoResult MappingTable::AddMapping(
+    scoped_ptr<embedder::PlatformSharedBufferMapping> mapping) {
+  DCHECK(mapping);
+
+  if (address_to_mapping_map_.size() >= kMaxMappingTableSize)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  uintptr_t address = reinterpret_cast<uintptr_t>(mapping->GetBase());
+  DCHECK(address_to_mapping_map_.find(address) ==
+         address_to_mapping_map_.end());
+  address_to_mapping_map_[address] = mapping.release();
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MappingTable::RemoveMapping(uintptr_t address) {
+  AddressToMappingMap::iterator it = address_to_mapping_map_.find(address);
+  if (it == address_to_mapping_map_.end())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  embedder::PlatformSharedBufferMapping* mapping_to_delete = it->second;
+  address_to_mapping_map_.erase(it);
+  delete mapping_to_delete;
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/mapping_table.h b/mojo/edk/system/mapping_table.h
new file mode 100644
index 0000000..d2812d8
--- /dev/null
+++ b/mojo/edk/system/mapping_table.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_EDK_SYSTEM_MAPPING_TABLE_H_
+#define MOJO_EDK_SYSTEM_MAPPING_TABLE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformSharedBufferMapping;
+}
+
+namespace system {
+
+class Core;
+
+// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
+// so it can be friended.
+namespace internal {
+bool ShutdownCheckNoLeaks(Core*);
+}
+
+// This class provides the (global) table of memory mappings (owned by |Core|),
+// which maps mapping base addresses to |PlatformSharedBufferMapping|s.
+//
+// This class is NOT thread-safe; locking is left to |Core|.
+class MOJO_SYSTEM_IMPL_EXPORT MappingTable {
+ public:
+  MappingTable();
+  ~MappingTable();
+
+  // Tries to add a mapping. (Takes ownership of the mapping in all cases; on
+  // failure, it will be destroyed.)
+  MojoResult AddMapping(
+      scoped_ptr<embedder::PlatformSharedBufferMapping> mapping);
+  MojoResult RemoveMapping(uintptr_t address);
+
+ private:
+  friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+  typedef base::hash_map<uintptr_t, embedder::PlatformSharedBufferMapping*>
+      AddressToMappingMap;
+  AddressToMappingMap address_to_mapping_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MappingTable);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MAPPING_TABLE_H_
diff --git a/mojo/edk/system/memory.cc b/mojo/edk/system/memory.cc
new file mode 100644
index 0000000..606c08f
--- /dev/null
+++ b/mojo/edk/system/memory.cc
@@ -0,0 +1,89 @@
+// 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/edk/system/memory.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace system {
+namespace internal {
+
+template <size_t alignment>
+bool IsAligned(const void* pointer) {
+  return reinterpret_cast<uintptr_t>(pointer) % alignment == 0;
+}
+
+// MSVS (2010, 2013) sometimes (on the stack) aligns, e.g., |int64_t|s (for
+// which |__alignof(int64_t)| is 8) to 4-byte boundaries. http://goo.gl/Y2n56T
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+template <>
+bool IsAligned<8>(const void* pointer) {
+  return reinterpret_cast<uintptr_t>(pointer) % 4 == 0;
+}
+#endif
+
+template <size_t size, size_t alignment>
+void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer(const void* pointer) {
+  CHECK(pointer && IsAligned<alignment>(pointer));
+}
+
+// Explicitly instantiate the sizes we need. Add instantiations as needed.
+template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<1, 1>(const void*);
+template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<4, 4>(const void*);
+template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 4>(const void*);
+template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 8>(const void*);
+
+template <size_t size, size_t alignment>
+void MOJO_SYSTEM_IMPL_EXPORT
+CheckUserPointerWithCount(const void* pointer, size_t count) {
+  CHECK_LE(count, std::numeric_limits<size_t>::max() / size);
+  CHECK(count == 0 || (pointer && IsAligned<alignment>(pointer)));
+}
+
+// Explicitly instantiate the sizes we need. Add instantiations as needed.
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithCount<1, 1>(const void*, size_t);
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithCount<4, 4>(const void*, size_t);
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithCount<8, 4>(const void*, size_t);
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithCount<8, 8>(const void*, size_t);
+
+template <size_t alignment>
+void CheckUserPointerWithSize(const void* pointer, size_t size) {
+  // TODO(vtl): If running in kernel mode, do a full verification. For now, just
+  // check that it's non-null and aligned. (A faster user mode implementation is
+  // also possible if this check is skipped.)
+  CHECK(size == 0 || (!!pointer && internal::IsAligned<alignment>(pointer)));
+}
+
+// Explicitly instantiate the sizes we need. Add instantiations as needed.
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithSize<1>(const void*, size_t);
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithSize<4>(const void*, size_t);
+// Whereas the other |Check...()| functions are usually used with integral typs
+// or arrays of integral types, this one is used with Options structs for which
+// alignment has been explicitly been specified (using |MOJO_ALIGNAS()|), which
+// MSVS *does* respect.
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+template <>
+void MOJO_SYSTEM_IMPL_EXPORT
+CheckUserPointerWithSize<8>(const void* pointer, size_t size) {
+  CHECK(size == 0 ||
+        (!!pointer && reinterpret_cast<uintptr_t>(pointer) % 8 == 0));
+}
+#else
+template void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithSize<8>(const void*, size_t);
+#endif
+
+}  // namespace internal
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/memory.h b/mojo/edk/system/memory.h
new file mode 100644
index 0000000..7b5b724
--- /dev/null
+++ b/mojo/edk/system/memory.h
@@ -0,0 +1,377 @@
+// 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_EDK_SYSTEM_MEMORY_H_
+#define MOJO_EDK_SYSTEM_MEMORY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>  // For |memcpy()|.
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace system {
+
+namespace internal {
+
+// Removes |const| from |T| (available as |remove_const<T>::type|):
+// TODO(vtl): Remove these once we have the C++11 |remove_const|.
+template <typename T>
+struct remove_const {
+  typedef T type;
+};
+template <typename T>
+struct remove_const<const T> {
+  typedef T type;
+};
+
+// Yields |(const) char| if |T| is |(const) void|, else |T|:
+template <typename T>
+struct VoidToChar {
+  typedef T type;
+};
+template <>
+struct VoidToChar<void> {
+  typedef char type;
+};
+template <>
+struct VoidToChar<const void> {
+  typedef const char type;
+};
+
+// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to
+// a buffer of the given size and alignment (both in bytes).
+template <size_t size, size_t alignment>
+void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer(const void* pointer);
+
+// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to
+// a buffer of |count| elements of the given size and alignment (both in bytes).
+template <size_t size, size_t alignment>
+void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithCount(const void* pointer, size_t count);
+
+// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to
+// a buffer of the given size and alignment (both in bytes).
+template <size_t alignment>
+void MOJO_SYSTEM_IMPL_EXPORT
+    CheckUserPointerWithSize(const void* pointer, size_t size);
+
+}  // namespace internal
+
+// Forward declarations so that they can be friended.
+template <typename Type>
+class UserPointerReader;
+template <typename Type>
+class UserPointerWriter;
+template <typename Type>
+class UserPointerReaderWriter;
+template <class Options>
+class UserOptionsReader;
+
+// Provides a convenient way to implicitly get null |UserPointer<Type>|s.
+struct NullUserPointer {};
+
+// Represents a user pointer to a single |Type| (which must be POD), for Mojo
+// primitive parameters.
+//
+// Use a const |Type| for in parameters, and non-const |Type|s for out and
+// in-out parameters (in which case the |Put()| method is available).
+template <typename Type>
+class UserPointer {
+ private:
+  typedef typename internal::VoidToChar<Type>::type NonVoidType;
+
+ public:
+  // Instead of explicitly using these constructors, you can often use
+  // |MakeUserPointer()| (or |NullUserPointer()| for null pointers). (The common
+  // exception is when you have, e.g., a |char*| and want to get a
+  // |UserPointer<void>|.)
+  UserPointer() : pointer_(nullptr) {}
+  explicit UserPointer(Type* pointer) : pointer_(pointer) {}
+  // Allow implicit conversion from the "null user pointer".
+  UserPointer(NullUserPointer) : pointer_(nullptr) {}
+  ~UserPointer() {}
+
+  // Allow assignment from the "null user pointer".
+  UserPointer<Type>& operator=(NullUserPointer) {
+    pointer_ = nullptr;
+    return *this;
+  }
+
+  // Allow conversion to a "non-const" |UserPointer|.
+  operator UserPointer<const Type>() const {
+    return UserPointer<const Type>(pointer_);
+  }
+
+  bool IsNull() const { return !pointer_; }
+
+  // "Reinterpret casts" to a |UserPointer<ToType>|.
+  template <typename ToType>
+  UserPointer<ToType> ReinterpretCast() const {
+    return UserPointer<ToType>(reinterpret_cast<ToType*>(pointer_));
+  }
+
+  // Checks that this pointer points to a valid |Type| in the same way as
+  // |Get()| and |Put()|.
+  // TODO(vtl): Logically, there should be separate read checks and write
+  // checks.
+  void Check() const {
+    internal::CheckUserPointer<sizeof(NonVoidType), MOJO_ALIGNOF(NonVoidType)>(
+        pointer_);
+  }
+
+  // Checks that this pointer points to a valid array (of type |Type|, or just a
+  // buffer if |Type| is |void| or |const void|) of |count| elements (or bytes
+  // if |Type| is |void| or |const void|) in the same way as |GetArray()| and
+  // |PutArray()|.
+  // TODO(vtl): Logically, there should be separate read checks and write
+  // checks.
+  // TODO(vtl): Switch more things to use this.
+  void CheckArray(size_t count) const {
+    internal::CheckUserPointerWithCount<sizeof(NonVoidType),
+                                        MOJO_ALIGNOF(NonVoidType)>(pointer_,
+                                                                   count);
+  }
+
+  // Gets the value (of type |Type|, or a |char| if |Type| is |void|) pointed to
+  // by this user pointer. Use this when you'd use the rvalue |*user_pointer|,
+  // but be aware that this may be costly -- so if the value will be used
+  // multiple times, you should save it.
+  //
+  // (We want to force a copy here, so return |Type| not |const Type&|.)
+  NonVoidType Get() const {
+    Check();
+    internal::CheckUserPointer<sizeof(NonVoidType), MOJO_ALIGNOF(NonVoidType)>(
+        pointer_);
+    return *pointer_;
+  }
+
+  // Gets an array (of type |Type|, or just a buffer if |Type| is |void| or
+  // |const void|) of |count| elements (or bytes if |Type| is |void| or |const
+  // void|) from the location pointed to by this user pointer. Use this when
+  // you'd do something like |memcpy(destination, user_pointer, count *
+  // sizeof(Type)|.
+  void GetArray(typename internal::remove_const<Type>::type* destination,
+                size_t count) const {
+    CheckArray(count);
+    memcpy(destination, pointer_, count * sizeof(NonVoidType));
+  }
+
+  // Puts a value (of type |Type|, or of type |char| if |Type| is |void|) to the
+  // location pointed to by this user pointer. Use this when you'd use the
+  // lvalue |*user_pointer|. Since this may be costly, you should avoid using
+  // this (for the same user pointer) more than once.
+  //
+  // Note: This |Put()| method is not valid when |T| is const, e.g., |const
+  // uint32_t|, but it's okay to include them so long as this template is only
+  // implicitly instantiated (see 14.7.1 of the C++11 standard) and not
+  // explicitly instantiated. (On implicit instantiation, only the declarations
+  // need be valid, not the definitions.)
+  //
+  // In C++11, we could do something like:
+  //   template <typename _Type = Type>
+  //   typename enable_if<!is_const<_Type>::value &&
+  //                      !is_void<_Type>::value>::type Put(
+  //       const _Type& value) { ... }
+  // (which obviously be correct), but C++03 doesn't allow default function
+  // template arguments.
+  void Put(const NonVoidType& value) {
+    Check();
+    *pointer_ = value;
+  }
+
+  // Puts an array (of type |Type|, or just a buffer if |Type| is |void|) with
+  // |count| elements (or bytes |Type| is |void|) to the location pointed to by
+  // this user pointer. Use this when you'd do something like
+  // |memcpy(user_pointer, source, count * sizeof(Type))|.
+  //
+  // Note: The same comments about the validity of |Put()| (except for the part
+  // about |void|) apply here.
+  void PutArray(const Type* source, size_t count) {
+    CheckArray(count);
+    memcpy(pointer_, source, count * sizeof(NonVoidType));
+  }
+
+  // Gets a |UserPointer| at offset |i| (in |Type|s) relative to this.
+  UserPointer At(size_t i) const {
+    return UserPointer(
+        static_cast<Type*>(static_cast<NonVoidType*>(pointer_) + i));
+  }
+
+  // Gets the value of the |UserPointer| as a |uintptr_t|. This should not be
+  // casted back to a pointer (and dereferenced), but may be used as a key for
+  // lookup or passed back to the user.
+  uintptr_t GetPointerValue() const {
+    return reinterpret_cast<uintptr_t>(pointer_);
+  }
+
+  // These provides safe (read-only/write-only/read-and-write) access to a
+  // |UserPointer<Type>| (probably pointing to an array) using just an ordinary
+  // pointer (obtained via |GetPointer()|).
+  //
+  // The memory returned by |GetPointer()| may be a copy of the original user
+  // memory, but should be modified only if the user is intended to eventually
+  // see the change.) If any changes are made, |Commit()| should be called to
+  // guarantee that the changes are written back to user memory (it may be
+  // called multiple times).
+  //
+  // Note: These classes are designed to allow fast, unsafe implementations (in
+  // which |GetPointer()| just returns the user pointer) if desired. Thus if
+  // |Commit()| is *not* called, changes may or may not be made visible to the
+  // user.
+  //
+  // Use these classes in the following way:
+  //
+  //   MojoResult Core::PutFoos(UserPointer<const uint32_t> foos,
+  //                            uint32_t num_foos) {
+  //     UserPointer<const uint32_t>::Reader foos_reader(foos, num_foos);
+  //     return PutFoosImpl(foos_reader.GetPointer(), num_foos);
+  //   }
+  //
+  //   MojoResult Core::GetFoos(UserPointer<uint32_t> foos,
+  //                            uint32_t num_foos) {
+  //     UserPointer<uint32_t>::Writer foos_writer(foos, num_foos);
+  //     MojoResult rv = GetFoosImpl(foos.GetPointer(), num_foos);
+  //     foos_writer.Commit();
+  //     return rv;
+  //   }
+  //
+  // TODO(vtl): Possibly, since we're not really being safe, we should just not
+  // copy for Release builds.
+  typedef UserPointerReader<Type> Reader;
+  typedef UserPointerWriter<Type> Writer;
+  typedef UserPointerReaderWriter<Type> ReaderWriter;
+
+ private:
+  friend class UserPointerReader<Type>;
+  friend class UserPointerReader<const Type>;
+  friend class UserPointerWriter<Type>;
+  friend class UserPointerReaderWriter<Type>;
+  template <class Options>
+  friend class UserOptionsReader;
+
+  Type* pointer_;
+  // Allow copy and assignment.
+};
+
+// Provides a convenient way to make a |UserPointer<Type>|.
+template <typename Type>
+inline UserPointer<Type> MakeUserPointer(Type* pointer) {
+  return UserPointer<Type>(pointer);
+}
+
+// Implementation of |UserPointer<Type>::Reader|.
+template <typename Type>
+class UserPointerReader {
+ private:
+  typedef typename internal::remove_const<Type>::type TypeNoConst;
+
+ public:
+  // Note: If |count| is zero, |GetPointer()| will always return null.
+  UserPointerReader(UserPointer<const Type> user_pointer, size_t count) {
+    Init(user_pointer.pointer_, count, true);
+  }
+  UserPointerReader(UserPointer<TypeNoConst> user_pointer, size_t count) {
+    Init(user_pointer.pointer_, count, true);
+  }
+
+  const Type* GetPointer() const { return buffer_.get(); }
+
+ private:
+  template <class Options>
+  friend class UserOptionsReader;
+
+  struct NoCheck {};
+  UserPointerReader(NoCheck,
+                    UserPointer<const Type> user_pointer,
+                    size_t count) {
+    Init(user_pointer.pointer_, count, false);
+  }
+
+  void Init(const Type* user_pointer, size_t count, bool check) {
+    if (count == 0)
+      return;
+
+    if (check) {
+      internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>(
+          user_pointer, count);
+    }
+    buffer_.reset(new TypeNoConst[count]);
+    memcpy(buffer_.get(), user_pointer, count * sizeof(Type));
+  }
+
+  scoped_ptr<TypeNoConst[]> buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserPointerReader);
+};
+
+// Implementation of |UserPointer<Type>::Writer|.
+template <typename Type>
+class UserPointerWriter {
+ public:
+  // Note: If |count| is zero, |GetPointer()| will always return null.
+  UserPointerWriter(UserPointer<Type> user_pointer, size_t count)
+      : user_pointer_(user_pointer), count_(count) {
+    if (count_ > 0) {
+      buffer_.reset(new Type[count_]);
+      memset(buffer_.get(), 0, count_ * sizeof(Type));
+    }
+  }
+
+  Type* GetPointer() const { return buffer_.get(); }
+
+  void Commit() {
+    internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>(
+        user_pointer_.pointer_, count_);
+    memcpy(user_pointer_.pointer_, buffer_.get(), count_ * sizeof(Type));
+  }
+
+ private:
+  UserPointer<Type> user_pointer_;
+  size_t count_;
+  scoped_ptr<Type[]> buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserPointerWriter);
+};
+
+// Implementation of |UserPointer<Type>::ReaderWriter|.
+template <typename Type>
+class UserPointerReaderWriter {
+ public:
+  // Note: If |count| is zero, |GetPointer()| will always return null.
+  UserPointerReaderWriter(UserPointer<Type> user_pointer, size_t count)
+      : user_pointer_(user_pointer), count_(count) {
+    if (count_ > 0) {
+      internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>(
+          user_pointer_.pointer_, count_);
+      buffer_.reset(new Type[count]);
+      memcpy(buffer_.get(), user_pointer.pointer_, count * sizeof(Type));
+    }
+  }
+
+  Type* GetPointer() const { return buffer_.get(); }
+  size_t GetCount() const { return count_; }
+
+  void Commit() {
+    internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>(
+        user_pointer_.pointer_, count_);
+    memcpy(user_pointer_.pointer_, buffer_.get(), count_ * sizeof(Type));
+  }
+
+ private:
+  UserPointer<Type> user_pointer_;
+  size_t count_;
+  scoped_ptr<Type[]> buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserPointerReaderWriter);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MEMORY_H_
diff --git a/mojo/edk/system/memory_unittest.cc b/mojo/edk/system/memory_unittest.cc
new file mode 100644
index 0000000..46515c0
--- /dev/null
+++ b/mojo/edk/system/memory_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright 2014 The Chromium 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/edk/system/memory.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/c/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(MemoryTest, Valid) {
+  char my_char;
+  int32_t my_int32;
+  int64_t my_int64_array[5] = {};  // Zero initialize.
+
+  UserPointer<char> my_char_ptr(&my_char);
+  UserPointer<int32_t> my_int32_ptr(&my_int32);
+  UserPointer<int64_t> my_int64_array_ptr(my_int64_array);
+
+  // |UserPointer<>::IsNull()|:
+  EXPECT_FALSE(my_char_ptr.IsNull());
+  EXPECT_FALSE(my_int32_ptr.IsNull());
+  EXPECT_FALSE(my_int64_array_ptr.IsNull());
+
+  // |UserPointer<>::Put()| and |UserPointer<>::Get()|:
+  my_char_ptr.Put('x');
+  EXPECT_EQ('x', my_char);
+  EXPECT_EQ('x', my_char_ptr.Get());
+  my_int32_ptr.Put(123);
+  EXPECT_EQ(123, my_int32);
+  EXPECT_EQ(123, my_int32_ptr.Get());
+  my_int64_array_ptr.Put(456);
+  EXPECT_EQ(456, my_int64_array[0]);
+  EXPECT_EQ(456, my_int64_array_ptr.Get());
+
+  // |UserPointer<>::At()|, etc.:
+  my_int64_array_ptr.At(3).Put(789);
+  EXPECT_EQ(789, my_int64_array[3]);
+  {
+    // Copy construction:
+    UserPointer<int64_t> other(my_int64_array_ptr.At(3));
+    EXPECT_FALSE(other.IsNull());
+    EXPECT_EQ(789, other.Get());
+
+    // Assignment:
+    other = my_int64_array_ptr;
+    EXPECT_FALSE(other.IsNull());
+    EXPECT_EQ(456, other.Get());
+
+    // Assignment to |NullUserPointer()|:
+    other = NullUserPointer();
+    EXPECT_TRUE(other.IsNull());
+
+    // |MakeUserPointer()|:
+    other = MakeUserPointer(&my_int64_array[1]);
+    other.Put(-123);
+    EXPECT_EQ(-123, my_int64_array_ptr.At(1).Get());
+  }
+
+  // "const" |UserPointer<>|:
+  {
+    // Explicit constructor from |NullUserPointer()|:
+    UserPointer<const char> other((NullUserPointer()));
+    EXPECT_TRUE(other.IsNull());
+
+    // Conversion to "const":
+    other = my_char_ptr;
+    EXPECT_EQ('x', other.Get());
+  }
+
+  // Default constructor:
+  {
+    UserPointer<int32_t> other;
+    EXPECT_TRUE(other.IsNull());
+
+    other = my_int32_ptr;
+    other.Put(-456);
+    EXPECT_EQ(-456, my_int32_ptr.Get());
+  }
+
+  // |UserPointer<>::CheckArray()|:
+  my_int64_array_ptr.CheckArray(5);
+
+  // |UserPointer<>::GetArray()|:
+  {
+    // From a "const" |UserPointer<>| (why not?):
+    UserPointer<const int64_t> other(my_int64_array_ptr);
+    int64_t array[3] = {1, 2, 3};
+    other.At(1).GetArray(array, 3);
+    EXPECT_EQ(-123, array[0]);
+    EXPECT_EQ(0, array[1]);
+    EXPECT_EQ(789, array[2]);
+  }
+
+  // |UserPointer<>::PutArray()|:
+  {
+    const int64_t array[2] = {654, 321};
+    my_int64_array_ptr.At(3).PutArray(array, 2);
+    EXPECT_EQ(0, my_int64_array[2]);
+    EXPECT_EQ(654, my_int64_array[3]);
+    EXPECT_EQ(321, my_int64_array[4]);
+  }
+
+  // |UserPointer<>::Reader|:
+  {
+    UserPointer<int64_t>::Reader reader(my_int64_array_ptr, 5);
+    EXPECT_EQ(456, reader.GetPointer()[0]);
+    EXPECT_EQ(321, reader.GetPointer()[4]);
+  }
+
+  // Non-const to const:
+  {
+    UserPointer<const int64_t>::Reader reader(my_int64_array_ptr.At(3), 1);
+    const int64_t* ptr = reader.GetPointer();
+    EXPECT_EQ(654, *ptr);
+  }
+
+  // |UserPointer<>::Writer|:
+  {
+    UserPointer<int64_t>::Writer writer(my_int64_array_ptr.At(2), 1);
+    int64_t* ptr = writer.GetPointer();
+    *ptr = 1234567890123LL;
+    writer.Commit();
+    EXPECT_EQ(1234567890123LL, my_int64_array[2]);
+  }
+
+  // |UserPointer<>::ReaderWriter|:
+  {
+    UserPointer<int32_t>::ReaderWriter reader_writer(my_int32_ptr, 1);
+    int32_t* ptr = reader_writer.GetPointer();
+    EXPECT_EQ(-456, *ptr);
+    *ptr = 42;
+    reader_writer.Commit();
+    EXPECT_EQ(42, my_int32);
+  }
+
+  // |UserPointer<>::ReinterpretCast<>|:
+  // (This assumes little-endian, etc.)
+  {
+    UserPointer<const char> other(my_int32_ptr.ReinterpretCast<char>());
+    EXPECT_EQ(42, other.Get());
+    EXPECT_EQ(0, other.At(1).Get());
+    EXPECT_EQ(0, other.At(2).Get());
+    EXPECT_EQ(0, other.At(3).Get());
+  }
+
+  // |UserPointer<>::GetPointerValue()|:
+  {
+    UserPointer<int32_t> other;
+    EXPECT_EQ(0u, other.GetPointerValue());
+    other = my_int32_ptr;
+    EXPECT_EQ(reinterpret_cast<uintptr_t>(&my_int32), other.GetPointerValue());
+  }
+}
+
+TEST(MemoryTest, InvalidDeath) {
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+
+  // Note: |Check...()| are defined to be "best effort" checks (and may always
+  // return true). Thus these tests of invalid cases only reflect the current
+  // implementation.
+
+  // These tests depend on |int32_t| and |int64_t| having nontrivial alignment.
+  static_assert(MOJO_ALIGNOF(int32_t) != 1,
+                "int32_t does not require nontrivial alignment");
+  static_assert(MOJO_ALIGNOF(int64_t) != 1,
+                "int64_t does not require nontrivial alignment");
+
+  // Null:
+  {
+    UserPointer<char> ptr(nullptr);
+    char array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Put('x'), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex);
+  }
+  {
+    UserPointer<int32_t> ptr(nullptr);
+    int32_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex);
+  }
+  {
+    UserPointer<int64_t> ptr(nullptr);
+    int64_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex);
+  }
+  // Also check a const pointer:
+  {
+    UserPointer<const int32_t> ptr(nullptr);
+    int32_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+  }
+
+  // Unaligned:
+  {
+    int32_t x[10];
+    UserPointer<int32_t> ptr(
+        reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(x) + 1));
+    int32_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex);
+  }
+  {
+    int64_t x[10];
+    UserPointer<int64_t> ptr(
+        reinterpret_cast<int64_t*>(reinterpret_cast<uintptr_t>(x) + 1));
+    int64_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex);
+  }
+  // Also check a const pointer:
+  {
+    int32_t x[10];
+    UserPointer<const int32_t> ptr(
+        reinterpret_cast<const int32_t*>(reinterpret_cast<uintptr_t>(x) + 1));
+    int32_t array[5] = {};
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex);
+  }
+
+  // Count too big:
+  {
+    const size_t kTooBig =
+        std::numeric_limits<size_t>::max() / sizeof(int32_t) + 1;
+    int32_t x = 0;
+    UserPointer<int32_t> ptr(&x);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig),
+                              kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(&x, kTooBig),
+                              kMemoryCheckFailedRegex);
+  }
+  {
+    const size_t kTooBig =
+        std::numeric_limits<size_t>::max() / sizeof(int64_t) + 1;
+    int64_t x = 0;
+    UserPointer<int64_t> ptr(&x);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig),
+                              kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(&x, kTooBig),
+                              kMemoryCheckFailedRegex);
+  }
+  // Also check a const pointer:
+  {
+    const size_t kTooBig =
+        std::numeric_limits<size_t>::max() / sizeof(int32_t) + 1;
+    int32_t x = 0;
+    UserPointer<const int32_t> ptr(&x);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex);
+    EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig),
+                              kMemoryCheckFailedRegex);
+  }
+
+  // TODO(vtl): Tests for |UserPointer{Reader,Writer,ReaderWriter}|.
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_in_transit.cc b/mojo/edk/system/message_in_transit.cc
new file mode 100644
index 0000000..f1fb91d
--- /dev/null
+++ b/mojo/edk/system/message_in_transit.cc
@@ -0,0 +1,224 @@
+// 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/edk/system/message_in_transit.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+    MessageInTransit::kTypeMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+    MessageInTransit::kTypeMessagePipe;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+    MessageInTransit::kTypeChannel;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+    MessageInTransit::kTypeRawChannel;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+    MessageInTransit::kSubtypeMessagePipeEndpointData;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+    MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+    MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+    MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+    MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
+    MessageInTransit::kInvalidEndpointId;
+STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment;
+
+struct MessageInTransit::PrivateStructForCompileAsserts {
+  // The size of |Header| must be a multiple of the alignment.
+  static_assert(sizeof(Header) % kMessageAlignment == 0,
+                "sizeof(MessageInTransit::Header) invalid");
+  // Avoid dangerous situations, but making sure that the size of the "header" +
+  // the size of the data fits into a 31-bit number.
+  static_assert(static_cast<uint64_t>(sizeof(Header)) + kMaxMessageNumBytes <=
+                    0x7fffffffULL,
+                "kMaxMessageNumBytes too big");
+
+  // We assume (to avoid extra rounding code) that the maximum message (data)
+  // size is a multiple of the alignment.
+  static_assert(kMaxMessageNumBytes % kMessageAlignment == 0,
+                "kMessageAlignment not a multiple of alignment");
+};
+
+MessageInTransit::View::View(size_t message_size, const void* buffer)
+    : buffer_(buffer) {
+  size_t next_message_size = 0;
+  DCHECK(MessageInTransit::GetNextMessageSize(
+      buffer_, message_size, &next_message_size));
+  DCHECK_EQ(message_size, next_message_size);
+  // This should be equivalent.
+  DCHECK_EQ(message_size, total_size());
+}
+
+bool MessageInTransit::View::IsValid(size_t serialized_platform_handle_size,
+                                     const char** error_message) const {
+  // Note: This also implies a check on the |main_buffer_size()|, which is just
+  // |RoundUpMessageAlignment(sizeof(Header) + num_bytes())|.
+  if (num_bytes() > kMaxMessageNumBytes) {
+    *error_message = "Message data payload too large";
+    return false;
+  }
+
+  if (transport_data_buffer_size() > 0) {
+    const char* e =
+        TransportData::ValidateBuffer(serialized_platform_handle_size,
+                                      transport_data_buffer(),
+                                      transport_data_buffer_size());
+    if (e) {
+      *error_message = e;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+MessageInTransit::MessageInTransit(Type type,
+                                   Subtype subtype,
+                                   uint32_t num_bytes,
+                                   const void* bytes)
+    : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)),
+      main_buffer_(static_cast<char*>(
+          base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) {
+  ConstructorHelper(type, subtype, num_bytes);
+  if (bytes) {
+    memcpy(MessageInTransit::bytes(), bytes, num_bytes);
+    memset(static_cast<char*>(MessageInTransit::bytes()) + num_bytes,
+           0,
+           main_buffer_size_ - sizeof(Header) - num_bytes);
+  } else {
+    memset(MessageInTransit::bytes(), 0, main_buffer_size_ - sizeof(Header));
+  }
+}
+
+MessageInTransit::MessageInTransit(Type type,
+                                   Subtype subtype,
+                                   uint32_t num_bytes,
+                                   UserPointer<const void> bytes)
+    : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)),
+      main_buffer_(static_cast<char*>(
+          base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) {
+  ConstructorHelper(type, subtype, num_bytes);
+  bytes.GetArray(MessageInTransit::bytes(), num_bytes);
+}
+
+MessageInTransit::MessageInTransit(const View& message_view)
+    : main_buffer_size_(message_view.main_buffer_size()),
+      main_buffer_(static_cast<char*>(
+          base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) {
+  DCHECK_GE(main_buffer_size_, sizeof(Header));
+  DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
+
+  memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_);
+  DCHECK_EQ(main_buffer_size_,
+            RoundUpMessageAlignment(sizeof(Header) + num_bytes()));
+}
+
+MessageInTransit::~MessageInTransit() {
+  if (dispatchers_) {
+    for (size_t i = 0; i < dispatchers_->size(); i++) {
+      if (!(*dispatchers_)[i].get())
+        continue;
+
+      DCHECK((*dispatchers_)[i]->HasOneRef());
+      (*dispatchers_)[i]->Close();
+    }
+  }
+}
+
+// static
+bool MessageInTransit::GetNextMessageSize(const void* buffer,
+                                          size_t buffer_size,
+                                          size_t* next_message_size) {
+  DCHECK(next_message_size);
+  if (!buffer_size)
+    return false;
+  DCHECK(buffer);
+  DCHECK_EQ(
+      reinterpret_cast<uintptr_t>(buffer) % MessageInTransit::kMessageAlignment,
+      0u);
+
+  if (buffer_size < sizeof(Header))
+    return false;
+
+  const Header* header = static_cast<const Header*>(buffer);
+  *next_message_size = header->total_size;
+  DCHECK_EQ(*next_message_size % kMessageAlignment, 0u);
+  return true;
+}
+
+void MessageInTransit::SetDispatchers(
+    scoped_ptr<DispatcherVector> dispatchers) {
+  DCHECK(dispatchers);
+  DCHECK(!dispatchers_);
+  DCHECK(!transport_data_);
+
+  dispatchers_ = dispatchers.Pass();
+#ifndef NDEBUG
+  for (size_t i = 0; i < dispatchers_->size(); i++)
+    DCHECK(!(*dispatchers_)[i].get() || (*dispatchers_)[i]->HasOneRef());
+#endif
+}
+
+void MessageInTransit::SetTransportData(
+    scoped_ptr<TransportData> transport_data) {
+  DCHECK(transport_data);
+  DCHECK(!transport_data_);
+  DCHECK(!dispatchers_);
+
+  transport_data_ = transport_data.Pass();
+}
+
+void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) {
+  DCHECK(channel);
+  DCHECK(!transport_data_);
+
+  if (!dispatchers_ || !dispatchers_->size())
+    return;
+
+  transport_data_.reset(new TransportData(dispatchers_.Pass(), channel));
+
+  // Update the sizes in the message header.
+  UpdateTotalSize();
+}
+
+void MessageInTransit::ConstructorHelper(Type type,
+                                         Subtype subtype,
+                                         uint32_t num_bytes) {
+  DCHECK_LE(num_bytes, kMaxMessageNumBytes);
+
+  // |total_size| is updated below, from the other values.
+  header()->type = type;
+  header()->subtype = subtype;
+  header()->source_id = kInvalidEndpointId;
+  header()->destination_id = kInvalidEndpointId;
+  header()->num_bytes = num_bytes;
+  header()->unused = 0;
+  // Note: If dispatchers are subsequently attached, then |total_size| will have
+  // to be adjusted.
+  UpdateTotalSize();
+}
+
+void MessageInTransit::UpdateTotalSize() {
+  DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
+  header()->total_size = static_cast<uint32_t>(main_buffer_size_);
+  if (transport_data_) {
+    header()->total_size +=
+        static_cast<uint32_t>(transport_data_->buffer_size());
+  }
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_in_transit.h b/mojo/edk/system/message_in_transit.h
new file mode 100644
index 0000000..7714fa7
--- /dev/null
+++ b/mojo/edk/system/message_in_transit.h
@@ -0,0 +1,267 @@
+// 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_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class TransportData;
+
+// This class is used to represent data in transit. It is thread-unsafe.
+//
+// |MessageInTransit| buffers:
+//
+// A |MessageInTransit| can be serialized by writing the main buffer and then,
+// if it has one, the transport data buffer. Both buffers are
+// |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes
+// in size.
+//
+// The main buffer consists of the header (of type |Header|, which is an
+// internal detail of this class) followed immediately by the message data
+// (accessed by |bytes()| and of size |num_bytes()|, and also
+// |kMessageAlignment|-byte aligned), and then any padding needed to make the
+// main buffer a multiple of |kMessageAlignment| bytes in size.
+//
+// See |TransportData| for a description of the (serialized) transport data
+// buffer.
+class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
+ public:
+  typedef uint16_t Type;
+  // Messages that are forwarded to |MessagePipeEndpoint|s.
+  static const Type kTypeMessagePipeEndpoint = 0;
+  // Messages that are forwarded to |MessagePipe|s.
+  static const Type kTypeMessagePipe = 1;
+  // Messages that are consumed by the |Channel|.
+  static const Type kTypeChannel = 2;
+  // Messages that are consumed by the |RawChannel| (implementation).
+  static const Type kTypeRawChannel = 3;
+
+  typedef uint16_t Subtype;
+  // Subtypes for type |kTypeMessagePipeEndpoint|:
+  static const Subtype kSubtypeMessagePipeEndpointData = 0;
+  // Subtypes for type |kTypeMessagePipe|:
+  // Nothing currently.
+  // Subtypes for type |kTypeChannel|:
+  static const Subtype kSubtypeChannelRunMessagePipeEndpoint = 0;
+  static const Subtype kSubtypeChannelRemoveMessagePipeEndpoint = 1;
+  static const Subtype kSubtypeChannelRemoveMessagePipeEndpointAck = 2;
+  // Subtypes for type |kTypeRawChannel|:
+  static const Subtype kSubtypeRawChannelPosixExtraPlatformHandles = 0;
+
+  typedef uint32_t EndpointId;
+  // Never a valid endpoint ID.
+  static const EndpointId kInvalidEndpointId = 0;
+
+  // Messages (the header and data) must always be aligned to a multiple of this
+  // quantity (which must be a power of 2).
+  static const size_t kMessageAlignment = 8;
+
+  // Forward-declare |Header| so that |View| can use it:
+ private:
+  struct Header;
+
+ public:
+  // This represents a view of serialized message data in a raw buffer.
+  class MOJO_SYSTEM_IMPL_EXPORT View {
+   public:
+    // Constructs a view from the given buffer of the given size. (The size must
+    // be as provided by |MessageInTransit::GetNextMessageSize()|.) The buffer
+    // must remain alive/unmodified through the lifetime of this object.
+    // |buffer| should be |kMessageAlignment|-byte aligned.
+    View(size_t message_size, const void* buffer);
+
+    // Checks that the given |View| appears to be for a valid message, within
+    // predetermined limits (e.g., |num_bytes()| and |main_buffer_size()|, that
+    // |transport_data_buffer()|/|transport_data_buffer_size()| is for valid
+    // transport data -- see |TransportData::ValidateBuffer()|).
+    //
+    // It returns true (and leaves |error_message| alone) if this object appears
+    // to be a valid message (according to the above) and false, pointing
+    // |*error_message| to a suitable error message, if not.
+    bool IsValid(size_t serialized_platform_handle_size,
+                 const char** error_message) const;
+
+    // API parallel to that for |MessageInTransit| itself (mostly getters for
+    // header data).
+    const void* main_buffer() const { return buffer_; }
+    size_t main_buffer_size() const {
+      return RoundUpMessageAlignment(sizeof(Header) + header()->num_bytes);
+    }
+    const void* transport_data_buffer() const {
+      return (total_size() > main_buffer_size())
+                 ? static_cast<const char*>(buffer_) + main_buffer_size()
+                 : nullptr;
+    }
+    size_t transport_data_buffer_size() const {
+      return total_size() - main_buffer_size();
+    }
+    size_t total_size() const { return header()->total_size; }
+    uint32_t num_bytes() const { return header()->num_bytes; }
+    const void* bytes() const {
+      return static_cast<const char*>(buffer_) + sizeof(Header);
+    }
+    Type type() const { return header()->type; }
+    Subtype subtype() const { return header()->subtype; }
+    EndpointId source_id() const { return header()->source_id; }
+    EndpointId destination_id() const { return header()->destination_id; }
+
+   private:
+    const Header* header() const { return static_cast<const Header*>(buffer_); }
+
+    const void* const buffer_;
+
+    // Though this struct is trivial, disallow copy and assign, since it doesn't
+    // own its data. (If you're copying/assigning this, you're probably doing
+    // something wrong.)
+    DISALLOW_COPY_AND_ASSIGN(View);
+  };
+
+  // |bytes| is optional; if null, the message data will be zero-initialized.
+  MessageInTransit(Type type,
+                   Subtype subtype,
+                   uint32_t num_bytes,
+                   const void* bytes);
+  // |bytes| should be valid (and non-null), unless |num_bytes| is zero.
+  MessageInTransit(Type type,
+                   Subtype subtype,
+                   uint32_t num_bytes,
+                   UserPointer<const void> bytes);
+  // Constructs a |MessageInTransit| from a |View|.
+  explicit MessageInTransit(const View& message_view);
+
+  ~MessageInTransit();
+
+  // Gets the size of the next message from |buffer|, which has |buffer_size|
+  // bytes currently available, returning true and setting |*next_message_size|
+  // on success. |buffer| should be aligned on a |kMessageAlignment| boundary
+  // (and on success, |*next_message_size| will be a multiple of
+  // |kMessageAlignment|).
+  // TODO(vtl): In |RawChannelPosix|, the alignment requirements are currently
+  // satisified on a faith-based basis.
+  static bool GetNextMessageSize(const void* buffer,
+                                 size_t buffer_size,
+                                 size_t* next_message_size);
+
+  // Makes this message "own" the given set of dispatchers. The dispatchers must
+  // not be referenced from anywhere else (in particular, not from the handle
+  // table), i.e., each dispatcher must have a reference count of 1. This
+  // message must not already have dispatchers.
+  void SetDispatchers(scoped_ptr<DispatcherVector> dispatchers);
+
+  // Sets the |TransportData| for this message. This should only be done when
+  // there are no dispatchers and no existing |TransportData|.
+  void SetTransportData(scoped_ptr<TransportData> transport_data);
+
+  // Serializes any dispatchers to the secondary buffer. This message must not
+  // already have a secondary buffer (so this must only be called once). The
+  // caller must ensure (e.g., by holding on to a reference) that |channel|
+  // stays alive through the call.
+  void SerializeAndCloseDispatchers(Channel* channel);
+
+  // Gets the main buffer and its size (in number of bytes), respectively.
+  const void* main_buffer() const { return main_buffer_.get(); }
+  size_t main_buffer_size() const { return main_buffer_size_; }
+
+  // Gets the transport data buffer (if any).
+  const TransportData* transport_data() const { return transport_data_.get(); }
+  TransportData* transport_data() { return transport_data_.get(); }
+
+  // Gets the total size of the message (see comment in |Header|, below).
+  size_t total_size() const { return header()->total_size; }
+
+  // Gets the size of the message data.
+  uint32_t num_bytes() const { return header()->num_bytes; }
+
+  // Gets the message data (of size |num_bytes()| bytes).
+  const void* bytes() const { return main_buffer_.get() + sizeof(Header); }
+  void* bytes() { return main_buffer_.get() + sizeof(Header); }
+
+  Type type() const { return header()->type; }
+  Subtype subtype() const { return header()->subtype; }
+  EndpointId source_id() const { return header()->source_id; }
+  EndpointId destination_id() const { return header()->destination_id; }
+
+  void set_source_id(EndpointId source_id) { header()->source_id = source_id; }
+  void set_destination_id(EndpointId destination_id) {
+    header()->destination_id = destination_id;
+  }
+
+  // Gets the dispatchers attached to this message; this may return null if
+  // there are none. Note that the caller may mutate the set of dispatchers
+  // (e.g., take ownership of all the dispatchers, leaving the vector empty).
+  DispatcherVector* dispatchers() { return dispatchers_.get(); }
+
+  // Returns true if this message has dispatchers attached.
+  bool has_dispatchers() const {
+    return dispatchers_ && !dispatchers_->empty();
+  }
+
+  // Rounds |n| up to a multiple of |kMessageAlignment|.
+  static inline size_t RoundUpMessageAlignment(size_t n) {
+    return (n + kMessageAlignment - 1) & ~(kMessageAlignment - 1);
+  }
+
+ private:
+  // To allow us to make compile-assertions about |Header| in the .cc file.
+  struct PrivateStructForCompileAsserts;
+
+  // Header for the data (main buffer). Must be a multiple of
+  // |kMessageAlignment| bytes in size. Must be POD.
+  struct Header {
+    // Total size of the message, including the header, the message data
+    // ("bytes") including padding (to make it a multiple of |kMessageAlignment|
+    // bytes), and serialized handle information. Note that this may not be the
+    // correct value if dispatchers are attached but
+    // |SerializeAndCloseDispatchers()| has not been called.
+    uint32_t total_size;
+    Type type;                  // 2 bytes.
+    Subtype subtype;            // 2 bytes.
+    EndpointId source_id;       // 4 bytes.
+    EndpointId destination_id;  // 4 bytes.
+    // Size of actual message data.
+    uint32_t num_bytes;
+    uint32_t unused;
+  };
+
+  const Header* header() const {
+    return reinterpret_cast<const Header*>(main_buffer_.get());
+  }
+  Header* header() { return reinterpret_cast<Header*>(main_buffer_.get()); }
+
+  void ConstructorHelper(Type type, Subtype subtype, uint32_t num_bytes);
+  void UpdateTotalSize();
+
+  const size_t main_buffer_size_;
+  const scoped_ptr<char, base::AlignedFreeDeleter> main_buffer_;  // Never null.
+
+  scoped_ptr<TransportData> transport_data_;  // May be null.
+
+  // Any dispatchers that may be attached to this message. These dispatchers
+  // should be "owned" by this message, i.e., have a ref count of exactly 1. (We
+  // allow a dispatcher entry to be null, in case it couldn't be duplicated for
+  // some reason.)
+  scoped_ptr<DispatcherVector> dispatchers_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageInTransit);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_
diff --git a/mojo/edk/system/message_in_transit_queue.cc b/mojo/edk/system/message_in_transit_queue.cc
new file mode 100644
index 0000000..ab5195e
--- /dev/null
+++ b/mojo/edk/system/message_in_transit_queue.cc
@@ -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.
+
+#include "mojo/edk/system/message_in_transit_queue.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace mojo {
+namespace system {
+
+MessageInTransitQueue::MessageInTransitQueue() {
+}
+
+MessageInTransitQueue::~MessageInTransitQueue() {
+  if (!IsEmpty()) {
+    LOG(WARNING) << "Destroying nonempty message queue";
+    Clear();
+  }
+}
+
+void MessageInTransitQueue::Clear() {
+  STLDeleteElements(&queue_);
+}
+
+void MessageInTransitQueue::Swap(MessageInTransitQueue* other) {
+  queue_.swap(other->queue_);
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_in_transit_queue.h b/mojo/edk/system/message_in_transit_queue.h
new file mode 100644
index 0000000..d81bd2b
--- /dev/null
+++ b/mojo/edk/system/message_in_transit_queue.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_
+
+#include <deque>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// A simple queue for |MessageInTransit|s (that owns its messages).
+// This class is not thread-safe.
+// TODO(vtl): Write tests.
+class MOJO_SYSTEM_IMPL_EXPORT MessageInTransitQueue {
+ public:
+  MessageInTransitQueue();
+  ~MessageInTransitQueue();
+
+  bool IsEmpty() const { return queue_.empty(); }
+
+  void AddMessage(scoped_ptr<MessageInTransit> message) {
+    queue_.push_back(message.release());
+  }
+
+  scoped_ptr<MessageInTransit> GetMessage() {
+    MessageInTransit* rv = queue_.front();
+    queue_.pop_front();
+    return make_scoped_ptr(rv);
+  }
+
+  MessageInTransit* PeekMessage() { return queue_.front(); }
+
+  void DiscardMessage() {
+    delete queue_.front();
+    queue_.pop_front();
+  }
+
+  void Clear();
+
+  // Efficiently swaps contents with |*other|.
+  void Swap(MessageInTransitQueue* other);
+
+ private:
+  // TODO(vtl): When C++11 is available, switch this to a deque of
+  // |scoped_ptr|/|unique_ptr|s.
+  std::deque<MessageInTransit*> queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageInTransitQueue);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_
diff --git a/mojo/edk/system/message_pipe.cc b/mojo/edk/system/message_pipe.cc
new file mode 100644
index 0000000..55f1285
--- /dev/null
+++ b/mojo/edk/system/message_pipe.cc
@@ -0,0 +1,286 @@
+// 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/edk/system/message_pipe.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/local_message_pipe_endpoint.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/message_pipe_endpoint.h"
+#include "mojo/edk/system/proxy_message_pipe_endpoint.h"
+
+namespace mojo {
+namespace system {
+
+// static
+MessagePipe* MessagePipe::CreateLocalLocal() {
+  MessagePipe* message_pipe = new MessagePipe();
+  message_pipe->endpoints_[0].reset(new LocalMessagePipeEndpoint());
+  message_pipe->endpoints_[1].reset(new LocalMessagePipeEndpoint());
+  return message_pipe;
+}
+
+// static
+MessagePipe* MessagePipe::CreateLocalProxy(
+    scoped_refptr<ChannelEndpoint>* channel_endpoint) {
+  DCHECK(!channel_endpoint->get());  // Not technically wrong, but unlikely.
+  MessagePipe* message_pipe = new MessagePipe();
+  message_pipe->endpoints_[0].reset(new LocalMessagePipeEndpoint());
+  *channel_endpoint = new ChannelEndpoint(message_pipe, 1);
+  message_pipe->endpoints_[1].reset(
+      new ProxyMessagePipeEndpoint(channel_endpoint->get()));
+  return message_pipe;
+}
+
+// static
+MessagePipe* MessagePipe::CreateProxyLocal(
+    scoped_refptr<ChannelEndpoint>* channel_endpoint) {
+  DCHECK(!channel_endpoint->get());  // Not technically wrong, but unlikely.
+  MessagePipe* message_pipe = new MessagePipe();
+  *channel_endpoint = new ChannelEndpoint(message_pipe, 0);
+  message_pipe->endpoints_[0].reset(
+      new ProxyMessagePipeEndpoint(channel_endpoint->get()));
+  message_pipe->endpoints_[1].reset(new LocalMessagePipeEndpoint());
+  return message_pipe;
+}
+
+// static
+unsigned MessagePipe::GetPeerPort(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+  return port ^ 1;
+}
+
+MessagePipeEndpoint::Type MessagePipe::GetType(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+
+  return endpoints_[port]->GetType();
+}
+
+void MessagePipe::CancelAllWaiters(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+  endpoints_[port]->CancelAllWaiters();
+}
+
+void MessagePipe::Close(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+
+  unsigned destination_port = GetPeerPort(port);
+
+  base::AutoLock locker(lock_);
+  // The endpoint's |OnPeerClose()| may have been called first and returned
+  // false, which would have resulted in its destruction.
+  if (!endpoints_[port])
+    return;
+
+  endpoints_[port]->Close();
+  if (endpoints_[destination_port]) {
+    if (!endpoints_[destination_port]->OnPeerClose())
+      endpoints_[destination_port].reset();
+  }
+  endpoints_[port].reset();
+}
+
+// TODO(vtl): Handle flags.
+MojoResult MessagePipe::WriteMessage(
+    unsigned port,
+    UserPointer<const void> bytes,
+    uint32_t num_bytes,
+    std::vector<DispatcherTransport>* transports,
+    MojoWriteMessageFlags flags) {
+  DCHECK(port == 0 || port == 1);
+  return EnqueueMessageInternal(
+      GetPeerPort(port),
+      make_scoped_ptr(new MessageInTransit(
+          MessageInTransit::kTypeMessagePipeEndpoint,
+          MessageInTransit::kSubtypeMessagePipeEndpointData,
+          num_bytes,
+          bytes)),
+      transports);
+}
+
+MojoResult MessagePipe::ReadMessage(unsigned port,
+                                    UserPointer<void> bytes,
+                                    UserPointer<uint32_t> num_bytes,
+                                    DispatcherVector* dispatchers,
+                                    uint32_t* num_dispatchers,
+                                    MojoReadMessageFlags flags) {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+
+  return endpoints_[port]->ReadMessage(
+      bytes, num_bytes, dispatchers, num_dispatchers, flags);
+}
+
+HandleSignalsState MessagePipe::GetHandleSignalsState(unsigned port) const {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(const_cast<base::Lock&>(lock_));
+  DCHECK(endpoints_[port]);
+
+  return endpoints_[port]->GetHandleSignalsState();
+}
+
+MojoResult MessagePipe::AddWaiter(unsigned port,
+                                  Waiter* waiter,
+                                  MojoHandleSignals signals,
+                                  uint32_t context,
+                                  HandleSignalsState* signals_state) {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+
+  return endpoints_[port]->AddWaiter(waiter, signals, context, signals_state);
+}
+
+void MessagePipe::RemoveWaiter(unsigned port,
+                               Waiter* waiter,
+                               HandleSignalsState* signals_state) {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+
+  endpoints_[port]->RemoveWaiter(waiter, signals_state);
+}
+
+scoped_refptr<ChannelEndpoint> MessagePipe::ConvertLocalToProxy(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[port]);
+  DCHECK_EQ(endpoints_[port]->GetType(), MessagePipeEndpoint::kTypeLocal);
+
+  // TODO(vtl): Allowing this case is a temporary hack. It'll set up a
+  // |MessagePipe| with two proxy endpoints, which will then act as a proxy
+  // (rather than trying to connect the two ends directly).
+  DLOG_IF(WARNING,
+          !!endpoints_[GetPeerPort(port)] &&
+              endpoints_[GetPeerPort(port)]->GetType() !=
+                  MessagePipeEndpoint::kTypeLocal)
+      << "Direct message pipe passing across multiple channels not yet "
+         "implemented; will proxy";
+
+  scoped_ptr<MessagePipeEndpoint> old_endpoint(endpoints_[port].Pass());
+  scoped_refptr<ChannelEndpoint> channel_endpoint(
+      new ChannelEndpoint(this, port));
+  endpoints_[port].reset(new ProxyMessagePipeEndpoint(channel_endpoint.get()));
+  channel_endpoint->TakeMessages(static_cast<LocalMessagePipeEndpoint*>(
+                                     old_endpoint.get())->message_queue());
+  old_endpoint->Close();
+
+  return channel_endpoint;
+}
+
+MojoResult MessagePipe::EnqueueMessage(unsigned port,
+                                       scoped_ptr<MessageInTransit> message) {
+  return EnqueueMessageInternal(port, message.Pass(), nullptr);
+}
+
+MessagePipe::MessagePipe() {
+}
+
+MessagePipe::~MessagePipe() {
+  // Owned by the dispatchers. The owning dispatchers should only release us via
+  // their |Close()| method, which should inform us of being closed via our
+  // |Close()|. Thus these should already be null.
+  DCHECK(!endpoints_[0]);
+  DCHECK(!endpoints_[1]);
+}
+
+MojoResult MessagePipe::EnqueueMessageInternal(
+    unsigned port,
+    scoped_ptr<MessageInTransit> message,
+    std::vector<DispatcherTransport>* transports) {
+  DCHECK(port == 0 || port == 1);
+  DCHECK(message);
+
+  if (message->type() == MessageInTransit::kTypeMessagePipe) {
+    DCHECK(!transports);
+    return HandleControlMessage(port, message.Pass());
+  }
+
+  DCHECK_EQ(message->type(), MessageInTransit::kTypeMessagePipeEndpoint);
+
+  base::AutoLock locker(lock_);
+  DCHECK(endpoints_[GetPeerPort(port)]);
+
+  // The destination port need not be open, unlike the source port.
+  if (!endpoints_[port])
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  if (transports) {
+    MojoResult result = AttachTransportsNoLock(port, message.get(), transports);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  // The endpoint's |EnqueueMessage()| may not report failure.
+  endpoints_[port]->EnqueueMessage(message.Pass());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MessagePipe::AttachTransportsNoLock(
+    unsigned port,
+    MessageInTransit* message,
+    std::vector<DispatcherTransport>* transports) {
+  DCHECK(!message->has_dispatchers());
+
+  // You're not allowed to send either handle to a message pipe over the message
+  // pipe, so check for this. (The case of trying to write a handle to itself is
+  // taken care of by |Core|. That case kind of makes sense, but leads to
+  // complications if, e.g., both sides try to do the same thing with their
+  // respective handles simultaneously. The other case, of trying to write the
+  // peer handle to a handle, doesn't make sense -- since no handle will be
+  // available to read the message from.)
+  for (size_t i = 0; i < transports->size(); i++) {
+    if (!(*transports)[i].is_valid())
+      continue;
+    if ((*transports)[i].GetType() == Dispatcher::kTypeMessagePipe) {
+      MessagePipeDispatcherTransport mp_transport((*transports)[i]);
+      if (mp_transport.GetMessagePipe() == this) {
+        // The other case should have been disallowed by |Core|. (Note: |port|
+        // is the peer port of the handle given to |WriteMessage()|.)
+        DCHECK_EQ(mp_transport.GetPort(), port);
+        return MOJO_RESULT_INVALID_ARGUMENT;
+      }
+    }
+  }
+
+  // Clone the dispatchers and attach them to the message. (This must be done as
+  // a separate loop, since we want to leave the dispatchers alone on failure.)
+  scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector());
+  dispatchers->reserve(transports->size());
+  for (size_t i = 0; i < transports->size(); i++) {
+    if ((*transports)[i].is_valid()) {
+      dispatchers->push_back(
+          (*transports)[i].CreateEquivalentDispatcherAndClose());
+    } else {
+      LOG(WARNING) << "Enqueueing null dispatcher";
+      dispatchers->push_back(scoped_refptr<Dispatcher>());
+    }
+  }
+  message->SetDispatchers(dispatchers.Pass());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MessagePipe::HandleControlMessage(
+    unsigned /*port*/,
+    scoped_ptr<MessageInTransit> message) {
+  LOG(WARNING) << "Unrecognized MessagePipe control message subtype "
+               << message->subtype();
+  return MOJO_RESULT_UNKNOWN;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe.h b/mojo/edk/system/message_pipe.h
new file mode 100644
index 0000000..e1d9f63
--- /dev/null
+++ b/mojo/edk/system/message_pipe.h
@@ -0,0 +1,132 @@
+// 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_EDK_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe_endpoint.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+class ChannelEndpoint;
+class Waiter;
+
+// |MessagePipe| is the secondary object implementing a message pipe (see the
+// explanatory comment in core.cc). It is typically owned by the dispatcher(s)
+// corresponding to the local endpoints. This class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipe
+    : public base::RefCountedThreadSafe<MessagePipe> {
+ public:
+  // Creates a |MessagePipe| with two new |LocalMessagePipeEndpoint|s.
+  static MessagePipe* CreateLocalLocal();
+
+  // Creates a |MessagePipe| with a |LocalMessagePipeEndpoint| on port 0 and a
+  // |ProxyMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the
+  // (newly-created) |ChannelEndpoint| for the latter.
+  static MessagePipe* CreateLocalProxy(
+      scoped_refptr<ChannelEndpoint>* channel_endpoint);
+
+  // Creates a |MessagePipe| with a |ProxyMessagePipeEndpoint| on port 0 and a
+  // |LocalMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the
+  // (newly-created) |ChannelEndpoint| for the former.
+  // Note: This is really only needed in tests (outside of tests, this
+  // configuration arises from a local message pipe having its port 0
+  // "converted" using |ConvertLocalToProxy()|).
+  static MessagePipe* CreateProxyLocal(
+      scoped_refptr<ChannelEndpoint>* channel_endpoint);
+
+  // Gets the other port number (i.e., 0 -> 1, 1 -> 0).
+  static unsigned GetPeerPort(unsigned port);
+
+  // Gets the type of the endpoint (used for assertions, etc.).
+  MessagePipeEndpoint::Type GetType(unsigned port);
+
+  // These are called by the dispatcher to implement its methods of
+  // corresponding names. In all cases, the port |port| must be open.
+  void CancelAllWaiters(unsigned port);
+  void Close(unsigned port);
+  // Unlike |MessagePipeDispatcher::WriteMessage()|, this does not validate its
+  // arguments.
+  MojoResult WriteMessage(unsigned port,
+                          UserPointer<const void> bytes,
+                          uint32_t num_bytes,
+                          std::vector<DispatcherTransport>* transports,
+                          MojoWriteMessageFlags flags);
+  MojoResult ReadMessage(unsigned port,
+                         UserPointer<void> bytes,
+                         UserPointer<uint32_t> num_bytes,
+                         DispatcherVector* dispatchers,
+                         uint32_t* num_dispatchers,
+                         MojoReadMessageFlags flags);
+  HandleSignalsState GetHandleSignalsState(unsigned port) const;
+  MojoResult AddWaiter(unsigned port,
+                       Waiter* waiter,
+                       MojoHandleSignals signals,
+                       uint32_t context,
+                       HandleSignalsState* signals_state);
+  void RemoveWaiter(unsigned port,
+                    Waiter* waiter,
+                    HandleSignalsState* signals_state);
+
+  // This is called by the dispatcher to convert a local endpoint to a proxy
+  // endpoint.
+  scoped_refptr<ChannelEndpoint> ConvertLocalToProxy(unsigned port);
+
+  // This is used by |Channel| to enqueue messages (typically to a
+  // |LocalMessagePipeEndpoint|). Unlike |WriteMessage()|, |port| is the
+  // *destination* port.
+  MojoResult EnqueueMessage(unsigned port,
+                            scoped_ptr<MessageInTransit> message);
+
+ private:
+  MessagePipe();
+
+  friend class base::RefCountedThreadSafe<MessagePipe>;
+  virtual ~MessagePipe();
+
+  // This is used internally by |WriteMessage()| and by |EnqueueMessage()|.
+  // |transports| may be non-null only if it's nonempty and |message| has no
+  // dispatchers attached.
+  MojoResult EnqueueMessageInternal(
+      unsigned port,
+      scoped_ptr<MessageInTransit> message,
+      std::vector<DispatcherTransport>* transports);
+
+  // Helper for |EnqueueMessageInternal()|. Must be called with |lock_| held.
+  MojoResult AttachTransportsNoLock(
+      unsigned port,
+      MessageInTransit* message,
+      std::vector<DispatcherTransport>* transports);
+
+  // Used by |EnqueueMessageInternal()| to handle control messages that are
+  // actually meant for us.
+  MojoResult HandleControlMessage(unsigned port,
+                                  scoped_ptr<MessageInTransit> message);
+
+  base::Lock lock_;  // Protects the following members.
+  scoped_ptr<MessagePipeEndpoint> endpoints_[2];
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipe);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
new file mode 100644
index 0000000..2b3f028
--- /dev/null
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -0,0 +1,280 @@
+// 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/edk/system/message_pipe_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/local_message_pipe_endpoint.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/options_validation.h"
+#include "mojo/edk/system/proxy_message_pipe_endpoint.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+const unsigned kInvalidPort = static_cast<unsigned>(-1);
+
+struct SerializedMessagePipeDispatcher {
+  MessageInTransit::EndpointId endpoint_id;
+};
+
+}  // namespace
+
+// MessagePipeDispatcher -------------------------------------------------------
+
+// static
+const MojoCreateMessagePipeOptions
+    MessagePipeDispatcher::kDefaultCreateOptions = {
+        static_cast<uint32_t>(sizeof(MojoCreateMessagePipeOptions)),
+        MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE};
+
+MessagePipeDispatcher::MessagePipeDispatcher(
+    const MojoCreateMessagePipeOptions& /*validated_options*/)
+    : port_(kInvalidPort) {
+}
+
+// static
+MojoResult MessagePipeDispatcher::ValidateCreateOptions(
+    UserPointer<const MojoCreateMessagePipeOptions> in_options,
+    MojoCreateMessagePipeOptions* out_options) {
+  const MojoCreateMessagePipeOptionsFlags kKnownFlags =
+      MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE;
+
+  *out_options = kDefaultCreateOptions;
+  if (in_options.IsNull())
+    return MOJO_RESULT_OK;
+
+  UserOptionsReader<MojoCreateMessagePipeOptions> reader(in_options);
+  if (!reader.is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateMessagePipeOptions, flags, reader))
+    return MOJO_RESULT_OK;
+  if ((reader.options().flags & ~kKnownFlags))
+    return MOJO_RESULT_UNIMPLEMENTED;
+  out_options->flags = reader.options().flags;
+
+  // Checks for fields beyond |flags|:
+
+  // (Nothing here yet.)
+
+  return MOJO_RESULT_OK;
+}
+
+void MessagePipeDispatcher::Init(scoped_refptr<MessagePipe> message_pipe,
+                                 unsigned port) {
+  DCHECK(message_pipe.get());
+  DCHECK(port == 0 || port == 1);
+
+  message_pipe_ = message_pipe;
+  port_ = port;
+}
+
+Dispatcher::Type MessagePipeDispatcher::GetType() const {
+  return kTypeMessagePipe;
+}
+
+// static
+scoped_refptr<MessagePipeDispatcher>
+MessagePipeDispatcher::CreateRemoteMessagePipe(
+    scoped_refptr<ChannelEndpoint>* channel_endpoint) {
+  scoped_refptr<MessagePipe> message_pipe(
+      MessagePipe::CreateLocalProxy(channel_endpoint));
+  scoped_refptr<MessagePipeDispatcher> dispatcher(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  dispatcher->Init(message_pipe, 0);
+  return dispatcher;
+}
+
+// static
+scoped_refptr<MessagePipeDispatcher> MessagePipeDispatcher::Deserialize(
+    Channel* channel,
+    const void* source,
+    size_t size) {
+  if (size != sizeof(SerializedMessagePipeDispatcher)) {
+    LOG(ERROR) << "Invalid serialized message pipe dispatcher";
+    return scoped_refptr<MessagePipeDispatcher>();
+  }
+
+  scoped_refptr<ChannelEndpoint> channel_endpoint;
+  scoped_refptr<MessagePipeDispatcher> dispatcher =
+      CreateRemoteMessagePipe(&channel_endpoint);
+
+  MessageInTransit::EndpointId remote_id =
+      static_cast<const SerializedMessagePipeDispatcher*>(source)->endpoint_id;
+  if (remote_id == MessageInTransit::kInvalidEndpointId) {
+    // This means that the other end was closed, and there were no messages
+    // enqueued for us.
+    // TODO(vtl): This is wrong. We should produce a "dead" message pipe
+    // dispatcher.
+    NOTIMPLEMENTED();
+    return scoped_refptr<MessagePipeDispatcher>();
+  }
+  MessageInTransit::EndpointId local_id =
+      channel->AttachEndpoint(channel_endpoint);
+  if (local_id == MessageInTransit::kInvalidEndpointId) {
+    LOG(ERROR) << "Failed to deserialize message pipe dispatcher (failed to "
+                  "attach; remote ID = " << remote_id << ")";
+    return scoped_refptr<MessagePipeDispatcher>();
+  }
+  DVLOG(2) << "Deserializing message pipe dispatcher (remote ID = " << remote_id
+           << ", new local ID = " << local_id << ")";
+
+  channel->RunEndpoint(channel_endpoint, remote_id);
+
+  // TODO(vtl): FIXME -- Need some error handling here.
+  channel->RunRemoteMessagePipeEndpoint(local_id, remote_id);
+  return dispatcher;
+}
+
+MessagePipeDispatcher::~MessagePipeDispatcher() {
+  // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+  DCHECK(!message_pipe_.get());
+}
+
+MessagePipe* MessagePipeDispatcher::GetMessagePipeNoLock() const {
+  lock().AssertAcquired();
+  return message_pipe_.get();
+}
+
+unsigned MessagePipeDispatcher::GetPortNoLock() const {
+  lock().AssertAcquired();
+  return port_;
+}
+
+void MessagePipeDispatcher::CancelAllWaitersNoLock() {
+  lock().AssertAcquired();
+  message_pipe_->CancelAllWaiters(port_);
+}
+
+void MessagePipeDispatcher::CloseImplNoLock() {
+  lock().AssertAcquired();
+  message_pipe_->Close(port_);
+  message_pipe_ = nullptr;
+  port_ = kInvalidPort;
+}
+
+scoped_refptr<Dispatcher>
+MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+  lock().AssertAcquired();
+
+  // TODO(vtl): Currently, there are no options, so we just use
+  // |kDefaultCreateOptions|. Eventually, we'll have to duplicate the options
+  // too.
+  scoped_refptr<MessagePipeDispatcher> rv =
+      new MessagePipeDispatcher(kDefaultCreateOptions);
+  rv->Init(message_pipe_, port_);
+  message_pipe_ = nullptr;
+  port_ = kInvalidPort;
+  return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult MessagePipeDispatcher::WriteMessageImplNoLock(
+    UserPointer<const void> bytes,
+    uint32_t num_bytes,
+    std::vector<DispatcherTransport>* transports,
+    MojoWriteMessageFlags flags) {
+  DCHECK(!transports || (transports->size() > 0 &&
+                         transports->size() <= kMaxMessageNumHandles));
+
+  lock().AssertAcquired();
+
+  if (num_bytes > kMaxMessageNumBytes)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  return message_pipe_->WriteMessage(
+      port_, bytes, num_bytes, transports, flags);
+}
+
+MojoResult MessagePipeDispatcher::ReadMessageImplNoLock(
+    UserPointer<void> bytes,
+    UserPointer<uint32_t> num_bytes,
+    DispatcherVector* dispatchers,
+    uint32_t* num_dispatchers,
+    MojoReadMessageFlags flags) {
+  lock().AssertAcquired();
+  return message_pipe_->ReadMessage(
+      port_, bytes, num_bytes, dispatchers, num_dispatchers, flags);
+}
+
+HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateImplNoLock()
+    const {
+  lock().AssertAcquired();
+  return message_pipe_->GetHandleSignalsState(port_);
+}
+
+MojoResult MessagePipeDispatcher::AddWaiterImplNoLock(
+    Waiter* waiter,
+    MojoHandleSignals signals,
+    uint32_t context,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  return message_pipe_->AddWaiter(
+      port_, waiter, signals, context, signals_state);
+}
+
+void MessagePipeDispatcher::RemoveWaiterImplNoLock(
+    Waiter* waiter,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  message_pipe_->RemoveWaiter(port_, waiter, signals_state);
+}
+
+void MessagePipeDispatcher::StartSerializeImplNoLock(
+    Channel* /*channel*/,
+    size_t* max_size,
+    size_t* max_platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  *max_size = sizeof(SerializedMessagePipeDispatcher);
+  *max_platform_handles = 0;
+}
+
+bool MessagePipeDispatcher::EndSerializeAndCloseImplNoLock(
+    Channel* channel,
+    void* destination,
+    size_t* actual_size,
+    embedder::PlatformHandleVector* /*platform_handles*/) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+
+  // Convert the local endpoint to a proxy endpoint (moving the message queue)
+  // and attach it to the channel.
+  MessageInTransit::EndpointId endpoint_id =
+      channel->AttachEndpoint(message_pipe_->ConvertLocalToProxy(port_));
+  // Note: It's okay to get an endpoint ID of |kInvalidEndpointId|. (It's
+  // possible that the other endpoint -- the one that we're not sending -- was
+  // closed in the intervening time.) In that case, we need to deserialize a
+  // "dead" message pipe dispatcher on the other end. (Note that this is
+  // different from just producing |MOJO_HANDLE_INVALID|.)
+  DVLOG(2) << "Serializing message pipe dispatcher (local ID = " << endpoint_id
+           << ")";
+
+  // We now have a local ID. Before we can run the proxy endpoint, we need to
+  // get an ack back from the other side with the remote ID.
+  static_cast<SerializedMessagePipeDispatcher*>(destination)->endpoint_id =
+      endpoint_id;
+
+  message_pipe_ = nullptr;
+  port_ = kInvalidPort;
+
+  *actual_size = sizeof(SerializedMessagePipeDispatcher);
+  return true;
+}
+
+// MessagePipeDispatcherTransport ----------------------------------------------
+
+MessagePipeDispatcherTransport::MessagePipeDispatcherTransport(
+    DispatcherTransport transport)
+    : DispatcherTransport(transport) {
+  DCHECK_EQ(message_pipe_dispatcher()->GetType(), Dispatcher::kTypeMessagePipe);
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
new file mode 100644
index 0000000..de789ba
--- /dev/null
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -0,0 +1,135 @@
+// 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_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class ChannelEndpoint;
+class MessagePipe;
+class MessagePipeDispatcherTransport;
+
+// This is the |Dispatcher| implementation for message pipes (created by the
+// Mojo primitive |MojoCreateMessagePipe()|). This class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipeDispatcher : public Dispatcher {
+ public:
+  // The default options to use for |MojoCreateMessagePipe()|. (Real uses
+  // should obtain this via |ValidateCreateOptions()| with a null |in_options|;
+  // this is exposed directly for testing convenience.)
+  static const MojoCreateMessagePipeOptions kDefaultCreateOptions;
+
+  MessagePipeDispatcher(
+      const MojoCreateMessagePipeOptions& /*validated_options*/);
+
+  // Validates and/or sets default options for |MojoCreateMessagePipeOptions|.
+  // If non-null, |in_options| must point to a struct of at least
+  // |in_options->struct_size| bytes. |out_options| must point to a (current)
+  // |MojoCreateMessagePipeOptions| and will be entirely overwritten on success
+  // (it may be partly overwritten on failure).
+  static MojoResult ValidateCreateOptions(
+      UserPointer<const MojoCreateMessagePipeOptions> in_options,
+      MojoCreateMessagePipeOptions* out_options);
+
+  // Must be called before any other methods. (This method is not thread-safe.)
+  void Init(scoped_refptr<MessagePipe> message_pipe, unsigned port);
+
+  // |Dispatcher| public methods:
+  virtual Type GetType() const override;
+
+  // Creates a |MessagePipe| with a local endpoint (at port 0) and a proxy
+  // endpoint, and creates/initializes a |MessagePipeDispatcher| (attached to
+  // the message pipe, port 0).
+  // TODO(vtl): This currently uses |kDefaultCreateOptions|, which is okay since
+  // there aren't any options, but eventually options should be plumbed through.
+  static scoped_refptr<MessagePipeDispatcher> CreateRemoteMessagePipe(
+      scoped_refptr<ChannelEndpoint>* channel_endpoint);
+
+  // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+  // |Dispatcher::Deserialize()|.)
+  static scoped_refptr<MessagePipeDispatcher> Deserialize(Channel* channel,
+                                                          const void* source,
+                                                          size_t size);
+
+ private:
+  friend class MessagePipeDispatcherTransport;
+
+  virtual ~MessagePipeDispatcher();
+
+  // Gets a dumb pointer to |message_pipe_|. This must be called under the
+  // |Dispatcher| lock (that it's a dumb pointer is okay since it's under lock).
+  // This is needed when sending handles across processes, where nontrivial,
+  // invasive work needs to be done.
+  MessagePipe* GetMessagePipeNoLock() const;
+  // Similarly for the port.
+  unsigned GetPortNoLock() const;
+
+  // |Dispatcher| protected methods:
+  virtual void CancelAllWaitersNoLock() override;
+  virtual void CloseImplNoLock() override;
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  virtual MojoResult WriteMessageImplNoLock(
+      UserPointer<const void> bytes,
+      uint32_t num_bytes,
+      std::vector<DispatcherTransport>* transports,
+      MojoWriteMessageFlags flags) override;
+  virtual MojoResult ReadMessageImplNoLock(UserPointer<void> bytes,
+                                           UserPointer<uint32_t> num_bytes,
+                                           DispatcherVector* dispatchers,
+                                           uint32_t* num_dispatchers,
+                                           MojoReadMessageFlags flags) override;
+  virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const override;
+  virtual MojoResult AddWaiterImplNoLock(
+      Waiter* waiter,
+      MojoHandleSignals signals,
+      uint32_t context,
+      HandleSignalsState* signals_state) override;
+  virtual void RemoveWaiterImplNoLock(
+      Waiter* waiter,
+      HandleSignalsState* signals_state) override;
+  virtual void StartSerializeImplNoLock(Channel* channel,
+                                        size_t* max_size,
+                                        size_t* max_platform_handles) override;
+  virtual bool EndSerializeAndCloseImplNoLock(
+      Channel* channel,
+      void* destination,
+      size_t* actual_size,
+      embedder::PlatformHandleVector* platform_handles) override;
+
+  // Protected by |lock()|:
+  scoped_refptr<MessagePipe> message_pipe_;  // This will be null if closed.
+  unsigned port_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
+};
+
+class MessagePipeDispatcherTransport : public DispatcherTransport {
+ public:
+  explicit MessagePipeDispatcherTransport(DispatcherTransport transport);
+
+  MessagePipe* GetMessagePipe() {
+    return message_pipe_dispatcher()->GetMessagePipeNoLock();
+  }
+  unsigned GetPort() { return message_pipe_dispatcher()->GetPortNoLock(); }
+
+ private:
+  MessagePipeDispatcher* message_pipe_dispatcher() {
+    return static_cast<MessagePipeDispatcher*>(dispatcher());
+  }
+
+  // Copy and assign allowed.
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
diff --git a/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
new file mode 100644
index 0000000..96ca436
--- /dev/null
+++ b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
@@ -0,0 +1,729 @@
+// 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/rand_util.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(MessagePipeDispatcherTest, Basic) {
+  test::Stopwatch stopwatch;
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+  for (unsigned i = 0; i < 2; i++) {
+    scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    EXPECT_EQ(Dispatcher::kTypeMessagePipe, d0->GetType());
+    scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    {
+      scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+      d0->Init(mp, i);      // 0, 1.
+      d1->Init(mp, i ^ 1);  // 1, 0.
+    }
+    Waiter w;
+    uint32_t context = 0;
+    HandleSignalsState hss;
+
+    // Try adding a writable waiter when already writable.
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+    // Shouldn't need to remove the waiter (it was not added).
+
+    // Add a readable waiter to |d0|, then make it readable (by writing to
+    // |d1|), then wait.
+    w.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr));
+    buffer[0] = 123456789;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d1->WriteMessage(UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    stopwatch.Start();
+    EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+    EXPECT_EQ(1u, context);
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+    hss = HandleSignalsState();
+    d0->RemoveWaiter(&w, &hss);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    // Try adding a readable waiter when already readable (from above).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+    // Shouldn't need to remove the waiter (it was not added).
+
+    // Make |d0| no longer readable (by reading from it).
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d0->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(kBufferSize, buffer_size);
+    EXPECT_EQ(123456789, buffer[0]);
+
+    // Wait for zero time for readability on |d0| (will time out).
+    w.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr));
+    stopwatch.Start();
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+    hss = HandleSignalsState();
+    d0->RemoveWaiter(&w, &hss);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    // Wait for non-zero, finite time for readability on |d0| (will time out).
+    w.Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr));
+    stopwatch.Start();
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+              w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), nullptr));
+    base::TimeDelta elapsed = stopwatch.Elapsed();
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+    hss = HandleSignalsState();
+    d0->RemoveWaiter(&w, &hss);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+    EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+  }
+}
+
+TEST(MessagePipeDispatcherTest, InvalidParams) {
+  char buffer[1];
+
+  scoped_refptr<MessagePipeDispatcher> d0(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  scoped_refptr<MessagePipeDispatcher> d1(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    d0->Init(mp, 0);
+    d1->Init(mp, 1);
+  }
+
+  // |WriteMessage|:
+  // Huge buffer size.
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            d0->WriteMessage(UserPointer<const void>(buffer),
+                             std::numeric_limits<uint32_t>::max(),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+  EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+}
+
+// These test invalid arguments that should cause death if we're being paranoid
+// about checking arguments (which we would want to do if, e.g., we were in a
+// true "kernel" situation, but we might not want to do otherwise for
+// performance reasons). Probably blatant errors like passing in null pointers
+// (for required pointer arguments) will still cause death, but perhaps not
+// predictably.
+TEST(MessagePipeDispatcherTest, InvalidParamsDeath) {
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+
+  scoped_refptr<MessagePipeDispatcher> d0(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  scoped_refptr<MessagePipeDispatcher> d1(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    d0->Init(mp, 0);
+    d1->Init(mp, 1);
+  }
+
+  // |WriteMessage|:
+  // Null buffer with nonzero buffer size.
+  EXPECT_DEATH_IF_SUPPORTED(
+      d0->WriteMessage(
+          NullUserPointer(), 1, nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE),
+      kMemoryCheckFailedRegex);
+
+  // |ReadMessage|:
+  // Null buffer with nonzero buffer size.
+  // First write something so that we actually have something to read.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            d1->WriteMessage(UserPointer<const void>("x"),
+                             1,
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  uint32_t buffer_size = 1;
+  EXPECT_DEATH_IF_SUPPORTED(d0->ReadMessage(NullUserPointer(),
+                                            MakeUserPointer(&buffer_size),
+                                            0,
+                                            nullptr,
+                                            MOJO_READ_MESSAGE_FLAG_NONE),
+                            kMemoryCheckFailedRegex);
+
+  EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+  EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+}
+
+// Test what happens when one end is closed (single-threaded test).
+TEST(MessagePipeDispatcherTest, BasicClosed) {
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+  for (unsigned i = 0; i < 2; i++) {
+    scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    {
+      scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+      d0->Init(mp, i);      // 0, 1.
+      d1->Init(mp, i ^ 1);  // 1, 0.
+    }
+    Waiter w;
+    HandleSignalsState hss;
+
+    // Write (twice) to |d1|.
+    buffer[0] = 123456789;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d1->WriteMessage(UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    buffer[0] = 234567890;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d1->WriteMessage(UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Try waiting for readable on |d0|; should fail (already satisfied).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    // Try reading from |d1|; should fail (nothing to read).
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+              d1->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+
+    // Close |d1|.
+    EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+
+    // Try waiting for readable on |d0|; should fail (already satisfied).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 1, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read from |d0|.
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d0->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(kBufferSize, buffer_size);
+    EXPECT_EQ(123456789, buffer[0]);
+
+    // Try waiting for readable on |d0|; should fail (already satisfied).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+    // Read again from |d0|.
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d0->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(kBufferSize, buffer_size);
+    EXPECT_EQ(234567890, buffer[0]);
+
+    // Try waiting for readable on |d0|; should fail (unsatisfiable).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, &hss));
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    // Try waiting for writable on |d0|; should fail (unsatisfiable).
+    w.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss));
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    // Try reading from |d0|; should fail (nothing to read and other end
+    // closed).
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              d0->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+
+    // Try writing to |d0|; should fail (other end closed).
+    buffer[0] = 345678901;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              d0->WriteMessage(UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+  }
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/396386
+#define MAYBE_BasicThreaded DISABLED_BasicThreaded
+#else
+#define MAYBE_BasicThreaded BasicThreaded
+#endif
+TEST(MessagePipeDispatcherTest, MAYBE_BasicThreaded) {
+  test::Stopwatch stopwatch;
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+  base::TimeDelta elapsed;
+  bool did_wait;
+  MojoResult result;
+  uint32_t context;
+  HandleSignalsState hss;
+
+  // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+  for (unsigned i = 0; i < 2; i++) {
+    scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    {
+      scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+      d0->Init(mp, i);      // 0, 1.
+      d1->Init(mp, i ^ 1);  // 1, 0.
+    }
+
+    // Wait for readable on |d1|, which will become readable after some time.
+    {
+      test::WaiterThread thread(d1,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                1,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+      // Wake it up by writing to |d0|.
+      buffer[0] = 123456789;
+      EXPECT_EQ(MOJO_RESULT_OK,
+                d0->WriteMessage(UserPointer<const void>(buffer),
+                                 kBufferSize,
+                                 nullptr,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+    }  // Joins the thread.
+    elapsed = stopwatch.Elapsed();
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+    EXPECT_TRUE(did_wait);
+    EXPECT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(1u, context);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    // Now |d1| is already readable. Try waiting for it again.
+    {
+      test::WaiterThread thread(d1,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                2,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+    }  // Joins the thread.
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+    EXPECT_FALSE(did_wait);
+    EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    // Consume what we wrote to |d0|.
+    buffer[0] = 0;
+    buffer_size = kBufferSize;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              d1->ReadMessage(UserPointer<void>(buffer),
+                              MakeUserPointer(&buffer_size),
+                              0,
+                              nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(kBufferSize, buffer_size);
+    EXPECT_EQ(123456789, buffer[0]);
+
+    // Wait for readable on |d1| and close |d0| after some time, which should
+    // cancel that wait.
+    {
+      test::WaiterThread thread(d1,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                3,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+      EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+    }  // Joins the thread.
+    elapsed = stopwatch.Elapsed();
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+    EXPECT_TRUE(did_wait);
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+    EXPECT_EQ(3u, context);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+  }
+
+  for (unsigned i = 0; i < 2; i++) {
+    scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+        MessagePipeDispatcher::kDefaultCreateOptions));
+    {
+      scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+      d0->Init(mp, i);      // 0, 1.
+      d1->Init(mp, i ^ 1);  // 1, 0.
+    }
+
+    // Wait for readable on |d1| and close |d1| after some time, which should
+    // cancel that wait.
+    {
+      test::WaiterThread thread(d1,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                4,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+      EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+    }  // Joins the thread.
+    elapsed = stopwatch.Elapsed();
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+    EXPECT_TRUE(did_wait);
+    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+    EXPECT_EQ(4u, context);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+  }
+}
+
+// Stress test -----------------------------------------------------------------
+
+const size_t kMaxMessageSize = 2000;
+
+class WriterThread : public base::SimpleThread {
+ public:
+  // |*messages_written| and |*bytes_written| belong to the thread while it's
+  // alive.
+  WriterThread(scoped_refptr<Dispatcher> write_dispatcher,
+               size_t* messages_written,
+               size_t* bytes_written)
+      : base::SimpleThread("writer_thread"),
+        write_dispatcher_(write_dispatcher),
+        messages_written_(messages_written),
+        bytes_written_(bytes_written) {
+    *messages_written_ = 0;
+    *bytes_written_ = 0;
+  }
+
+  virtual ~WriterThread() { Join(); }
+
+ private:
+  virtual void Run() override {
+    // Make some data to write.
+    unsigned char buffer[kMaxMessageSize];
+    for (size_t i = 0; i < kMaxMessageSize; i++)
+      buffer[i] = static_cast<unsigned char>(i);
+
+    // Number of messages to write.
+    *messages_written_ = static_cast<size_t>(base::RandInt(1000, 6000));
+
+    // Write messages.
+    for (size_t i = 0; i < *messages_written_; i++) {
+      uint32_t bytes_to_write = static_cast<uint32_t>(
+          base::RandInt(1, static_cast<int>(kMaxMessageSize)));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                write_dispatcher_->WriteMessage(UserPointer<const void>(buffer),
+                                                bytes_to_write,
+                                                nullptr,
+                                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+      *bytes_written_ += bytes_to_write;
+    }
+
+    // Write one last "quit" message.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              write_dispatcher_->WriteMessage(UserPointer<const void>("quit"),
+                                              4,
+                                              nullptr,
+                                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  const scoped_refptr<Dispatcher> write_dispatcher_;
+  size_t* const messages_written_;
+  size_t* const bytes_written_;
+
+  DISALLOW_COPY_AND_ASSIGN(WriterThread);
+};
+
+class ReaderThread : public base::SimpleThread {
+ public:
+  // |*messages_read| and |*bytes_read| belong to the thread while it's alive.
+  ReaderThread(scoped_refptr<Dispatcher> read_dispatcher,
+               size_t* messages_read,
+               size_t* bytes_read)
+      : base::SimpleThread("reader_thread"),
+        read_dispatcher_(read_dispatcher),
+        messages_read_(messages_read),
+        bytes_read_(bytes_read) {
+    *messages_read_ = 0;
+    *bytes_read_ = 0;
+  }
+
+  virtual ~ReaderThread() { Join(); }
+
+ private:
+  virtual void Run() override {
+    unsigned char buffer[kMaxMessageSize];
+    Waiter w;
+    HandleSignalsState hss;
+    MojoResult result;
+
+    // Read messages.
+    for (;;) {
+      // Wait for it to be readable.
+      w.Init();
+      hss = HandleSignalsState();
+      result =
+          read_dispatcher_->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss);
+      EXPECT_TRUE(result == MOJO_RESULT_OK ||
+                  result == MOJO_RESULT_ALREADY_EXISTS)
+          << "result: " << result;
+      if (result == MOJO_RESULT_OK) {
+        // Actually need to wait.
+        EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+        read_dispatcher_->RemoveWaiter(&w, &hss);
+      }
+      // We may not actually be readable, since we're racing with other threads.
+      EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+
+      // Now, try to do the read.
+      // Clear the buffer so that we can check the result.
+      memset(buffer, 0, sizeof(buffer));
+      uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      result = read_dispatcher_->ReadMessage(UserPointer<void>(buffer),
+                                             MakeUserPointer(&buffer_size),
+                                             0,
+                                             nullptr,
+                                             MOJO_READ_MESSAGE_FLAG_NONE);
+      EXPECT_TRUE(result == MOJO_RESULT_OK || result == MOJO_RESULT_SHOULD_WAIT)
+          << "result: " << result;
+      // We're racing with others to read, so maybe we failed.
+      if (result == MOJO_RESULT_SHOULD_WAIT)
+        continue;  // In which case, try again.
+      // Check for quit.
+      if (buffer_size == 4 && memcmp("quit", buffer, 4) == 0)
+        return;
+      EXPECT_GE(buffer_size, 1u);
+      EXPECT_LE(buffer_size, kMaxMessageSize);
+      EXPECT_TRUE(IsValidMessage(buffer, buffer_size));
+
+      (*messages_read_)++;
+      *bytes_read_ += buffer_size;
+    }
+  }
+
+  static bool IsValidMessage(const unsigned char* buffer,
+                             uint32_t message_size) {
+    size_t i;
+    for (i = 0; i < message_size; i++) {
+      if (buffer[i] != static_cast<unsigned char>(i))
+        return false;
+    }
+    // Check that the remaining bytes weren't stomped on.
+    for (; i < kMaxMessageSize; i++) {
+      if (buffer[i] != 0)
+        return false;
+    }
+    return true;
+  }
+
+  const scoped_refptr<Dispatcher> read_dispatcher_;
+  size_t* const messages_read_;
+  size_t* const bytes_read_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReaderThread);
+};
+
+TEST(MessagePipeDispatcherTest, Stress) {
+  static const size_t kNumWriters = 30;
+  static const size_t kNumReaders = kNumWriters;
+
+  scoped_refptr<MessagePipeDispatcher> d_write(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  scoped_refptr<MessagePipeDispatcher> d_read(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    d_write->Init(mp, 0);
+    d_read->Init(mp, 1);
+  }
+
+  size_t messages_written[kNumWriters];
+  size_t bytes_written[kNumWriters];
+  size_t messages_read[kNumReaders];
+  size_t bytes_read[kNumReaders];
+  {
+    // Make writers.
+    ScopedVector<WriterThread> writers;
+    for (size_t i = 0; i < kNumWriters; i++) {
+      writers.push_back(
+          new WriterThread(d_write, &messages_written[i], &bytes_written[i]));
+    }
+
+    // Make readers.
+    ScopedVector<ReaderThread> readers;
+    for (size_t i = 0; i < kNumReaders; i++) {
+      readers.push_back(
+          new ReaderThread(d_read, &messages_read[i], &bytes_read[i]));
+    }
+
+    // Start writers.
+    for (size_t i = 0; i < kNumWriters; i++)
+      writers[i]->Start();
+
+    // Start readers.
+    for (size_t i = 0; i < kNumReaders; i++)
+      readers[i]->Start();
+
+    // TODO(vtl): Maybe I should have an event that triggers all the threads to
+    // start doing stuff for real (so that the first ones created/started aren't
+    // advantaged).
+  }  // Joins all the threads.
+
+  size_t total_messages_written = 0;
+  size_t total_bytes_written = 0;
+  for (size_t i = 0; i < kNumWriters; i++) {
+    total_messages_written += messages_written[i];
+    total_bytes_written += bytes_written[i];
+  }
+  size_t total_messages_read = 0;
+  size_t total_bytes_read = 0;
+  for (size_t i = 0; i < kNumReaders; i++) {
+    total_messages_read += messages_read[i];
+    total_bytes_read += bytes_read[i];
+    // We'd have to be really unlucky to have read no messages on a thread.
+    EXPECT_GT(messages_read[i], 0u) << "reader: " << i;
+    EXPECT_GE(bytes_read[i], messages_read[i]) << "reader: " << i;
+  }
+  EXPECT_EQ(total_messages_written, total_messages_read);
+  EXPECT_EQ(total_bytes_written, total_bytes_read);
+
+  EXPECT_EQ(MOJO_RESULT_OK, d_write->Close());
+  EXPECT_EQ(MOJO_RESULT_OK, d_read->Close());
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_endpoint.cc b/mojo/edk/system/message_pipe_endpoint.cc
new file mode 100644
index 0000000..df623d4
--- /dev/null
+++ b/mojo/edk/system/message_pipe_endpoint.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/edk/system/message_pipe_endpoint.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+void MessagePipeEndpoint::CancelAllWaiters() {
+  NOTREACHED();
+}
+
+MojoResult MessagePipeEndpoint::ReadMessage(UserPointer<void> /*bytes*/,
+                                            UserPointer<uint32_t> /*num_bytes*/,
+                                            DispatcherVector* /*dispatchers*/,
+                                            uint32_t* /*num_dispatchers*/,
+                                            MojoReadMessageFlags /*flags*/) {
+  NOTREACHED();
+  return MOJO_RESULT_INTERNAL;
+}
+
+HandleSignalsState MessagePipeEndpoint::GetHandleSignalsState() const {
+  NOTREACHED();
+  return HandleSignalsState();
+}
+
+MojoResult MessagePipeEndpoint::AddWaiter(Waiter* /*waiter*/,
+                                          MojoHandleSignals /*signals*/,
+                                          uint32_t /*context*/,
+                                          HandleSignalsState* signals_state) {
+  NOTREACHED();
+  if (signals_state)
+    *signals_state = HandleSignalsState();
+  return MOJO_RESULT_INTERNAL;
+}
+
+void MessagePipeEndpoint::RemoveWaiter(Waiter* /*waiter*/,
+                                       HandleSignalsState* signals_state) {
+  NOTREACHED();
+  if (signals_state)
+    *signals_state = HandleSignalsState();
+}
+
+void MessagePipeEndpoint::Attach(ChannelEndpoint* /*channel_endpoint*/) {
+  NOTREACHED();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_endpoint.h b/mojo/edk/system/message_pipe_endpoint.h
new file mode 100644
index 0000000..7bc0a0d
--- /dev/null
+++ b/mojo/edk/system/message_pipe_endpoint.h
@@ -0,0 +1,89 @@
+// 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_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+class ChannelEndpoint;
+class Waiter;
+
+// This is an interface to one of the ends of a message pipe, and is used by
+// |MessagePipe|. Its most important role is to provide a sink for messages
+// (i.e., a place where messages can be sent). It has a secondary role: When the
+// endpoint is local (i.e., in the current process), there'll be a dispatcher
+// corresponding to the endpoint. In that case, the implementation of
+// |MessagePipeEndpoint| also implements the functionality required by the
+// dispatcher, e.g., to read messages and to wait. Implementations of this class
+// are not thread-safe; instances are protected by |MesssagePipe|'s lock.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipeEndpoint {
+ public:
+  virtual ~MessagePipeEndpoint() {}
+
+  enum Type { kTypeLocal, kTypeProxy };
+  virtual Type GetType() const = 0;
+
+  // All implementations must implement these.
+  // Returns false if the endpoint should be closed and destroyed, else true.
+  virtual bool OnPeerClose() = 0;
+  // Implements |MessagePipe::EnqueueMessage()|. The major differences are that:
+  //  a) Dispatchers have been vetted and cloned/attached to the message.
+  //  b) At this point, we cannot report failure (if, e.g., a channel is torn
+  //     down at this point, we should silently swallow the message).
+  virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) = 0;
+  virtual void Close() = 0;
+
+  // Implementations must override these if they represent a local endpoint,
+  // i.e., one for which there's a |MessagePipeDispatcher| (and thus a handle).
+  // An implementation for a proxy endpoint (for which there's no dispatcher)
+  // needs not override these methods, since they should never be called.
+  //
+  // These methods implement the methods of the same name in |MessagePipe|,
+  // though |MessagePipe|'s implementation may have to do a little more if the
+  // operation involves both endpoints.
+  virtual void CancelAllWaiters();
+  virtual MojoResult ReadMessage(UserPointer<void> bytes,
+                                 UserPointer<uint32_t> num_bytes,
+                                 DispatcherVector* dispatchers,
+                                 uint32_t* num_dispatchers,
+                                 MojoReadMessageFlags flags);
+  virtual HandleSignalsState GetHandleSignalsState() const;
+  virtual MojoResult AddWaiter(Waiter* waiter,
+                               MojoHandleSignals signals,
+                               uint32_t context,
+                               HandleSignalsState* signals_state);
+  virtual void RemoveWaiter(Waiter* waiter, HandleSignalsState* signals_state);
+
+  // Implementations must override these if they represent a proxy endpoint. An
+  // implementation for a local endpoint needs not override these methods, since
+  // they should never be called.
+  virtual void Attach(ChannelEndpoint* channel_endpoint);
+
+ protected:
+  MessagePipeEndpoint() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeEndpoint);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc
new file mode 100644
index 0000000..9861e0b
--- /dev/null
+++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -0,0 +1,180 @@
+// Copyright 2014 The Chromium 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "base/time/time.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/local_message_pipe_endpoint.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/message_pipe_test_utils.h"
+#include "mojo/edk/system/proxy_message_pipe_endpoint.h"
+#include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class MultiprocessMessagePipePerfTest
+    : public test::MultiprocessMessagePipeTestBase {
+ public:
+  MultiprocessMessagePipePerfTest() : message_count_(0), message_size_(0) {}
+
+  void SetUpMeasurement(int message_count, size_t message_size) {
+    message_count_ = message_count;
+    message_size_ = message_size;
+    payload_ = Pickle();
+    payload_.WriteString(std::string(message_size, '*'));
+    read_buffer_.resize(message_size * 2);
+  }
+
+ protected:
+  void WriteWaitThenRead(scoped_refptr<MessagePipe> mp) {
+    CHECK_EQ(mp->WriteMessage(0,
+                              UserPointer<const void>(payload_.data()),
+                              static_cast<uint32_t>(payload_.size()),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    HandleSignalsState hss;
+    CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
+             MOJO_RESULT_OK);
+    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size());
+    CHECK_EQ(mp->ReadMessage(0,
+                             UserPointer<void>(&read_buffer_[0]),
+                             MakeUserPointer(&read_buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    CHECK_EQ(read_buffer_size, static_cast<uint32_t>(payload_.size()));
+  }
+
+  void SendQuitMessage(scoped_refptr<MessagePipe> mp) {
+    CHECK_EQ(mp->WriteMessage(0,
+                              UserPointer<const void>(""),
+                              0,
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  }
+
+  void Measure(scoped_refptr<MessagePipe> mp) {
+    // Have one ping-pong to ensure channel being established.
+    WriteWaitThenRead(mp);
+
+    std::string test_name =
+        base::StringPrintf("IPC_Perf_%dx_%u",
+                           message_count_,
+                           static_cast<unsigned>(message_size_));
+    base::PerfTimeLogger logger(test_name.c_str());
+
+    for (int i = 0; i < message_count_; ++i)
+      WriteWaitThenRead(mp);
+
+    logger.Done();
+  }
+
+ private:
+  int message_count_;
+  size_t message_size_;
+  Pickle payload_;
+  std::string read_buffer_;
+  scoped_ptr<base::PerfTimeLogger> perf_logger_;
+};
+
+// For each message received, sends a reply message with the same contents
+// repeated twice, until the other end is closed or it receives "quitquitquit"
+// (which it doesn't reply to). It'll return the number of messages received,
+// not including any "quitquitquit" message, modulo 100.
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(PingPongClient) {
+  embedder::SimplePlatformSupport platform_support;
+  test::ChannelThread channel_thread(&platform_support);
+  embedder::ScopedPlatformHandle client_platform_handle =
+      mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+  CHECK(client_platform_handle.is_valid());
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  channel_thread.Start(client_platform_handle.Pass(), ep);
+
+  std::string buffer(1000000, '\0');
+  int rv = 0;
+  while (true) {
+    // Wait for our end of the message pipe to be readable.
+    HandleSignalsState hss;
+    MojoResult result =
+        test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss);
+    if (result != MOJO_RESULT_OK) {
+      rv = result;
+      break;
+    }
+
+    uint32_t read_size = static_cast<uint32_t>(buffer.size());
+    CHECK_EQ(mp->ReadMessage(0,
+                             UserPointer<void>(&buffer[0]),
+                             MakeUserPointer(&read_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+
+    // Empty message indicates quitting
+    if (0 == read_size)
+      break;
+
+    CHECK_EQ(mp->WriteMessage(0,
+                              UserPointer<const void>(&buffer[0]),
+                              static_cast<uint32_t>(read_size),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  }
+
+  mp->Close(0);
+  return rv;
+}
+
+// Repeatedly sends messages as previous one got replied by the child.
+// Waits for the child to close its end before quitting once specified
+// number of messages has been sent.
+TEST_F(MultiprocessMessagePipePerfTest, PingPong) {
+  helper()->StartChild("PingPongClient");
+
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  Init(ep);
+
+  // This values are set to align with one at ipc_pertests.cc for comparison.
+  const size_t kMsgSize[5] = {12, 144, 1728, 20736, 248832};
+  const int kMessageCount[5] = {50000, 50000, 50000, 12000, 1000};
+
+  for (size_t i = 0; i < 5; i++) {
+    SetUpMeasurement(kMessageCount[i], kMsgSize[i]);
+    Measure(mp);
+  }
+
+  SendQuitMessage(mp);
+  mp->Close(0);
+  EXPECT_EQ(0, helper()->WaitForChildShutdown());
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_test_utils.cc b/mojo/edk/system/message_pipe_test_utils.cc
new file mode 100644
index 0000000..b600945
--- /dev/null
+++ b/mojo/edk/system/message_pipe_test_utils.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/edk/system/message_pipe_test_utils.h"
+
+#include "base/bind.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/waiter.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+MojoResult WaitIfNecessary(scoped_refptr<MessagePipe> mp,
+                           MojoHandleSignals signals,
+                           HandleSignalsState* signals_state) {
+  Waiter waiter;
+  waiter.Init();
+
+  MojoResult add_result = mp->AddWaiter(0, &waiter, signals, 0, signals_state);
+  if (add_result != MOJO_RESULT_OK) {
+    return (add_result == MOJO_RESULT_ALREADY_EXISTS) ? MOJO_RESULT_OK
+                                                      : add_result;
+  }
+
+  MojoResult wait_result = waiter.Wait(MOJO_DEADLINE_INDEFINITE, nullptr);
+  mp->RemoveWaiter(0, &waiter, signals_state);
+  return wait_result;
+}
+
+ChannelThread::ChannelThread(embedder::PlatformSupport* platform_support)
+    : platform_support_(platform_support),
+      test_io_thread_(base::TestIOThread::kManualStart) {
+}
+
+ChannelThread::~ChannelThread() {
+  Stop();
+}
+
+void ChannelThread::Start(embedder::ScopedPlatformHandle platform_handle,
+                          scoped_refptr<ChannelEndpoint> channel_endpoint) {
+  test_io_thread_.Start();
+  test_io_thread_.PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&ChannelThread::InitChannelOnIOThread,
+                 base::Unretained(this),
+                 base::Passed(&platform_handle),
+                 channel_endpoint));
+}
+
+void ChannelThread::Stop() {
+  if (channel_.get()) {
+    // Hack to flush write buffers before quitting.
+    // TODO(vtl): Remove this once |Channel| has a
+    // |FlushWriteBufferAndShutdown()| (or whatever).
+    while (!channel_->IsWriteBufferEmpty())
+      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+    test_io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&ChannelThread::ShutdownChannelOnIOThread,
+                   base::Unretained(this)));
+  }
+  test_io_thread_.Stop();
+}
+
+void ChannelThread::InitChannelOnIOThread(
+    embedder::ScopedPlatformHandle platform_handle,
+    scoped_refptr<ChannelEndpoint> channel_endpoint) {
+  CHECK_EQ(base::MessageLoop::current(), test_io_thread_.message_loop());
+  CHECK(platform_handle.is_valid());
+
+  // Create and initialize |Channel|.
+  channel_ = new Channel(platform_support_);
+  CHECK(channel_->Init(RawChannel::Create(platform_handle.Pass())));
+
+  // Attach the message pipe endpoint.
+  // Note: On the "server" (parent process) side, we need not attach the
+  // message pipe endpoint immediately. However, on the "client" (child
+  // process) side, this *must* be done here -- otherwise, the |Channel| may
+  // receive/process messages (which it can do as soon as it's hooked up to
+  // the IO thread message loop, and that message loop runs) before the
+  // message pipe endpoint is attached.
+  CHECK_EQ(channel_->AttachEndpoint(channel_endpoint),
+           Channel::kBootstrapEndpointId);
+  channel_->RunEndpoint(channel_endpoint, Channel::kBootstrapEndpointId);
+}
+
+void ChannelThread::ShutdownChannelOnIOThread() {
+  CHECK(channel_.get());
+  channel_->Shutdown();
+  channel_ = nullptr;
+}
+
+#if !defined(OS_IOS)
+MultiprocessMessagePipeTestBase::MultiprocessMessagePipeTestBase()
+    : channel_thread_(&platform_support_) {
+}
+
+MultiprocessMessagePipeTestBase::~MultiprocessMessagePipeTestBase() {
+}
+
+void MultiprocessMessagePipeTestBase::Init(scoped_refptr<ChannelEndpoint> ep) {
+  channel_thread_.Start(helper_.server_platform_handle.Pass(), ep);
+}
+#endif
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_test_utils.h b/mojo/edk/system/message_pipe_test_utils.h
new file mode 100644
index 0000000..9e6a4b7
--- /dev/null
+++ b/mojo/edk/system/message_pipe_test_utils.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_
+
+#include "base/test/test_io_thread.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class ChannelEndpoint;
+class MessagePipe;
+
+namespace test {
+
+MojoResult WaitIfNecessary(scoped_refptr<MessagePipe> mp,
+                           MojoHandleSignals signals,
+                           HandleSignalsState* signals_state);
+
+class ChannelThread {
+ public:
+  explicit ChannelThread(embedder::PlatformSupport* platform_support);
+  ~ChannelThread();
+
+  void Start(embedder::ScopedPlatformHandle platform_handle,
+             scoped_refptr<ChannelEndpoint> channel_endpoint);
+  void Stop();
+
+ private:
+  void InitChannelOnIOThread(embedder::ScopedPlatformHandle platform_handle,
+                             scoped_refptr<ChannelEndpoint> channel_endpoint);
+  void ShutdownChannelOnIOThread();
+
+  embedder::PlatformSupport* const platform_support_;
+  base::TestIOThread test_io_thread_;
+  scoped_refptr<Channel> channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelThread);
+};
+
+#if !defined(OS_IOS)
+class MultiprocessMessagePipeTestBase : public testing::Test {
+ public:
+  MultiprocessMessagePipeTestBase();
+  virtual ~MultiprocessMessagePipeTestBase();
+
+ protected:
+  void Init(scoped_refptr<ChannelEndpoint> ep);
+
+  embedder::PlatformSupport* platform_support() { return &platform_support_; }
+  mojo::test::MultiprocessTestHelper* helper() { return &helper_; }
+
+ private:
+  embedder::SimplePlatformSupport platform_support_;
+  ChannelThread channel_thread_;
+  mojo::test::MultiprocessTestHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiprocessMessagePipeTestBase);
+};
+#endif
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
new file mode 100644
index 0000000..dbffb4c
--- /dev/null
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -0,0 +1,626 @@
+// 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/edk/system/message_pipe.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Tests:
+//  - only default flags
+//  - reading messages from a port
+//    - when there are no/one/two messages available for that port
+//    - with buffer size 0 (and null buffer) -- should get size
+//    - with too-small buffer -- should get size
+//    - also verify that buffers aren't modified when/where they shouldn't be
+//  - writing messages to a port
+//    - in the obvious scenarios (as above)
+//    - to a port that's been closed
+//  - writing a message to a port, closing the other (would be the source) port,
+//    and reading it
+TEST(MessagePipeTest, Basic) {
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+
+  int32_t buffer[2];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Nothing to read yet on port 0.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kBufferSize, buffer_size);
+  EXPECT_EQ(123, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Ditto for port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 789012345;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read from port 0.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(789012345, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Write two messages from port 0 (to port 1).
+  buffer[0] = 123456789;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  buffer[0] = 234567890;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read from port 1 with buffer size 0 (should get the size of next message).
+  // Also test that giving a null buffer is okay when the buffer size is 0.
+  buffer_size = 0;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(1,
+                            NullUserPointer(),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+  // Read from port 1 with buffer size 1 (too small; should get the size of next
+  // message).
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(123, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read from port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(123456789, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read again from port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(234567890, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read again from port 1 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Write from port 0 (to port 1).
+  buffer[0] = 345678901;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Close port 0.
+  mp->Close(0);
+
+  // Try to write from port 1 (to port 0).
+  buffer[0] = 456789012;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read from port 1; should still get message (even though port 0 was closed).
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(345678901, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read again from port 1 -- it should be empty (and port 0 is closed).
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+
+  mp->Close(1);
+}
+
+TEST(MessagePipeTest, CloseWithQueuedIncomingMessages) {
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Write some messages from port 1 (to port 0).
+  for (int32_t i = 0; i < 5; i++) {
+    buffer[0] = i;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp->WriteMessage(1,
+                               UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  // Port 0 shouldn't be empty.
+  buffer_size = 0;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(0,
+                            NullUserPointer(),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kBufferSize, buffer_size);
+
+  // Close port 0 first, which should have outstanding (incoming) messages.
+  mp->Close(0);
+  mp->Close(1);
+}
+
+TEST(MessagePipeTest, DiscardMode) {
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+
+  int32_t buffer[2];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 789012345;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read/discard from port 0 (no buffer); get size.
+  buffer_size = 0;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(0,
+                            NullUserPointer(),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 890123456;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read from port 0 (buffer big enough).
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  EXPECT_EQ(890123456, buffer[0]);
+  EXPECT_EQ(456, buffer[1]);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 901234567;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Read/discard from port 0 (buffer too small); get size.
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 123456789;
+  buffer[1] = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(1,
+                             UserPointer<const void>(buffer),
+                             static_cast<uint32_t>(sizeof(buffer[0])),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Discard from port 0.
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            mp->ReadMessage(0,
+                            NullUserPointer(),
+                            NullUserPointer(),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp->ReadMessage(0,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  mp->Close(0);
+  mp->Close(1);
+}
+
+TEST(MessagePipeTest, BasicWaiting) {
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+  Waiter waiter;
+  HandleSignalsState hss;
+
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+  uint32_t buffer_size;
+
+  // Always writable (until the other port is closed).
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      mp->AddWaiter(0,
+                    &waiter,
+                    MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+                    0,
+                    &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Not yet readable.
+  waiter.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  mp->RemoveWaiter(0, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Write from port 0 (to port 1), to make port 1 readable.
+  buffer[0] = 123456789;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(buffer),
+                             kBufferSize,
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Port 1 should already be readable now.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(
+      MOJO_RESULT_ALREADY_EXISTS,
+      mp->AddWaiter(1,
+                    &waiter,
+                    MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+                    0,
+                    &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  // ... and still writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 3, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Close port 0.
+  mp->Close(0);
+
+  // Now port 1 should not be writable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // But it should still be readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 5, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Read from port 1.
+  buffer[0] = 0;
+  buffer_size = kBufferSize;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(1,
+                            UserPointer<void>(buffer),
+                            MakeUserPointer(&buffer_size),
+                            0,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(123456789, buffer[0]);
+
+  // Now port 1 should no longer be readable.
+  waiter.Init();
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 6, nullptr));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(1);
+}
+
+TEST(MessagePipeTest, ThreadedWaiting) {
+  int32_t buffer[1];
+  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+
+  MojoResult result;
+  uint32_t context;
+
+  // Write to wake up waiter waiting for read.
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    test::SimpleWaiterThread thread(&result, &context);
+
+    thread.waiter()->Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              mp->AddWaiter(
+                  1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr));
+    thread.Start();
+
+    buffer[0] = 123456789;
+    // Write from port 0 (to port 1), which should wake up the waiter.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp->WriteMessage(0,
+                               UserPointer<const void>(buffer),
+                               kBufferSize,
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    HandleSignalsState hss;
+    mp->RemoveWaiter(1, thread.waiter(), &hss);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+              hss.satisfiable_signals);
+
+    mp->Close(0);
+    mp->Close(1);
+  }  // Joins |thread|.
+  // The waiter should have woken up successfully.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(1u, context);
+
+  // Close to cancel waiter.
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    test::SimpleWaiterThread thread(&result, &context);
+
+    thread.waiter()->Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              mp->AddWaiter(
+                  1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 2, nullptr));
+    thread.Start();
+
+    // Close port 1 first -- this should result in the waiter being cancelled.
+    mp->CancelAllWaiters(1);
+    mp->Close(1);
+
+    // Port 1 is closed, so |Dispatcher::RemoveWaiter()| wouldn't call into the
+    // |MessagePipe| to remove any waiter.
+
+    mp->Close(0);
+  }  // Joins |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(2u, context);
+
+  // Close to make waiter un-wake-up-able.
+  {
+    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal());
+    test::SimpleWaiterThread thread(&result, &context);
+
+    thread.waiter()->Init();
+    ASSERT_EQ(MOJO_RESULT_OK,
+              mp->AddWaiter(
+                  1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr));
+    thread.Start();
+
+    // Close port 0 first -- this should wake the waiter up, since port 1 will
+    // never be readable.
+    mp->CancelAllWaiters(0);
+    mp->Close(0);
+
+    HandleSignalsState hss;
+    mp->RemoveWaiter(1, thread.waiter(), &hss);
+    EXPECT_EQ(0u, hss.satisfied_signals);
+    EXPECT_EQ(0u, hss.satisfiable_signals);
+
+    mp->CancelAllWaiters(1);
+    mp->Close(1);
+  }  // Joins |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(3u, context);
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
new file mode 100644
index 0000000..2f22d67
--- /dev/null
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -0,0 +1,523 @@
+// 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"  // TODO(vtl): Remove this.
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/message_pipe_test_utils.h"
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+#include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/shared_buffer_dispatcher.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class MultiprocessMessagePipeTest
+    : public test::MultiprocessMessagePipeTestBase {};
+
+// For each message received, sends a reply message with the same contents
+// repeated twice, until the other end is closed or it receives "quitquitquit"
+// (which it doesn't reply to). It'll return the number of messages received,
+// not including any "quitquitquit" message, modulo 100.
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(EchoEcho) {
+  embedder::SimplePlatformSupport platform_support;
+  test::ChannelThread channel_thread(&platform_support);
+  embedder::ScopedPlatformHandle client_platform_handle =
+      mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+  CHECK(client_platform_handle.is_valid());
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  channel_thread.Start(client_platform_handle.Pass(), ep);
+
+  const std::string quitquitquit("quitquitquit");
+  int rv = 0;
+  for (;; rv = (rv + 1) % 100) {
+    // Wait for our end of the message pipe to be readable.
+    HandleSignalsState hss;
+    MojoResult result =
+        test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss);
+    if (result != MOJO_RESULT_OK) {
+      // It was closed, probably.
+      CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
+      CHECK_EQ(hss.satisfied_signals, 0u);
+      CHECK_EQ(hss.satisfiable_signals, 0u);
+      break;
+    } else {
+      CHECK((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+      CHECK((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+    }
+
+    std::string read_buffer(1000, '\0');
+    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+    CHECK_EQ(mp->ReadMessage(0,
+                             UserPointer<void>(&read_buffer[0]),
+                             MakeUserPointer(&read_buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+    VLOG(2) << "Child got: " << read_buffer;
+
+    if (read_buffer == quitquitquit) {
+      VLOG(2) << "Child quitting.";
+      break;
+    }
+
+    std::string write_buffer = read_buffer + read_buffer;
+    CHECK_EQ(mp->WriteMessage(0,
+                              UserPointer<const void>(write_buffer.data()),
+                              static_cast<uint32_t>(write_buffer.size()),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  }
+
+  mp->Close(0);
+  return rv;
+}
+
+// Sends "hello" to child, and expects "hellohello" back.
+TEST_F(MultiprocessMessagePipeTest, Basic) {
+  helper()->StartChild("EchoEcho");
+
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  Init(ep);
+
+  std::string hello("hello");
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(hello.data()),
+                             static_cast<uint32_t>(hello.size()),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+  // The child may or may not have closed its end of the message pipe and died
+  // (and we may or may not know it yet), so our end may or may not appear as
+  // writable.
+  EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+
+  std::string read_buffer(1000, '\0');
+  uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+  CHECK_EQ(mp->ReadMessage(0,
+                           UserPointer<void>(&read_buffer[0]),
+                           MakeUserPointer(&read_buffer_size),
+                           nullptr,
+                           nullptr,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(read_buffer_size);
+  VLOG(2) << "Parent got: " << read_buffer;
+  EXPECT_EQ(hello + hello, read_buffer);
+
+  mp->Close(0);
+
+  // We sent one message.
+  EXPECT_EQ(1 % 100, helper()->WaitForChildShutdown());
+}
+
+// Sends a bunch of messages to the child. Expects them "repeated" back. Waits
+// for the child to close its end before quitting.
+TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
+  helper()->StartChild("EchoEcho");
+
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  Init(ep);
+
+  static const size_t kNumMessages = 1001;
+  for (size_t i = 0; i < kNumMessages; i++) {
+    std::string write_buffer(i, 'A' + (i % 26));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp->WriteMessage(0,
+                               UserPointer<const void>(write_buffer.data()),
+                               static_cast<uint32_t>(write_buffer.size()),
+                               nullptr,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  const std::string quitquitquit("quitquitquit");
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(quitquitquit.data()),
+                             static_cast<uint32_t>(quitquitquit.size()),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  for (size_t i = 0; i < kNumMessages; i++) {
+    HandleSignalsState hss;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+    // The child may or may not have closed its end of the message pipe and died
+    // (and we may or may not know it yet), so our end may or may not appear as
+    // writable.
+    EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+    EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+
+    std::string read_buffer(kNumMessages * 2, '\0');
+    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+    CHECK_EQ(mp->ReadMessage(0,
+                             UserPointer<void>(&read_buffer[0]),
+                             MakeUserPointer(&read_buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+
+    EXPECT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
+  }
+
+  // Wait for it to become readable, which should fail (since we sent
+  // "quitquitquit").
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(0);
+
+  EXPECT_EQ(static_cast<int>(kNumMessages % 100),
+            helper()->WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) {
+  embedder::SimplePlatformSupport platform_support;
+  test::ChannelThread channel_thread(&platform_support);
+  embedder::ScopedPlatformHandle client_platform_handle =
+      mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+  CHECK(client_platform_handle.is_valid());
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  channel_thread.Start(client_platform_handle.Pass(), ep);
+
+  // Wait for the first message from our parent.
+  HandleSignalsState hss;
+  CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
+           MOJO_RESULT_OK);
+  // In this test, the parent definitely doesn't close its end of the message
+  // pipe before we do.
+  CHECK_EQ(hss.satisfied_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+  CHECK_EQ(hss.satisfiable_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // It should have a shared buffer.
+  std::string read_buffer(100, '\0');
+  uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+  DispatcherVector dispatchers;
+  uint32_t num_dispatchers = 10;  // Maximum number to receive.
+  CHECK_EQ(mp->ReadMessage(0,
+                           UserPointer<void>(&read_buffer[0]),
+                           MakeUserPointer(&num_bytes),
+                           &dispatchers,
+                           &num_dispatchers,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(num_bytes);
+  CHECK_EQ(read_buffer, std::string("go 1"));
+  CHECK_EQ(num_dispatchers, 1u);
+
+  CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypeSharedBuffer);
+
+  scoped_refptr<SharedBufferDispatcher> dispatcher(
+      static_cast<SharedBufferDispatcher*>(dispatchers[0].get()));
+
+  // Make a mapping.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
+  CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping),
+           MOJO_RESULT_OK);
+  CHECK(mapping);
+  CHECK(mapping->GetBase());
+  CHECK_EQ(mapping->GetLength(), 100u);
+
+  // Write some stuff to the shared buffer.
+  static const char kHello[] = "hello";
+  memcpy(mapping->GetBase(), kHello, sizeof(kHello));
+
+  // We should be able to close the dispatcher now.
+  dispatcher->Close();
+
+  // And send a message to signal that we've written stuff.
+  const std::string go2("go 2");
+  CHECK_EQ(mp->WriteMessage(0,
+                            UserPointer<const void>(&go2[0]),
+                            static_cast<uint32_t>(go2.size()),
+                            nullptr,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+
+  // Now wait for our parent to send us a message.
+  hss = HandleSignalsState();
+  CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
+           MOJO_RESULT_OK);
+  CHECK_EQ(hss.satisfied_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+  CHECK_EQ(hss.satisfiable_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  read_buffer = std::string(100, '\0');
+  num_bytes = static_cast<uint32_t>(read_buffer.size());
+  CHECK_EQ(mp->ReadMessage(0,
+                           UserPointer<void>(&read_buffer[0]),
+                           MakeUserPointer(&num_bytes),
+                           nullptr,
+                           nullptr,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(num_bytes);
+  CHECK_EQ(read_buffer, std::string("go 3"));
+
+  // It should have written something to the shared buffer.
+  static const char kWorld[] = "world!!!";
+  CHECK_EQ(memcmp(mapping->GetBase(), kWorld, sizeof(kWorld)), 0);
+
+  // And we're done.
+  mp->Close(0);
+
+  return 0;
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_SharedBufferPassing SharedBufferPassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
+#endif
+TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) {
+  helper()->StartChild("CheckSharedBuffer");
+
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  Init(ep);
+
+  // Make a shared buffer.
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher));
+  ASSERT_TRUE(dispatcher.get());
+
+  // Make a mapping.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  ASSERT_TRUE(mapping);
+  ASSERT_TRUE(mapping->GetBase());
+  ASSERT_EQ(100u, mapping->GetLength());
+
+  // Send the shared buffer.
+  const std::string go1("go 1");
+  DispatcherTransport transport(
+      test::DispatcherTryStartTransport(dispatcher.get()));
+  ASSERT_TRUE(transport.is_valid());
+
+  std::vector<DispatcherTransport> transports;
+  transports.push_back(transport);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(&go1[0]),
+                             static_cast<uint32_t>(go1.size()),
+                             &transports,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  transport.End();
+
+  EXPECT_TRUE(dispatcher->HasOneRef());
+  dispatcher = nullptr;
+
+  // Wait for a message from the child.
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+  EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+
+  std::string read_buffer(100, '\0');
+  uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->ReadMessage(0,
+                            UserPointer<void>(&read_buffer[0]),
+                            MakeUserPointer(&num_bytes),
+                            nullptr,
+                            nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  read_buffer.resize(num_bytes);
+  EXPECT_EQ(std::string("go 2"), read_buffer);
+
+  // After we get it, the child should have written something to the shared
+  // buffer.
+  static const char kHello[] = "hello";
+  EXPECT_EQ(0, memcmp(mapping->GetBase(), kHello, sizeof(kHello)));
+
+  // Now we'll write some stuff to the shared buffer.
+  static const char kWorld[] = "world!!!";
+  memcpy(mapping->GetBase(), kWorld, sizeof(kWorld));
+
+  // And send a message to signal that we've written stuff.
+  const std::string go3("go 3");
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(&go3[0]),
+                             static_cast<uint32_t>(go3.size()),
+                             nullptr,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Wait for |mp| to become readable, which should fail.
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(0);
+
+  EXPECT_EQ(0, helper()->WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckPlatformHandleFile) {
+  embedder::SimplePlatformSupport platform_support;
+  test::ChannelThread channel_thread(&platform_support);
+  embedder::ScopedPlatformHandle client_platform_handle =
+      mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+  CHECK(client_platform_handle.is_valid());
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  channel_thread.Start(client_platform_handle.Pass(), ep);
+
+  HandleSignalsState hss;
+  CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
+           MOJO_RESULT_OK);
+  CHECK_EQ(hss.satisfied_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+  CHECK_EQ(hss.satisfiable_signals,
+           MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  std::string read_buffer(100, '\0');
+  uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+  DispatcherVector dispatchers;
+  uint32_t num_dispatchers = 10;  // Maximum number to receive.
+  CHECK_EQ(mp->ReadMessage(0,
+                           UserPointer<void>(&read_buffer[0]),
+                           MakeUserPointer(&num_bytes),
+                           &dispatchers,
+                           &num_dispatchers,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  mp->Close(0);
+
+  read_buffer.resize(num_bytes);
+  CHECK_EQ(read_buffer, std::string("hello"));
+  CHECK_EQ(num_dispatchers, 1u);
+
+  CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypePlatformHandle);
+
+  scoped_refptr<PlatformHandleDispatcher> dispatcher(
+      static_cast<PlatformHandleDispatcher*>(dispatchers[0].get()));
+  embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+  CHECK(h.is_valid());
+  dispatcher->Close();
+
+  base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h.Pass(), "r"));
+  CHECK(fp);
+  std::string fread_buffer(100, '\0');
+  size_t bytes_read = fread(&fread_buffer[0], 1, fread_buffer.size(), fp.get());
+  fread_buffer.resize(bytes_read);
+  CHECK_EQ(fread_buffer, "world");
+
+  return 0;
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_PlatformHandlePassing PlatformHandlePassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing
+#endif
+TEST_F(MultiprocessMessagePipeTest, MAYBE_PlatformHandlePassing) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  helper()->StartChild("CheckPlatformHandleFile");
+
+  scoped_refptr<ChannelEndpoint> ep;
+  scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
+  Init(ep);
+
+  base::FilePath unused;
+  base::ScopedFILE fp(
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+  const std::string world("world");
+  ASSERT_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
+  fflush(fp.get());
+  rewind(fp.get());
+
+  embedder::ScopedPlatformHandle h(
+      mojo::test::PlatformHandleFromFILE(fp.Pass()));
+  scoped_refptr<PlatformHandleDispatcher> dispatcher(
+      new PlatformHandleDispatcher(h.Pass()));
+
+  const std::string hello("hello");
+  DispatcherTransport transport(
+      test::DispatcherTryStartTransport(dispatcher.get()));
+  ASSERT_TRUE(transport.is_valid());
+
+  std::vector<DispatcherTransport> transports;
+  transports.push_back(transport);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp->WriteMessage(0,
+                             UserPointer<const void>(&hello[0]),
+                             static_cast<uint32_t>(hello.size()),
+                             &transports,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  transport.End();
+
+  EXPECT_TRUE(dispatcher->HasOneRef());
+  dispatcher = nullptr;
+
+  // Wait for it to become readable, which should fail.
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  mp->Close(0);
+
+  EXPECT_EQ(0, helper()->WaitForChildShutdown());
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h
new file mode 100644
index 0000000..4772508
--- /dev/null
+++ b/mojo/edk/system/options_validation.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Functions to help with verifying various |Mojo...Options| structs from the
+// (public, C) API. These are "extensible" structs, which all have |struct_size|
+// as their first member. All fields (other than |struct_size|) are optional,
+// but any |flags| specified must be known to the system (otherwise, an error of
+// |MOJO_RESULT_UNIMPLEMENTED| should be returned).
+
+#ifndef MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_
+#define MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+template <class Options>
+class UserOptionsReader {
+ public:
+  // Constructor from a |UserPointer<const Options>| (which it checks -- this
+  // constructor has side effects!).
+  // Note: We initialize |options_reader_| without checking, since we do a check
+  // in |GetSizeForReader()|.
+  explicit UserOptionsReader(UserPointer<const Options> options)
+      : options_reader_(UserPointer<const char>::Reader::NoCheck(),
+                        options.template ReinterpretCast<const char>(),
+                        GetSizeForReader(options)) {
+    static_assert(offsetof(Options, struct_size) == 0,
+                  "struct_size not first member of Options");
+    // TODO(vtl): Enable when MSVC supports this (C++11 extended sizeof):
+    //   static_assert(sizeof(Options::struct_size) == sizeof(uint32_t),
+    //                 "Options::struct_size not a uint32_t");
+    // (Or maybe assert that its type is uint32_t?)
+  }
+
+  bool is_valid() const { return !!options_reader_.GetPointer(); }
+
+  const Options& options() const {
+    DCHECK(is_valid());
+    return *reinterpret_cast<const Options*>(options_reader_.GetPointer());
+  }
+
+  // Checks that the given (variable-size) |options| passed to the constructor
+  // (plausibly) has a member at the given offset with the given size. You
+  // probably want to use |OPTIONS_STRUCT_HAS_MEMBER()| instead.
+  bool HasMember(size_t offset, size_t size) const {
+    DCHECK(is_valid());
+    // We assume that |offset| and |size| are reasonable, since they should come
+    // from |offsetof(Options, some_member)| and |sizeof(Options::some_member)|,
+    // respectively.
+    return options().struct_size >= offset + size;
+  }
+
+ private:
+  static inline size_t GetSizeForReader(UserPointer<const Options> options) {
+    uint32_t struct_size =
+        options.template ReinterpretCast<const uint32_t>().Get();
+    if (struct_size < sizeof(uint32_t))
+      return 0;
+
+    // Check the full requested size.
+    // Note: Use |MOJO_ALIGNOF()| here to match the exact macro used in the
+    // declaration of Options structs.
+    internal::CheckUserPointerWithSize<MOJO_ALIGNOF(Options)>(options.pointer_,
+                                                              struct_size);
+    options.template ReinterpretCast<const char>().CheckArray(struct_size);
+    // But we'll never look at more than |sizeof(Options)| bytes.
+    return std::min(static_cast<size_t>(struct_size), sizeof(Options));
+  }
+
+  UserPointer<const char>::Reader options_reader_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserOptionsReader);
+};
+
+// Macro to invoke |UserOptionsReader<Options>::HasMember()| parametrized by
+// member name instead of offset and size.
+//
+// (We can't just give |HasMember()| a member pointer template argument instead,
+// since there's no good/strictly-correct way to get an offset from that.)
+//
+// TODO(vtl): With C++11, use |sizeof(Options::member)| instead of (the
+// contortion below). We might also be able to pull out the type |Options| from
+// |reader| (using |decltype|) instead of requiring a parameter.
+#define OPTIONS_STRUCT_HAS_MEMBER(Options, member, reader) \
+  reader.HasMember(offsetof(Options, member), sizeof(reader.options().member))
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_
diff --git a/mojo/edk/system/options_validation_unittest.cc b/mojo/edk/system/options_validation_unittest.cc
new file mode 100644
index 0000000..f410f24
--- /dev/null
+++ b/mojo/edk/system/options_validation_unittest.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/edk/system/options_validation.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Declare a test options struct just as we do in actual public headers.
+
+typedef uint32_t TestOptionsFlags;
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) TestOptions {
+  uint32_t struct_size;
+  TestOptionsFlags flags;
+  uint32_t member1;
+  uint32_t member2;
+};
+MOJO_COMPILE_ASSERT(sizeof(TestOptions) == 16, TestOptions_has_wrong_size);
+
+const uint32_t kSizeOfTestOptions = static_cast<uint32_t>(sizeof(TestOptions));
+
+TEST(OptionsValidationTest, Valid) {
+  {
+    const TestOptions kOptions = {kSizeOfTestOptions};
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions));
+    EXPECT_TRUE(reader.is_valid());
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
+  }
+  {
+    const TestOptions kOptions = {static_cast<uint32_t>(
+        offsetof(TestOptions, struct_size) + sizeof(uint32_t))};
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions));
+    EXPECT_TRUE(reader.is_valid());
+    EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
+    EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
+    EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
+  }
+
+  {
+    const TestOptions kOptions = {
+        static_cast<uint32_t>(offsetof(TestOptions, flags) + sizeof(uint32_t))};
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions));
+    EXPECT_TRUE(reader.is_valid());
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
+    EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
+    EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
+  }
+  {
+    MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {};
+    TestOptions* options = reinterpret_cast<TestOptions*>(buf);
+    options->struct_size = kSizeOfTestOptions + 1;
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(options));
+    EXPECT_TRUE(reader.is_valid());
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
+  }
+  {
+    MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {};
+    TestOptions* options = reinterpret_cast<TestOptions*>(buf);
+    options->struct_size = kSizeOfTestOptions + 4;
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(options));
+    EXPECT_TRUE(reader.is_valid());
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
+    EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
+  }
+}
+
+TEST(OptionsValidationTest, Invalid) {
+  // Size too small:
+  for (size_t i = 0; i < sizeof(uint32_t); i++) {
+    TestOptions options = {static_cast<uint32_t>(i)};
+    UserOptionsReader<TestOptions> reader(MakeUserPointer(&options));
+    EXPECT_FALSE(reader.is_valid()) << i;
+  }
+}
+
+// These test invalid arguments that should cause death if we're being paranoid
+// about checking arguments (which we would want to do if, e.g., we were in a
+// true "kernel" situation, but we might not want to do otherwise for
+// performance reasons). Probably blatant errors like passing in null pointers
+// (for required pointer arguments) will still cause death, but perhaps not
+// predictably.
+TEST(OptionsValidationTest, InvalidDeath) {
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+
+  // Null:
+  EXPECT_DEATH_IF_SUPPORTED(
+      { UserOptionsReader<TestOptions> reader((NullUserPointer())); },
+      kMemoryCheckFailedRegex);
+
+  // Unaligned:
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        UserOptionsReader<TestOptions> reader(
+            MakeUserPointer(reinterpret_cast<const TestOptions*>(1)));
+      },
+      kMemoryCheckFailedRegex);
+  // Note: The current implementation checks the size only after checking the
+  // alignment versus that required for the |uint32_t| size, so it won't die in
+  // the expected way if you pass, e.g., 4. So we have to manufacture a valid
+  // pointer at an offset of alignment 4.
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        uint32_t buffer[100] = {};
+        TestOptions* options = (reinterpret_cast<uintptr_t>(buffer) % 8 == 0)
+                                   ? reinterpret_cast<TestOptions*>(&buffer[1])
+                                   : reinterpret_cast<TestOptions*>(&buffer[0]);
+        options->struct_size = static_cast<uint32_t>(sizeof(TestOptions));
+        UserOptionsReader<TestOptions> reader(MakeUserPointer(options));
+      },
+      kMemoryCheckFailedRegex);
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/platform_handle_dispatcher.cc b/mojo/edk/system/platform_handle_dispatcher.cc
new file mode 100644
index 0000000..a6072cf
--- /dev/null
+++ b/mojo/edk/system/platform_handle_dispatcher.cc
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+const size_t kInvalidPlatformHandleIndex = static_cast<size_t>(-1);
+
+struct SerializedPlatformHandleDispatcher {
+  size_t platform_handle_index;  // (Or |kInvalidPlatformHandleIndex|.)
+};
+
+}  // namespace
+
+PlatformHandleDispatcher::PlatformHandleDispatcher(
+    embedder::ScopedPlatformHandle platform_handle)
+    : platform_handle_(platform_handle.Pass()) {
+}
+
+embedder::ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() {
+  base::AutoLock locker(lock());
+  return platform_handle_.Pass();
+}
+
+Dispatcher::Type PlatformHandleDispatcher::GetType() const {
+  return kTypePlatformHandle;
+}
+
+// static
+scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize(
+    Channel* channel,
+    const void* source,
+    size_t size,
+    embedder::PlatformHandleVector* platform_handles) {
+  if (size != sizeof(SerializedPlatformHandleDispatcher)) {
+    LOG(ERROR) << "Invalid serialized platform handle dispatcher (bad size)";
+    return scoped_refptr<PlatformHandleDispatcher>();
+  }
+
+  const SerializedPlatformHandleDispatcher* serialization =
+      static_cast<const SerializedPlatformHandleDispatcher*>(source);
+  size_t platform_handle_index = serialization->platform_handle_index;
+
+  // Starts off invalid, which is what we want.
+  embedder::PlatformHandle platform_handle;
+
+  if (platform_handle_index != kInvalidPlatformHandleIndex) {
+    if (!platform_handles ||
+        platform_handle_index >= platform_handles->size()) {
+      LOG(ERROR)
+          << "Invalid serialized platform handle dispatcher (missing handles)";
+      return scoped_refptr<PlatformHandleDispatcher>();
+    }
+
+    // We take ownership of the handle, so we have to invalidate the one in
+    // |platform_handles|.
+    std::swap(platform_handle, (*platform_handles)[platform_handle_index]);
+  }
+
+  return scoped_refptr<PlatformHandleDispatcher>(new PlatformHandleDispatcher(
+      embedder::ScopedPlatformHandle(platform_handle)));
+}
+
+PlatformHandleDispatcher::~PlatformHandleDispatcher() {
+}
+
+void PlatformHandleDispatcher::CloseImplNoLock() {
+  lock().AssertAcquired();
+  platform_handle_.reset();
+}
+
+scoped_refptr<Dispatcher>
+PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+  lock().AssertAcquired();
+  return scoped_refptr<Dispatcher>(
+      new PlatformHandleDispatcher(platform_handle_.Pass()));
+}
+
+void PlatformHandleDispatcher::StartSerializeImplNoLock(
+    Channel* /*channel*/,
+    size_t* max_size,
+    size_t* max_platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  *max_size = sizeof(SerializedPlatformHandleDispatcher);
+  *max_platform_handles = 1;
+}
+
+bool PlatformHandleDispatcher::EndSerializeAndCloseImplNoLock(
+    Channel* /*channel*/,
+    void* destination,
+    size_t* actual_size,
+    embedder::PlatformHandleVector* platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+
+  SerializedPlatformHandleDispatcher* serialization =
+      static_cast<SerializedPlatformHandleDispatcher*>(destination);
+  if (platform_handle_.is_valid()) {
+    serialization->platform_handle_index = platform_handles->size();
+    platform_handles->push_back(platform_handle_.release());
+  } else {
+    serialization->platform_handle_index = kInvalidPlatformHandleIndex;
+  }
+
+  *actual_size = sizeof(SerializedPlatformHandleDispatcher);
+  return true;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h
new file mode 100644
index 0000000..eb5eb5d
--- /dev/null
+++ b/mojo/edk/system/platform_handle_dispatcher.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/simple_dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// A dispatcher that simply wraps/transports a |PlatformHandle| (only for use by
+// the embedder).
+class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher
+    : public SimpleDispatcher {
+ public:
+  explicit PlatformHandleDispatcher(
+      embedder::ScopedPlatformHandle platform_handle);
+
+  embedder::ScopedPlatformHandle PassPlatformHandle();
+
+  // |Dispatcher| public methods:
+  virtual Type GetType() const override;
+
+  // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+  // |Dispatcher::Deserialize()|.)
+  static scoped_refptr<PlatformHandleDispatcher> Deserialize(
+      Channel* channel,
+      const void* source,
+      size_t size,
+      embedder::PlatformHandleVector* platform_handles);
+
+ private:
+  virtual ~PlatformHandleDispatcher();
+
+  // |Dispatcher| protected methods:
+  virtual void CloseImplNoLock() override;
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  virtual void StartSerializeImplNoLock(Channel* channel,
+                                        size_t* max_size,
+                                        size_t* max_platform_handles) override;
+  virtual bool EndSerializeAndCloseImplNoLock(
+      Channel* channel,
+      void* destination,
+      size_t* actual_size,
+      embedder::PlatformHandleVector* platform_handles) override;
+
+  embedder::ScopedPlatformHandle platform_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
new file mode 100644
index 0000000..1d784a6
--- /dev/null
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.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/edk/system/platform_handle_dispatcher.h"
+
+#include <stdio.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(PlatformHandleDispatcherTest, Basic) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static const char kHelloWorld[] = "hello world";
+
+  base::FilePath unused;
+  base::ScopedFILE fp(
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+  ASSERT_TRUE(fp);
+  EXPECT_EQ(sizeof(kHelloWorld),
+            fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get()));
+
+  embedder::ScopedPlatformHandle h(
+      mojo::test::PlatformHandleFromFILE(fp.Pass()));
+  EXPECT_FALSE(fp);
+  ASSERT_TRUE(h.is_valid());
+
+  scoped_refptr<PlatformHandleDispatcher> dispatcher(
+      new PlatformHandleDispatcher(h.Pass()));
+  EXPECT_FALSE(h.is_valid());
+  EXPECT_EQ(Dispatcher::kTypePlatformHandle, dispatcher->GetType());
+
+  h = dispatcher->PassPlatformHandle().Pass();
+  EXPECT_TRUE(h.is_valid());
+
+  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+  EXPECT_FALSE(h.is_valid());
+  EXPECT_TRUE(fp);
+
+  rewind(fp.get());
+  char read_buffer[1000] = {};
+  EXPECT_EQ(sizeof(kHelloWorld),
+            fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+  EXPECT_STREQ(kHelloWorld, read_buffer);
+
+  // Try getting the handle again. (It should fail cleanly.)
+  h = dispatcher->PassPlatformHandle().Pass();
+  EXPECT_FALSE(h.is_valid());
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static const char kFooBar[] = "foo bar";
+
+  base::FilePath unused;
+  base::ScopedFILE fp(
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+  EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get()));
+
+  scoped_refptr<PlatformHandleDispatcher> dispatcher(
+      new PlatformHandleDispatcher(
+          mojo::test::PlatformHandleFromFILE(fp.Pass())));
+
+  DispatcherTransport transport(
+      test::DispatcherTryStartTransport(dispatcher.get()));
+  EXPECT_TRUE(transport.is_valid());
+  EXPECT_EQ(Dispatcher::kTypePlatformHandle, transport.GetType());
+  EXPECT_FALSE(transport.IsBusy());
+
+  scoped_refptr<Dispatcher> generic_dispatcher =
+      transport.CreateEquivalentDispatcherAndClose();
+  ASSERT_TRUE(generic_dispatcher.get());
+
+  transport.End();
+  EXPECT_TRUE(dispatcher->HasOneRef());
+  dispatcher = nullptr;
+
+  ASSERT_EQ(Dispatcher::kTypePlatformHandle, generic_dispatcher->GetType());
+  dispatcher = static_cast<PlatformHandleDispatcher*>(generic_dispatcher.get());
+
+  fp = mojo::test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(),
+                                          "rb").Pass();
+  EXPECT_TRUE(fp);
+
+  rewind(fp.get());
+  char read_buffer[1000] = {};
+  EXPECT_EQ(sizeof(kFooBar),
+            fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+  EXPECT_STREQ(kFooBar, read_buffer);
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.cc b/mojo/edk/system/proxy_message_pipe_endpoint.cc
new file mode 100644
index 0000000..85437bb
--- /dev/null
+++ b/mojo/edk/system/proxy_message_pipe_endpoint.cc
@@ -0,0 +1,57 @@
+// 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/edk/system/proxy_message_pipe_endpoint.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/local_message_pipe_endpoint.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+ProxyMessagePipeEndpoint::ProxyMessagePipeEndpoint(
+    ChannelEndpoint* channel_endpoint)
+    : channel_endpoint_(channel_endpoint) {
+}
+
+ProxyMessagePipeEndpoint::~ProxyMessagePipeEndpoint() {
+  DCHECK(!channel_endpoint_.get());
+}
+
+MessagePipeEndpoint::Type ProxyMessagePipeEndpoint::GetType() const {
+  return kTypeProxy;
+}
+
+bool ProxyMessagePipeEndpoint::OnPeerClose() {
+  DetachIfNecessary();
+  return false;
+}
+
+// Note: We may have to enqueue messages even when our (local) peer isn't open
+// -- it may have been written to and closed immediately, before we were ready.
+// This case is handled in |Run()| (which will call us).
+void ProxyMessagePipeEndpoint::EnqueueMessage(
+    scoped_ptr<MessageInTransit> message) {
+  DCHECK(channel_endpoint_.get());
+  LOG_IF(WARNING, !channel_endpoint_->EnqueueMessage(message.Pass()))
+      << "Failed to write enqueue message to channel";
+}
+
+void ProxyMessagePipeEndpoint::Close() {
+  DetachIfNecessary();
+}
+
+void ProxyMessagePipeEndpoint::DetachIfNecessary() {
+  if (channel_endpoint_.get()) {
+    channel_endpoint_->DetachFromMessagePipe();
+    channel_endpoint_ = nullptr;
+  }
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.h b/mojo/edk/system/proxy_message_pipe_endpoint.h
new file mode 100644
index 0000000..c884240
--- /dev/null
+++ b/mojo/edk/system/proxy_message_pipe_endpoint.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_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/message_pipe_endpoint.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class ChannelEndpoint;
+class LocalMessagePipeEndpoint;
+class MessagePipe;
+
+// A |ProxyMessagePipeEndpoint| is an endpoint which delegates everything to a
+// |ChannelEndpoint|, which may be co-owned by a |Channel|. Like any
+// |MessagePipeEndpoint|, a |ProxyMessagePipeEndpoint| is owned by a
+// |MessagePipe|.
+//
+// For example, a |MessagePipe| with one endpoint local and the other endpoint
+// remote consists of a |LocalMessagePipeEndpoint| and a
+// |ProxyMessagePipeEndpoint|, with only the local endpoint being accessible via
+// a |MessagePipeDispatcher|.
+class MOJO_SYSTEM_IMPL_EXPORT ProxyMessagePipeEndpoint
+    : public MessagePipeEndpoint {
+ public:
+  explicit ProxyMessagePipeEndpoint(ChannelEndpoint* channel_endpoint);
+  virtual ~ProxyMessagePipeEndpoint();
+
+  // |MessagePipeEndpoint| implementation:
+  virtual Type GetType() const override;
+  virtual bool OnPeerClose() override;
+  virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) override;
+  virtual void Close() override;
+
+ private:
+  void DetachIfNecessary();
+
+  scoped_refptr<ChannelEndpoint> channel_endpoint_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyMessagePipeEndpoint);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/mojo/edk/system/raw_channel.cc b/mojo/edk/system/raw_channel.cc
new file mode 100644
index 0000000..0bfe104
--- /dev/null
+++ b/mojo/edk/system/raw_channel.cc
@@ -0,0 +1,522 @@
+// Copyright 2014 The Chromium 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/edk/system/raw_channel.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+const size_t kReadSize = 4096;
+
+// RawChannel::ReadBuffer ------------------------------------------------------
+
+RawChannel::ReadBuffer::ReadBuffer() : buffer_(kReadSize), num_valid_bytes_(0) {
+}
+
+RawChannel::ReadBuffer::~ReadBuffer() {
+}
+
+void RawChannel::ReadBuffer::GetBuffer(char** addr, size_t* size) {
+  DCHECK_GE(buffer_.size(), num_valid_bytes_ + kReadSize);
+  *addr = &buffer_[0] + num_valid_bytes_;
+  *size = kReadSize;
+}
+
+// RawChannel::WriteBuffer -----------------------------------------------------
+
+RawChannel::WriteBuffer::WriteBuffer(size_t serialized_platform_handle_size)
+    : serialized_platform_handle_size_(serialized_platform_handle_size),
+      platform_handles_offset_(0),
+      data_offset_(0) {
+}
+
+RawChannel::WriteBuffer::~WriteBuffer() {
+  STLDeleteElements(&message_queue_);
+}
+
+bool RawChannel::WriteBuffer::HavePlatformHandlesToSend() const {
+  if (message_queue_.empty())
+    return false;
+
+  const TransportData* transport_data =
+      message_queue_.front()->transport_data();
+  if (!transport_data)
+    return false;
+
+  const embedder::PlatformHandleVector* all_platform_handles =
+      transport_data->platform_handles();
+  if (!all_platform_handles) {
+    DCHECK_EQ(platform_handles_offset_, 0u);
+    return false;
+  }
+  if (platform_handles_offset_ >= all_platform_handles->size()) {
+    DCHECK_EQ(platform_handles_offset_, all_platform_handles->size());
+    return false;
+  }
+
+  return true;
+}
+
+void RawChannel::WriteBuffer::GetPlatformHandlesToSend(
+    size_t* num_platform_handles,
+    embedder::PlatformHandle** platform_handles,
+    void** serialization_data) {
+  DCHECK(HavePlatformHandlesToSend());
+
+  TransportData* transport_data = message_queue_.front()->transport_data();
+  embedder::PlatformHandleVector* all_platform_handles =
+      transport_data->platform_handles();
+  *num_platform_handles =
+      all_platform_handles->size() - platform_handles_offset_;
+  *platform_handles = &(*all_platform_handles)[platform_handles_offset_];
+  size_t serialization_data_offset =
+      transport_data->platform_handle_table_offset();
+  DCHECK_GT(serialization_data_offset, 0u);
+  serialization_data_offset +=
+      platform_handles_offset_ * serialized_platform_handle_size_;
+  *serialization_data =
+      static_cast<char*>(transport_data->buffer()) + serialization_data_offset;
+}
+
+void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const {
+  buffers->clear();
+
+  if (message_queue_.empty())
+    return;
+
+  MessageInTransit* message = message_queue_.front();
+  DCHECK_LT(data_offset_, message->total_size());
+  size_t bytes_to_write = message->total_size() - data_offset_;
+
+  size_t transport_data_buffer_size =
+      message->transport_data() ? message->transport_data()->buffer_size() : 0;
+
+  if (!transport_data_buffer_size) {
+    // Only write from the main buffer.
+    DCHECK_LT(data_offset_, message->main_buffer_size());
+    DCHECK_LE(bytes_to_write, message->main_buffer_size());
+    Buffer buffer = {
+        static_cast<const char*>(message->main_buffer()) + data_offset_,
+        bytes_to_write};
+    buffers->push_back(buffer);
+    return;
+  }
+
+  if (data_offset_ >= message->main_buffer_size()) {
+    // Only write from the transport data buffer.
+    DCHECK_LT(data_offset_ - message->main_buffer_size(),
+              transport_data_buffer_size);
+    DCHECK_LE(bytes_to_write, transport_data_buffer_size);
+    Buffer buffer = {
+        static_cast<const char*>(message->transport_data()->buffer()) +
+            (data_offset_ - message->main_buffer_size()),
+        bytes_to_write};
+    buffers->push_back(buffer);
+    return;
+  }
+
+  // TODO(vtl): We could actually send out buffers from multiple messages, with
+  // the "stopping" condition being reaching a message with platform handles
+  // attached.
+
+  // Write from both buffers.
+  DCHECK_EQ(
+      bytes_to_write,
+      message->main_buffer_size() - data_offset_ + transport_data_buffer_size);
+  Buffer buffer1 = {
+      static_cast<const char*>(message->main_buffer()) + data_offset_,
+      message->main_buffer_size() - data_offset_};
+  buffers->push_back(buffer1);
+  Buffer buffer2 = {
+      static_cast<const char*>(message->transport_data()->buffer()),
+      transport_data_buffer_size};
+  buffers->push_back(buffer2);
+}
+
+// RawChannel ------------------------------------------------------------------
+
+RawChannel::RawChannel()
+    : message_loop_for_io_(nullptr),
+      delegate_(nullptr),
+      read_stopped_(false),
+      write_stopped_(false),
+      weak_ptr_factory_(this) {
+}
+
+RawChannel::~RawChannel() {
+  DCHECK(!read_buffer_);
+  DCHECK(!write_buffer_);
+
+  // No need to take the |write_lock_| here -- if there are still weak pointers
+  // outstanding, then we're hosed anyway (since we wouldn't be able to
+  // invalidate them cleanly, since we might not be on the I/O thread).
+  DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+}
+
+bool RawChannel::Init(Delegate* delegate) {
+  DCHECK(delegate);
+
+  DCHECK(!delegate_);
+  delegate_ = delegate;
+
+  CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
+  DCHECK(!message_loop_for_io_);
+  message_loop_for_io_ =
+      static_cast<base::MessageLoopForIO*>(base::MessageLoop::current());
+
+  // No need to take the lock. No one should be using us yet.
+  DCHECK(!read_buffer_);
+  read_buffer_.reset(new ReadBuffer);
+  DCHECK(!write_buffer_);
+  write_buffer_.reset(new WriteBuffer(GetSerializedPlatformHandleSize()));
+
+  if (!OnInit()) {
+    delegate_ = nullptr;
+    message_loop_for_io_ = nullptr;
+    read_buffer_.reset();
+    write_buffer_.reset();
+    return false;
+  }
+
+  IOResult io_result = ScheduleRead();
+  if (io_result != IO_PENDING) {
+    // This will notify the delegate about the read failure. Although we're on
+    // the I/O thread, don't call it in the nested context.
+    message_loop_for_io_->PostTask(FROM_HERE,
+                                   base::Bind(&RawChannel::OnReadCompleted,
+                                              weak_ptr_factory_.GetWeakPtr(),
+                                              io_result,
+                                              0));
+  }
+
+  // ScheduleRead() failure is treated as a read failure (by notifying the
+  // delegate), not as an init failure.
+  return true;
+}
+
+void RawChannel::Shutdown() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+
+  base::AutoLock locker(write_lock_);
+
+  LOG_IF(WARNING, !write_buffer_->message_queue_.empty())
+      << "Shutting down RawChannel with write buffer nonempty";
+
+  // Reset the delegate so that it won't receive further calls.
+  delegate_ = nullptr;
+  read_stopped_ = true;
+  write_stopped_ = true;
+  weak_ptr_factory_.InvalidateWeakPtrs();
+
+  OnShutdownNoLock(read_buffer_.Pass(), write_buffer_.Pass());
+}
+
+// Reminder: This must be thread-safe.
+bool RawChannel::WriteMessage(scoped_ptr<MessageInTransit> message) {
+  DCHECK(message);
+
+  base::AutoLock locker(write_lock_);
+  if (write_stopped_)
+    return false;
+
+  if (!write_buffer_->message_queue_.empty()) {
+    EnqueueMessageNoLock(message.Pass());
+    return true;
+  }
+
+  EnqueueMessageNoLock(message.Pass());
+  DCHECK_EQ(write_buffer_->data_offset_, 0u);
+
+  size_t platform_handles_written = 0;
+  size_t bytes_written = 0;
+  IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written);
+  if (io_result == IO_PENDING)
+    return true;
+
+  bool result = OnWriteCompletedNoLock(
+      io_result, platform_handles_written, bytes_written);
+  if (!result) {
+    // Even if we're on the I/O thread, don't call |OnError()| in the nested
+    // context.
+    message_loop_for_io_->PostTask(FROM_HERE,
+                                   base::Bind(&RawChannel::CallOnError,
+                                              weak_ptr_factory_.GetWeakPtr(),
+                                              Delegate::ERROR_WRITE));
+  }
+
+  return result;
+}
+
+// Reminder: This must be thread-safe.
+bool RawChannel::IsWriteBufferEmpty() {
+  base::AutoLock locker(write_lock_);
+  return write_buffer_->message_queue_.empty();
+}
+
+void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+
+  if (read_stopped_) {
+    NOTREACHED();
+    return;
+  }
+
+  // Keep reading data in a loop, and dispatch messages if enough data is
+  // received. Exit the loop if any of the following happens:
+  //   - one or more messages were dispatched;
+  //   - the last read failed, was a partial read or would block;
+  //   - |Shutdown()| was called.
+  do {
+    switch (io_result) {
+      case IO_SUCCEEDED:
+        break;
+      case IO_FAILED_SHUTDOWN:
+      case IO_FAILED_BROKEN:
+      case IO_FAILED_UNKNOWN:
+        read_stopped_ = true;
+        CallOnError(ReadIOResultToError(io_result));
+        return;
+      case IO_PENDING:
+        NOTREACHED();
+        return;
+    }
+
+    read_buffer_->num_valid_bytes_ += bytes_read;
+
+    // Dispatch all the messages that we can.
+    bool did_dispatch_message = false;
+    // Tracks the offset of the first undispatched message in |read_buffer_|.
+    // Currently, we copy data to ensure that this is zero at the beginning.
+    size_t read_buffer_start = 0;
+    size_t remaining_bytes = read_buffer_->num_valid_bytes_;
+    size_t message_size;
+    // Note that we rely on short-circuit evaluation here:
+    //   - |read_buffer_start| may be an invalid index into
+    //     |read_buffer_->buffer_| if |remaining_bytes| is zero.
+    //   - |message_size| is only valid if |GetNextMessageSize()| returns true.
+    // TODO(vtl): Use |message_size| more intelligently (e.g., to request the
+    // next read).
+    // TODO(vtl): Validate that |message_size| is sane.
+    while (remaining_bytes > 0 && MessageInTransit::GetNextMessageSize(
+                                      &read_buffer_->buffer_[read_buffer_start],
+                                      remaining_bytes,
+                                      &message_size) &&
+           remaining_bytes >= message_size) {
+      MessageInTransit::View message_view(
+          message_size, &read_buffer_->buffer_[read_buffer_start]);
+      DCHECK_EQ(message_view.total_size(), message_size);
+
+      const char* error_message = nullptr;
+      if (!message_view.IsValid(GetSerializedPlatformHandleSize(),
+                                &error_message)) {
+        DCHECK(error_message);
+        LOG(ERROR) << "Received invalid message: " << error_message;
+        read_stopped_ = true;
+        CallOnError(Delegate::ERROR_READ_BAD_MESSAGE);
+        return;
+      }
+
+      if (message_view.type() == MessageInTransit::kTypeRawChannel) {
+        if (!OnReadMessageForRawChannel(message_view)) {
+          read_stopped_ = true;
+          CallOnError(Delegate::ERROR_READ_BAD_MESSAGE);
+          return;
+        }
+      } else {
+        embedder::ScopedPlatformHandleVectorPtr platform_handles;
+        if (message_view.transport_data_buffer()) {
+          size_t num_platform_handles;
+          const void* platform_handle_table;
+          TransportData::GetPlatformHandleTable(
+              message_view.transport_data_buffer(),
+              &num_platform_handles,
+              &platform_handle_table);
+
+          if (num_platform_handles > 0) {
+            platform_handles =
+                GetReadPlatformHandles(num_platform_handles,
+                                       platform_handle_table).Pass();
+            if (!platform_handles) {
+              LOG(ERROR) << "Invalid number of platform handles received";
+              read_stopped_ = true;
+              CallOnError(Delegate::ERROR_READ_BAD_MESSAGE);
+              return;
+            }
+          }
+        }
+
+        // TODO(vtl): In the case that we aren't expecting any platform handles,
+        // for the POSIX implementation, we should confirm that none are stored.
+
+        // Dispatch the message.
+        DCHECK(delegate_);
+        delegate_->OnReadMessage(message_view, platform_handles.Pass());
+        if (read_stopped_) {
+          // |Shutdown()| was called in |OnReadMessage()|.
+          // TODO(vtl): Add test for this case.
+          return;
+        }
+      }
+
+      did_dispatch_message = true;
+
+      // Update our state.
+      read_buffer_start += message_size;
+      remaining_bytes -= message_size;
+    }
+
+    if (read_buffer_start > 0) {
+      // Move data back to start.
+      read_buffer_->num_valid_bytes_ = remaining_bytes;
+      if (read_buffer_->num_valid_bytes_ > 0) {
+        memmove(&read_buffer_->buffer_[0],
+                &read_buffer_->buffer_[read_buffer_start],
+                remaining_bytes);
+      }
+      read_buffer_start = 0;
+    }
+
+    if (read_buffer_->buffer_.size() - read_buffer_->num_valid_bytes_ <
+        kReadSize) {
+      // Use power-of-2 buffer sizes.
+      // TODO(vtl): Make sure the buffer doesn't get too large (and enforce the
+      // maximum message size to whatever extent necessary).
+      // TODO(vtl): We may often be able to peek at the header and get the real
+      // required extra space (which may be much bigger than |kReadSize|).
+      size_t new_size = std::max(read_buffer_->buffer_.size(), kReadSize);
+      while (new_size < read_buffer_->num_valid_bytes_ + kReadSize)
+        new_size *= 2;
+
+      // TODO(vtl): It's suboptimal to zero out the fresh memory.
+      read_buffer_->buffer_.resize(new_size, 0);
+    }
+
+    // (1) If we dispatched any messages, stop reading for now (and let the
+    // message loop do its thing for another round).
+    // TODO(vtl): Is this the behavior we want? (Alternatives: i. Dispatch only
+    // a single message. Risks: slower, more complex if we want to avoid lots of
+    // copying. ii. Keep reading until there's no more data and dispatch all the
+    // messages we can. Risks: starvation of other users of the message loop.)
+    // (2) If we didn't max out |kReadSize|, stop reading for now.
+    bool schedule_for_later = did_dispatch_message || bytes_read < kReadSize;
+    bytes_read = 0;
+    io_result = schedule_for_later ? ScheduleRead() : Read(&bytes_read);
+  } while (io_result != IO_PENDING);
+}
+
+void RawChannel::OnWriteCompleted(IOResult io_result,
+                                  size_t platform_handles_written,
+                                  size_t bytes_written) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+  DCHECK_NE(io_result, IO_PENDING);
+
+  bool did_fail = false;
+  {
+    base::AutoLock locker(write_lock_);
+    DCHECK_EQ(write_stopped_, write_buffer_->message_queue_.empty());
+
+    if (write_stopped_) {
+      NOTREACHED();
+      return;
+    }
+
+    did_fail = !OnWriteCompletedNoLock(
+                   io_result, platform_handles_written, bytes_written);
+  }
+
+  if (did_fail)
+    CallOnError(Delegate::ERROR_WRITE);
+}
+
+void RawChannel::EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message) {
+  write_lock_.AssertAcquired();
+  write_buffer_->message_queue_.push_back(message.release());
+}
+
+bool RawChannel::OnReadMessageForRawChannel(
+    const MessageInTransit::View& message_view) {
+  // No non-implementation specific |RawChannel| control messages.
+  LOG(ERROR) << "Invalid control message (subtype " << message_view.subtype()
+             << ")";
+  return false;
+}
+
+// static
+RawChannel::Delegate::Error RawChannel::ReadIOResultToError(
+    IOResult io_result) {
+  switch (io_result) {
+    case IO_FAILED_SHUTDOWN:
+      return Delegate::ERROR_READ_SHUTDOWN;
+    case IO_FAILED_BROKEN:
+      return Delegate::ERROR_READ_BROKEN;
+    case IO_FAILED_UNKNOWN:
+      return Delegate::ERROR_READ_UNKNOWN;
+    case IO_SUCCEEDED:
+    case IO_PENDING:
+      NOTREACHED();
+      break;
+  }
+  return Delegate::ERROR_READ_UNKNOWN;
+}
+
+void RawChannel::CallOnError(Delegate::Error error) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+  // TODO(vtl): Add a "write_lock_.AssertNotAcquired()"?
+  if (delegate_)
+    delegate_->OnError(error);
+}
+
+bool RawChannel::OnWriteCompletedNoLock(IOResult io_result,
+                                        size_t platform_handles_written,
+                                        size_t bytes_written) {
+  write_lock_.AssertAcquired();
+
+  DCHECK(!write_stopped_);
+  DCHECK(!write_buffer_->message_queue_.empty());
+
+  if (io_result == IO_SUCCEEDED) {
+    write_buffer_->platform_handles_offset_ += platform_handles_written;
+    write_buffer_->data_offset_ += bytes_written;
+
+    MessageInTransit* message = write_buffer_->message_queue_.front();
+    if (write_buffer_->data_offset_ >= message->total_size()) {
+      // Complete write.
+      CHECK_EQ(write_buffer_->data_offset_, message->total_size());
+      write_buffer_->message_queue_.pop_front();
+      delete message;
+      write_buffer_->platform_handles_offset_ = 0;
+      write_buffer_->data_offset_ = 0;
+
+      if (write_buffer_->message_queue_.empty())
+        return true;
+    }
+
+    // Schedule the next write.
+    io_result = ScheduleWriteNoLock();
+    if (io_result == IO_PENDING)
+      return true;
+    DCHECK_NE(io_result, IO_SUCCEEDED);
+  }
+
+  write_stopped_ = true;
+  STLDeleteElements(&write_buffer_->message_queue_);
+  write_buffer_->platform_handles_offset_ = 0;
+  write_buffer_->data_offset_ = 0;
+  return false;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/raw_channel.h b/mojo/edk/system/raw_channel.h
new file mode 100644
index 0000000..1a077ef
--- /dev/null
+++ b/mojo/edk/system/raw_channel.h
@@ -0,0 +1,331 @@
+// 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_EDK_SYSTEM_RAW_CHANNEL_H_
+#define MOJO_EDK_SYSTEM_RAW_CHANNEL_H_
+
+#include <deque>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace base {
+class MessageLoopForIO;
+}
+
+namespace mojo {
+namespace system {
+
+// |RawChannel| is an interface and base class for objects that wrap an OS
+// "pipe". It presents the following interface to users:
+//  - Receives and dispatches messages on an I/O thread (running a
+//    |MessageLoopForIO|.
+//  - Provides a thread-safe way of writing messages (|WriteMessage()|);
+//    writing/queueing messages will not block and is atomic from the point of
+//    view of the caller. If necessary, messages are queued (to be written on
+//    the aforementioned thread).
+//
+// OS-specific implementation subclasses are to be instantiated using the
+// |Create()| static factory method.
+//
+// With the exception of |WriteMessage()|, this class is thread-unsafe (and in
+// general its methods should only be used on the I/O thread, i.e., the thread
+// on which |Init()| is called).
+class MOJO_SYSTEM_IMPL_EXPORT RawChannel {
+ public:
+  virtual ~RawChannel();
+
+  // The |Delegate| is only accessed on the same thread as the message loop
+  // (passed in on creation).
+  class MOJO_SYSTEM_IMPL_EXPORT Delegate {
+   public:
+    enum Error {
+      // Failed read due to raw channel shutdown (e.g., on the other side).
+      ERROR_READ_SHUTDOWN,
+      // Failed read due to raw channel being broken (e.g., if the other side
+      // died without shutting down).
+      ERROR_READ_BROKEN,
+      // Received a bad message.
+      ERROR_READ_BAD_MESSAGE,
+      // Unknown read error.
+      ERROR_READ_UNKNOWN,
+      // Generic write error.
+      ERROR_WRITE
+    };
+
+    // Called when a message is read. This may call |Shutdown()| (on the
+    // |RawChannel|), but must not destroy it.
+    virtual void OnReadMessage(
+        const MessageInTransit::View& message_view,
+        embedder::ScopedPlatformHandleVectorPtr platform_handles) = 0;
+
+    // Called when there's a (fatal) error. This may call the raw channel's
+    // |Shutdown()|, but must not destroy it.
+    //
+    // For each raw channel, there'll be at most one |ERROR_READ_...| and at
+    // most one |ERROR_WRITE| notification. After |OnError(ERROR_READ_...)|,
+    // |OnReadMessage()| won't be called again.
+    virtual void OnError(Error error) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Static factory method. |handle| should be a handle to a
+  // (platform-appropriate) bidirectional communication channel (e.g., a socket
+  // on POSIX, a named pipe on Windows).
+  static scoped_ptr<RawChannel> Create(embedder::ScopedPlatformHandle handle);
+
+  // This must be called (on an I/O thread) before this object is used. Does
+  // *not* take ownership of |delegate|. Both the I/O thread and |delegate| must
+  // remain alive until |Shutdown()| is called (unless this fails); |delegate|
+  // will no longer be used after |Shutdown()|. Returns true on success. On
+  // failure, |Shutdown()| should *not* be called.
+  bool Init(Delegate* delegate);
+
+  // This must be called (on the I/O thread) before this object is destroyed.
+  void Shutdown();
+
+  // Writes the given message (or schedules it to be written). |message| must
+  // have no |Dispatcher|s still attached (i.e.,
+  // |SerializeAndCloseDispatchers()| should have been called). This method is
+  // thread-safe and may be called from any thread. Returns true on success.
+  bool WriteMessage(scoped_ptr<MessageInTransit> message);
+
+  // Returns true if the write buffer is empty (i.e., all messages written using
+  // |WriteMessage()| have actually been sent.
+  // TODO(vtl): We should really also notify our delegate when the write buffer
+  // becomes empty (or something like that).
+  bool IsWriteBufferEmpty();
+
+  // Returns the amount of space needed in the |MessageInTransit|'s
+  // |TransportData|'s "platform handle table" per platform handle (to be
+  // attached to a message). (This amount may be zero.)
+  virtual size_t GetSerializedPlatformHandleSize() const = 0;
+
+ protected:
+  // Result of I/O operations.
+  enum IOResult {
+    IO_SUCCEEDED,
+    // Failed due to a (probably) clean shutdown (e.g., of the other end).
+    IO_FAILED_SHUTDOWN,
+    // Failed due to the connection being broken (e.g., the other end dying).
+    IO_FAILED_BROKEN,
+    // Failed due to some other (unexpected) reason.
+    IO_FAILED_UNKNOWN,
+    IO_PENDING
+  };
+
+  class MOJO_SYSTEM_IMPL_EXPORT ReadBuffer {
+   public:
+    ReadBuffer();
+    ~ReadBuffer();
+
+    void GetBuffer(char** addr, size_t* size);
+
+   private:
+    friend class RawChannel;
+
+    // We store data from |[Schedule]Read()|s in |buffer_|. The start of
+    // |buffer_| is always aligned with a message boundary (we will copy memory
+    // to ensure this), but |buffer_| may be larger than the actual number of
+    // bytes we have.
+    std::vector<char> buffer_;
+    size_t num_valid_bytes_;
+
+    DISALLOW_COPY_AND_ASSIGN(ReadBuffer);
+  };
+
+  class MOJO_SYSTEM_IMPL_EXPORT WriteBuffer {
+   public:
+    struct Buffer {
+      const char* addr;
+      size_t size;
+    };
+
+    explicit WriteBuffer(size_t serialized_platform_handle_size);
+    ~WriteBuffer();
+
+    // Returns true if there are (more) platform handles to be sent (from the
+    // front of |message_queue_|).
+    bool HavePlatformHandlesToSend() const;
+    // Gets platform handles to be sent (from the front of |message_queue_|).
+    // This should only be called if |HavePlatformHandlesToSend()| returned
+    // true. There are two components to this: the actual |PlatformHandle|s
+    // (which should be closed once sent) and any additional serialization
+    // information (which will be embedded in the message's data; there are
+    // |GetSerializedPlatformHandleSize()| bytes per handle). Once all platform
+    // handles have been sent, the message data should be written next (see
+    // |GetBuffers()|).
+    void GetPlatformHandlesToSend(size_t* num_platform_handles,
+                                  embedder::PlatformHandle** platform_handles,
+                                  void** serialization_data);
+
+    // Gets buffers to be written. These buffers will always come from the front
+    // of |message_queue_|. Once they are completely written, the front
+    // |MessageInTransit| should be popped (and destroyed); this is done in
+    // |OnWriteCompletedNoLock()|.
+    void GetBuffers(std::vector<Buffer>* buffers) const;
+
+   private:
+    friend class RawChannel;
+
+    const size_t serialized_platform_handle_size_;
+
+    // TODO(vtl): When C++11 is available, switch this to a deque of
+    // |scoped_ptr|/|unique_ptr|s.
+    std::deque<MessageInTransit*> message_queue_;
+    // Platform handles are sent before the message data, but doing so may
+    // require several passes. |platform_handles_offset_| indicates the position
+    // in the first message's vector of platform handles to send next.
+    size_t platform_handles_offset_;
+    // The first message's data may have been partially sent. |data_offset_|
+    // indicates the position in the first message's data to start the next
+    // write.
+    size_t data_offset_;
+
+    DISALLOW_COPY_AND_ASSIGN(WriteBuffer);
+  };
+
+  RawChannel();
+
+  // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT
+  // |write_lock_| held.
+  void OnReadCompleted(IOResult io_result, size_t bytes_read);
+  // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT
+  // |write_lock_| held.
+  void OnWriteCompleted(IOResult io_result,
+                        size_t platform_handles_written,
+                        size_t bytes_written);
+
+  base::MessageLoopForIO* message_loop_for_io() { return message_loop_for_io_; }
+  base::Lock& write_lock() { return write_lock_; }
+
+  // Should only be called on the I/O thread.
+  ReadBuffer* read_buffer() { return read_buffer_.get(); }
+
+  // Only called under |write_lock_|.
+  WriteBuffer* write_buffer_no_lock() {
+    write_lock_.AssertAcquired();
+    return write_buffer_.get();
+  }
+
+  // Adds |message| to the write message queue. Implementation subclasses may
+  // override this to add any additional "control" messages needed. This is
+  // called (on any thread) with |write_lock_| held.
+  virtual void EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message);
+
+  // Handles any control messages targeted to the |RawChannel| (or
+  // implementation subclass). Implementation subclasses may override this to
+  // handle any implementation-specific control messages, but should call
+  // |RawChannel::OnReadMessageForRawChannel()| for any remaining messages.
+  // Returns true on success and false on error (e.g., invalid control message).
+  // This is only called on the I/O thread.
+  virtual bool OnReadMessageForRawChannel(
+      const MessageInTransit::View& message_view);
+
+  // Reads into |read_buffer()|.
+  // This class guarantees that:
+  // - the area indicated by |GetBuffer()| will stay valid until read completion
+  //   (but please also see the comments for |OnShutdownNoLock()|);
+  // - a second read is not started if there is a pending read;
+  // - the method is called on the I/O thread WITHOUT |write_lock_| held.
+  //
+  // The implementing subclass must guarantee that:
+  // - |bytes_read| is untouched unless |Read()| returns |IO_SUCCEEDED|;
+  // - if the method returns |IO_PENDING|, |OnReadCompleted()| will be called on
+  //   the I/O thread to report the result, unless |Shutdown()| is called.
+  virtual IOResult Read(size_t* bytes_read) = 0;
+  // Similar to |Read()|, except that the implementing subclass must also
+  // guarantee that the method doesn't succeed synchronously, i.e., it only
+  // returns |IO_FAILED_...| or |IO_PENDING|.
+  virtual IOResult ScheduleRead() = 0;
+
+  // Called by |OnReadCompleted()| to get the platform handles associated with
+  // the given platform handle table (from a message). This should only be
+  // called when |num_platform_handles| is nonzero. Returns null if the
+  // |num_platform_handles| handles are not available. Only called on the I/O
+  // thread (without |write_lock_| held).
+  virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+      size_t num_platform_handles,
+      const void* platform_handle_table) = 0;
+
+  // Writes contents in |write_buffer_no_lock()|.
+  // This class guarantees that:
+  // - the |PlatformHandle|s given by |GetPlatformHandlesToSend()| and the
+  //   buffer(s) given by |GetBuffers()| will remain valid until write
+  //   completion (see also the comments for |OnShutdownNoLock()|);
+  // - a second write is not started if there is a pending write;
+  // - the method is called under |write_lock_|.
+  //
+  // The implementing subclass must guarantee that:
+  // - |platform_handles_written| and |bytes_written| are untouched unless
+  //   |WriteNoLock()| returns |IO_SUCCEEDED|;
+  // - if the method returns |IO_PENDING|, |OnWriteCompleted()| will be called
+  //   on the I/O thread to report the result, unless |Shutdown()| is called.
+  virtual IOResult WriteNoLock(size_t* platform_handles_written,
+                               size_t* bytes_written) = 0;
+  // Similar to |WriteNoLock()|, except that the implementing subclass must also
+  // guarantee that the method doesn't succeed synchronously, i.e., it only
+  // returns |IO_FAILED_...| or |IO_PENDING|.
+  virtual IOResult ScheduleWriteNoLock() = 0;
+
+  // Must be called on the I/O thread WITHOUT |write_lock_| held.
+  virtual bool OnInit() = 0;
+  // On shutdown, passes the ownership of the buffers to subclasses, which may
+  // want to preserve them if there are pending read/write. Must be called on
+  // the I/O thread under |write_lock_|.
+  virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
+                                scoped_ptr<WriteBuffer> write_buffer) = 0;
+
+ private:
+  // Converts an |IO_FAILED_...| for a read to a |Delegate::Error|.
+  static Delegate::Error ReadIOResultToError(IOResult io_result);
+
+  // Calls |delegate_->OnError(error)|. Must be called on the I/O thread WITHOUT
+  // |write_lock_| held.
+  void CallOnError(Delegate::Error error);
+
+  // If |io_result| is |IO_SUCCESS|, updates the write buffer and schedules a
+  // write operation to run later if there is more to write. If |io_result| is
+  // failure or any other error occurs, cancels pending writes and returns
+  // false. Must be called under |write_lock_| and only if |write_stopped_| is
+  // false.
+  bool OnWriteCompletedNoLock(IOResult io_result,
+                              size_t platform_handles_written,
+                              size_t bytes_written);
+
+  // Set in |Init()| and never changed (hence usable on any thread without
+  // locking):
+  base::MessageLoopForIO* message_loop_for_io_;
+
+  // Only used on the I/O thread:
+  Delegate* delegate_;
+  bool read_stopped_;
+  scoped_ptr<ReadBuffer> read_buffer_;
+
+  base::Lock write_lock_;  // Protects the following members.
+  bool write_stopped_;
+  scoped_ptr<WriteBuffer> write_buffer_;
+
+  // This is used for posting tasks from write threads to the I/O thread. It
+  // must only be accessed under |write_lock_|. The weak pointers it produces
+  // are only used/invalidated on the I/O thread.
+  base::WeakPtrFactory<RawChannel> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawChannel);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_RAW_CHANNEL_H_
diff --git a/mojo/edk/system/raw_channel_posix.cc b/mojo/edk/system/raw_channel_posix.cc
new file mode 100644
index 0000000..05a01aa
--- /dev/null
+++ b/mojo/edk/system/raw_channel_posix.cc
@@ -0,0 +1,486 @@
+// 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/edk/system/raw_channel.h"
+
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <deque>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+class RawChannelPosix : public RawChannel,
+                        public base::MessageLoopForIO::Watcher {
+ public:
+  explicit RawChannelPosix(embedder::ScopedPlatformHandle handle);
+  virtual ~RawChannelPosix();
+
+  // |RawChannel| public methods:
+  virtual size_t GetSerializedPlatformHandleSize() const override;
+
+ private:
+  // |RawChannel| protected methods:
+  // Actually override this so that we can send multiple messages with (only)
+  // FDs if necessary.
+  virtual void EnqueueMessageNoLock(
+      scoped_ptr<MessageInTransit> message) override;
+  // Override this to handle those extra FD-only messages.
+  virtual bool OnReadMessageForRawChannel(
+      const MessageInTransit::View& message_view) override;
+  virtual IOResult Read(size_t* bytes_read) override;
+  virtual IOResult ScheduleRead() override;
+  virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+      size_t num_platform_handles,
+      const void* platform_handle_table) override;
+  virtual IOResult WriteNoLock(size_t* platform_handles_written,
+                               size_t* bytes_written) override;
+  virtual IOResult ScheduleWriteNoLock() override;
+  virtual bool OnInit() override;
+  virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
+                                scoped_ptr<WriteBuffer> write_buffer) override;
+
+  // |base::MessageLoopForIO::Watcher| implementation:
+  virtual void OnFileCanReadWithoutBlocking(int fd) override;
+  virtual void OnFileCanWriteWithoutBlocking(int fd) override;
+
+  // Implements most of |Read()| (except for a bit of clean-up):
+  IOResult ReadImpl(size_t* bytes_read);
+
+  // Watches for |fd_| to become writable. Must be called on the I/O thread.
+  void WaitToWrite();
+
+  embedder::ScopedPlatformHandle fd_;
+
+  // The following members are only used on the I/O thread:
+  scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> read_watcher_;
+  scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> write_watcher_;
+
+  bool pending_read_;
+
+  std::deque<embedder::PlatformHandle> read_platform_handles_;
+
+  // The following members are used on multiple threads and protected by
+  // |write_lock()|:
+  bool pending_write_;
+
+  // This is used for posting tasks from write threads to the I/O thread. It
+  // must only be accessed under |write_lock_|. The weak pointers it produces
+  // are only used/invalidated on the I/O thread.
+  base::WeakPtrFactory<RawChannelPosix> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawChannelPosix);
+};
+
+RawChannelPosix::RawChannelPosix(embedder::ScopedPlatformHandle handle)
+    : fd_(handle.Pass()),
+      pending_read_(false),
+      pending_write_(false),
+      weak_ptr_factory_(this) {
+  DCHECK(fd_.is_valid());
+}
+
+RawChannelPosix::~RawChannelPosix() {
+  DCHECK(!pending_read_);
+  DCHECK(!pending_write_);
+
+  // No need to take the |write_lock()| here -- if there are still weak pointers
+  // outstanding, then we're hosed anyway (since we wouldn't be able to
+  // invalidate them cleanly, since we might not be on the I/O thread).
+  DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+
+  // These must have been shut down/destroyed on the I/O thread.
+  DCHECK(!read_watcher_);
+  DCHECK(!write_watcher_);
+
+  embedder::CloseAllPlatformHandles(&read_platform_handles_);
+}
+
+size_t RawChannelPosix::GetSerializedPlatformHandleSize() const {
+  // We don't actually need any space on POSIX (since we just send FDs).
+  return 0;
+}
+
+void RawChannelPosix::EnqueueMessageNoLock(
+    scoped_ptr<MessageInTransit> message) {
+  if (message->transport_data()) {
+    embedder::PlatformHandleVector* const platform_handles =
+        message->transport_data()->platform_handles();
+    if (platform_handles &&
+        platform_handles->size() > embedder::kPlatformChannelMaxNumHandles) {
+      // We can't attach all the FDs to a single message, so we have to "split"
+      // the message. Send as many control messages as needed first with FDs
+      // attached (and no data).
+      size_t i = 0;
+      for (; platform_handles->size() - i >
+                 embedder::kPlatformChannelMaxNumHandles;
+           i += embedder::kPlatformChannelMaxNumHandles) {
+        scoped_ptr<MessageInTransit> fd_message(new MessageInTransit(
+            MessageInTransit::kTypeRawChannel,
+            MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles,
+            0,
+            nullptr));
+        embedder::ScopedPlatformHandleVectorPtr fds(
+            new embedder::PlatformHandleVector(
+                platform_handles->begin() + i,
+                platform_handles->begin() + i +
+                    embedder::kPlatformChannelMaxNumHandles));
+        fd_message->SetTransportData(
+            make_scoped_ptr(new TransportData(fds.Pass())));
+        RawChannel::EnqueueMessageNoLock(fd_message.Pass());
+      }
+
+      // Remove the handles that we "moved" into the other messages.
+      platform_handles->erase(platform_handles->begin(),
+                              platform_handles->begin() + i);
+    }
+  }
+
+  RawChannel::EnqueueMessageNoLock(message.Pass());
+}
+
+bool RawChannelPosix::OnReadMessageForRawChannel(
+    const MessageInTransit::View& message_view) {
+  DCHECK_EQ(message_view.type(), MessageInTransit::kTypeRawChannel);
+
+  if (message_view.subtype() ==
+      MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles) {
+    // We don't need to do anything. |RawChannel| won't extract the platform
+    // handles, and they'll be accumulated in |Read()|.
+    return true;
+  }
+
+  return RawChannel::OnReadMessageForRawChannel(message_view);
+}
+
+RawChannel::IOResult RawChannelPosix::Read(size_t* bytes_read) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  DCHECK(!pending_read_);
+
+  IOResult rv = ReadImpl(bytes_read);
+  if (rv != IO_SUCCEEDED && rv != IO_PENDING) {
+    // Make sure that |OnFileCanReadWithoutBlocking()| won't be called again.
+    read_watcher_.reset();
+  }
+  return rv;
+}
+
+RawChannel::IOResult RawChannelPosix::ScheduleRead() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  DCHECK(!pending_read_);
+
+  pending_read_ = true;
+
+  return IO_PENDING;
+}
+
+embedder::ScopedPlatformHandleVectorPtr RawChannelPosix::GetReadPlatformHandles(
+    size_t num_platform_handles,
+    const void* /*platform_handle_table*/) {
+  DCHECK_GT(num_platform_handles, 0u);
+
+  if (read_platform_handles_.size() < num_platform_handles) {
+    embedder::CloseAllPlatformHandles(&read_platform_handles_);
+    read_platform_handles_.clear();
+    return embedder::ScopedPlatformHandleVectorPtr();
+  }
+
+  embedder::ScopedPlatformHandleVectorPtr rv(
+      new embedder::PlatformHandleVector(num_platform_handles));
+  rv->assign(read_platform_handles_.begin(),
+             read_platform_handles_.begin() + num_platform_handles);
+  read_platform_handles_.erase(
+      read_platform_handles_.begin(),
+      read_platform_handles_.begin() + num_platform_handles);
+  return rv.Pass();
+}
+
+RawChannel::IOResult RawChannelPosix::WriteNoLock(
+    size_t* platform_handles_written,
+    size_t* bytes_written) {
+  write_lock().AssertAcquired();
+
+  DCHECK(!pending_write_);
+
+  size_t num_platform_handles = 0;
+  ssize_t write_result;
+  if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
+    embedder::PlatformHandle* platform_handles;
+    void* serialization_data;  // Actually unused.
+    write_buffer_no_lock()->GetPlatformHandlesToSend(
+        &num_platform_handles, &platform_handles, &serialization_data);
+    DCHECK_GT(num_platform_handles, 0u);
+    DCHECK_LE(num_platform_handles, embedder::kPlatformChannelMaxNumHandles);
+    DCHECK(platform_handles);
+
+    // TODO(vtl): Reduce code duplication. (This is duplicated from below.)
+    std::vector<WriteBuffer::Buffer> buffers;
+    write_buffer_no_lock()->GetBuffers(&buffers);
+    DCHECK(!buffers.empty());
+    const size_t kMaxBufferCount = 10;
+    iovec iov[kMaxBufferCount];
+    size_t buffer_count = std::min(buffers.size(), kMaxBufferCount);
+    for (size_t i = 0; i < buffer_count; ++i) {
+      iov[i].iov_base = const_cast<char*>(buffers[i].addr);
+      iov[i].iov_len = buffers[i].size;
+    }
+
+    write_result = embedder::PlatformChannelSendmsgWithHandles(
+        fd_.get(), iov, buffer_count, platform_handles, num_platform_handles);
+    for (size_t i = 0; i < num_platform_handles; i++)
+      platform_handles[i].CloseIfNecessary();
+  } else {
+    std::vector<WriteBuffer::Buffer> buffers;
+    write_buffer_no_lock()->GetBuffers(&buffers);
+    DCHECK(!buffers.empty());
+
+    if (buffers.size() == 1) {
+      write_result = embedder::PlatformChannelWrite(
+          fd_.get(), buffers[0].addr, buffers[0].size);
+    } else {
+      const size_t kMaxBufferCount = 10;
+      iovec iov[kMaxBufferCount];
+      size_t buffer_count = std::min(buffers.size(), kMaxBufferCount);
+      for (size_t i = 0; i < buffer_count; ++i) {
+        iov[i].iov_base = const_cast<char*>(buffers[i].addr);
+        iov[i].iov_len = buffers[i].size;
+      }
+
+      write_result =
+          embedder::PlatformChannelWritev(fd_.get(), iov, buffer_count);
+    }
+  }
+
+  if (write_result >= 0) {
+    *platform_handles_written = num_platform_handles;
+    *bytes_written = static_cast<size_t>(write_result);
+    return IO_SUCCEEDED;
+  }
+
+  if (errno == EPIPE)
+    return IO_FAILED_SHUTDOWN;
+
+  if (errno != EAGAIN && errno != EWOULDBLOCK) {
+    PLOG(WARNING) << "sendmsg/write/writev";
+    return IO_FAILED_UNKNOWN;
+  }
+
+  return ScheduleWriteNoLock();
+}
+
+RawChannel::IOResult RawChannelPosix::ScheduleWriteNoLock() {
+  write_lock().AssertAcquired();
+
+  DCHECK(!pending_write_);
+
+  // Set up to wait for the FD to become writable.
+  // If we're not on the I/O thread, we have to post a task to do this.
+  if (base::MessageLoop::current() != message_loop_for_io()) {
+    message_loop_for_io()->PostTask(FROM_HERE,
+                                    base::Bind(&RawChannelPosix::WaitToWrite,
+                                               weak_ptr_factory_.GetWeakPtr()));
+    pending_write_ = true;
+    return IO_PENDING;
+  }
+
+  if (message_loop_for_io()->WatchFileDescriptor(
+          fd_.get().fd,
+          false,
+          base::MessageLoopForIO::WATCH_WRITE,
+          write_watcher_.get(),
+          this)) {
+    pending_write_ = true;
+    return IO_PENDING;
+  }
+
+  return IO_FAILED_UNKNOWN;
+}
+
+bool RawChannelPosix::OnInit() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+  DCHECK(!read_watcher_);
+  read_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher());
+  DCHECK(!write_watcher_);
+  write_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher());
+
+  if (!message_loop_for_io()->WatchFileDescriptor(
+          fd_.get().fd,
+          true,
+          base::MessageLoopForIO::WATCH_READ,
+          read_watcher_.get(),
+          this)) {
+    // TODO(vtl): I'm not sure |WatchFileDescriptor()| actually fails cleanly
+    // (in the sense of returning the message loop's state to what it was before
+    // it was called).
+    read_watcher_.reset();
+    write_watcher_.reset();
+    return false;
+  }
+
+  return true;
+}
+
+void RawChannelPosix::OnShutdownNoLock(
+    scoped_ptr<ReadBuffer> /*read_buffer*/,
+    scoped_ptr<WriteBuffer> /*write_buffer*/) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  write_lock().AssertAcquired();
+
+  read_watcher_.reset();   // This will stop watching (if necessary).
+  write_watcher_.reset();  // This will stop watching (if necessary).
+
+  pending_read_ = false;
+  pending_write_ = false;
+
+  DCHECK(fd_.is_valid());
+  fd_.reset();
+
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void RawChannelPosix::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK_EQ(fd, fd_.get().fd);
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+  if (!pending_read_) {
+    NOTREACHED();
+    return;
+  }
+
+  pending_read_ = false;
+  size_t bytes_read = 0;
+  IOResult io_result = Read(&bytes_read);
+  if (io_result != IO_PENDING)
+    OnReadCompleted(io_result, bytes_read);
+
+  // On failure, |read_watcher_| must have been reset; on success,
+  // we assume that |OnReadCompleted()| always schedules another read.
+  // Otherwise, we could end up spinning -- getting
+  // |OnFileCanReadWithoutBlocking()| again and again but not doing any actual
+  // read.
+  // TODO(yzshen): An alternative is to stop watching if RawChannel doesn't
+  // schedule a new read. But that code won't be reached under the current
+  // RawChannel implementation.
+  DCHECK(!read_watcher_ || pending_read_);
+}
+
+void RawChannelPosix::OnFileCanWriteWithoutBlocking(int fd) {
+  DCHECK_EQ(fd, fd_.get().fd);
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+  IOResult io_result;
+  size_t platform_handles_written = 0;
+  size_t bytes_written = 0;
+  {
+    base::AutoLock locker(write_lock());
+
+    DCHECK(pending_write_);
+
+    pending_write_ = false;
+    io_result = WriteNoLock(&platform_handles_written, &bytes_written);
+  }
+
+  if (io_result != IO_PENDING)
+    OnWriteCompleted(io_result, platform_handles_written, bytes_written);
+}
+
+RawChannel::IOResult RawChannelPosix::ReadImpl(size_t* bytes_read) {
+  char* buffer = nullptr;
+  size_t bytes_to_read = 0;
+  read_buffer()->GetBuffer(&buffer, &bytes_to_read);
+
+  size_t old_num_platform_handles = read_platform_handles_.size();
+  ssize_t read_result = embedder::PlatformChannelRecvmsg(
+      fd_.get(), buffer, bytes_to_read, &read_platform_handles_);
+  if (read_platform_handles_.size() > old_num_platform_handles) {
+    DCHECK_LE(read_platform_handles_.size() - old_num_platform_handles,
+              embedder::kPlatformChannelMaxNumHandles);
+
+    // We should never accumulate more than |TransportData::kMaxPlatformHandles
+    // + embedder::kPlatformChannelMaxNumHandles| handles. (The latter part is
+    // possible because we could have accumulated all the handles for a message,
+    // then received the message data plus the first set of handles for the next
+    // message in the subsequent |recvmsg()|.)
+    if (read_platform_handles_.size() >
+        (TransportData::kMaxPlatformHandles +
+         embedder::kPlatformChannelMaxNumHandles)) {
+      LOG(ERROR) << "Received too many platform handles";
+      embedder::CloseAllPlatformHandles(&read_platform_handles_);
+      read_platform_handles_.clear();
+      return IO_FAILED_UNKNOWN;
+    }
+  }
+
+  if (read_result > 0) {
+    *bytes_read = static_cast<size_t>(read_result);
+    return IO_SUCCEEDED;
+  }
+
+  // |read_result == 0| means "end of file".
+  if (read_result == 0)
+    return IO_FAILED_SHUTDOWN;
+
+  if (errno == EAGAIN || errno == EWOULDBLOCK)
+    return ScheduleRead();
+
+  if (errno == ECONNRESET)
+    return IO_FAILED_BROKEN;
+
+  PLOG(WARNING) << "recvmsg";
+  return IO_FAILED_UNKNOWN;
+}
+
+void RawChannelPosix::WaitToWrite() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+  DCHECK(write_watcher_);
+
+  if (!message_loop_for_io()->WatchFileDescriptor(
+          fd_.get().fd,
+          false,
+          base::MessageLoopForIO::WATCH_WRITE,
+          write_watcher_.get(),
+          this)) {
+    {
+      base::AutoLock locker(write_lock());
+
+      DCHECK(pending_write_);
+      pending_write_ = false;
+    }
+    OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0);
+  }
+}
+
+}  // namespace
+
+// -----------------------------------------------------------------------------
+
+// Static factory method declared in raw_channel.h.
+// static
+scoped_ptr<RawChannel> RawChannel::Create(
+    embedder::ScopedPlatformHandle handle) {
+  return make_scoped_ptr(new RawChannelPosix(handle.Pass()));
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/raw_channel_unittest.cc b/mojo/edk/system/raw_channel_unittest.cc
new file mode 100644
index 0000000..7531cf3
--- /dev/null
+++ b/mojo/edk/system/raw_channel_unittest.cc
@@ -0,0 +1,688 @@
+// Copyright 2014 The Chromium 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/edk/system/raw_channel.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/rand_util.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_io_thread.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) {
+  std::vector<unsigned char> bytes(num_bytes, 0);
+  for (size_t i = 0; i < num_bytes; i++)
+    bytes[i] = static_cast<unsigned char>(i + num_bytes);
+  return make_scoped_ptr(
+      new MessageInTransit(MessageInTransit::kTypeMessagePipeEndpoint,
+                           MessageInTransit::kSubtypeMessagePipeEndpointData,
+                           num_bytes,
+                           bytes.empty() ? nullptr : &bytes[0]));
+}
+
+bool CheckMessageData(const void* bytes, uint32_t num_bytes) {
+  const unsigned char* b = static_cast<const unsigned char*>(bytes);
+  for (uint32_t i = 0; i < num_bytes; i++) {
+    if (b[i] != static_cast<unsigned char>(i + num_bytes))
+      return false;
+  }
+  return true;
+}
+
+void InitOnIOThread(RawChannel* raw_channel, RawChannel::Delegate* delegate) {
+  CHECK(raw_channel->Init(delegate));
+}
+
+bool WriteTestMessageToHandle(const embedder::PlatformHandle& handle,
+                              uint32_t num_bytes) {
+  scoped_ptr<MessageInTransit> message(MakeTestMessage(num_bytes));
+
+  size_t write_size = 0;
+  mojo::test::BlockingWrite(
+      handle, message->main_buffer(), message->main_buffer_size(), &write_size);
+  return write_size == message->main_buffer_size();
+}
+
+// -----------------------------------------------------------------------------
+
+class RawChannelTest : public testing::Test {
+ public:
+  RawChannelTest() : io_thread_(base::TestIOThread::kManualStart) {}
+  virtual ~RawChannelTest() {}
+
+  virtual void SetUp() override {
+    embedder::PlatformChannelPair channel_pair;
+    handles[0] = channel_pair.PassServerHandle();
+    handles[1] = channel_pair.PassClientHandle();
+    io_thread_.Start();
+  }
+
+  virtual void TearDown() override {
+    io_thread_.Stop();
+    handles[0].reset();
+    handles[1].reset();
+  }
+
+ protected:
+  base::TestIOThread* io_thread() { return &io_thread_; }
+
+  embedder::ScopedPlatformHandle handles[2];
+
+ private:
+  base::TestIOThread io_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawChannelTest);
+};
+
+// RawChannelTest.WriteMessage -------------------------------------------------
+
+class WriteOnlyRawChannelDelegate : public RawChannel::Delegate {
+ public:
+  WriteOnlyRawChannelDelegate() {}
+  virtual ~WriteOnlyRawChannelDelegate() {}
+
+  // |RawChannel::Delegate| implementation:
+  virtual void OnReadMessage(
+      const MessageInTransit::View& /*message_view*/,
+      embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override {
+    CHECK(false);  // Should not get called.
+  }
+  virtual void OnError(Error error) override {
+    // We'll get a read (shutdown) error when the connection is closed.
+    CHECK_EQ(error, ERROR_READ_SHUTDOWN);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WriteOnlyRawChannelDelegate);
+};
+
+static const int64_t kMessageReaderSleepMs = 1;
+static const size_t kMessageReaderMaxPollIterations = 3000;
+
+class TestMessageReaderAndChecker {
+ public:
+  explicit TestMessageReaderAndChecker(embedder::PlatformHandle handle)
+      : handle_(handle) {}
+  ~TestMessageReaderAndChecker() { CHECK(bytes_.empty()); }
+
+  bool ReadAndCheckNextMessage(uint32_t expected_size) {
+    unsigned char buffer[4096];
+
+    for (size_t i = 0; i < kMessageReaderMaxPollIterations;) {
+      size_t read_size = 0;
+      CHECK(mojo::test::NonBlockingRead(
+          handle_, buffer, sizeof(buffer), &read_size));
+
+      // Append newly-read data to |bytes_|.
+      bytes_.insert(bytes_.end(), buffer, buffer + read_size);
+
+      // If we have the header....
+      size_t message_size;
+      if (MessageInTransit::GetNextMessageSize(
+              bytes_.empty() ? nullptr : &bytes_[0],
+              bytes_.size(),
+              &message_size)) {
+        // If we've read the whole message....
+        if (bytes_.size() >= message_size) {
+          bool rv = true;
+          MessageInTransit::View message_view(message_size, &bytes_[0]);
+          CHECK_EQ(message_view.main_buffer_size(), message_size);
+
+          if (message_view.num_bytes() != expected_size) {
+            LOG(ERROR) << "Wrong size: " << message_size << " instead of "
+                       << expected_size << " bytes.";
+            rv = false;
+          } else if (!CheckMessageData(message_view.bytes(),
+                                       message_view.num_bytes())) {
+            LOG(ERROR) << "Incorrect message bytes.";
+            rv = false;
+          }
+
+          // Erase message data.
+          bytes_.erase(bytes_.begin(),
+                       bytes_.begin() + message_view.main_buffer_size());
+          return rv;
+        }
+      }
+
+      if (static_cast<size_t>(read_size) < sizeof(buffer)) {
+        i++;
+        base::PlatformThread::Sleep(
+            base::TimeDelta::FromMilliseconds(kMessageReaderSleepMs));
+      }
+    }
+
+    LOG(ERROR) << "Too many iterations.";
+    return false;
+  }
+
+ private:
+  const embedder::PlatformHandle handle_;
+
+  // The start of the received data should always be on a message boundary.
+  std::vector<unsigned char> bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestMessageReaderAndChecker);
+};
+
+// Tests writing (and verifies reading using our own custom reader).
+TEST_F(RawChannelTest, WriteMessage) {
+  WriteOnlyRawChannelDelegate delegate;
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  TestMessageReaderAndChecker checker(handles[1].get());
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Write and read, for a variety of sizes.
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
+    EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
+    EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
+  }
+
+  // Write/queue and read afterwards, for a variety of sizes.
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+    EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+    EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
+}
+
+// RawChannelTest.OnReadMessage ------------------------------------------------
+
+class ReadCheckerRawChannelDelegate : public RawChannel::Delegate {
+ public:
+  ReadCheckerRawChannelDelegate() : done_event_(false, false), position_(0) {}
+  virtual ~ReadCheckerRawChannelDelegate() {}
+
+  // |RawChannel::Delegate| implementation (called on the I/O thread):
+  virtual void OnReadMessage(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
+    EXPECT_FALSE(platform_handles);
+
+    size_t position;
+    size_t expected_size;
+    bool should_signal = false;
+    {
+      base::AutoLock locker(lock_);
+      CHECK_LT(position_, expected_sizes_.size());
+      position = position_;
+      expected_size = expected_sizes_[position];
+      position_++;
+      if (position_ >= expected_sizes_.size())
+        should_signal = true;
+    }
+
+    EXPECT_EQ(expected_size, message_view.num_bytes()) << position;
+    if (message_view.num_bytes() == expected_size) {
+      EXPECT_TRUE(
+          CheckMessageData(message_view.bytes(), message_view.num_bytes()))
+          << position;
+    }
+
+    if (should_signal)
+      done_event_.Signal();
+  }
+  virtual void OnError(Error error) override {
+    // We'll get a read (shutdown) error when the connection is closed.
+    CHECK_EQ(error, ERROR_READ_SHUTDOWN);
+  }
+
+  // Waits for all the messages (of sizes |expected_sizes_|) to be seen.
+  void Wait() { done_event_.Wait(); }
+
+  void SetExpectedSizes(const std::vector<uint32_t>& expected_sizes) {
+    base::AutoLock locker(lock_);
+    CHECK_EQ(position_, expected_sizes_.size());
+    expected_sizes_ = expected_sizes;
+    position_ = 0;
+  }
+
+ private:
+  base::WaitableEvent done_event_;
+
+  base::Lock lock_;  // Protects the following members.
+  std::vector<uint32_t> expected_sizes_;
+  size_t position_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadCheckerRawChannelDelegate);
+};
+
+// Tests reading (writing using our own custom writer).
+TEST_F(RawChannelTest, OnReadMessage) {
+  ReadCheckerRawChannelDelegate delegate;
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Write and read, for a variety of sizes.
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
+    delegate.SetExpectedSizes(std::vector<uint32_t>(1, size));
+
+    EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
+
+    delegate.Wait();
+  }
+
+  // Set up reader and write as fast as we can.
+  // Write/queue and read afterwards, for a variety of sizes.
+  std::vector<uint32_t> expected_sizes;
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+    expected_sizes.push_back(size);
+  delegate.SetExpectedSizes(expected_sizes);
+  for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+    EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
+  delegate.Wait();
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
+}
+
+// RawChannelTest.WriteMessageAndOnReadMessage ---------------------------------
+
+class RawChannelWriterThread : public base::SimpleThread {
+ public:
+  RawChannelWriterThread(RawChannel* raw_channel, size_t write_count)
+      : base::SimpleThread("raw_channel_writer_thread"),
+        raw_channel_(raw_channel),
+        left_to_write_(write_count) {}
+
+  virtual ~RawChannelWriterThread() { Join(); }
+
+ private:
+  virtual void Run() override {
+    static const int kMaxRandomMessageSize = 25000;
+
+    while (left_to_write_-- > 0) {
+      EXPECT_TRUE(raw_channel_->WriteMessage(MakeTestMessage(
+          static_cast<uint32_t>(base::RandInt(1, kMaxRandomMessageSize)))));
+    }
+  }
+
+  RawChannel* const raw_channel_;
+  size_t left_to_write_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawChannelWriterThread);
+};
+
+class ReadCountdownRawChannelDelegate : public RawChannel::Delegate {
+ public:
+  explicit ReadCountdownRawChannelDelegate(size_t expected_count)
+      : done_event_(false, false), expected_count_(expected_count), count_(0) {}
+  virtual ~ReadCountdownRawChannelDelegate() {}
+
+  // |RawChannel::Delegate| implementation (called on the I/O thread):
+  virtual void OnReadMessage(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
+    EXPECT_FALSE(platform_handles);
+
+    EXPECT_LT(count_, expected_count_);
+    count_++;
+
+    EXPECT_TRUE(
+        CheckMessageData(message_view.bytes(), message_view.num_bytes()));
+
+    if (count_ >= expected_count_)
+      done_event_.Signal();
+  }
+  virtual void OnError(Error error) override {
+    // We'll get a read (shutdown) error when the connection is closed.
+    CHECK_EQ(error, ERROR_READ_SHUTDOWN);
+  }
+
+  // Waits for all the messages to have been seen.
+  void Wait() { done_event_.Wait(); }
+
+ private:
+  base::WaitableEvent done_event_;
+  size_t expected_count_;
+  size_t count_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadCountdownRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, WriteMessageAndOnReadMessage) {
+  static const size_t kNumWriterThreads = 10;
+  static const size_t kNumWriteMessagesPerThread = 4000;
+
+  WriteOnlyRawChannelDelegate writer_delegate;
+  scoped_ptr<RawChannel> writer_rc(RawChannel::Create(handles[0].Pass()));
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&InitOnIOThread,
+                                          writer_rc.get(),
+                                          base::Unretained(&writer_delegate)));
+
+  ReadCountdownRawChannelDelegate reader_delegate(kNumWriterThreads *
+                                                  kNumWriteMessagesPerThread);
+  scoped_ptr<RawChannel> reader_rc(RawChannel::Create(handles[1].Pass()));
+  io_thread()->PostTaskAndWait(FROM_HERE,
+                               base::Bind(&InitOnIOThread,
+                                          reader_rc.get(),
+                                          base::Unretained(&reader_delegate)));
+
+  {
+    ScopedVector<RawChannelWriterThread> writer_threads;
+    for (size_t i = 0; i < kNumWriterThreads; i++) {
+      writer_threads.push_back(new RawChannelWriterThread(
+          writer_rc.get(), kNumWriteMessagesPerThread));
+    }
+    for (size_t i = 0; i < writer_threads.size(); i++)
+      writer_threads[i]->Start();
+  }  // Joins all the writer threads.
+
+  // Sleep a bit, to let any extraneous reads be processed. (There shouldn't be
+  // any, but we want to know about them.)
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+
+  // Wait for reading to finish.
+  reader_delegate.Wait();
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&RawChannel::Shutdown, base::Unretained(reader_rc.get())));
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&RawChannel::Shutdown, base::Unretained(writer_rc.get())));
+}
+
+// RawChannelTest.OnError ------------------------------------------------------
+
+class ErrorRecordingRawChannelDelegate
+    : public ReadCountdownRawChannelDelegate {
+ public:
+  ErrorRecordingRawChannelDelegate(size_t expected_read_count,
+                                   bool expect_read_error,
+                                   bool expect_write_error)
+      : ReadCountdownRawChannelDelegate(expected_read_count),
+        got_read_error_event_(false, false),
+        got_write_error_event_(false, false),
+        expecting_read_error_(expect_read_error),
+        expecting_write_error_(expect_write_error) {}
+
+  virtual ~ErrorRecordingRawChannelDelegate() {}
+
+  virtual void OnError(Error error) override {
+    switch (error) {
+      case ERROR_READ_SHUTDOWN:
+        ASSERT_TRUE(expecting_read_error_);
+        expecting_read_error_ = false;
+        got_read_error_event_.Signal();
+        break;
+      case ERROR_READ_BROKEN:
+        // TODO(vtl): Test broken connections.
+        CHECK(false);
+        break;
+      case ERROR_READ_BAD_MESSAGE:
+        // TODO(vtl): Test reception/detection of bad messages.
+        CHECK(false);
+        break;
+      case ERROR_READ_UNKNOWN:
+        // TODO(vtl): Test however it is we might get here.
+        CHECK(false);
+        break;
+      case ERROR_WRITE:
+        ASSERT_TRUE(expecting_write_error_);
+        expecting_write_error_ = false;
+        got_write_error_event_.Signal();
+        break;
+    }
+  }
+
+  void WaitForReadError() { got_read_error_event_.Wait(); }
+  void WaitForWriteError() { got_write_error_event_.Wait(); }
+
+ private:
+  base::WaitableEvent got_read_error_event_;
+  base::WaitableEvent got_write_error_event_;
+
+  bool expecting_read_error_;
+  bool expecting_write_error_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrorRecordingRawChannelDelegate);
+};
+
+// Tests (fatal) errors.
+TEST_F(RawChannelTest, OnError) {
+  ErrorRecordingRawChannelDelegate delegate(0, true, true);
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Close the handle of the other end, which should make writing fail.
+  handles[1].reset();
+
+  EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+  // We should get a write error.
+  delegate.WaitForWriteError();
+
+  // We should also get a read error.
+  delegate.WaitForReadError();
+
+  EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(2)));
+
+  // Sleep a bit, to make sure we don't get another |OnError()|
+  // notification. (If we actually get another one, |OnError()| crashes.)
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
+}
+
+// RawChannelTest.ReadUnaffectedByWriteError -----------------------------------
+
+TEST_F(RawChannelTest, ReadUnaffectedByWriteError) {
+  const size_t kMessageCount = 5;
+
+  // Write a few messages into the other end.
+  uint32_t message_size = 1;
+  for (size_t i = 0; i < kMessageCount;
+       i++, message_size += message_size / 2 + 1)
+    EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), message_size));
+
+  // Close the other end, which should make writing fail.
+  handles[1].reset();
+
+  // Only start up reading here. The system buffer should still contain the
+  // messages that were written.
+  ErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true);
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+  // We should definitely get a write error.
+  delegate.WaitForWriteError();
+
+  // Wait for reading to finish. A writing failure shouldn't affect reading.
+  delegate.Wait();
+
+  // And then we should get a read error.
+  delegate.WaitForReadError();
+
+  io_thread()->PostTaskAndWait(
+      FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
+}
+
+// RawChannelTest.WriteMessageAfterShutdown ------------------------------------
+
+// Makes sure that calling |WriteMessage()| after |Shutdown()| behaves
+// correctly.
+TEST_F(RawChannelTest, WriteMessageAfterShutdown) {
+  WriteOnlyRawChannelDelegate delegate;
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+  io_thread()->PostTaskAndWait(
+      FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
+
+  EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+}
+
+// RawChannelTest.ShutdownOnReadMessage ----------------------------------------
+
+class ShutdownOnReadMessageRawChannelDelegate : public RawChannel::Delegate {
+ public:
+  explicit ShutdownOnReadMessageRawChannelDelegate(RawChannel* raw_channel)
+      : raw_channel_(raw_channel),
+        done_event_(false, false),
+        did_shutdown_(false) {}
+  virtual ~ShutdownOnReadMessageRawChannelDelegate() {}
+
+  // |RawChannel::Delegate| implementation (called on the I/O thread):
+  virtual void OnReadMessage(
+      const MessageInTransit::View& message_view,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
+    EXPECT_FALSE(platform_handles);
+    EXPECT_FALSE(did_shutdown_);
+    EXPECT_TRUE(
+        CheckMessageData(message_view.bytes(), message_view.num_bytes()));
+    raw_channel_->Shutdown();
+    did_shutdown_ = true;
+    done_event_.Signal();
+  }
+  virtual void OnError(Error /*error*/) override {
+    CHECK(false);  // Should not get called.
+  }
+
+  // Waits for shutdown.
+  void Wait() {
+    done_event_.Wait();
+    EXPECT_TRUE(did_shutdown_);
+  }
+
+ private:
+  RawChannel* const raw_channel_;
+  base::WaitableEvent done_event_;
+  bool did_shutdown_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShutdownOnReadMessageRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, ShutdownOnReadMessage) {
+  // Write a few messages into the other end.
+  for (size_t count = 0; count < 5; count++)
+    EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), 10));
+
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  ShutdownOnReadMessageRawChannelDelegate delegate(rc.get());
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Wait for the delegate, which will shut the |RawChannel| down.
+  delegate.Wait();
+}
+
+// RawChannelTest.ShutdownOnError{Read, Write} ---------------------------------
+
+class ShutdownOnErrorRawChannelDelegate : public RawChannel::Delegate {
+ public:
+  ShutdownOnErrorRawChannelDelegate(RawChannel* raw_channel,
+                                    Error shutdown_on_error_type)
+      : raw_channel_(raw_channel),
+        shutdown_on_error_type_(shutdown_on_error_type),
+        done_event_(false, false),
+        did_shutdown_(false) {}
+  virtual ~ShutdownOnErrorRawChannelDelegate() {}
+
+  // |RawChannel::Delegate| implementation (called on the I/O thread):
+  virtual void OnReadMessage(
+      const MessageInTransit::View& /*message_view*/,
+      embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override {
+    CHECK(false);  // Should not get called.
+  }
+  virtual void OnError(Error error) override {
+    EXPECT_FALSE(did_shutdown_);
+    if (error != shutdown_on_error_type_)
+      return;
+    raw_channel_->Shutdown();
+    did_shutdown_ = true;
+    done_event_.Signal();
+  }
+
+  // Waits for shutdown.
+  void Wait() {
+    done_event_.Wait();
+    EXPECT_TRUE(did_shutdown_);
+  }
+
+ private:
+  RawChannel* const raw_channel_;
+  const Error shutdown_on_error_type_;
+  base::WaitableEvent done_event_;
+  bool did_shutdown_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShutdownOnErrorRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, ShutdownOnErrorRead) {
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  ShutdownOnErrorRawChannelDelegate delegate(
+      rc.get(), RawChannel::Delegate::ERROR_READ_SHUTDOWN);
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Close the handle of the other end, which should stuff fail.
+  handles[1].reset();
+
+  // Wait for the delegate, which will shut the |RawChannel| down.
+  delegate.Wait();
+}
+
+TEST_F(RawChannelTest, ShutdownOnErrorWrite) {
+  scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+  ShutdownOnErrorRawChannelDelegate delegate(rc.get(),
+                                             RawChannel::Delegate::ERROR_WRITE);
+  io_thread()->PostTaskAndWait(
+      FROM_HERE,
+      base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
+
+  // Close the handle of the other end, which should stuff fail.
+  handles[1].reset();
+
+  EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+  // Wait for the delegate, which will shut the |RawChannel| down.
+  delegate.Wait();
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/raw_channel_win.cc b/mojo/edk/system/raw_channel_win.cc
new file mode 100644
index 0000000..208d595
--- /dev/null
+++ b/mojo/edk/system/raw_channel_win.cc
@@ -0,0 +1,585 @@
+// 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/edk/system/raw_channel.h"
+
+#include <windows.h>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/win/windows_version.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+class VistaOrHigherFunctions {
+ public:
+  VistaOrHigherFunctions();
+
+  bool is_vista_or_higher() const { return is_vista_or_higher_; }
+
+  BOOL SetFileCompletionNotificationModes(HANDLE handle, UCHAR flags) {
+    return set_file_completion_notification_modes_(handle, flags);
+  }
+
+  BOOL CancelIoEx(HANDLE handle, LPOVERLAPPED overlapped) {
+    return cancel_io_ex_(handle, overlapped);
+  }
+
+ private:
+  typedef BOOL(WINAPI* SetFileCompletionNotificationModesFunc)(HANDLE, UCHAR);
+  typedef BOOL(WINAPI* CancelIoExFunc)(HANDLE, LPOVERLAPPED);
+
+  bool is_vista_or_higher_;
+  SetFileCompletionNotificationModesFunc
+      set_file_completion_notification_modes_;
+  CancelIoExFunc cancel_io_ex_;
+};
+
+VistaOrHigherFunctions::VistaOrHigherFunctions()
+    : is_vista_or_higher_(base::win::GetVersion() >= base::win::VERSION_VISTA),
+      set_file_completion_notification_modes_(nullptr),
+      cancel_io_ex_(nullptr) {
+  if (!is_vista_or_higher_)
+    return;
+
+  HMODULE module = GetModuleHandleW(L"kernel32.dll");
+  set_file_completion_notification_modes_ =
+      reinterpret_cast<SetFileCompletionNotificationModesFunc>(
+          GetProcAddress(module, "SetFileCompletionNotificationModes"));
+  DCHECK(set_file_completion_notification_modes_);
+
+  cancel_io_ex_ =
+      reinterpret_cast<CancelIoExFunc>(GetProcAddress(module, "CancelIoEx"));
+  DCHECK(cancel_io_ex_);
+}
+
+base::LazyInstance<VistaOrHigherFunctions> g_vista_or_higher_functions =
+    LAZY_INSTANCE_INITIALIZER;
+
+class RawChannelWin : public RawChannel {
+ public:
+  RawChannelWin(embedder::ScopedPlatformHandle handle);
+  virtual ~RawChannelWin();
+
+  // |RawChannel| public methods:
+  virtual size_t GetSerializedPlatformHandleSize() const override;
+
+ private:
+  // RawChannelIOHandler receives OS notifications for I/O completion. It must
+  // be created on the I/O thread.
+  //
+  // It manages its own destruction. Destruction happens on the I/O thread when
+  // all the following conditions are satisfied:
+  //   - |DetachFromOwnerNoLock()| has been called;
+  //   - there is no pending read;
+  //   - there is no pending write.
+  class RawChannelIOHandler : public base::MessageLoopForIO::IOHandler {
+   public:
+    RawChannelIOHandler(RawChannelWin* owner,
+                        embedder::ScopedPlatformHandle handle);
+
+    HANDLE handle() const { return handle_.get().handle; }
+
+    // The following methods are only called by the owner on the I/O thread.
+    bool pending_read() const;
+    base::MessageLoopForIO::IOContext* read_context();
+    // Instructs the object to wait for an |OnIOCompleted()| notification.
+    void OnPendingReadStarted();
+
+    // The following methods are only called by the owner under
+    // |owner_->write_lock()|.
+    bool pending_write_no_lock() const;
+    base::MessageLoopForIO::IOContext* write_context_no_lock();
+    // Instructs the object to wait for an |OnIOCompleted()| notification.
+    void OnPendingWriteStartedNoLock();
+
+    // |base::MessageLoopForIO::IOHandler| implementation:
+    // Must be called on the I/O thread. It could be called before or after
+    // detached from the owner.
+    virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
+                               DWORD bytes_transferred,
+                               DWORD error) override;
+
+    // Must be called on the I/O thread under |owner_->write_lock()|.
+    // After this call, the owner must not make any further calls on this
+    // object, and therefore the object is used on the I/O thread exclusively
+    // (if it stays alive).
+    void DetachFromOwnerNoLock(scoped_ptr<ReadBuffer> read_buffer,
+                               scoped_ptr<WriteBuffer> write_buffer);
+
+   private:
+    virtual ~RawChannelIOHandler();
+
+    // Returns true if |owner_| has been reset and there is not pending read or
+    // write.
+    // Must be called on the I/O thread.
+    bool ShouldSelfDestruct() const;
+
+    // Must be called on the I/O thread. It may be called before or after
+    // detaching from the owner.
+    void OnReadCompleted(DWORD bytes_read, DWORD error);
+    // Must be called on the I/O thread. It may be called before or after
+    // detaching from the owner.
+    void OnWriteCompleted(DWORD bytes_written, DWORD error);
+
+    embedder::ScopedPlatformHandle handle_;
+
+    // |owner_| is reset on the I/O thread under |owner_->write_lock()|.
+    // Therefore, it may be used on any thread under lock; or on the I/O thread
+    // without locking.
+    RawChannelWin* owner_;
+
+    // The following members must be used on the I/O thread.
+    scoped_ptr<ReadBuffer> preserved_read_buffer_after_detach_;
+    scoped_ptr<WriteBuffer> preserved_write_buffer_after_detach_;
+    bool suppress_self_destruct_;
+
+    bool pending_read_;
+    base::MessageLoopForIO::IOContext read_context_;
+
+    // The following members must be used under |owner_->write_lock()| while the
+    // object is still attached to the owner, and only on the I/O thread
+    // afterwards.
+    bool pending_write_;
+    base::MessageLoopForIO::IOContext write_context_;
+
+    DISALLOW_COPY_AND_ASSIGN(RawChannelIOHandler);
+  };
+
+  // |RawChannel| private methods:
+  virtual IOResult Read(size_t* bytes_read) override;
+  virtual IOResult ScheduleRead() override;
+  virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+      size_t num_platform_handles,
+      const void* platform_handle_table) override;
+  virtual IOResult WriteNoLock(size_t* platform_handles_written,
+                               size_t* bytes_written) override;
+  virtual IOResult ScheduleWriteNoLock() override;
+  virtual bool OnInit() override;
+  virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
+                                scoped_ptr<WriteBuffer> write_buffer) override;
+
+  // Passed to |io_handler_| during initialization.
+  embedder::ScopedPlatformHandle handle_;
+
+  RawChannelIOHandler* io_handler_;
+
+  const bool skip_completion_port_on_success_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawChannelWin);
+};
+
+RawChannelWin::RawChannelIOHandler::RawChannelIOHandler(
+    RawChannelWin* owner,
+    embedder::ScopedPlatformHandle handle)
+    : handle_(handle.Pass()),
+      owner_(owner),
+      suppress_self_destruct_(false),
+      pending_read_(false),
+      pending_write_(false) {
+  memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped));
+  read_context_.handler = this;
+  memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped));
+  write_context_.handler = this;
+
+  owner_->message_loop_for_io()->RegisterIOHandler(handle_.get().handle, this);
+}
+
+RawChannelWin::RawChannelIOHandler::~RawChannelIOHandler() {
+  DCHECK(ShouldSelfDestruct());
+}
+
+bool RawChannelWin::RawChannelIOHandler::pending_read() const {
+  DCHECK(owner_);
+  DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+  return pending_read_;
+}
+
+base::MessageLoopForIO::IOContext*
+RawChannelWin::RawChannelIOHandler::read_context() {
+  DCHECK(owner_);
+  DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+  return &read_context_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnPendingReadStarted() {
+  DCHECK(owner_);
+  DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+  DCHECK(!pending_read_);
+  pending_read_ = true;
+}
+
+bool RawChannelWin::RawChannelIOHandler::pending_write_no_lock() const {
+  DCHECK(owner_);
+  owner_->write_lock().AssertAcquired();
+  return pending_write_;
+}
+
+base::MessageLoopForIO::IOContext*
+RawChannelWin::RawChannelIOHandler::write_context_no_lock() {
+  DCHECK(owner_);
+  owner_->write_lock().AssertAcquired();
+  return &write_context_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnPendingWriteStartedNoLock() {
+  DCHECK(owner_);
+  owner_->write_lock().AssertAcquired();
+  DCHECK(!pending_write_);
+  pending_write_ = true;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnIOCompleted(
+    base::MessageLoopForIO::IOContext* context,
+    DWORD bytes_transferred,
+    DWORD error) {
+  DCHECK(!owner_ ||
+         base::MessageLoop::current() == owner_->message_loop_for_io());
+
+  {
+    // Suppress self-destruction inside |OnReadCompleted()|, etc. (in case they
+    // result in a call to |Shutdown()|).
+    base::AutoReset<bool> resetter(&suppress_self_destruct_, true);
+
+    if (context == &read_context_)
+      OnReadCompleted(bytes_transferred, error);
+    else if (context == &write_context_)
+      OnWriteCompleted(bytes_transferred, error);
+    else
+      NOTREACHED();
+  }
+
+  if (ShouldSelfDestruct())
+    delete this;
+}
+
+void RawChannelWin::RawChannelIOHandler::DetachFromOwnerNoLock(
+    scoped_ptr<ReadBuffer> read_buffer,
+    scoped_ptr<WriteBuffer> write_buffer) {
+  DCHECK(owner_);
+  DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+  owner_->write_lock().AssertAcquired();
+
+  // If read/write is pending, we have to retain the corresponding buffer.
+  if (pending_read_)
+    preserved_read_buffer_after_detach_ = read_buffer.Pass();
+  if (pending_write_)
+    preserved_write_buffer_after_detach_ = write_buffer.Pass();
+
+  owner_ = nullptr;
+  if (ShouldSelfDestruct())
+    delete this;
+}
+
+bool RawChannelWin::RawChannelIOHandler::ShouldSelfDestruct() const {
+  if (owner_ || suppress_self_destruct_)
+    return false;
+
+  // Note: Detached, hence no lock needed for |pending_write_|.
+  return !pending_read_ && !pending_write_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read,
+                                                         DWORD error) {
+  DCHECK(!owner_ ||
+         base::MessageLoop::current() == owner_->message_loop_for_io());
+  DCHECK(suppress_self_destruct_);
+
+  CHECK(pending_read_);
+  pending_read_ = false;
+  if (!owner_)
+    return;
+
+  if (error == ERROR_SUCCESS) {
+    DCHECK_GT(bytes_read, 0u);
+    owner_->OnReadCompleted(IO_SUCCEEDED, bytes_read);
+  } else if (error == ERROR_BROKEN_PIPE) {
+    DCHECK_EQ(bytes_read, 0u);
+    owner_->OnReadCompleted(IO_FAILED_SHUTDOWN, 0);
+  } else {
+    DCHECK_EQ(bytes_read, 0u);
+    LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error);
+    owner_->OnReadCompleted(IO_FAILED_UNKNOWN, 0);
+  }
+}
+
+void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written,
+                                                          DWORD error) {
+  DCHECK(!owner_ ||
+         base::MessageLoop::current() == owner_->message_loop_for_io());
+  DCHECK(suppress_self_destruct_);
+
+  if (!owner_) {
+    // No lock needed.
+    CHECK(pending_write_);
+    pending_write_ = false;
+    return;
+  }
+
+  {
+    base::AutoLock locker(owner_->write_lock());
+    CHECK(pending_write_);
+    pending_write_ = false;
+  }
+
+  if (error == ERROR_SUCCESS) {
+    owner_->OnWriteCompleted(IO_SUCCEEDED, 0, bytes_written);
+  } else if (error == ERROR_BROKEN_PIPE) {
+    owner_->OnWriteCompleted(IO_FAILED_SHUTDOWN, 0, 0);
+  } else {
+    LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error);
+    owner_->OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0);
+  }
+}
+
+RawChannelWin::RawChannelWin(embedder::ScopedPlatformHandle handle)
+    : handle_(handle.Pass()),
+      io_handler_(nullptr),
+      skip_completion_port_on_success_(
+          g_vista_or_higher_functions.Get().is_vista_or_higher()) {
+  DCHECK(handle_.is_valid());
+}
+
+RawChannelWin::~RawChannelWin() {
+  DCHECK(!io_handler_);
+}
+
+size_t RawChannelWin::GetSerializedPlatformHandleSize() const {
+  // TODO(vtl): Implement.
+  return 0;
+}
+
+RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  DCHECK(io_handler_);
+  DCHECK(!io_handler_->pending_read());
+
+  char* buffer = nullptr;
+  size_t bytes_to_read = 0;
+  read_buffer()->GetBuffer(&buffer, &bytes_to_read);
+
+  BOOL result = ReadFile(io_handler_->handle(),
+                         buffer,
+                         static_cast<DWORD>(bytes_to_read),
+                         nullptr,
+                         &io_handler_->read_context()->overlapped);
+  if (!result) {
+    DWORD error = GetLastError();
+    if (error == ERROR_BROKEN_PIPE)
+      return IO_FAILED_SHUTDOWN;
+    if (error != ERROR_IO_PENDING) {
+      LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error);
+      return IO_FAILED_UNKNOWN;
+    }
+  }
+
+  if (result && skip_completion_port_on_success_) {
+    DWORD bytes_read_dword = 0;
+    BOOL get_size_result =
+        GetOverlappedResult(io_handler_->handle(),
+                            &io_handler_->read_context()->overlapped,
+                            &bytes_read_dword,
+                            FALSE);
+    DPCHECK(get_size_result);
+    *bytes_read = bytes_read_dword;
+    return IO_SUCCEEDED;
+  }
+
+  // If the read is pending or the read has succeeded but we don't skip
+  // completion port on success, instruct |io_handler_| to wait for the
+  // completion packet.
+  //
+  // TODO(yzshen): It seems there isn't document saying that all error cases
+  // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
+  // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
+  // will crash so we will learn about it.
+
+  io_handler_->OnPendingReadStarted();
+  return IO_PENDING;
+}
+
+RawChannel::IOResult RawChannelWin::ScheduleRead() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  DCHECK(io_handler_);
+  DCHECK(!io_handler_->pending_read());
+
+  size_t bytes_read = 0;
+  IOResult io_result = Read(&bytes_read);
+  if (io_result == IO_SUCCEEDED) {
+    DCHECK(skip_completion_port_on_success_);
+
+    // We have finished reading successfully. Queue a notification manually.
+    io_handler_->OnPendingReadStarted();
+    // |io_handler_| won't go away before the task is run, so it is safe to use
+    // |base::Unretained()|.
+    message_loop_for_io()->PostTask(
+        FROM_HERE,
+        base::Bind(&RawChannelIOHandler::OnIOCompleted,
+                   base::Unretained(io_handler_),
+                   base::Unretained(io_handler_->read_context()),
+                   static_cast<DWORD>(bytes_read),
+                   ERROR_SUCCESS));
+    return IO_PENDING;
+  }
+
+  return io_result;
+}
+
+embedder::ScopedPlatformHandleVectorPtr RawChannelWin::GetReadPlatformHandles(
+    size_t num_platform_handles,
+    const void* platform_handle_table) {
+  // TODO(vtl): Implement.
+  NOTIMPLEMENTED();
+  return embedder::ScopedPlatformHandleVectorPtr();
+}
+
+RawChannel::IOResult RawChannelWin::WriteNoLock(
+    size_t* platform_handles_written,
+    size_t* bytes_written) {
+  write_lock().AssertAcquired();
+
+  DCHECK(io_handler_);
+  DCHECK(!io_handler_->pending_write_no_lock());
+
+  if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
+    // TODO(vtl): Implement.
+    NOTIMPLEMENTED();
+  }
+
+  std::vector<WriteBuffer::Buffer> buffers;
+  write_buffer_no_lock()->GetBuffers(&buffers);
+  DCHECK(!buffers.empty());
+
+  // TODO(yzshen): Handle multi-segment writes more efficiently.
+  DWORD bytes_written_dword = 0;
+  BOOL result = WriteFile(io_handler_->handle(),
+                          buffers[0].addr,
+                          static_cast<DWORD>(buffers[0].size),
+                          &bytes_written_dword,
+                          &io_handler_->write_context_no_lock()->overlapped);
+  if (!result) {
+    DWORD error = GetLastError();
+    if (error == ERROR_BROKEN_PIPE)
+      return IO_FAILED_SHUTDOWN;
+    if (error != ERROR_IO_PENDING) {
+      LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error);
+      return IO_FAILED_UNKNOWN;
+    }
+  }
+
+  if (result && skip_completion_port_on_success_) {
+    *platform_handles_written = 0;
+    *bytes_written = bytes_written_dword;
+    return IO_SUCCEEDED;
+  }
+
+  // If the write is pending or the write has succeeded but we don't skip
+  // completion port on success, instruct |io_handler_| to wait for the
+  // completion packet.
+  //
+  // TODO(yzshen): it seems there isn't document saying that all error cases
+  // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
+  // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
+  // will crash so we will learn about it.
+
+  io_handler_->OnPendingWriteStartedNoLock();
+  return IO_PENDING;
+}
+
+RawChannel::IOResult RawChannelWin::ScheduleWriteNoLock() {
+  write_lock().AssertAcquired();
+
+  DCHECK(io_handler_);
+  DCHECK(!io_handler_->pending_write_no_lock());
+
+  // TODO(vtl): Do something with |platform_handles_written|.
+  size_t platform_handles_written = 0;
+  size_t bytes_written = 0;
+  IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written);
+  if (io_result == IO_SUCCEEDED) {
+    DCHECK(skip_completion_port_on_success_);
+
+    // We have finished writing successfully. Queue a notification manually.
+    io_handler_->OnPendingWriteStartedNoLock();
+    // |io_handler_| won't go away before that task is run, so it is safe to use
+    // |base::Unretained()|.
+    message_loop_for_io()->PostTask(
+        FROM_HERE,
+        base::Bind(&RawChannelIOHandler::OnIOCompleted,
+                   base::Unretained(io_handler_),
+                   base::Unretained(io_handler_->write_context_no_lock()),
+                   static_cast<DWORD>(bytes_written),
+                   ERROR_SUCCESS));
+    return IO_PENDING;
+  }
+
+  return io_result;
+}
+
+bool RawChannelWin::OnInit() {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+  DCHECK(handle_.is_valid());
+  if (skip_completion_port_on_success_ &&
+      !g_vista_or_higher_functions.Get().SetFileCompletionNotificationModes(
+          handle_.get().handle, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
+    return false;
+  }
+
+  DCHECK(!io_handler_);
+  io_handler_ = new RawChannelIOHandler(this, handle_.Pass());
+
+  return true;
+}
+
+void RawChannelWin::OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
+                                     scoped_ptr<WriteBuffer> write_buffer) {
+  DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+  DCHECK(io_handler_);
+
+  write_lock().AssertAcquired();
+
+  if (io_handler_->pending_read() || io_handler_->pending_write_no_lock()) {
+    // |io_handler_| will be alive until pending read/write (if any) completes.
+    // Call |CancelIoEx()| or |CancelIo()| so that resources can be freed up as
+    // soon as possible.
+    // Note: |CancelIo()| only cancels read/write requests started from this
+    // thread.
+    if (g_vista_or_higher_functions.Get().is_vista_or_higher()) {
+      g_vista_or_higher_functions.Get().CancelIoEx(io_handler_->handle(),
+                                                   nullptr);
+    } else {
+      CancelIo(io_handler_->handle());
+    }
+  }
+
+  io_handler_->DetachFromOwnerNoLock(read_buffer.Pass(), write_buffer.Pass());
+  io_handler_ = nullptr;
+}
+
+}  // namespace
+
+// -----------------------------------------------------------------------------
+
+// Static factory method declared in raw_channel.h.
+// static
+scoped_ptr<RawChannel> RawChannel::Create(
+    embedder::ScopedPlatformHandle handle) {
+  return make_scoped_ptr(new RawChannelWin(handle.Pass()));
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/remote_message_pipe_unittest.cc b/mojo/edk/system/remote_message_pipe_unittest.cc
new file mode 100644
index 0000000..942d496
--- /dev/null
+++ b/mojo/edk/system/remote_message_pipe_unittest.cc
@@ -0,0 +1,1191 @@
+// Copyright 2014 The Chromium 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/test_io_thread.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "build/build_config.h"              // TODO(vtl): Remove this.
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/message_pipe.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+#include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/shared_buffer_dispatcher.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class RemoteMessagePipeTest : public testing::Test {
+ public:
+  RemoteMessagePipeTest() : io_thread_(base::TestIOThread::kAutoStart) {}
+  virtual ~RemoteMessagePipeTest() {}
+
+  virtual void SetUp() override {
+    io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeTest::SetUpOnIOThread,
+                   base::Unretained(this)));
+  }
+
+  virtual void TearDown() override {
+    io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeTest::TearDownOnIOThread,
+                   base::Unretained(this)));
+  }
+
+ protected:
+  // This connects the two given |ChannelEndpoint|s.
+  void ConnectChannelEndpoints(scoped_refptr<ChannelEndpoint> ep0,
+                               scoped_refptr<ChannelEndpoint> ep1) {
+    io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeTest::ConnectChannelEndpointsOnIOThread,
+                   base::Unretained(this),
+                   ep0,
+                   ep1));
+  }
+
+  // This bootstraps |ep| on |channels_[channel_index]|. It assumes/requires
+  // that this is the bootstrap case, i.e., that the endpoint IDs are both/will
+  // both be |Channel::kBootstrapEndpointId|. This returns *without* waiting for
+  // it to finish connecting.
+  void BootstrapChannelEndpointNoWait(unsigned channel_index,
+                                      scoped_refptr<ChannelEndpoint> ep) {
+    io_thread_.PostTask(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeTest::BootstrapChannelEndpointOnIOThread,
+                   base::Unretained(this),
+                   channel_index,
+                   ep));
+  }
+
+  void RestoreInitialState() {
+    io_thread_.PostTaskAndWait(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeTest::RestoreInitialStateOnIOThread,
+                   base::Unretained(this)));
+  }
+
+  embedder::PlatformSupport* platform_support() { return &platform_support_; }
+  base::TestIOThread* io_thread() { return &io_thread_; }
+
+ private:
+  void SetUpOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    embedder::PlatformChannelPair channel_pair;
+    platform_handles_[0] = channel_pair.PassServerHandle();
+    platform_handles_[1] = channel_pair.PassClientHandle();
+  }
+
+  void TearDownOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    if (channels_[0].get()) {
+      channels_[0]->Shutdown();
+      channels_[0] = nullptr;
+    }
+    if (channels_[1].get()) {
+      channels_[1]->Shutdown();
+      channels_[1] = nullptr;
+    }
+  }
+
+  void CreateAndInitChannel(unsigned channel_index) {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+    CHECK(channel_index == 0 || channel_index == 1);
+    CHECK(!channels_[channel_index].get());
+
+    channels_[channel_index] = new Channel(&platform_support_);
+    CHECK(channels_[channel_index]->Init(
+        RawChannel::Create(platform_handles_[channel_index].Pass())));
+  }
+
+  void ConnectChannelEndpointsOnIOThread(scoped_refptr<ChannelEndpoint> ep0,
+                                         scoped_refptr<ChannelEndpoint> ep1) {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    if (!channels_[0].get())
+      CreateAndInitChannel(0);
+    if (!channels_[1].get())
+      CreateAndInitChannel(1);
+
+    MessageInTransit::EndpointId local_id0 = channels_[0]->AttachEndpoint(ep0);
+    MessageInTransit::EndpointId local_id1 = channels_[1]->AttachEndpoint(ep1);
+
+    channels_[0]->RunEndpoint(ep0, local_id1);
+    channels_[1]->RunEndpoint(ep1, local_id0);
+  }
+
+  void BootstrapChannelEndpointOnIOThread(unsigned channel_index,
+                                          scoped_refptr<ChannelEndpoint> ep) {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+    CHECK(channel_index == 0 || channel_index == 1);
+
+    CreateAndInitChannel(channel_index);
+    MessageInTransit::EndpointId endpoint_id =
+        channels_[channel_index]->AttachEndpoint(ep);
+    if (endpoint_id == MessageInTransit::kInvalidEndpointId)
+      return;
+
+    CHECK_EQ(endpoint_id, Channel::kBootstrapEndpointId);
+    channels_[channel_index]->RunEndpoint(ep, Channel::kBootstrapEndpointId);
+  }
+
+  void RestoreInitialStateOnIOThread() {
+    CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+    TearDownOnIOThread();
+    SetUpOnIOThread();
+  }
+
+  embedder::SimplePlatformSupport platform_support_;
+  base::TestIOThread io_thread_;
+  embedder::ScopedPlatformHandle platform_handles_[2];
+  scoped_refptr<Channel> channels_[2];
+
+  DISALLOW_COPY_AND_ASSIGN(RemoteMessagePipeTest);
+};
+
+TEST_F(RemoteMessagePipeTest, Basic) {
+  static const char kHello[] = "hello";
+  static const char kWorld[] = "world!!!1!!!1!";
+  char buffer[100] = {0};
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  // Connect message pipes. MP 0, port 1 will be attached to channel 0 and
+  // connected to MP 1, port 0, which will be attached to channel 1. This leaves
+  // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints.
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  // Write in one direction: MP 0, port 0 -> ... -> MP 1, port 1.
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Write to MP 0, port 0.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp0->WriteMessage(0,
+                              UserPointer<const void>(kHello),
+                              sizeof(kHello),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 1, port 1.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+  EXPECT_STREQ(kHello, buffer);
+
+  // Write in the other direction: MP 1, port 1 -> ... -> MP 0, port 0.
+
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp0->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->WriteMessage(1,
+                              UserPointer<const void>(kWorld),
+                              sizeof(kWorld),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(456u, context);
+  hss = HandleSignalsState();
+  mp0->RemoveWaiter(0, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp0->ReadMessage(0,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size));
+  EXPECT_STREQ(kWorld, buffer);
+
+  // Close MP 0, port 0.
+  mp0->Close(0);
+
+  // Try to wait for MP 1, port 1 to become readable. This will eventually fail
+  // when it realizes that MP 0, port 0 has been closed. (It may also fail
+  // immediately.)
+  waiter.Init();
+  hss = HandleSignalsState();
+  MojoResult result =
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, &hss);
+  if (result == MOJO_RESULT_OK) {
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+    EXPECT_EQ(789u, context);
+    hss = HandleSignalsState();
+    mp1->RemoveWaiter(1, &waiter, &hss);
+  }
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  // And MP 1, port 1.
+  mp1->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, Multiplex) {
+  static const char kHello[] = "hello";
+  static const char kWorld[] = "world!!!1!!!1!";
+  char buffer[100] = {0};
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  // Connect message pipes as in the |Basic| test.
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  // Now put another message pipe on the channel.
+
+  scoped_refptr<ChannelEndpoint> ep2;
+  scoped_refptr<MessagePipe> mp2(MessagePipe::CreateLocalProxy(&ep2));
+  scoped_refptr<ChannelEndpoint> ep3;
+  scoped_refptr<MessagePipe> mp3(MessagePipe::CreateProxyLocal(&ep3));
+  ConnectChannelEndpoints(ep2, ep3);
+
+  // Write: MP 2, port 0 -> MP 3, port 1.
+
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp3->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp2->WriteMessage(0,
+                              UserPointer<const void>(kHello),
+                              sizeof(kHello),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(789u, context);
+  hss = HandleSignalsState();
+  mp3->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Make sure there's nothing on MP 0, port 0 or MP 1, port 1 or MP 2, port 0.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp0->ReadMessage(0,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp2->ReadMessage(0,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Read from MP 3, port 1.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp3->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+  EXPECT_STREQ(kHello, buffer);
+
+  // Write: MP 0, port 0 -> MP 1, port 1 again.
+
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp0->WriteMessage(0,
+                              UserPointer<const void>(kWorld),
+                              sizeof(kWorld),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Make sure there's nothing on the other ports.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp0->ReadMessage(0,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp2->ReadMessage(0,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            mp3->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size));
+  EXPECT_STREQ(kWorld, buffer);
+
+  mp0->Close(0);
+  mp1->Close(1);
+  mp2->Close(0);
+  mp3->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, CloseBeforeConnect) {
+  static const char kHello[] = "hello";
+  char buffer[100] = {0};
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  // Connect message pipes. MP 0, port 1 will be attached to channel 0 and
+  // connected to MP 1, port 0, which will be attached to channel 1. This leaves
+  // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints.
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+
+  // Write to MP 0, port 0.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp0->WriteMessage(0,
+                              UserPointer<const void>(kHello),
+                              sizeof(kHello),
+                              nullptr,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  BootstrapChannelEndpointNoWait(0, ep0);
+
+  // Close MP 0, port 0 before channel 1 is even connected.
+  mp0->Close(0);
+
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  BootstrapChannelEndpointNoWait(1, ep1);
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  // Note: MP 1, port 1 should definitely should be readable, but it may or may
+  // not appear as writable (there's a race, and it may not have noticed that
+  // the other side was closed yet -- e.g., inserting a sleep here would make it
+  // much more likely to notice that it's no longer writable).
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
+
+  // Read from MP 1, port 1.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(buffer),
+                             MakeUserPointer(&buffer_size),
+                             nullptr,
+                             nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+  EXPECT_STREQ(kHello, buffer);
+
+  // And MP 1, port 1.
+  mp1->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, HandlePassing) {
+  static const char kHello[] = "hello";
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  // We'll try to pass this dispatcher.
+  scoped_refptr<MessagePipeDispatcher> dispatcher(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  scoped_refptr<MessagePipe> local_mp(MessagePipe::CreateLocalLocal());
+  dispatcher->Init(local_mp, 0);
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Write to MP 0, port 0.
+  {
+    DispatcherTransport transport(
+        test::DispatcherTryStartTransport(dispatcher.get()));
+    EXPECT_TRUE(transport.is_valid());
+
+    std::vector<DispatcherTransport> transports;
+    transports.push_back(transport);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp0->WriteMessage(0,
+                                UserPointer<const void>(kHello),
+                                sizeof(kHello),
+                                &transports,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+    transport.End();
+
+    // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+    // |dispatcher| is destroyed.
+    EXPECT_TRUE(dispatcher->HasOneRef());
+    dispatcher = nullptr;
+  }
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 1, port 1.
+  char read_buffer[100] = {0};
+  uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  DispatcherVector read_dispatchers;
+  uint32_t read_num_dispatchers = 10;  // Maximum to get.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(read_buffer),
+                             MakeUserPointer(&read_buffer_size),
+                             &read_dispatchers,
+                             &read_num_dispatchers,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+  EXPECT_EQ(1u, read_dispatchers.size());
+  EXPECT_EQ(1u, read_num_dispatchers);
+  ASSERT_TRUE(read_dispatchers[0].get());
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+  EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType());
+  dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get());
+
+  // Add the waiter now, before it becomes readable to avoid a race.
+  waiter.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            dispatcher->AddWaiter(
+                &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, nullptr));
+
+  // Write to "local_mp", port 1.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            local_mp->WriteMessage(1,
+                                   UserPointer<const void>(kHello),
+                                   sizeof(kHello),
+                                   nullptr,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // TODO(vtl): FIXME -- We (racily) crash if I close |dispatcher| immediately
+  // here. (We don't crash if I sleep and then close.)
+
+  // Wait for the dispatcher to become readable.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(456u, context);
+  hss = HandleSignalsState();
+  dispatcher->RemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from the dispatcher.
+  memset(read_buffer, 0, sizeof(read_buffer));
+  read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dispatcher->ReadMessage(UserPointer<void>(read_buffer),
+                                    MakeUserPointer(&read_buffer_size),
+                                    0,
+                                    nullptr,
+                                    MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+
+  // Prepare to wait on "local_mp", port 1.
+  waiter.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            local_mp->AddWaiter(
+                1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, nullptr));
+
+  // Write to the dispatcher.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dispatcher->WriteMessage(UserPointer<const void>(kHello),
+                                     sizeof(kHello),
+                                     nullptr,
+                                     MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(789u, context);
+  hss = HandleSignalsState();
+  local_mp->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from "local_mp", port 1.
+  memset(read_buffer, 0, sizeof(read_buffer));
+  read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            local_mp->ReadMessage(1,
+                                  UserPointer<void>(read_buffer),
+                                  MakeUserPointer(&read_buffer_size),
+                                  nullptr,
+                                  nullptr,
+                                  MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+
+  // TODO(vtl): Also test that messages queued up before the handle was sent are
+  // delivered properly.
+
+  // Close everything that belongs to us.
+  mp0->Close(0);
+  mp1->Close(1);
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+  // Note that |local_mp|'s port 0 belong to |dispatcher|, which was closed.
+  local_mp->Close(1);
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_SharedBufferPassing SharedBufferPassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
+#endif
+TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) {
+  static const char kHello[] = "hello";
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  // We'll try to pass this dispatcher.
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher));
+  ASSERT_TRUE(dispatcher.get());
+
+  // Make a mapping.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping0;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping0));
+  ASSERT_TRUE(mapping0);
+  ASSERT_TRUE(mapping0->GetBase());
+  ASSERT_EQ(100u, mapping0->GetLength());
+  static_cast<char*>(mapping0->GetBase())[0] = 'A';
+  static_cast<char*>(mapping0->GetBase())[50] = 'B';
+  static_cast<char*>(mapping0->GetBase())[99] = 'C';
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Write to MP 0, port 0.
+  {
+    DispatcherTransport transport(
+        test::DispatcherTryStartTransport(dispatcher.get()));
+    EXPECT_TRUE(transport.is_valid());
+
+    std::vector<DispatcherTransport> transports;
+    transports.push_back(transport);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp0->WriteMessage(0,
+                                UserPointer<const void>(kHello),
+                                sizeof(kHello),
+                                &transports,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+    transport.End();
+
+    // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+    // |dispatcher| is destroyed.
+    EXPECT_TRUE(dispatcher->HasOneRef());
+    dispatcher = nullptr;
+  }
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 1, port 1.
+  char read_buffer[100] = {0};
+  uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  DispatcherVector read_dispatchers;
+  uint32_t read_num_dispatchers = 10;  // Maximum to get.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(read_buffer),
+                             MakeUserPointer(&read_buffer_size),
+                             &read_dispatchers,
+                             &read_num_dispatchers,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+  EXPECT_EQ(1u, read_dispatchers.size());
+  EXPECT_EQ(1u, read_num_dispatchers);
+  ASSERT_TRUE(read_dispatchers[0].get());
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+  EXPECT_EQ(Dispatcher::kTypeSharedBuffer, read_dispatchers[0]->GetType());
+  dispatcher = static_cast<SharedBufferDispatcher*>(read_dispatchers[0].get());
+
+  // Make another mapping.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping1;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1));
+  ASSERT_TRUE(mapping1);
+  ASSERT_TRUE(mapping1->GetBase());
+  ASSERT_EQ(100u, mapping1->GetLength());
+  EXPECT_NE(mapping1->GetBase(), mapping0->GetBase());
+  EXPECT_EQ('A', static_cast<char*>(mapping1->GetBase())[0]);
+  EXPECT_EQ('B', static_cast<char*>(mapping1->GetBase())[50]);
+  EXPECT_EQ('C', static_cast<char*>(mapping1->GetBase())[99]);
+
+  // Write stuff either way.
+  static_cast<char*>(mapping1->GetBase())[1] = 'x';
+  EXPECT_EQ('x', static_cast<char*>(mapping0->GetBase())[1]);
+  static_cast<char*>(mapping0->GetBase())[2] = 'y';
+  EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[2]);
+
+  // Kill the first mapping; the second should still be valid.
+  mapping0.reset();
+  EXPECT_EQ('A', static_cast<char*>(mapping1->GetBase())[0]);
+
+  // Close everything that belongs to us.
+  mp0->Close(0);
+  mp1->Close(1);
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+
+  // The second mapping should still be good.
+  EXPECT_EQ('x', static_cast<char*>(mapping1->GetBase())[1]);
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_PlatformHandlePassing PlatformHandlePassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing
+#endif
+TEST_F(RemoteMessagePipeTest, MAYBE_PlatformHandlePassing) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static const char kHello[] = "hello";
+  static const char kWorld[] = "world";
+  Waiter waiter;
+  uint32_t context = 0;
+  HandleSignalsState hss;
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  base::FilePath unused;
+  base::ScopedFILE fp(
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+  EXPECT_EQ(sizeof(kHello), fwrite(kHello, 1, sizeof(kHello), fp.get()));
+  // We'll try to pass this dispatcher, which will cause a |PlatformHandle| to
+  // be passed.
+  scoped_refptr<PlatformHandleDispatcher> dispatcher(
+      new PlatformHandleDispatcher(
+          mojo::test::PlatformHandleFromFILE(fp.Pass())));
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Write to MP 0, port 0.
+  {
+    DispatcherTransport transport(
+        test::DispatcherTryStartTransport(dispatcher.get()));
+    EXPECT_TRUE(transport.is_valid());
+
+    std::vector<DispatcherTransport> transports;
+    transports.push_back(transport);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp0->WriteMessage(0,
+                                UserPointer<const void>(kWorld),
+                                sizeof(kWorld),
+                                &transports,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+    transport.End();
+
+    // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+    // |dispatcher| is destroyed.
+    EXPECT_TRUE(dispatcher->HasOneRef());
+    dispatcher = nullptr;
+  }
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 1, port 1.
+  char read_buffer[100] = {0};
+  uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  DispatcherVector read_dispatchers;
+  uint32_t read_num_dispatchers = 10;  // Maximum to get.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(read_buffer),
+                             MakeUserPointer(&read_buffer_size),
+                             &read_dispatchers,
+                             &read_num_dispatchers,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kWorld, read_buffer);
+  EXPECT_EQ(1u, read_dispatchers.size());
+  EXPECT_EQ(1u, read_num_dispatchers);
+  ASSERT_TRUE(read_dispatchers[0].get());
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+  EXPECT_EQ(Dispatcher::kTypePlatformHandle, read_dispatchers[0]->GetType());
+  dispatcher =
+      static_cast<PlatformHandleDispatcher*>(read_dispatchers[0].get());
+
+  embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+  EXPECT_TRUE(h.is_valid());
+
+  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+  EXPECT_FALSE(h.is_valid());
+  EXPECT_TRUE(fp);
+
+  rewind(fp.get());
+  memset(read_buffer, 0, sizeof(read_buffer));
+  EXPECT_EQ(sizeof(kHello),
+            fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+  EXPECT_STREQ(kHello, read_buffer);
+
+  // Close everything that belongs to us.
+  mp0->Close(0);
+  mp1->Close(1);
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+// Test racing closes (on each end).
+// Note: A flaky failure would almost certainly indicate a problem in the code
+// itself (not in the test). Also, any logged warnings/errors would also
+// probably be indicative of bugs.
+TEST_F(RemoteMessagePipeTest, RacingClosesStress) {
+  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(5);
+
+  for (unsigned i = 0; i < 256; i++) {
+    DVLOG(2) << "---------------------------------------- " << i;
+    scoped_refptr<ChannelEndpoint> ep0;
+    scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+    BootstrapChannelEndpointNoWait(0, ep0);
+
+    scoped_refptr<ChannelEndpoint> ep1;
+    scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+    BootstrapChannelEndpointNoWait(1, ep1);
+
+    if (i & 1u) {
+      io_thread()->task_runner()->PostTask(
+          FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay));
+    }
+    if (i & 2u)
+      base::PlatformThread::Sleep(delay);
+
+    mp0->Close(0);
+
+    if (i & 4u) {
+      io_thread()->task_runner()->PostTask(
+          FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay));
+    }
+    if (i & 8u)
+      base::PlatformThread::Sleep(delay);
+
+    mp1->Close(1);
+
+    RestoreInitialState();
+  }
+}
+
+// Tests passing an end of a message pipe over a remote message pipe, and then
+// passing that end back.
+// TODO(vtl): Also test passing a message pipe across two remote message pipes.
+TEST_F(RemoteMessagePipeTest, PassMessagePipeHandleAcrossAndBack) {
+  static const char kHello[] = "hello";
+  static const char kWorld[] = "world";
+  Waiter waiter;
+  HandleSignalsState hss;
+  uint32_t context = 0;
+
+  scoped_refptr<ChannelEndpoint> ep0;
+  scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0));
+  scoped_refptr<ChannelEndpoint> ep1;
+  scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1));
+  ConnectChannelEndpoints(ep0, ep1);
+
+  // We'll try to pass this dispatcher.
+  scoped_refptr<MessagePipeDispatcher> dispatcher(
+      new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions));
+  scoped_refptr<MessagePipe> local_mp(MessagePipe::CreateLocalLocal());
+  dispatcher->Init(local_mp, 0);
+
+  // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr));
+
+  // Write to MP 0, port 0.
+  {
+    DispatcherTransport transport(
+        test::DispatcherTryStartTransport(dispatcher.get()));
+    EXPECT_TRUE(transport.is_valid());
+
+    std::vector<DispatcherTransport> transports;
+    transports.push_back(transport);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp0->WriteMessage(0,
+                                UserPointer<const void>(kHello),
+                                sizeof(kHello),
+                                &transports,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+    transport.End();
+
+    // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+    // |dispatcher| is destroyed.
+    EXPECT_TRUE(dispatcher->HasOneRef());
+    dispatcher = nullptr;
+  }
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(123u, context);
+  hss = HandleSignalsState();
+  mp1->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 1, port 1.
+  char read_buffer[100] = {0};
+  uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  DispatcherVector read_dispatchers;
+  uint32_t read_num_dispatchers = 10;  // Maximum to get.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp1->ReadMessage(1,
+                             UserPointer<void>(read_buffer),
+                             MakeUserPointer(&read_buffer_size),
+                             &read_dispatchers,
+                             &read_num_dispatchers,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+  EXPECT_EQ(1u, read_dispatchers.size());
+  EXPECT_EQ(1u, read_num_dispatchers);
+  ASSERT_TRUE(read_dispatchers[0].get());
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+  EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType());
+  dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get());
+  read_dispatchers.clear();
+
+  // Now pass it back.
+
+  // Prepare to wait on MP 0, port 0. (Add the waiter now. Otherwise, if we do
+  // it later, it might already be readable.)
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      mp0->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, nullptr));
+
+  // Write to MP 1, port 1.
+  {
+    DispatcherTransport transport(
+        test::DispatcherTryStartTransport(dispatcher.get()));
+    EXPECT_TRUE(transport.is_valid());
+
+    std::vector<DispatcherTransport> transports;
+    transports.push_back(transport);
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mp1->WriteMessage(1,
+                                UserPointer<const void>(kWorld),
+                                sizeof(kWorld),
+                                &transports,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+    transport.End();
+
+    // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+    // |dispatcher| is destroyed.
+    EXPECT_TRUE(dispatcher->HasOneRef());
+    dispatcher = nullptr;
+  }
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(456u, context);
+  hss = HandleSignalsState();
+  mp0->RemoveWaiter(0, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from MP 0, port 0.
+  read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  read_num_dispatchers = 10;  // Maximum to get.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mp0->ReadMessage(0,
+                             UserPointer<void>(read_buffer),
+                             MakeUserPointer(&read_buffer_size),
+                             &read_dispatchers,
+                             &read_num_dispatchers,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kWorld, read_buffer);
+  EXPECT_EQ(1u, read_dispatchers.size());
+  EXPECT_EQ(1u, read_num_dispatchers);
+  ASSERT_TRUE(read_dispatchers[0].get());
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+  EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType());
+  dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get());
+  read_dispatchers.clear();
+
+  // Add the waiter now, before it becomes readable to avoid a race.
+  waiter.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            dispatcher->AddWaiter(
+                &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, nullptr));
+
+  // Write to "local_mp", port 1.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            local_mp->WriteMessage(1,
+                                   UserPointer<const void>(kHello),
+                                   sizeof(kHello),
+                                   nullptr,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Wait for the dispatcher to become readable.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(789u, context);
+  hss = HandleSignalsState();
+  dispatcher->RemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from the dispatcher.
+  memset(read_buffer, 0, sizeof(read_buffer));
+  read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dispatcher->ReadMessage(UserPointer<void>(read_buffer),
+                                    MakeUserPointer(&read_buffer_size),
+                                    0,
+                                    nullptr,
+                                    MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+
+  // Prepare to wait on "local_mp", port 1.
+  waiter.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            local_mp->AddWaiter(
+                1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, nullptr));
+
+  // Write to the dispatcher.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            dispatcher->WriteMessage(UserPointer<const void>(kHello),
+                                     sizeof(kHello),
+                                     nullptr,
+                                     MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Wait.
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_EQ(789u, context);
+  hss = HandleSignalsState();
+  local_mp->RemoveWaiter(1, &waiter, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Read from "local_mp", port 1.
+  memset(read_buffer, 0, sizeof(read_buffer));
+  read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            local_mp->ReadMessage(1,
+                                  UserPointer<void>(read_buffer),
+                                  MakeUserPointer(&read_buffer_size),
+                                  nullptr,
+                                  nullptr,
+                                  MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+  EXPECT_STREQ(kHello, read_buffer);
+
+  // TODO(vtl): Also test the cases where messages are written and read (at
+  // various points) on the message pipe being passed around.
+
+  // Close everything that belongs to us.
+  mp0->Close(0);
+  mp1->Close(1);
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+  // Note that |local_mp|'s port 0 belong to |dispatcher|, which was closed.
+  local_mp->Close(1);
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/run_all_unittests.cc b/mojo/edk/system/run_all_unittests.cc
new file mode 100644
index 0000000..2855e96
--- /dev/null
+++ b/mojo/edk/system/run_all_unittests.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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  // Silence death test thread warnings on Linux. We can afford to run our death
+  // tests a little more slowly (< 10 ms per death test on a Z620).
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+
+  base::TestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(
+      argc,
+      argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
new file mode 100644
index 0000000..7e345d9
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -0,0 +1,276 @@
+// Copyright 2014 The Chromium 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/edk/system/shared_buffer_dispatcher.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/platform_support.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/options_validation.h"
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+struct SerializedSharedBufferDispatcher {
+  size_t num_bytes;
+  size_t platform_handle_index;
+};
+
+}  // namespace
+
+// static
+const MojoCreateSharedBufferOptions
+    SharedBufferDispatcher::kDefaultCreateOptions = {
+        static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
+        MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
+
+// static
+MojoResult SharedBufferDispatcher::ValidateCreateOptions(
+    UserPointer<const MojoCreateSharedBufferOptions> in_options,
+    MojoCreateSharedBufferOptions* out_options) {
+  const MojoCreateSharedBufferOptionsFlags kKnownFlags =
+      MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+  *out_options = kDefaultCreateOptions;
+  if (in_options.IsNull())
+    return MOJO_RESULT_OK;
+
+  UserOptionsReader<MojoCreateSharedBufferOptions> reader(in_options);
+  if (!reader.is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateSharedBufferOptions, flags, reader))
+    return MOJO_RESULT_OK;
+  if ((reader.options().flags & ~kKnownFlags))
+    return MOJO_RESULT_UNIMPLEMENTED;
+  out_options->flags = reader.options().flags;
+
+  // Checks for fields beyond |flags|:
+
+  // (Nothing here yet.)
+
+  return MOJO_RESULT_OK;
+}
+
+// static
+MojoResult SharedBufferDispatcher::Create(
+    embedder::PlatformSupport* platform_support,
+    const MojoCreateSharedBufferOptions& /*validated_options*/,
+    uint64_t num_bytes,
+    scoped_refptr<SharedBufferDispatcher>* result) {
+  if (!num_bytes)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (num_bytes > kMaxSharedMemoryNumBytes)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer(
+      platform_support->CreateSharedBuffer(static_cast<size_t>(num_bytes)));
+  if (!shared_buffer.get())
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  *result = new SharedBufferDispatcher(shared_buffer);
+  return MOJO_RESULT_OK;
+}
+
+Dispatcher::Type SharedBufferDispatcher::GetType() const {
+  return kTypeSharedBuffer;
+}
+
+// static
+scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
+    Channel* channel,
+    const void* source,
+    size_t size,
+    embedder::PlatformHandleVector* platform_handles) {
+  DCHECK(channel);
+
+  if (size != sizeof(SerializedSharedBufferDispatcher)) {
+    LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
+    return scoped_refptr<SharedBufferDispatcher>();
+  }
+
+  const SerializedSharedBufferDispatcher* serialization =
+      static_cast<const SerializedSharedBufferDispatcher*>(source);
+  size_t num_bytes = serialization->num_bytes;
+  size_t platform_handle_index = serialization->platform_handle_index;
+
+  if (!num_bytes) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (invalid num_bytes)";
+    return scoped_refptr<SharedBufferDispatcher>();
+  }
+
+  if (!platform_handles || platform_handle_index >= platform_handles->size()) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (missing handles)";
+    return scoped_refptr<SharedBufferDispatcher>();
+  }
+
+  // Starts off invalid, which is what we want.
+  embedder::PlatformHandle platform_handle;
+  // We take ownership of the handle, so we have to invalidate the one in
+  // |platform_handles|.
+  std::swap(platform_handle, (*platform_handles)[platform_handle_index]);
+
+  // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be
+  // closed even if creation fails.
+  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer(
+      channel->platform_support()->CreateSharedBufferFromHandle(
+          num_bytes, embedder::ScopedPlatformHandle(platform_handle)));
+  if (!shared_buffer.get()) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
+    return scoped_refptr<SharedBufferDispatcher>();
+  }
+
+  return scoped_refptr<SharedBufferDispatcher>(
+      new SharedBufferDispatcher(shared_buffer));
+}
+
+SharedBufferDispatcher::SharedBufferDispatcher(
+    scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer)
+    : shared_buffer_(shared_buffer) {
+  DCHECK(shared_buffer_.get());
+}
+
+SharedBufferDispatcher::~SharedBufferDispatcher() {
+}
+
+// static
+MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
+    UserPointer<const MojoDuplicateBufferHandleOptions> in_options,
+    MojoDuplicateBufferHandleOptions* out_options) {
+  const MojoDuplicateBufferHandleOptionsFlags kKnownFlags =
+      MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE;
+  static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
+      static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
+      MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+
+  *out_options = kDefaultOptions;
+  if (in_options.IsNull())
+    return MOJO_RESULT_OK;
+
+  UserOptionsReader<MojoDuplicateBufferHandleOptions> reader(in_options);
+  if (!reader.is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!OPTIONS_STRUCT_HAS_MEMBER(
+          MojoDuplicateBufferHandleOptions, flags, reader))
+    return MOJO_RESULT_OK;
+  if ((reader.options().flags & ~kKnownFlags))
+    return MOJO_RESULT_UNIMPLEMENTED;
+  out_options->flags = reader.options().flags;
+
+  // Checks for fields beyond |flags|:
+
+  // (Nothing here yet.)
+
+  return MOJO_RESULT_OK;
+}
+
+void SharedBufferDispatcher::CloseImplNoLock() {
+  lock().AssertAcquired();
+  DCHECK(shared_buffer_.get());
+  shared_buffer_ = nullptr;
+}
+
+scoped_refptr<Dispatcher>
+SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+  lock().AssertAcquired();
+  DCHECK(shared_buffer_.get());
+  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer;
+  shared_buffer.swap(shared_buffer_);
+  return scoped_refptr<Dispatcher>(new SharedBufferDispatcher(shared_buffer));
+}
+
+MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock(
+    UserPointer<const MojoDuplicateBufferHandleOptions> options,
+    scoped_refptr<Dispatcher>* new_dispatcher) {
+  lock().AssertAcquired();
+
+  MojoDuplicateBufferHandleOptions validated_options;
+  MojoResult result = ValidateDuplicateOptions(options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  *new_dispatcher = new SharedBufferDispatcher(shared_buffer_);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult SharedBufferDispatcher::MapBufferImplNoLock(
+    uint64_t offset,
+    uint64_t num_bytes,
+    MojoMapBufferFlags flags,
+    scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) {
+  lock().AssertAcquired();
+  DCHECK(shared_buffer_.get());
+
+  if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!shared_buffer_->IsValidMap(static_cast<size_t>(offset),
+                                  static_cast<size_t>(num_bytes)))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  DCHECK(mapping);
+  *mapping = shared_buffer_->MapNoCheck(static_cast<size_t>(offset),
+                                        static_cast<size_t>(num_bytes));
+  if (!*mapping)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  return MOJO_RESULT_OK;
+}
+
+void SharedBufferDispatcher::StartSerializeImplNoLock(
+    Channel* /*channel*/,
+    size_t* max_size,
+    size_t* max_platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  *max_size = sizeof(SerializedSharedBufferDispatcher);
+  *max_platform_handles = 1;
+}
+
+bool SharedBufferDispatcher::EndSerializeAndCloseImplNoLock(
+    Channel* /*channel*/,
+    void* destination,
+    size_t* actual_size,
+    embedder::PlatformHandleVector* platform_handles) {
+  DCHECK(HasOneRef());  // Only one ref => no need to take the lock.
+  DCHECK(shared_buffer_.get());
+
+  SerializedSharedBufferDispatcher* serialization =
+      static_cast<SerializedSharedBufferDispatcher*>(destination);
+  // If there's only one reference to |shared_buffer_|, then it's ours (and no
+  // one else can make any more references to it), so we can just take its
+  // handle.
+  embedder::ScopedPlatformHandle platform_handle(
+      shared_buffer_->HasOneRef() ? shared_buffer_->PassPlatformHandle()
+                                  : shared_buffer_->DuplicatePlatformHandle());
+  if (!platform_handle.is_valid()) {
+    shared_buffer_ = nullptr;
+    return false;
+  }
+
+  serialization->num_bytes = shared_buffer_->GetNumBytes();
+  serialization->platform_handle_index = platform_handles->size();
+  platform_handles->push_back(platform_handle.release());
+  *actual_size = sizeof(SerializedSharedBufferDispatcher);
+
+  shared_buffer_ = nullptr;
+
+  return true;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
new file mode 100644
index 0000000..3e5ade0
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/memory.h"
+#include "mojo/edk/system/simple_dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformSupport;
+}
+
+namespace system {
+
+// TODO(vtl): We derive from SimpleDispatcher, even though we don't currently
+// have anything that's waitable. I want to add a "transferrable" wait flag
+// (which would entail overriding |GetHandleSignalsStateImplNoLock()|, etc.).
+class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher {
+ public:
+  // The default options to use for |MojoCreateSharedBuffer()|. (Real uses
+  // should obtain this via |ValidateCreateOptions()| with a null |in_options|;
+  // this is exposed directly for testing convenience.)
+  static const MojoCreateSharedBufferOptions kDefaultCreateOptions;
+
+  // Validates and/or sets default options for |MojoCreateSharedBufferOptions|.
+  // If non-null, |in_options| must point to a struct of at least
+  // |in_options->struct_size| bytes. |out_options| must point to a (current)
+  // |MojoCreateSharedBufferOptions| and will be entirely overwritten on success
+  // (it may be partly overwritten on failure).
+  static MojoResult ValidateCreateOptions(
+      UserPointer<const MojoCreateSharedBufferOptions> in_options,
+      MojoCreateSharedBufferOptions* out_options);
+
+  // Static factory method: |validated_options| must be validated (obviously).
+  // On failure, |*result| will be left as-is.
+  static MojoResult Create(
+      embedder::PlatformSupport* platform_support,
+      const MojoCreateSharedBufferOptions& validated_options,
+      uint64_t num_bytes,
+      scoped_refptr<SharedBufferDispatcher>* result);
+
+  // |Dispatcher| public methods:
+  virtual Type GetType() const override;
+
+  // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+  // |Dispatcher::Deserialize()|.)
+  static scoped_refptr<SharedBufferDispatcher> Deserialize(
+      Channel* channel,
+      const void* source,
+      size_t size,
+      embedder::PlatformHandleVector* platform_handles);
+
+ private:
+  explicit SharedBufferDispatcher(
+      scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer_);
+  virtual ~SharedBufferDispatcher();
+
+  // Validates and/or sets default options for
+  // |MojoDuplicateBufferHandleOptions|. If non-null, |in_options| must point to
+  // a struct of at least |in_options->struct_size| bytes. |out_options| must
+  // point to a (current) |MojoDuplicateBufferHandleOptions| and will be
+  // entirely overwritten on success (it may be partly overwritten on failure).
+  static MojoResult ValidateDuplicateOptions(
+      UserPointer<const MojoDuplicateBufferHandleOptions> in_options,
+      MojoDuplicateBufferHandleOptions* out_options);
+
+  // |Dispatcher| protected methods:
+  virtual void CloseImplNoLock() override;
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  virtual MojoResult DuplicateBufferHandleImplNoLock(
+      UserPointer<const MojoDuplicateBufferHandleOptions> options,
+      scoped_refptr<Dispatcher>* new_dispatcher) override;
+  virtual MojoResult MapBufferImplNoLock(
+      uint64_t offset,
+      uint64_t num_bytes,
+      MojoMapBufferFlags flags,
+      scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) override;
+  virtual void StartSerializeImplNoLock(Channel* channel,
+                                        size_t* max_size,
+                                        size_t* max_platform_handles) override;
+  virtual bool EndSerializeAndCloseImplNoLock(
+      Channel* channel,
+      void* destination,
+      size_t* actual_size,
+      embedder::PlatformHandleVector* platform_handles) override;
+
+  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
diff --git a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
new file mode 100644
index 0000000..d46ffe3
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
@@ -0,0 +1,299 @@
+// Copyright 2014 The Chromium 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/edk/system/shared_buffer_dispatcher.h"
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// NOTE(vtl): There's currently not much to test for in
+// |SharedBufferDispatcher::ValidateCreateOptions()|, but the tests should be
+// expanded if/when options are added, so I've kept the general form of the
+// tests from data_pipe_unittest.cc.
+
+const uint32_t kSizeOfCreateOptions = sizeof(MojoCreateSharedBufferOptions);
+
+// Does a cursory sanity check of |validated_options|. Calls
+// |ValidateCreateOptions()| on already-validated options. The validated options
+// should be valid, and the revalidated copy should be the same.
+void RevalidateCreateOptions(
+    const MojoCreateSharedBufferOptions& validated_options) {
+  EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size);
+  // Nothing to check for flags.
+
+  MojoCreateSharedBufferOptions revalidated_options = {};
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::ValidateCreateOptions(
+                MakeUserPointer(&validated_options), &revalidated_options));
+  EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size);
+  EXPECT_EQ(validated_options.flags, revalidated_options.flags);
+}
+
+class SharedBufferDispatcherTest : public testing::Test {
+ public:
+  SharedBufferDispatcherTest() {}
+  virtual ~SharedBufferDispatcherTest() {}
+
+  embedder::PlatformSupport* platform_support() { return &platform_support_; }
+
+ private:
+  embedder::SimplePlatformSupport platform_support_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcherTest);
+};
+
+// Tests valid inputs to |ValidateCreateOptions()|.
+TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsValid) {
+  // Default options.
+  {
+    MojoCreateSharedBufferOptions validated_options = {};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              SharedBufferDispatcher::ValidateCreateOptions(
+                  NullUserPointer(), &validated_options));
+    RevalidateCreateOptions(validated_options);
+  }
+
+  // Different flags.
+  MojoCreateSharedBufferOptionsFlags flags_values[] = {
+      MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
+  for (size_t i = 0; i < arraysize(flags_values); i++) {
+    const MojoCreateSharedBufferOptionsFlags flags = flags_values[i];
+
+    // Different capacities (size 1).
+    for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) {
+      MojoCreateSharedBufferOptions options = {
+          kSizeOfCreateOptions,  // |struct_size|.
+          flags                  // |flags|.
+      };
+      MojoCreateSharedBufferOptions validated_options = {};
+      EXPECT_EQ(MOJO_RESULT_OK,
+                SharedBufferDispatcher::ValidateCreateOptions(
+                    MakeUserPointer(&options), &validated_options))
+          << capacity;
+      RevalidateCreateOptions(validated_options);
+      EXPECT_EQ(options.flags, validated_options.flags);
+    }
+  }
+}
+
+TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) {
+  // Invalid |struct_size|.
+  {
+    MojoCreateSharedBufferOptions options = {
+        1,                                           // |struct_size|.
+        MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE  // |flags|.
+    };
+    MojoCreateSharedBufferOptions unused;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              SharedBufferDispatcher::ValidateCreateOptions(
+                  MakeUserPointer(&options), &unused));
+  }
+
+  // Unknown |flags|.
+  {
+    MojoCreateSharedBufferOptions options = {
+        kSizeOfCreateOptions,  // |struct_size|.
+        ~0u                    // |flags|.
+    };
+    MojoCreateSharedBufferOptions unused;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+              SharedBufferDispatcher::ValidateCreateOptions(
+                  MakeUserPointer(&options), &unused));
+  }
+}
+
+TEST_F(SharedBufferDispatcherTest, CreateAndMapBuffer) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher));
+  ASSERT_TRUE(dispatcher.get());
+  EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher->GetType());
+
+  // Make a couple of mappings.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping1;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1));
+  ASSERT_TRUE(mapping1);
+  ASSERT_TRUE(mapping1->GetBase());
+  EXPECT_EQ(100u, mapping1->GetLength());
+  // Write something.
+  static_cast<char*>(mapping1->GetBase())[50] = 'x';
+
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping2;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher->MapBuffer(50, 50, MOJO_MAP_BUFFER_FLAG_NONE, &mapping2));
+  ASSERT_TRUE(mapping2);
+  ASSERT_TRUE(mapping2->GetBase());
+  EXPECT_EQ(50u, mapping2->GetLength());
+  EXPECT_EQ('x', static_cast<char*>(mapping2->GetBase())[0]);
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+
+  // Check that we can still read/write to mappings after the dispatcher has
+  // gone away.
+  static_cast<char*>(mapping2->GetBase())[1] = 'y';
+  EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[51]);
+}
+
+TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandle) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher1));
+
+  // Map and write something.
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher1->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  static_cast<char*>(mapping->GetBase())[0] = 'x';
+  mapping.reset();
+
+  // Duplicate |dispatcher1| and then close it.
+  scoped_refptr<Dispatcher> dispatcher2;
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher1->DuplicateBufferHandle(NullUserPointer(), &dispatcher2));
+  ASSERT_TRUE(dispatcher2.get());
+  EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType());
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+
+  // Map |dispatcher2| and read something.
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dispatcher2->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  EXPECT_EQ('x', static_cast<char*>(mapping->GetBase())[0]);
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close());
+}
+
+TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher1));
+
+  MojoDuplicateBufferHandleOptions options[] = {
+      {sizeof(MojoDuplicateBufferHandleOptions),
+       MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE},
+      {sizeof(MojoDuplicateBufferHandleOptionsFlags), ~0u}};
+  for (size_t i = 0; i < arraysize(options); i++) {
+    scoped_refptr<Dispatcher> dispatcher2;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              dispatcher1->DuplicateBufferHandle(MakeUserPointer(&options[i]),
+                                                 &dispatcher2));
+    ASSERT_TRUE(dispatcher2.get());
+    EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType());
+    EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close());
+  }
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+}
+
+TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher1));
+
+  // Invalid |struct_size|.
+  {
+    MojoDuplicateBufferHandleOptions options = {
+        1u, MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+    scoped_refptr<Dispatcher> dispatcher2;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              dispatcher1->DuplicateBufferHandle(MakeUserPointer(&options),
+                                                 &dispatcher2));
+    EXPECT_FALSE(dispatcher2.get());
+  }
+
+  // Unknown |flags|.
+  {
+    MojoDuplicateBufferHandleOptions options = {
+        sizeof(MojoDuplicateBufferHandleOptions), ~0u};
+    scoped_refptr<Dispatcher> dispatcher2;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+              dispatcher1->DuplicateBufferHandle(MakeUserPointer(&options),
+                                                 &dispatcher2));
+    EXPECT_FALSE(dispatcher2.get());
+  }
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+}
+
+TEST_F(SharedBufferDispatcherTest, CreateInvalidNumBytes) {
+  // Size too big.
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                std::numeric_limits<uint64_t>::max(),
+                &dispatcher));
+  EXPECT_FALSE(dispatcher.get());
+
+  // Zero size.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                0,
+                &dispatcher));
+  EXPECT_FALSE(dispatcher.get());
+}
+
+TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::Create(
+                platform_support(),
+                SharedBufferDispatcher::kDefaultCreateOptions,
+                100,
+                &dispatcher));
+
+  scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher->MapBuffer(0, 101, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  EXPECT_FALSE(mapping);
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher->MapBuffer(1, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  EXPECT_FALSE(mapping);
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            dispatcher->MapBuffer(0, 0, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+  EXPECT_FALSE(mapping);
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/simple_dispatcher.cc b/mojo/edk/system/simple_dispatcher.cc
new file mode 100644
index 0000000..3eb7a4f
--- /dev/null
+++ b/mojo/edk/system/simple_dispatcher.cc
@@ -0,0 +1,61 @@
+// 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/edk/system/simple_dispatcher.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+SimpleDispatcher::SimpleDispatcher() {
+}
+
+SimpleDispatcher::~SimpleDispatcher() {
+}
+
+void SimpleDispatcher::HandleSignalsStateChangedNoLock() {
+  lock().AssertAcquired();
+  waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsStateImplNoLock());
+}
+
+void SimpleDispatcher::CancelAllWaitersNoLock() {
+  lock().AssertAcquired();
+  waiter_list_.CancelAllWaiters();
+}
+
+MojoResult SimpleDispatcher::AddWaiterImplNoLock(
+    Waiter* waiter,
+    MojoHandleSignals signals,
+    uint32_t context,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+
+  HandleSignalsState state(GetHandleSignalsStateImplNoLock());
+  if (state.satisfies(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+  if (!state.can_satisfy(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  waiter_list_.AddWaiter(waiter, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void SimpleDispatcher::RemoveWaiterImplNoLock(
+    Waiter* waiter,
+    HandleSignalsState* signals_state) {
+  lock().AssertAcquired();
+  waiter_list_.RemoveWaiter(waiter);
+  if (signals_state)
+    *signals_state = GetHandleSignalsStateImplNoLock();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/simple_dispatcher.h b/mojo/edk/system/simple_dispatcher.h
new file mode 100644
index 0000000..400e302
--- /dev/null
+++ b/mojo/edk/system/simple_dispatcher.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_EDK_SYSTEM_SIMPLE_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_SIMPLE_DISPATCHER_H_
+
+#include <list>
+
+#include "base/macros.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+// A base class for simple dispatchers. "Simple" means that there's a one-to-one
+// correspondence between handles and dispatchers (see the explanatory comment
+// in core.cc). This class implements the standard waiter-signalling mechanism
+// in that case.
+class MOJO_SYSTEM_IMPL_EXPORT SimpleDispatcher : public Dispatcher {
+ protected:
+  SimpleDispatcher();
+  virtual ~SimpleDispatcher();
+
+  // To be called by subclasses when the state changes (so
+  // |GetHandleSignalsStateImplNoLock()| should be checked again). Must be
+  // called under lock.
+  void HandleSignalsStateChangedNoLock();
+
+  // |Dispatcher| protected methods:
+  virtual void CancelAllWaitersNoLock() override;
+  virtual MojoResult AddWaiterImplNoLock(
+      Waiter* waiter,
+      MojoHandleSignals signals,
+      uint32_t context,
+      HandleSignalsState* signals_state) override;
+  virtual void RemoveWaiterImplNoLock(
+      Waiter* waiter,
+      HandleSignalsState* signals_state) override;
+
+ private:
+  // Protected by |lock()|:
+  WaiterList waiter_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleDispatcher);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_SIMPLE_DISPATCHER_H_
diff --git a/mojo/edk/system/simple_dispatcher_unittest.cc b/mojo/edk/system/simple_dispatcher_unittest.cc
new file mode 100644
index 0000000..d2b7eaa
--- /dev/null
+++ b/mojo/edk/system/simple_dispatcher_unittest.cc
@@ -0,0 +1,669 @@
+// 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/simple_dispatcher.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class MockSimpleDispatcher : public SimpleDispatcher {
+ public:
+  MockSimpleDispatcher()
+      : state_(MOJO_HANDLE_SIGNAL_NONE,
+               MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE) {}
+
+  void SetSatisfiedSignals(MojoHandleSignals new_satisfied_signals) {
+    base::AutoLock locker(lock());
+
+    // Any new signals that are set should be satisfiable.
+    CHECK_EQ(new_satisfied_signals & ~state_.satisfied_signals,
+             new_satisfied_signals & ~state_.satisfied_signals &
+                 state_.satisfiable_signals);
+
+    if (new_satisfied_signals == state_.satisfied_signals)
+      return;
+
+    state_.satisfied_signals = new_satisfied_signals;
+    HandleSignalsStateChangedNoLock();
+  }
+
+  void SetSatisfiableSignals(MojoHandleSignals new_satisfiable_signals) {
+    base::AutoLock locker(lock());
+
+    // Satisfied implies satisfiable.
+    CHECK_EQ(new_satisfiable_signals & state_.satisfied_signals,
+             state_.satisfied_signals);
+
+    if (new_satisfiable_signals == state_.satisfiable_signals)
+      return;
+
+    state_.satisfiable_signals = new_satisfiable_signals;
+    HandleSignalsStateChangedNoLock();
+  }
+
+  virtual Type GetType() const override { return kTypeUnknown; }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockSimpleDispatcher>;
+  virtual ~MockSimpleDispatcher() {}
+
+  virtual scoped_refptr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock() override {
+    scoped_refptr<MockSimpleDispatcher> rv(new MockSimpleDispatcher());
+    rv->state_ = state_;
+    return scoped_refptr<Dispatcher>(rv.get());
+  }
+
+  // |Dispatcher| override:
+  virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const override {
+    lock().AssertAcquired();
+    return state_;
+  }
+
+  // Protected by |lock()|:
+  HandleSignalsState state_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSimpleDispatcher);
+};
+
+#if defined(OS_WIN)
+// http://crbug.com/396404
+#define MAYBE_Basic DISABLED_Basic
+#else
+#define MAYBE_Basic Basic
+#endif
+TEST(SimpleDispatcherTest, MAYBE_Basic) {
+  test::Stopwatch stopwatch;
+
+  scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+  Waiter w;
+  uint32_t context = 0;
+  HandleSignalsState hss;
+
+  // Try adding a readable waiter when already readable.
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+  // Shouldn't need to remove the waiter (it was not added).
+
+  // Wait (forever) for writable when already writable.
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, nullptr));
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(1u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for zero time for writable when already writable.
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(2u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for non-zero, finite time for writable when already writable.
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(3u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for zero time for writable when not writable (will time out).
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for non-zero, finite time for writable when not writable (will time
+  // out).
+  w.Init();
+  d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 5, nullptr));
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), nullptr));
+  base::TimeDelta elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicUnsatisfiable) {
+  test::Stopwatch stopwatch;
+
+  scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+  Waiter w;
+  uint32_t context = 0;
+  HandleSignalsState hss;
+
+  // Try adding a writable waiter when it can never be writable.
+  w.Init();
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  d->SetSatisfiedSignals(0);
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  // Shouldn't need to remove the waiter (it was not added).
+
+  // Wait (forever) for writable and then it becomes never writable.
+  w.Init();
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+                           MOJO_HANDLE_SIGNAL_WRITABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(2u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Wait for zero time for writable and then it becomes never writable.
+  w.Init();
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+                           MOJO_HANDLE_SIGNAL_WRITABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(3u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  // Wait for non-zero, finite time for writable and then it becomes never
+  // writable.
+  w.Init();
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+                           MOJO_HANDLE_SIGNAL_WRITABLE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+  d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(4u, context);
+  hss = HandleSignalsState();
+  d->RemoveWaiter(&w, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicClosed) {
+  test::Stopwatch stopwatch;
+
+  scoped_refptr<MockSimpleDispatcher> d;
+  Waiter w;
+  uint32_t context = 0;
+  HandleSignalsState hss;
+
+  // Try adding a writable waiter when the dispatcher has been closed.
+  d = new MockSimpleDispatcher();
+  w.Init();
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss));
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+  // Shouldn't need to remove the waiter (it was not added).
+
+  // Wait (forever) for writable and then the dispatcher is closed.
+  d = new MockSimpleDispatcher();
+  w.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(2u, context);
+  // Don't need to remove waiters from closed dispatchers.
+
+  // Wait for zero time for writable and then the dispatcher is closed.
+  d = new MockSimpleDispatcher();
+  w.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0, &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(3u, context);
+  // Don't need to remove waiters from closed dispatchers.
+
+  // Wait for non-zero, finite time for writable and then the dispatcher is
+  // closed.
+  d = new MockSimpleDispatcher();
+  w.Init();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_CANCELLED,
+            w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_EQ(4u, context);
+  // Don't need to remove waiters from closed dispatchers.
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/396393
+#define MAYBE_BasicThreaded DISABLED_BasicThreaded
+#else
+#define MAYBE_BasicThreaded BasicThreaded
+#endif
+TEST(SimpleDispatcherTest, MAYBE_BasicThreaded) {
+  test::Stopwatch stopwatch;
+  bool did_wait;
+  MojoResult result;
+  uint32_t context;
+  HandleSignalsState hss;
+
+  // Wait for readable (already readable).
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    {
+      d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+      test::WaiterThread thread(d,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                1,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+    }  // Joins the thread.
+    // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+  EXPECT_FALSE(did_wait);
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for readable and becomes readable after some time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    {
+      test::WaiterThread thread(d,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                2,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+      d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    }  // Joins the thread.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }
+  base::TimeDelta elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  EXPECT_TRUE(did_wait);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(2u, context);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+
+  // Wait for readable and becomes never-readable after some time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    {
+      test::WaiterThread thread(d,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_DEADLINE_INDEFINITE,
+                                3,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+      d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_NONE);
+    }  // Joins the thread.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  EXPECT_TRUE(did_wait);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(3u, context);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  // Wait for readable and dispatcher gets closed.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    test::WaiterThread thread(d,
+                              MOJO_HANDLE_SIGNAL_READABLE,
+                              MOJO_DEADLINE_INDEFINITE,
+                              4,
+                              &did_wait,
+                              &result,
+                              &context,
+                              &hss);
+    stopwatch.Start();
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }  // Joins the thread.
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  EXPECT_TRUE(did_wait);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(4u, context);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(0u, hss.satisfiable_signals);
+
+  // Wait for readable and times out.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    {
+      test::WaiterThread thread(d,
+                                MOJO_HANDLE_SIGNAL_READABLE,
+                                2 * test::EpsilonTimeout().InMicroseconds(),
+                                5,
+                                &did_wait,
+                                &result,
+                                &context,
+                                &hss);
+      stopwatch.Start();
+      thread.Start();
+      base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+      // Not what we're waiting for.
+      d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+    }  // Joins the thread (after its wait times out).
+    // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  EXPECT_TRUE(did_wait);
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfiable_signals);
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/387124
+#define MAYBE_MultipleWaiters DISABLED_MultipleWaiters
+#else
+#define MAYBE_MultipleWaiters MultipleWaiters
+#endif
+TEST(SimpleDispatcherTest, MAYBE_MultipleWaiters) {
+  static const uint32_t kNumWaiters = 20;
+
+  bool did_wait[kNumWaiters];
+  MojoResult result[kNumWaiters];
+  uint32_t context[kNumWaiters];
+  HandleSignalsState hss[kNumWaiters];
+
+  // All wait for readable and becomes readable after some time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    ScopedVector<test::WaiterThread> threads;
+    for (uint32_t i = 0; i < kNumWaiters; i++) {
+      threads.push_back(new test::WaiterThread(d,
+                                               MOJO_HANDLE_SIGNAL_READABLE,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               i,
+                                               &did_wait[i],
+                                               &result[i],
+                                               &context[i],
+                                               &hss[i]));
+      threads.back()->Start();
+    }
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }  // Joins the threads.
+  for (uint32_t i = 0; i < kNumWaiters; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+
+  // Some wait for readable, some for writable, and becomes readable after some
+  // time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    ScopedVector<test::WaiterThread> threads;
+    for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+      threads.push_back(new test::WaiterThread(d,
+                                               MOJO_HANDLE_SIGNAL_READABLE,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               i,
+                                               &did_wait[i],
+                                               &result[i],
+                                               &context[i],
+                                               &hss[i]));
+      threads.back()->Start();
+    }
+    for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+      threads.push_back(new test::WaiterThread(d,
+                                               MOJO_HANDLE_SIGNAL_WRITABLE,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               i,
+                                               &did_wait[i],
+                                               &result[i],
+                                               &context[i],
+                                               &hss[i]));
+      threads.back()->Start();
+    }
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    // This will wake up the ones waiting to write.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }  // Joins the threads.
+  for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+  for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_CANCELLED, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+
+  // Some wait for readable, some for writable, and becomes readable and
+  // never-writable after some time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    ScopedVector<test::WaiterThread> threads;
+    for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+      threads.push_back(new test::WaiterThread(d,
+                                               MOJO_HANDLE_SIGNAL_READABLE,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               i,
+                                               &did_wait[i],
+                                               &result[i],
+                                               &context[i],
+                                               &hss[i]));
+      threads.back()->Start();
+    }
+    for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+      threads.push_back(new test::WaiterThread(d,
+                                               MOJO_HANDLE_SIGNAL_WRITABLE,
+                                               MOJO_DEADLINE_INDEFINITE,
+                                               i,
+                                               &did_wait[i],
+                                               &result[i],
+                                               &context[i],
+                                               &hss[i]));
+      threads.back()->Start();
+    }
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+    d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+    d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }  // Joins the threads.
+  for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+  for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+
+  // Some wait for readable, some for writable, and becomes readable after some
+  // time.
+  {
+    scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+    ScopedVector<test::WaiterThread> threads;
+    for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+      threads.push_back(
+          new test::WaiterThread(d,
+                                 MOJO_HANDLE_SIGNAL_READABLE,
+                                 3 * test::EpsilonTimeout().InMicroseconds(),
+                                 i,
+                                 &did_wait[i],
+                                 &result[i],
+                                 &context[i],
+                                 &hss[i]));
+      threads.back()->Start();
+    }
+    for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+      threads.push_back(
+          new test::WaiterThread(d,
+                                 MOJO_HANDLE_SIGNAL_WRITABLE,
+                                 1 * test::EpsilonTimeout().InMicroseconds(),
+                                 i,
+                                 &did_wait[i],
+                                 &result[i],
+                                 &context[i],
+                                 &hss[i]));
+      threads.back()->Start();
+    }
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+    // All those waiting for writable should have timed out.
+    EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+  }  // Joins the threads.
+  for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+    EXPECT_EQ(i, context[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+  for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+    EXPECT_TRUE(did_wait[i]) << i;
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result[i]) << i;
+    // Since we closed before joining, we can't say much about what each thread
+    // saw as the state.
+  }
+}
+
+// TODO(vtl): Stress test?
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/system_impl_export.h b/mojo/edk/system/system_impl_export.h
new file mode 100644
index 0000000..5bbf005
--- /dev/null
+++ b/mojo/edk/system/system_impl_export.h
@@ -0,0 +1,29 @@
+// 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_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_
+#define MOJO_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllimport)
+#endif  // defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+#define MOJO_SYSTEM_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_IMPL_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define MOJO_SYSTEM_IMPL_EXPORT
+#endif
+
+#endif  // MOJO_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_
diff --git a/mojo/edk/system/test_utils.cc b/mojo/edk/system/test_utils.cc
new file mode 100644
index 0000000..3217c00
--- /dev/null
+++ b/mojo/edk/system/test_utils.cc
@@ -0,0 +1,54 @@
+// 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/edk/system/test_utils.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+namespace {
+
+void PostTaskAndWaitHelper(base::WaitableEvent* event,
+                           const base::Closure& task) {
+  task.Run();
+  event->Signal();
+}
+
+}  // namespace
+
+void PostTaskAndWait(scoped_refptr<base::TaskRunner> task_runner,
+                     const tracked_objects::Location& from_here,
+                     const base::Closure& task) {
+  base::WaitableEvent event(false, false);
+  task_runner->PostTask(from_here,
+                        base::Bind(&PostTaskAndWaitHelper, &event, task));
+  event.Wait();
+}
+
+base::TimeDelta EpsilonTimeout() {
+// Originally, our epsilon timeout was 10 ms, which was mostly fine but flaky on
+// some Windows bots. I don't recall ever seeing flakes on other bots. At 30 ms
+// tests seem reliable on Windows bots, but not at 25 ms. We'd like this timeout
+// to be as small as possible (see the description in the .h file).
+//
+// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN,
+// etc.). Based on this, set it to (usually be) 30 ms on Windows and 20 ms
+// elsewhere.
+#if defined(OS_WIN)
+  return (TestTimeouts::tiny_timeout() * 3) / 10;
+#else
+  return (TestTimeouts::tiny_timeout() * 2) / 10;
+#endif
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h
new file mode 100644
index 0000000..3ab1907
--- /dev/null
+++ b/mojo/edk/system/test_utils.h
@@ -0,0 +1,61 @@
+// 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_EDK_SYSTEM_TEST_UTILS_H_
+#define MOJO_EDK_SYSTEM_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+
+namespace tracked_objects {
+class Location;
+}
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// Posts the given task (to the given task runner) and waits for it to complete.
+// (Note: Doesn't spin the current thread's message loop, so if you're careless
+// this could easily deadlock.)
+void PostTaskAndWait(scoped_refptr<base::TaskRunner> task_runner,
+                     const tracked_objects::Location& from_here,
+                     const base::Closure& task);
+
+// A timeout smaller than |TestTimeouts::tiny_timeout()|. Warning: This may lead
+// to flakiness, but this is unavoidable if, e.g., you're trying to ensure that
+// functions with timeouts are reasonably accurate. We want this to be as small
+// as possible without causing too much flakiness.
+base::TimeDelta EpsilonTimeout();
+
+// Stopwatch -------------------------------------------------------------------
+
+// A simple "stopwatch" for measuring time elapsed from a given starting point.
+class Stopwatch {
+ public:
+  Stopwatch() {}
+  ~Stopwatch() {}
+
+  void Start() { start_time_ = base::TimeTicks::Now(); }
+
+  base::TimeDelta Elapsed() { return base::TimeTicks::Now() - start_time_; }
+
+ private:
+  base::TimeTicks start_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(Stopwatch);
+};
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_UTILS_H_
diff --git a/mojo/edk/system/transport_data.cc b/mojo/edk/system/transport_data.cc
new file mode 100644
index 0000000..774b744
--- /dev/null
+++ b/mojo/edk/system/transport_data.cc
@@ -0,0 +1,346 @@
+// Copyright 2014 The Chromium 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/edk/system/transport_data.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/constants.h"
+#include "mojo/edk/system/message_in_transit.h"
+
+namespace mojo {
+namespace system {
+
+// The maximum amount of space needed per platform handle.
+// (|{Channel,RawChannel}::GetSerializedPlatformHandleSize()| should always
+// return a value which is at most this. This is only used to calculate
+// |TransportData::kMaxBufferSize|. This value should be a multiple of the
+// alignment in order to simplify calculations, even though the actual amount of
+// space needed need not be a multiple of the alignment.
+const size_t kMaxSizePerPlatformHandle = 8;
+static_assert(kMaxSizePerPlatformHandle % MessageInTransit::kMessageAlignment ==
+                  0,
+              "kMaxSizePerPlatformHandle not a multiple of alignment");
+
+STATIC_CONST_MEMBER_DEFINITION const size_t
+    TransportData::kMaxSerializedDispatcherSize;
+STATIC_CONST_MEMBER_DEFINITION const size_t
+    TransportData::kMaxSerializedDispatcherPlatformHandles;
+
+// static
+const size_t TransportData::kMaxPlatformHandles =
+    kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles;
+
+// In additional to the header, for each attached (Mojo) handle there'll be a
+// handle table entry and serialized dispatcher data.
+// Note: This definition must follow the one for |kMaxPlatformHandles|;
+// otherwise, we get a static initializer with gcc (but not clang).
+// static
+const size_t TransportData::kMaxBufferSize =
+    sizeof(Header) +
+    kMaxMessageNumHandles *
+        (sizeof(HandleTableEntry) + kMaxSerializedDispatcherSize) +
+    kMaxPlatformHandles * kMaxSizePerPlatformHandle;
+
+struct TransportData::PrivateStructForCompileAsserts {
+  static_assert(sizeof(Header) % MessageInTransit::kMessageAlignment == 0,
+                "sizeof(MessageInTransit::Header) not a multiple of alignment");
+  static_assert(kMaxSerializedDispatcherSize %
+                        MessageInTransit::kMessageAlignment ==
+                    0,
+                "kMaxSerializedDispatcherSize not a multiple of alignment");
+  static_assert(sizeof(HandleTableEntry) %
+                        MessageInTransit::kMessageAlignment ==
+                    0,
+                "sizeof(MessageInTransit::HandleTableEntry) not a multiple of "
+                "alignment");
+};
+
+TransportData::TransportData(scoped_ptr<DispatcherVector> dispatchers,
+                             Channel* channel) {
+  DCHECK(dispatchers);
+  DCHECK(channel);
+
+  const size_t num_handles = dispatchers->size();
+  DCHECK_GT(num_handles, 0u);
+
+  // The offset to the start of the (Mojo) handle table.
+  const size_t handle_table_start_offset = sizeof(Header);
+  // The offset to the start of the serialized dispatcher data.
+  const size_t serialized_dispatcher_start_offset =
+      handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
+  // The estimated size of the secondary buffer. We compute this estimate below.
+  // It must be at least as big as the (eventual) actual size.
+  size_t estimated_size = serialized_dispatcher_start_offset;
+  size_t estimated_num_platform_handles = 0;
+#if DCHECK_IS_ON
+  std::vector<size_t> all_max_sizes(num_handles);
+  std::vector<size_t> all_max_platform_handles(num_handles);
+#endif
+  for (size_t i = 0; i < num_handles; i++) {
+    if (Dispatcher* dispatcher = (*dispatchers)[i].get()) {
+      size_t max_size = 0;
+      size_t max_platform_handles = 0;
+      Dispatcher::TransportDataAccess::StartSerialize(
+          dispatcher, channel, &max_size, &max_platform_handles);
+
+      DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
+      estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size);
+      DCHECK_LE(estimated_size, kMaxBufferSize);
+
+      DCHECK_LE(max_platform_handles, kMaxSerializedDispatcherPlatformHandles);
+      estimated_num_platform_handles += max_platform_handles;
+      DCHECK_LE(estimated_num_platform_handles, kMaxPlatformHandles);
+
+#if DCHECK_IS_ON
+      all_max_sizes[i] = max_size;
+      all_max_platform_handles[i] = max_platform_handles;
+#endif
+    }
+  }
+
+  size_t size_per_platform_handle = 0;
+  if (estimated_num_platform_handles > 0) {
+    size_per_platform_handle = channel->GetSerializedPlatformHandleSize();
+    DCHECK_LE(size_per_platform_handle, kMaxSizePerPlatformHandle);
+    estimated_size += estimated_num_platform_handles * size_per_platform_handle;
+    estimated_size = MessageInTransit::RoundUpMessageAlignment(estimated_size);
+    DCHECK_LE(estimated_size, kMaxBufferSize);
+  }
+
+  buffer_.reset(static_cast<char*>(
+      base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment)));
+  // Entirely clear out the secondary buffer, since then we won't have to worry
+  // about clearing padding or unused space (e.g., if a dispatcher fails to
+  // serialize).
+  memset(buffer_.get(), 0, estimated_size);
+
+  if (estimated_num_platform_handles > 0) {
+    DCHECK(!platform_handles_);
+    platform_handles_.reset(new embedder::PlatformHandleVector());
+  }
+
+  Header* header = reinterpret_cast<Header*>(buffer_.get());
+  header->num_handles = static_cast<uint32_t>(num_handles);
+  // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and
+  // |unused| be zero; we'll set the former two later if necessary.)
+
+  HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
+      buffer_.get() + handle_table_start_offset);
+  size_t current_offset = serialized_dispatcher_start_offset;
+  for (size_t i = 0; i < num_handles; i++) {
+    Dispatcher* dispatcher = (*dispatchers)[i].get();
+    if (!dispatcher) {
+      static_assert(Dispatcher::kTypeUnknown == 0,
+                    "Value of Dispatcher::kTypeUnknown must be 0");
+      continue;
+    }
+
+#if DCHECK_IS_ON
+    size_t old_platform_handles_size =
+        platform_handles_ ? platform_handles_->size() : 0;
+#endif
+
+    void* destination = buffer_.get() + current_offset;
+    size_t actual_size = 0;
+    if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
+            dispatcher,
+            channel,
+            destination,
+            &actual_size,
+            platform_handles_.get())) {
+      handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
+      handle_table[i].offset = static_cast<uint32_t>(current_offset);
+      handle_table[i].size = static_cast<uint32_t>(actual_size);
+// (Okay to not set |unused| since we cleared the entire buffer.)
+
+#if DCHECK_IS_ON
+      DCHECK_LE(actual_size, all_max_sizes[i]);
+      DCHECK_LE(platform_handles_
+                    ? (platform_handles_->size() - old_platform_handles_size)
+                    : 0,
+                all_max_platform_handles[i]);
+#endif
+    } else {
+      // Nothing to do on failure, since |buffer_| was cleared, and
+      // |kTypeUnknown| is zero. The handle was simply closed.
+      LOG(ERROR) << "Failed to serialize handle to remote message pipe";
+    }
+
+    current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size);
+    DCHECK_LE(current_offset, estimated_size);
+    DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0,
+              estimated_num_platform_handles);
+  }
+
+  if (platform_handles_ && platform_handles_->size() > 0) {
+    header->platform_handle_table_offset =
+        static_cast<uint32_t>(current_offset);
+    header->num_platform_handles =
+        static_cast<uint32_t>(platform_handles_->size());
+    current_offset += platform_handles_->size() * size_per_platform_handle;
+    current_offset = MessageInTransit::RoundUpMessageAlignment(current_offset);
+  }
+
+  // There's no aligned realloc, so it's no good way to release unused space (if
+  // we overshot our estimated space requirements).
+  buffer_size_ = current_offset;
+
+  // |dispatchers_| will be destroyed as it goes out of scope.
+}
+
+#if defined(OS_POSIX)
+TransportData::TransportData(
+    embedder::ScopedPlatformHandleVectorPtr platform_handles)
+    : buffer_size_(sizeof(Header)), platform_handles_(platform_handles.Pass()) {
+  buffer_.reset(static_cast<char*>(
+      base::AlignedAlloc(buffer_size_, MessageInTransit::kMessageAlignment)));
+  memset(buffer_.get(), 0, buffer_size_);
+}
+#endif  // defined(OS_POSIX)
+
+TransportData::~TransportData() {
+}
+
+// static
+const char* TransportData::ValidateBuffer(
+    size_t serialized_platform_handle_size,
+    const void* buffer,
+    size_t buffer_size) {
+  DCHECK(buffer);
+  DCHECK_GT(buffer_size, 0u);
+
+  // Always make sure that the buffer size is sane; if it's not, someone's
+  // messing with us.
+  if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize ||
+      buffer_size % MessageInTransit::kMessageAlignment != 0)
+    return "Invalid message secondary buffer size";
+
+  const Header* header = static_cast<const Header*>(buffer);
+  const size_t num_handles = header->num_handles;
+
+#if !defined(OS_POSIX)
+  // On POSIX, we send control messages with platform handles (but no handles)
+  // attached (see the comments for
+  // |TransportData(embedder::ScopedPlatformHandleVectorPtr)|. (This check isn't
+  // important security-wise anyway.)
+  if (num_handles == 0)
+    return "Message has no handles attached, but secondary buffer present";
+#endif
+
+  // Sanity-check |num_handles| (before multiplying it against anything).
+  if (num_handles > kMaxMessageNumHandles)
+    return "Message handle payload too large";
+
+  if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry))
+    return "Message secondary buffer too small";
+
+  if (header->num_platform_handles == 0) {
+    // Then |platform_handle_table_offset| should also be zero.
+    if (header->platform_handle_table_offset != 0) {
+      return "Message has no handles attached, but platform handle table "
+             "present";
+    }
+  } else {
+    // |num_handles| has already been validated, so the multiplication is okay.
+    if (header->num_platform_handles >
+        num_handles * kMaxSerializedDispatcherPlatformHandles)
+      return "Message has too many platform handles attached";
+
+    static const char kInvalidPlatformHandleTableOffset[] =
+        "Message has invalid platform handle table offset";
+    // This doesn't check that the platform handle table doesn't alias other
+    // stuff, but it doesn't matter, since it's all read-only.
+    if (header->platform_handle_table_offset %
+            MessageInTransit::kMessageAlignment !=
+        0)
+      return kInvalidPlatformHandleTableOffset;
+
+    // ">" instead of ">=" since the size per handle may be zero.
+    if (header->platform_handle_table_offset > buffer_size)
+      return kInvalidPlatformHandleTableOffset;
+
+    // We already checked |platform_handle_table_offset| and
+    // |num_platform_handles|, so the addition and multiplication are okay.
+    if (header->platform_handle_table_offset +
+            header->num_platform_handles * serialized_platform_handle_size >
+        buffer_size)
+      return kInvalidPlatformHandleTableOffset;
+  }
+
+  const HandleTableEntry* handle_table =
+      reinterpret_cast<const HandleTableEntry*>(
+          static_cast<const char*>(buffer) + sizeof(Header));
+  static const char kInvalidSerializedDispatcher[] =
+      "Message contains invalid serialized dispatcher";
+  for (size_t i = 0; i < num_handles; i++) {
+    size_t offset = handle_table[i].offset;
+    if (offset % MessageInTransit::kMessageAlignment != 0)
+      return kInvalidSerializedDispatcher;
+
+    size_t size = handle_table[i].size;
+    if (size > kMaxSerializedDispatcherSize || size > buffer_size)
+      return kInvalidSerializedDispatcher;
+
+    // Note: This is an overflow-safe check for |offset + size > buffer_size|
+    // (we know that |size <= buffer_size| from the previous check).
+    if (offset > buffer_size - size)
+      return kInvalidSerializedDispatcher;
+  }
+
+  return nullptr;
+}
+
+// static
+void TransportData::GetPlatformHandleTable(const void* transport_data_buffer,
+                                           size_t* num_platform_handles,
+                                           const void** platform_handle_table) {
+  DCHECK(transport_data_buffer);
+  DCHECK(num_platform_handles);
+  DCHECK(platform_handle_table);
+
+  const Header* header = static_cast<const Header*>(transport_data_buffer);
+  *num_platform_handles = header->num_platform_handles;
+  *platform_handle_table = static_cast<const char*>(transport_data_buffer) +
+                           header->platform_handle_table_offset;
+}
+
+// static
+scoped_ptr<DispatcherVector> TransportData::DeserializeDispatchers(
+    const void* buffer,
+    size_t buffer_size,
+    embedder::ScopedPlatformHandleVectorPtr platform_handles,
+    Channel* channel) {
+  DCHECK(buffer);
+  DCHECK_GT(buffer_size, 0u);
+  DCHECK(channel);
+
+  const Header* header = static_cast<const Header*>(buffer);
+  const size_t num_handles = header->num_handles;
+  scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector(num_handles));
+
+  const HandleTableEntry* handle_table =
+      reinterpret_cast<const HandleTableEntry*>(
+          static_cast<const char*>(buffer) + sizeof(Header));
+  for (size_t i = 0; i < num_handles; i++) {
+    size_t offset = handle_table[i].offset;
+    size_t size = handle_table[i].size;
+    // Should already have been checked by |ValidateBuffer()|:
+    DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u);
+    DCHECK_LE(offset, buffer_size);
+    DCHECK_LE(offset + size, buffer_size);
+
+    const void* source = static_cast<const char*>(buffer) + offset;
+    (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize(
+        channel, handle_table[i].type, source, size, platform_handles.get());
+  }
+
+  return dispatchers.Pass();
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/transport_data.h b/mojo/edk/system/transport_data.h
new file mode 100644
index 0000000..395bc73
--- /dev/null
+++ b/mojo/edk/system/transport_data.h
@@ -0,0 +1,192 @@
+// Copyright 2014 The Chromium 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_EDK_SYSTEM_TRANSPORT_DATA_H_
+#define MOJO_EDK_SYSTEM_TRANSPORT_DATA_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+
+// This class is used by |MessageInTransit| to represent handles (|Dispatcher|s)
+// in various stages of serialization.
+//
+// The stages are:
+//   - Before reaching |TransportData|: Turn |DispatcherTransport|s into
+//     |Dispatcher|s that are "owned" by (and attached to) a |MessageInTransit|.
+//     This invalidates the handles in the space of the sending application
+//     (and, e.g., if another thread is waiting on such a handle, it'll be
+//     notified of this invalidation).
+//   - Serialize these dispatchers into the |TransportData|: First, for each
+//     attached dispatcher, there's an entry in the |TransportData|'s "handle
+//     table", which points to a segment of (dispatcher-type-dependent) data.
+//   - During the serialization of the dispatchers, |PlatformHandle|s may be
+//     detached from the dispatchers and attached to the |TransportData|.
+//   - Before sending the |MessageInTransit|, including its main buffer and the
+//     |TransportData|'s buffer, the |Channel| sends any |PlatformHandle|s (in a
+//     platform-, and possibly sandbox-situation-, specific way) first. In doing
+//     so, it appends a "platform handle table" to the |TransportData|
+//     containing information about how to deserialize these |PlatformHandle|s.
+//   - Finally, at this point, to send the |MessageInTransit|, there only
+//     remains "inert" data: the |MessageInTransit|'s main buffer and data from
+//     the |TransportData|, consisting of the "handle table" (one entry for each
+//     attached dispatcher), dispatcher-type-specific data (one segment for each
+//     entry in the "handle table"), and the "platform handle table" (one entry
+//     for each attached |PlatformHandle|).
+//
+// To receive a message (|MessageInTransit|), the "reverse" happens:
+//   - On POSIX, receive and buffer |PlatformHandle|s (i.e., FDs), which were
+//     sent before the "inert" data.
+//   - Receive the "inert" data from the |MessageInTransit|. Examine its
+//     "platform handle table". On POSIX, match its entries with the buffered
+//     |PlatformHandle|s, which were previously received. On Windows, do what's
+//     necessary to obtain |PlatformHandle|s (e.g.: i. if the sender is fully
+//     trusted and able to duplicate handle into the receiver, then just pick
+//     out the |HANDLE| value; ii. if the receiver is fully trusted and able to
+//     duplicate handles from the receiver, do the |DuplicateHandle()|; iii.
+//     otherwise, talk to a broker to get handles). Reattach all the
+//     |PlatformHandle|s to the |MessageInTransit|.
+//   - For each entry in the "handle table", use serialized dispatcher data to
+//     reconstitute a dispatcher, taking ownership of associated
+//     |PlatformHandle|s (and detaching them). Attach these dispatchers to the
+//     |MessageInTransit|.
+//   - At this point, the |MessageInTransit| consists of its main buffer
+//     (primarily the data payload) and the attached dispatchers; the
+//     |TransportData| can be discarded.
+//   - When |MojoReadMessage()| is to give data to the application, attach the
+//     dispatchers to the (global, "core") handle table, getting handles; give
+//     the application the data payload and these handles.
+//
+// TODO(vtl): Everything above involving |PlatformHandle|s.
+class MOJO_SYSTEM_IMPL_EXPORT TransportData {
+ public:
+  // The maximum size of a single serialized dispatcher. This must be a multiple
+  // of |kMessageAlignment|.
+  static const size_t kMaxSerializedDispatcherSize = 10000;
+
+  // The maximum number of platform handles to attach for a single serialized
+  // dispatcher.
+  static const size_t kMaxSerializedDispatcherPlatformHandles = 2;
+
+  // The maximum possible size of a valid transport data buffer.
+  static const size_t kMaxBufferSize;
+
+  // The maximum total number of platform handles that may be attached.
+  static const size_t kMaxPlatformHandles;
+
+  TransportData(scoped_ptr<DispatcherVector> dispatchers, Channel* channel);
+
+#if defined(OS_POSIX)
+  // This is a hacky POSIX-only constructor to directly attach only platform
+  // handles to a message, used by |RawChannelPosix| to split messages with too
+  // many platform handles into multiple messages. |Header| will be present, but
+  // be zero. (No other information will be attached, and
+  // |RawChannel::GetSerializedPlatformHandleSize()| should return zero.)
+  explicit TransportData(
+      embedder::ScopedPlatformHandleVectorPtr platform_handles);
+#endif
+
+  ~TransportData();
+
+  const void* buffer() const { return buffer_.get(); }
+  void* buffer() { return buffer_.get(); }
+  size_t buffer_size() const { return buffer_size_; }
+
+  uint32_t platform_handle_table_offset() const {
+    return header()->platform_handle_table_offset;
+  }
+
+  // Gets attached platform-specific handles; this may return null if there are
+  // none. Note that the caller may mutate the set of platform-specific handles.
+  const embedder::PlatformHandleVector* platform_handles() const {
+    return platform_handles_.get();
+  }
+  embedder::PlatformHandleVector* platform_handles() {
+    return platform_handles_.get();
+  }
+
+  // Receive-side functions:
+
+  // Checks if the given buffer (from the "wire") looks like a valid
+  // |TransportData| buffer. (Should only be called if |buffer_size| is
+  // nonzero.) Returns null if valid, and a pointer to a human-readable error
+  // message (for debug/logging purposes) on error. Note: This checks the
+  // validity of the handle table entries (i.e., does range checking), but does
+  // not check that the validity of the actual serialized dispatcher
+  // information.
+  static const char* ValidateBuffer(size_t serialized_platform_handle_size,
+                                    const void* buffer,
+                                    size_t buffer_size);
+
+  // Gets the platform handle table from a (valid) |TransportData| buffer (which
+  // should have been validated using |ValidateBuffer()| first).
+  static void GetPlatformHandleTable(const void* transport_data_buffer,
+                                     size_t* num_platform_handles,
+                                     const void** platform_handle_table);
+
+  // Deserializes dispatchers from the given (serialized) transport data buffer
+  // (typically from a |MessageInTransit::View|) and vector of platform handles.
+  // |buffer| should be non-null and |buffer_size| should be nonzero.
+  static scoped_ptr<DispatcherVector> DeserializeDispatchers(
+      const void* buffer,
+      size_t buffer_size,
+      embedder::ScopedPlatformHandleVectorPtr platform_handles,
+      Channel* channel);
+
+ private:
+  // To allow us to make compile-assertions about |Header|, etc. in the .cc
+  // file.
+  struct PrivateStructForCompileAsserts;
+
+  // Header for the "secondary buffer"/"transport data". Must be a multiple of
+  // |MessageInTransit::kMessageAlignment| in size. Must be POD.
+  struct Header {
+    uint32_t num_handles;
+    // TODO(vtl): Not used yet:
+    uint32_t platform_handle_table_offset;
+    uint32_t num_platform_handles;
+    uint32_t unused;
+  };
+
+  struct HandleTableEntry {
+    int32_t type;     // From |Dispatcher::Type| (|kTypeUnknown| for "invalid").
+    uint32_t offset;  // Relative to the start of the "secondary buffer".
+    uint32_t size;    // (Not including any padding.)
+    uint32_t unused;
+  };
+
+  const Header* header() const {
+    return reinterpret_cast<const Header*>(buffer_.get());
+  }
+
+  size_t buffer_size_;
+  scoped_ptr<char, base::AlignedFreeDeleter> buffer_;  // Never null.
+
+  // Any platform-specific handles attached to this message (for inter-process
+  // transport). The vector (if any) owns the handles that it contains (and is
+  // responsible for closing them).
+  // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|.
+  embedder::ScopedPlatformHandleVectorPtr platform_handles_;
+
+  DISALLOW_COPY_AND_ASSIGN(TransportData);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TRANSPORT_DATA_H_
diff --git a/mojo/edk/system/waiter.cc b/mojo/edk/system/waiter.cc
new file mode 100644
index 0000000..6dcd713
--- /dev/null
+++ b/mojo/edk/system/waiter.cc
@@ -0,0 +1,99 @@
+// 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/edk/system/waiter.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace mojo {
+namespace system {
+
+Waiter::Waiter()
+    : cv_(&lock_),
+#ifndef NDEBUG
+      initialized_(false),
+#endif
+      awoken_(false),
+      awake_result_(MOJO_RESULT_INTERNAL),
+      awake_context_(static_cast<uint32_t>(-1)) {
+}
+
+Waiter::~Waiter() {
+}
+
+void Waiter::Init() {
+#ifndef NDEBUG
+  initialized_ = true;
+#endif
+  awoken_ = false;
+  // NOTE(vtl): If performance ever becomes an issue, we can disable the setting
+  // of |awake_result_| (except the first one in |Awake()|) in Release builds.
+  awake_result_ = MOJO_RESULT_INTERNAL;
+}
+
+// TODO(vtl): Fast-path the |deadline == 0| case?
+MojoResult Waiter::Wait(MojoDeadline deadline, uint32_t* context) {
+  base::AutoLock locker(lock_);
+
+#ifndef NDEBUG
+  DCHECK(initialized_);
+  // It'll need to be re-initialized after this.
+  initialized_ = false;
+#endif
+
+  // Fast-path the already-awoken case:
+  if (awoken_) {
+    DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
+    if (context)
+      *context = awake_context_;
+    return awake_result_;
+  }
+
+  // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity.
+  // Treat any out-of-range deadline as "forever" (which is wrong, but okay
+  // since 2^63 microseconds is ~300000 years). Note that this also takes care
+  // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case.
+  if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+    do {
+      cv_.Wait();
+    } while (!awoken_);
+  } else {
+    // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition
+    // variables take an absolute deadline.
+    const base::TimeTicks end_time =
+        base::TimeTicks::Now() +
+        base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline));
+    do {
+      base::TimeTicks now_time = base::TimeTicks::Now();
+      if (now_time >= end_time)
+        return MOJO_RESULT_DEADLINE_EXCEEDED;
+
+      cv_.TimedWait(end_time - now_time);
+    } while (!awoken_);
+  }
+
+  DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
+  if (context)
+    *context = awake_context_;
+  return awake_result_;
+}
+
+void Waiter::Awake(MojoResult result, uint32_t context) {
+  base::AutoLock locker(lock_);
+
+  if (awoken_)
+    return;
+
+  awoken_ = true;
+  awake_result_ = result;
+  awake_context_ = context;
+  cv_.Signal();
+  // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter.h b/mojo/edk/system/waiter.h
new file mode 100644
index 0000000..03ada16
--- /dev/null
+++ b/mojo/edk/system/waiter.h
@@ -0,0 +1,81 @@
+// 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_EDK_SYSTEM_WAITER_H_
+#define MOJO_EDK_SYSTEM_WAITER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+// IMPORTANT (all-caps gets your attention, right?): |Waiter| methods are called
+// under other locks, in particular, |Dispatcher::lock_|s, so |Waiter| methods
+// must never call out to other objects (in particular, |Dispatcher|s). This
+// class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT Waiter {
+ public:
+  Waiter();
+  ~Waiter();
+
+  // A |Waiter| can be used multiple times; |Init()| should be called before
+  // each time it's used.
+  void Init();
+
+  // Waits until a suitable |Awake()| is called. (|context| may be null, in
+  // which case, obviously no context is ever returned.)
+  // Returns:
+  //   - The result given to the first call to |Awake()| (possibly before this
+  //     call to |Wait()|); in this case, |*context| is set to the value passed
+  //     to that call to |Awake()|.
+  //   - |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline was exceeded; in this
+  //     case |*context| is not modified.
+  //
+  // Usually, the context passed to |Awake()| will be the value passed to
+  // |Dispatcher::AddWaiter()|, which is usually the index to the array of
+  // handles passed to |MojoWaitMany()| (or 0 for |MojoWait()|).
+  //
+  // Typical |Awake()| results are:
+  //   - |MOJO_RESULT_OK| if one of the flags passed to
+  //     |MojoWait()|/|MojoWaitMany()| (hence |Dispatcher::AddWaiter()|) was
+  //     satisfied;
+  //   - |MOJO_RESULT_CANCELLED| if a handle (on which
+  //     |MojoWait()|/|MojoWaitMany()| was called) was closed (hence the
+  //     dispatcher closed); and
+  //   - |MOJO_RESULT_FAILED_PRECONDITION| if one of the set of flags passed to
+  //     |MojoWait()|/|MojoWaitMany()| cannot or can no longer be satisfied by
+  //     the corresponding handle (e.g., if the other end of a message or data
+  //     pipe is closed).
+  MojoResult Wait(MojoDeadline deadline, uint32_t* context);
+
+  // Wake the waiter up with the given result and context (or no-op if it's been
+  // woken up already).
+  void Awake(MojoResult result, uint32_t context);
+
+ private:
+  base::ConditionVariable cv_;  // Associated to |lock_|.
+  base::Lock lock_;             // Protects the following members.
+#ifndef NDEBUG
+  bool initialized_;
+#endif
+  bool awoken_;
+  MojoResult awake_result_;
+  // This is a |uint32_t| because we really only need to store an index (for
+  // |MojoWaitMany()|). But in tests, it's convenient to use this for other
+  // purposes (e.g., to distinguish between different wake-up reasons).
+  uint32_t awake_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAITER_H_
diff --git a/mojo/edk/system/waiter_list.cc b/mojo/edk/system/waiter_list.cc
new file mode 100644
index 0000000..42dfe15
--- /dev/null
+++ b/mojo/edk/system/waiter_list.cc
@@ -0,0 +1,57 @@
+// 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/edk/system/waiter_list.h"
+
+#include "base/logging.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/waiter.h"
+
+namespace mojo {
+namespace system {
+
+WaiterList::WaiterList() {
+}
+
+WaiterList::~WaiterList() {
+  DCHECK(waiters_.empty());
+}
+
+void WaiterList::AwakeWaitersForStateChange(const HandleSignalsState& state) {
+  for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();
+       ++it) {
+    if (state.satisfies(it->signals))
+      it->waiter->Awake(MOJO_RESULT_OK, it->context);
+    else if (!state.can_satisfy(it->signals))
+      it->waiter->Awake(MOJO_RESULT_FAILED_PRECONDITION, it->context);
+  }
+}
+
+void WaiterList::CancelAllWaiters() {
+  for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();
+       ++it) {
+    it->waiter->Awake(MOJO_RESULT_CANCELLED, it->context);
+  }
+  waiters_.clear();
+}
+
+void WaiterList::AddWaiter(Waiter* waiter,
+                           MojoHandleSignals signals,
+                           uint32_t context) {
+  waiters_.push_back(WaiterInfo(waiter, signals, context));
+}
+
+void WaiterList::RemoveWaiter(Waiter* waiter) {
+  // We allow a thread to wait on the same handle multiple times simultaneously,
+  // so we need to scan the entire list and remove all occurrences of |waiter|.
+  for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();) {
+    WaiterInfoList::iterator maybe_delete = it;
+    ++it;
+    if (maybe_delete->waiter == waiter)
+      waiters_.erase(maybe_delete);
+  }
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter_list.h b/mojo/edk/system/waiter_list.h
new file mode 100644
index 0000000..6bbc799
--- /dev/null
+++ b/mojo/edk/system/waiter_list.h
@@ -0,0 +1,58 @@
+// 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_EDK_SYSTEM_WAITER_LIST_H_
+#define MOJO_EDK_SYSTEM_WAITER_LIST_H_
+
+#include <stdint.h>
+
+#include <list>
+
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+
+class Waiter;
+struct HandleSignalsState;
+
+// |WaiterList| tracks all the |Waiter|s that are waiting on a given
+// handle/|Dispatcher|. There should be a |WaiterList| for each handle that can
+// be waited on (in any way). In the simple case, the |WaiterList| is owned by
+// the |Dispatcher|, whereas in more complex cases it is owned by the secondary
+// object (see simple_dispatcher.* and the explanatory comment in core.cc). This
+// class is thread-unsafe (all concurrent access must be protected by some
+// lock).
+class MOJO_SYSTEM_IMPL_EXPORT WaiterList {
+ public:
+  WaiterList();
+  ~WaiterList();
+
+  void AwakeWaitersForStateChange(const HandleSignalsState& state);
+  void CancelAllWaiters();
+  void AddWaiter(Waiter* waiter, MojoHandleSignals signals, uint32_t context);
+  void RemoveWaiter(Waiter* waiter);
+
+ private:
+  struct WaiterInfo {
+    WaiterInfo(Waiter* waiter, MojoHandleSignals signals, uint32_t context)
+        : waiter(waiter), signals(signals), context(context) {}
+
+    Waiter* waiter;
+    MojoHandleSignals signals;
+    uint32_t context;
+  };
+  typedef std::list<WaiterInfo> WaiterInfoList;
+
+  WaiterInfoList waiters_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaiterList);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAITER_LIST_H_
diff --git a/mojo/edk/system/waiter_list_unittest.cc b/mojo/edk/system/waiter_list_unittest.cc
new file mode 100644
index 0000000..129df71
--- /dev/null
+++ b/mojo/edk/system/waiter_list_unittest.cc
@@ -0,0 +1,289 @@
+// 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/waiter_list.h"
+
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(WaiterListTest, BasicCancel) {
+  MojoResult result;
+  uint32_t context;
+
+  // Cancel immediately after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    waiter_list.CancelAllWaiters();
+    // Double-remove okay:
+    waiter_list.RemoveWaiter(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(1u, context);
+
+  // Cancel before after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    waiter_list.CancelAllWaiters();
+    thread.Start();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(2u, context);
+
+  // Cancel some time after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.CancelAllWaiters();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, BasicAwakeSatisfied) {
+  MojoResult result;
+  uint32_t context;
+
+  // Awake immediately after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(1u, context);
+
+  // Awake before after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_WRITABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+    // Double-remove okay:
+    waiter_list.RemoveWaiter(thread.waiter());
+    thread.Start();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(2u, context);
+
+  // Awake some time after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, BasicAwakeUnsatisfiable) {
+  MojoResult result;
+  uint32_t context;
+
+  // Awake (for unsatisfiability) immediately after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(1u, context);
+
+  // Awake (for unsatisfiability) before after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+    thread.Start();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(2u, context);
+
+  // Awake (for unsatisfiability) some time after thread start.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread.waiter());
+    // Double-remove okay:
+    waiter_list.RemoveWaiter(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, MultipleWaiters) {
+  MojoResult result1;
+  MojoResult result2;
+  MojoResult result3;
+  MojoResult result4;
+  uint32_t context1;
+  uint32_t context2;
+  uint32_t context3;
+  uint32_t context4;
+
+  // Cancel two waiters.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    thread2.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.CancelAllWaiters();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
+  EXPECT_EQ(1u, context1);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+  EXPECT_EQ(2u, context2);
+
+  // Awake one waiter, cancel other.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4);
+    thread2.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread1.waiter());
+    waiter_list.CancelAllWaiters();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_OK, result1);
+  EXPECT_EQ(3u, context1);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+  EXPECT_EQ(4u, context2);
+
+  // Cancel one waiter, awake other for unsatisfiability.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 5);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6);
+    thread2.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE));
+    waiter_list.RemoveWaiter(thread2.waiter());
+    waiter_list.CancelAllWaiters();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
+  EXPECT_EQ(5u, context1);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);
+  EXPECT_EQ(6u, context2);
+
+  // Cancel one waiter, awake other for unsatisfiability.
+  {
+    WaiterList waiter_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7);
+    thread1.Start();
+
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+    // Should do nothing.
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8);
+    thread2.Start();
+
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+    // Awake #1.
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    waiter_list.RemoveWaiter(thread1.waiter());
+
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+    test::SimpleWaiterThread thread3(&result3, &context3);
+    waiter_list.AddWaiter(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9);
+    thread3.Start();
+
+    test::SimpleWaiterThread thread4(&result4, &context4);
+    waiter_list.AddWaiter(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10);
+    thread4.Start();
+
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+    // Awake #2 and #3 for unsatisfiability.
+    waiter_list.AwakeWaitersForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE));
+    waiter_list.RemoveWaiter(thread2.waiter());
+    waiter_list.RemoveWaiter(thread3.waiter());
+
+    // Cancel #4.
+    waiter_list.CancelAllWaiters();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_OK, result1);
+  EXPECT_EQ(7u, context1);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);
+  EXPECT_EQ(8u, context2);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result3);
+  EXPECT_EQ(9u, context3);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result4);
+  EXPECT_EQ(10u, context4);
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter_test_utils.cc b/mojo/edk/system/waiter_test_utils.cc
new file mode 100644
index 0000000..06a4033
--- /dev/null
+++ b/mojo/edk/system/waiter_test_utils.cc
@@ -0,0 +1,70 @@
+// 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/edk/system/waiter_test_utils.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+SimpleWaiterThread::SimpleWaiterThread(MojoResult* result, uint32_t* context)
+    : base::SimpleThread("waiter_thread"), result_(result), context_(context) {
+  waiter_.Init();
+  *result_ = -5420734;   // Totally invalid result.
+  *context_ = 23489023;  // "Random".
+}
+
+SimpleWaiterThread::~SimpleWaiterThread() {
+  Join();
+}
+
+void SimpleWaiterThread::Run() {
+  *result_ = waiter_.Wait(MOJO_DEADLINE_INDEFINITE, context_);
+}
+
+WaiterThread::WaiterThread(scoped_refptr<Dispatcher> dispatcher,
+                           MojoHandleSignals handle_signals,
+                           MojoDeadline deadline,
+                           uint32_t context,
+                           bool* did_wait_out,
+                           MojoResult* result_out,
+                           uint32_t* context_out,
+                           HandleSignalsState* signals_state_out)
+    : base::SimpleThread("waiter_thread"),
+      dispatcher_(dispatcher),
+      handle_signals_(handle_signals),
+      deadline_(deadline),
+      context_(context),
+      did_wait_out_(did_wait_out),
+      result_out_(result_out),
+      context_out_(context_out),
+      signals_state_out_(signals_state_out) {
+  *did_wait_out_ = false;
+  // Initialize these with invalid results (so that we'll be sure to catch any
+  // case where they're not set).
+  *result_out_ = -8542346;
+  *context_out_ = 89023444;
+  *signals_state_out_ = HandleSignalsState(~0u, ~0u);
+}
+
+WaiterThread::~WaiterThread() {
+  Join();
+}
+
+void WaiterThread::Run() {
+  waiter_.Init();
+
+  *result_out_ = dispatcher_->AddWaiter(
+      &waiter_, handle_signals_, context_, signals_state_out_);
+  if (*result_out_ != MOJO_RESULT_OK)
+    return;
+
+  *did_wait_out_ = true;
+  *result_out_ = waiter_.Wait(deadline_, context_out_);
+  dispatcher_->RemoveWaiter(&waiter_, signals_state_out_);
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter_test_utils.h b/mojo/edk/system/waiter_test_utils.h
new file mode 100644
index 0000000..25e5344
--- /dev/null
+++ b/mojo/edk/system/waiter_test_utils.h
@@ -0,0 +1,105 @@
+// 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_EDK_SYSTEM_WAITER_TEST_UTILS_H_
+#define MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// This is a very simple thread that has a |Waiter|, on which it waits
+// indefinitely (and records the result). It will create and initialize the
+// |Waiter| on creation, but the caller must start the thread with |Start()|. It
+// will join the thread on destruction.
+//
+// One usually uses it like:
+//
+//    MojoResult result;
+//    {
+//      WaiterList waiter_list;
+//      test::SimpleWaiterThread thread(&result);
+//      waiter_list.AddWaiter(thread.waiter(), ...);
+//      thread.Start();
+//      ... some stuff to wake the waiter ...
+//      waiter_list.RemoveWaiter(thread.waiter());
+//    }  // Join |thread|.
+//    EXPECT_EQ(..., result);
+//
+// There's a bit of unrealism in its use: In this sort of usage, calls such as
+// |Waiter::Init()|, |AddWaiter()|, and |RemoveWaiter()| are done in the main
+// (test) thread, not the waiter thread (as would actually happen in real code).
+// (We accept this unrealism for simplicity, since |WaiterList| is
+// thread-unsafe so making it more realistic would require adding nontrivial
+// synchronization machinery.)
+class SimpleWaiterThread : public base::SimpleThread {
+ public:
+  // For the duration of the lifetime of this object, |*result| belongs to it
+  // (in the sense that it will write to it whenever it wants).
+  SimpleWaiterThread(MojoResult* result, uint32_t* context);
+  virtual ~SimpleWaiterThread();  // Joins the thread.
+
+  Waiter* waiter() { return &waiter_; }
+
+ private:
+  virtual void Run() override;
+
+  MojoResult* const result_;
+  uint32_t* const context_;
+  Waiter waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleWaiterThread);
+};
+
+// This is a more complex and realistic thread that has a |Waiter|, on which it
+// waits for the given deadline (with the given flags). Unlike
+// |SimpleWaiterThread|, it requires the machinery of |Dispatcher|.
+class WaiterThread : public base::SimpleThread {
+ public:
+  // Note: |*did_wait_out|, |*result_out|, |*context_out| and
+  // |*signals_state_out| "belong" to this object (i.e., may be modified by, on
+  // some other thread) while it's alive.
+  WaiterThread(scoped_refptr<Dispatcher> dispatcher,
+               MojoHandleSignals handle_signals,
+               MojoDeadline deadline,
+               uint32_t context,
+               bool* did_wait_out,
+               MojoResult* result_out,
+               uint32_t* context_out,
+               HandleSignalsState* signals_state_out);
+  virtual ~WaiterThread();
+
+ private:
+  virtual void Run() override;
+
+  const scoped_refptr<Dispatcher> dispatcher_;
+  const MojoHandleSignals handle_signals_;
+  const MojoDeadline deadline_;
+  const uint32_t context_;
+  bool* const did_wait_out_;
+  MojoResult* const result_out_;
+  uint32_t* const context_out_;
+  HandleSignalsState* const signals_state_out_;
+
+  Waiter waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaiterThread);
+};
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_
diff --git a/mojo/edk/system/waiter_unittest.cc b/mojo/edk/system/waiter_unittest.cc
new file mode 100644
index 0000000..bbbc5b8
--- /dev/null
+++ b/mojo/edk/system/waiter_unittest.cc
@@ -0,0 +1,302 @@
+// 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/waiter.h"
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/edk/system/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const int64_t kMicrosPerMs = 1000;
+const int64_t kPollTimeMicros = 10 * kMicrosPerMs;  // 10 ms.
+
+class WaitingThread : public base::SimpleThread {
+ public:
+  explicit WaitingThread(MojoDeadline deadline)
+      : base::SimpleThread("waiting_thread"),
+        deadline_(deadline),
+        done_(false),
+        result_(MOJO_RESULT_UNKNOWN),
+        context_(static_cast<uint32_t>(-1)) {
+    waiter_.Init();
+  }
+
+  virtual ~WaitingThread() { Join(); }
+
+  void WaitUntilDone(MojoResult* result,
+                     uint32_t* context,
+                     base::TimeDelta* elapsed) {
+    for (;;) {
+      {
+        base::AutoLock locker(lock_);
+        if (done_) {
+          *result = result_;
+          *context = context_;
+          *elapsed = elapsed_;
+          break;
+        }
+      }
+
+      base::PlatformThread::Sleep(
+          base::TimeDelta::FromMicroseconds(kPollTimeMicros));
+    }
+  }
+
+  Waiter* waiter() { return &waiter_; }
+
+ private:
+  virtual void Run() override {
+    test::Stopwatch stopwatch;
+    MojoResult result;
+    uint32_t context = static_cast<uint32_t>(-1);
+    base::TimeDelta elapsed;
+
+    stopwatch.Start();
+    result = waiter_.Wait(deadline_, &context);
+    elapsed = stopwatch.Elapsed();
+
+    {
+      base::AutoLock locker(lock_);
+      done_ = true;
+      result_ = result;
+      context_ = context;
+      elapsed_ = elapsed;
+    }
+  }
+
+  const MojoDeadline deadline_;
+  Waiter waiter_;  // Thread-safe.
+
+  base::Lock lock_;  // Protects the following members.
+  bool done_;
+  MojoResult result_;
+  uint32_t context_;
+  base::TimeDelta elapsed_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitingThread);
+};
+
+TEST(WaiterTest, Basic) {
+  MojoResult result;
+  uint32_t context;
+  base::TimeDelta elapsed;
+
+  // Finite deadline.
+
+  // Awake immediately after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+    thread.Start();
+    thread.waiter()->Awake(MOJO_RESULT_OK, 1);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(1u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  // Awake before after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+    thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 2);
+    thread.Start();
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+    EXPECT_EQ(2u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  // Awake some time after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    thread.waiter()->Awake(1, 3);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(1, result);
+    EXPECT_EQ(3u, context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  }
+
+  // Awake some longer time after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+    thread.Start();
+    base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
+    thread.waiter()->Awake(2, 4);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(2, result);
+    EXPECT_EQ(4u, context);
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
+  }
+
+  // Don't awake -- time out (on another thread).
+  {
+    WaitingThread thread(2 * test::EpsilonTimeout().InMicroseconds());
+    thread.Start();
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+    EXPECT_EQ(static_cast<uint32_t>(-1), context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  }
+
+  // No (indefinite) deadline.
+
+  // Awake immediately after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    thread.waiter()->Awake(MOJO_RESULT_OK, 5);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(5u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  // Awake before after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 6);
+    thread.Start();
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+    EXPECT_EQ(6u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  // Awake some time after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    thread.waiter()->Awake(1, 7);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(1, result);
+    EXPECT_EQ(7u, context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  }
+
+  // Awake some longer time after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
+    thread.waiter()->Awake(2, 8);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(2, result);
+    EXPECT_EQ(8u, context);
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
+  }
+}
+
+TEST(WaiterTest, TimeOut) {
+  test::Stopwatch stopwatch;
+  base::TimeDelta elapsed;
+
+  Waiter waiter;
+  uint32_t context = 123;
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_LT(elapsed, test::EpsilonTimeout());
+  EXPECT_EQ(123u, context);
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            waiter.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
+  EXPECT_EQ(123u, context);
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            waiter.Wait(5 * test::EpsilonTimeout().InMicroseconds(), &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
+  EXPECT_EQ(123u, context);
+}
+
+// The first |Awake()| should always win.
+TEST(WaiterTest, MultipleAwakes) {
+  MojoResult result;
+  uint32_t context;
+  base::TimeDelta elapsed;
+
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    thread.waiter()->Awake(MOJO_RESULT_OK, 1);
+    thread.waiter()->Awake(1, 2);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(1u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.waiter()->Awake(1, 3);
+    thread.Start();
+    thread.waiter()->Awake(MOJO_RESULT_OK, 4);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(1, result);
+    EXPECT_EQ(3u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    thread.waiter()->Awake(10, 5);
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    thread.waiter()->Awake(20, 6);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(10, result);
+    EXPECT_EQ(5u, context);
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
+  }
+
+  {
+    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+    thread.Start();
+    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+    thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7);
+    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+    thread.waiter()->Awake(MOJO_RESULT_OK, 8);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+    EXPECT_EQ(7u, context);
+    EXPECT_GT(elapsed, (1 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (1 + 1) * test::EpsilonTimeout());
+  }
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
new file mode 100644
index 0000000..6a6f312
--- /dev/null
+++ b/mojo/edk/test/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_test_support
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "multiprocess_test_helper.cc",
+    "multiprocess_test_helper.h",
+    "test_utils.h",
+    "test_utils_posix.cc",
+    "test_utils_win.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
new file mode 100644
index 0000000..02367d6
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -0,0 +1,86 @@
+// 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/edk/test/multiprocess_test_helper.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/process/kill.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+
+namespace mojo {
+namespace test {
+
+MultiprocessTestHelper::MultiprocessTestHelper()
+    : test_child_handle_(base::kNullProcessHandle) {
+  platform_channel_pair_.reset(new embedder::PlatformChannelPair());
+  server_platform_handle = platform_channel_pair_->PassServerHandle();
+}
+
+MultiprocessTestHelper::~MultiprocessTestHelper() {
+  CHECK_EQ(test_child_handle_, base::kNullProcessHandle);
+  server_platform_handle.reset();
+  platform_channel_pair_.reset();
+}
+
+void MultiprocessTestHelper::StartChild(const std::string& test_child_name) {
+  CHECK(platform_channel_pair_.get());
+  CHECK(!test_child_name.empty());
+  CHECK_EQ(test_child_handle_, base::kNullProcessHandle);
+
+  std::string test_child_main = test_child_name + "TestChildMain";
+
+  base::CommandLine command_line(
+      base::GetMultiProcessTestChildBaseCommandLine());
+  embedder::HandlePassingInformation handle_passing_info;
+  platform_channel_pair_->PrepareToPassClientHandleToChildProcess(
+      &command_line, &handle_passing_info);
+
+  base::LaunchOptions options;
+#if defined(OS_POSIX)
+  options.fds_to_remap = &handle_passing_info;
+#elif defined(OS_WIN)
+  options.start_hidden = true;
+  options.handles_to_inherit = &handle_passing_info;
+#else
+#error "Not supported yet."
+#endif
+
+  test_child_handle_ =
+      base::SpawnMultiProcessTestChild(test_child_main, command_line, options);
+  platform_channel_pair_->ChildProcessLaunched();
+
+  CHECK_NE(test_child_handle_, base::kNullProcessHandle);
+}
+
+int MultiprocessTestHelper::WaitForChildShutdown() {
+  CHECK_NE(test_child_handle_, base::kNullProcessHandle);
+
+  int rv = -1;
+  CHECK(base::WaitForExitCodeWithTimeout(
+      test_child_handle_, &rv, TestTimeouts::action_timeout()));
+  base::CloseProcessHandle(test_child_handle_);
+  test_child_handle_ = base::kNullProcessHandle;
+  return rv;
+}
+
+bool MultiprocessTestHelper::WaitForChildTestShutdown() {
+  return WaitForChildShutdown() == 0;
+}
+
+// static
+void MultiprocessTestHelper::ChildSetup() {
+  CHECK(base::CommandLine::InitializedForCurrentProcess());
+  client_platform_handle =
+      embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
+          *base::CommandLine::ForCurrentProcess());
+}
+
+// static
+embedder::ScopedPlatformHandle MultiprocessTestHelper::client_platform_handle;
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h
new file mode 100644
index 0000000..ea77544
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -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.
+
+#ifndef MOJO_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_
+#define MOJO_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/process/process_handle.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace mojo {
+
+namespace embedder {
+class PlatformChannelPair;
+}
+
+namespace test {
+
+class MultiprocessTestHelper {
+ public:
+  MultiprocessTestHelper();
+  ~MultiprocessTestHelper();
+
+  // Start a child process and run the "main" function "named" |test_child_name|
+  // declared using |MOJO_MULTIPROCESS_TEST_CHILD_MAIN()| or
+  // |MOJO_MULTIPROCESS_TEST_CHILD_TEST()| (below).
+  void StartChild(const std::string& test_child_name);
+  // Wait for the child process to terminate.
+  // Returns the exit code of the child process. Note that, though it's declared
+  // to be an |int|, the exit code is subject to mangling by the OS. E.g., we
+  // usually return -1 on error in the child (e.g., if |test_child_name| was not
+  // found), but this is mangled to 255 on Linux. You should only rely on codes
+  // 0-127 being preserved, and -1 being outside the range 0-127.
+  int WaitForChildShutdown();
+
+  // Like |WaitForChildShutdown()|, but returns true on success (exit code of 0)
+  // and false otherwise. You probably want to do something like
+  // |EXPECT_TRUE(WaitForChildTestShutdown());|. Mainly for use with
+  // |MOJO_MULTIPROCESS_TEST_CHILD_TEST()|.
+  bool WaitForChildTestShutdown();
+
+  // For use by |MOJO_MULTIPROCESS_TEST_CHILD_MAIN()| only:
+  static void ChildSetup();
+
+  // For use in the main process:
+  embedder::ScopedPlatformHandle server_platform_handle;
+
+  // For use (and only valid) in the child process:
+  static embedder::ScopedPlatformHandle client_platform_handle;
+
+ private:
+  scoped_ptr<embedder::PlatformChannelPair> platform_channel_pair_;
+
+  // Valid after |StartChild()| and before |WaitForChildShutdown()|.
+  base::ProcessHandle test_child_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper);
+};
+
+// Use this to declare the child process's "main()" function for tests using
+// |MultiprocessTestHelper|. It returns an |int|, which will be the process's
+// exit code (but see the comment about |WaitForChildShutdown()|).
+#define MOJO_MULTIPROCESS_TEST_CHILD_MAIN(test_child_name) \
+    MULTIPROCESS_TEST_MAIN_WITH_SETUP( \
+        test_child_name ## TestChildMain, \
+        ::mojo::test::MultiprocessTestHelper::ChildSetup)
+
+// Use this (and |WaitForChildTestShutdown()|) for the child process's "main()",
+// if you want to use |EXPECT_...()| or |ASSERT_...()|; it has a |void| return
+// type. (Note that while an |ASSERT_...()| failure will abort the test in the
+// child, it will not abort the test in the parent.)
+#define MOJO_MULTIPROCESS_TEST_CHILD_TEST(test_child_name) \
+    void test_child_name ## TestChildTest(); \
+    MOJO_MULTIPROCESS_TEST_CHILD_MAIN(test_child_name) { \
+      test_child_name ## TestChildTest(); \
+      return (::testing::Test::HasFatalFailure() || \
+              ::testing::Test::HasNonfatalFailure()) ? 1 : 0; \
+    } \
+    void test_child_name ## TestChildTest()
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_
diff --git a/mojo/edk/test/multiprocess_test_helper_unittest.cc b/mojo/edk/test/multiprocess_test_helper_unittest.cc
new file mode 100644
index 0000000..ec2ba22
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper_unittest.cc
@@ -0,0 +1,157 @@
+// 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/edk/test/multiprocess_test_helper.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+#include <fcntl.h>
+#endif
+
+namespace mojo {
+namespace test {
+namespace {
+
+bool IsNonBlocking(const embedder::PlatformHandle& handle) {
+#if defined(OS_WIN)
+  // Haven't figured out a way to query whether a HANDLE was created with
+  // FILE_FLAG_OVERLAPPED.
+  return true;
+#else
+  return fcntl(handle.fd, F_GETFL) & O_NONBLOCK;
+#endif
+}
+
+bool WriteByte(const embedder::PlatformHandle& handle, char c) {
+  size_t bytes_written = 0;
+  BlockingWrite(handle, &c, 1, &bytes_written);
+  return bytes_written == 1;
+}
+
+bool ReadByte(const embedder::PlatformHandle& handle, char* c) {
+  size_t bytes_read = 0;
+  BlockingRead(handle, c, 1, &bytes_read);
+  return bytes_read == 1;
+}
+
+typedef testing::Test MultiprocessTestHelperTest;
+
+TEST_F(MultiprocessTestHelperTest, RunChild) {
+  MultiprocessTestHelper helper;
+  EXPECT_TRUE(helper.server_platform_handle.is_valid());
+
+  helper.StartChild("RunChild");
+  EXPECT_EQ(123, helper.WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(RunChild) {
+  CHECK(MultiprocessTestHelper::client_platform_handle.is_valid());
+  return 123;
+}
+
+TEST_F(MultiprocessTestHelperTest, TestChildMainNotFound) {
+  MultiprocessTestHelper helper;
+  helper.StartChild("NoSuchTestChildMain");
+  int result = helper.WaitForChildShutdown();
+  EXPECT_FALSE(result >= 0 && result <= 127);
+}
+
+TEST_F(MultiprocessTestHelperTest, PassedChannel) {
+  MultiprocessTestHelper helper;
+  EXPECT_TRUE(helper.server_platform_handle.is_valid());
+  helper.StartChild("PassedChannel");
+
+  // Take ownership of the handle.
+  embedder::ScopedPlatformHandle handle = helper.server_platform_handle.Pass();
+
+  // The handle should be non-blocking.
+  EXPECT_TRUE(IsNonBlocking(handle.get()));
+
+  // Write a byte.
+  const char c = 'X';
+  EXPECT_TRUE(WriteByte(handle.get(), c));
+
+  // It'll echo it back to us, incremented.
+  char d = 0;
+  EXPECT_TRUE(ReadByte(handle.get(), &d));
+  EXPECT_EQ(c + 1, d);
+
+  // And return it, incremented again.
+  EXPECT_EQ(c + 2, helper.WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(PassedChannel) {
+  CHECK(MultiprocessTestHelper::client_platform_handle.is_valid());
+
+  // Take ownership of the handle.
+  embedder::ScopedPlatformHandle handle =
+      MultiprocessTestHelper::client_platform_handle.Pass();
+
+  // The handle should be non-blocking.
+  EXPECT_TRUE(IsNonBlocking(handle.get()));
+
+  // Read a byte.
+  char c = 0;
+  EXPECT_TRUE(ReadByte(handle.get(), &c));
+
+  // Write it back, incremented.
+  c++;
+  EXPECT_TRUE(WriteByte(handle.get(), c));
+
+  // And return it, incremented again.
+  c++;
+  return static_cast<int>(c);
+}
+
+TEST_F(MultiprocessTestHelperTest, ChildTestPasses) {
+  MultiprocessTestHelper helper;
+  EXPECT_TRUE(helper.server_platform_handle.is_valid());
+  helper.StartChild("ChildTestPasses");
+  EXPECT_TRUE(helper.WaitForChildTestShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestPasses) {
+  ASSERT_TRUE(MultiprocessTestHelper::client_platform_handle.is_valid());
+  EXPECT_TRUE(IsNonBlocking(
+      MultiprocessTestHelper::client_platform_handle.get()));
+}
+
+TEST_F(MultiprocessTestHelperTest, ChildTestFailsAssert) {
+  MultiprocessTestHelper helper;
+  EXPECT_TRUE(helper.server_platform_handle.is_valid());
+  helper.StartChild("ChildTestFailsAssert");
+  EXPECT_FALSE(helper.WaitForChildTestShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestFailsAssert) {
+  ASSERT_FALSE(MultiprocessTestHelper::client_platform_handle.is_valid())
+      << "DISREGARD: Expected failure in child process";
+  ASSERT_FALSE(IsNonBlocking(
+      MultiprocessTestHelper::client_platform_handle.get())) << "Not reached";
+  CHECK(false) << "Not reached";
+}
+
+TEST_F(MultiprocessTestHelperTest, ChildTestFailsExpect) {
+  MultiprocessTestHelper helper;
+  EXPECT_TRUE(helper.server_platform_handle.is_valid());
+  helper.StartChild("ChildTestFailsExpect");
+  EXPECT_FALSE(helper.WaitForChildTestShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestFailsExpect) {
+  EXPECT_FALSE(MultiprocessTestHelper::client_platform_handle.is_valid())
+      << "DISREGARD: Expected failure #1 in child process";
+  EXPECT_FALSE(IsNonBlocking(
+      MultiprocessTestHelper::client_platform_handle.get()))
+      << "DISREGARD: Expected failure #2 in child process";
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/edk/test/test_utils.h b/mojo/edk/test/test_utils.h
new file mode 100644
index 0000000..9287457
--- /dev/null
+++ b/mojo/edk/test/test_utils.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_TEST_TEST_UTILS_H_
+#define MOJO_EDK_TEST_TEST_UTILS_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace test {
+
+// On success, |bytes_written| is updated to the number of bytes written;
+// otherwise it is untouched.
+bool BlockingWrite(const embedder::PlatformHandle& handle,
+                   const void* buffer,
+                   size_t bytes_to_write,
+                   size_t* bytes_written);
+
+// On success, |bytes_read| is updated to the number of bytes read; otherwise it
+// is untouched.
+bool BlockingRead(const embedder::PlatformHandle& handle,
+                  void* buffer,
+                  size_t buffer_size,
+                  size_t* bytes_read);
+
+// If the read is done successfully or would block, the function returns true
+// and updates |bytes_read| to the number of bytes read (0 if the read would
+// block); otherwise it returns false and leaves |bytes_read| untouched.
+// |handle| must already be in non-blocking mode.
+bool NonBlockingRead(const embedder::PlatformHandle& handle,
+                     void* buffer,
+                     size_t buffer_size,
+                     size_t* bytes_read);
+
+// Gets a (scoped) |PlatformHandle| from the given (scoped) |FILE|.
+embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp);
+
+// Gets a (scoped) |FILE| from a (scoped) |PlatformHandle|.
+base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h,
+                                        const char* mode);
+
+// Returns the path to the mojom js bindings file.
+base::FilePath GetFilePathForJSResource(const std::string& path);
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_TEST_UTILS_H_
diff --git a/mojo/edk/test/test_utils_posix.cc b/mojo/edk/test/test_utils_posix.cc
new file mode 100644
index 0000000..6491baf
--- /dev/null
+++ b/mojo/edk/test/test_utils_posix.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/test/test_utils.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace mojo {
+namespace test {
+
+bool BlockingWrite(const embedder::PlatformHandle& handle,
+                   const void* buffer,
+                   size_t bytes_to_write,
+                   size_t* bytes_written) {
+  int original_flags = fcntl(handle.fd, F_GETFL);
+  if (original_flags == -1 ||
+      fcntl(handle.fd, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) {
+    return false;
+  }
+
+  ssize_t result = HANDLE_EINTR(write(handle.fd, buffer, bytes_to_write));
+
+  fcntl(handle.fd, F_SETFL, original_flags);
+
+  if (result < 0)
+    return false;
+
+  *bytes_written = result;
+  return true;
+}
+
+bool BlockingRead(const embedder::PlatformHandle& handle,
+                  void* buffer,
+                  size_t buffer_size,
+                  size_t* bytes_read) {
+  int original_flags = fcntl(handle.fd, F_GETFL);
+  if (original_flags == -1 ||
+      fcntl(handle.fd, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) {
+    return false;
+  }
+
+  ssize_t result = HANDLE_EINTR(read(handle.fd, buffer, buffer_size));
+
+  fcntl(handle.fd, F_SETFL, original_flags);
+
+  if (result < 0)
+    return false;
+
+  *bytes_read = result;
+  return true;
+}
+
+bool NonBlockingRead(const embedder::PlatformHandle& handle,
+                     void* buffer,
+                     size_t buffer_size,
+                     size_t* bytes_read) {
+  ssize_t result = HANDLE_EINTR(read(handle.fd, buffer, buffer_size));
+
+  if (result < 0) {
+    if (errno != EAGAIN && errno != EWOULDBLOCK)
+      return false;
+
+    *bytes_read = 0;
+  } else {
+    *bytes_read = result;
+  }
+
+  return true;
+}
+
+embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) {
+  CHECK(fp);
+  int rv = dup(fileno(fp.get()));
+  PCHECK(rv != -1) << "dup";
+  return embedder::ScopedPlatformHandle(embedder::PlatformHandle(rv));
+}
+
+base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h,
+                                        const char* mode) {
+  CHECK(h.is_valid());
+  base::ScopedFILE rv(fdopen(h.release().fd, mode));
+  PCHECK(rv) << "fdopen";
+  return rv.Pass();
+}
+
+base::FilePath GetFilePathForJSResource(const std::string& path) {
+  std::string binding_path = "gen/" + path + ".js";
+  base::FilePath exe_dir;
+  PathService::Get(base::DIR_EXE, &exe_dir);
+  return exe_dir.AppendASCII(binding_path);
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/edk/test/test_utils_win.cc b/mojo/edk/test/test_utils_win.cc
new file mode 100644
index 0000000..42613a6
--- /dev/null
+++ b/mojo/edk/test/test_utils_win.cc
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium 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/edk/test/test_utils.h"
+
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#include <string.h>
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+
+namespace mojo {
+namespace test {
+
+bool BlockingWrite(const embedder::PlatformHandle& handle,
+                   const void* buffer,
+                   size_t bytes_to_write,
+                   size_t* bytes_written) {
+  OVERLAPPED overlapped = { 0 };
+  DWORD bytes_written_dword = 0;
+
+  if (!WriteFile(handle.handle, buffer, static_cast<DWORD>(bytes_to_write),
+                 &bytes_written_dword, &overlapped)) {
+    if (GetLastError() != ERROR_IO_PENDING ||
+        !GetOverlappedResult(handle.handle, &overlapped, &bytes_written_dword,
+                             TRUE)) {
+      return false;
+    }
+  }
+
+  *bytes_written = bytes_written_dword;
+  return true;
+}
+
+bool BlockingRead(const embedder::PlatformHandle& handle,
+                  void* buffer,
+                  size_t buffer_size,
+                  size_t* bytes_read) {
+  OVERLAPPED overlapped = { 0 };
+  DWORD bytes_read_dword = 0;
+
+  if (!ReadFile(handle.handle, buffer, static_cast<DWORD>(buffer_size),
+                &bytes_read_dword, &overlapped)) {
+    if (GetLastError() != ERROR_IO_PENDING ||
+        !GetOverlappedResult(handle.handle, &overlapped, &bytes_read_dword,
+                             TRUE)) {
+      return false;
+    }
+  }
+
+  *bytes_read = bytes_read_dword;
+  return true;
+}
+
+bool NonBlockingRead(const embedder::PlatformHandle& handle,
+                     void* buffer,
+                     size_t buffer_size,
+                     size_t* bytes_read) {
+  OVERLAPPED overlapped = { 0 };
+  DWORD bytes_read_dword = 0;
+
+  if (!ReadFile(handle.handle, buffer, static_cast<DWORD>(buffer_size),
+                &bytes_read_dword, &overlapped)) {
+    if (GetLastError() != ERROR_IO_PENDING)
+      return false;
+
+    CancelIo(handle.handle);
+
+    if (!GetOverlappedResult(handle.handle, &overlapped, &bytes_read_dword,
+                             TRUE)) {
+      *bytes_read = 0;
+      return true;
+    }
+  }
+
+  *bytes_read = bytes_read_dword;
+  return true;
+}
+
+embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) {
+  CHECK(fp);
+
+  HANDLE rv = INVALID_HANDLE_VALUE;
+  PCHECK(DuplicateHandle(
+      GetCurrentProcess(),
+      reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp.get()))),
+      GetCurrentProcess(),
+      &rv,
+      0,
+      TRUE,
+      DUPLICATE_SAME_ACCESS)) << "DuplicateHandle";
+  return embedder::ScopedPlatformHandle(embedder::PlatformHandle(rv));
+}
+
+base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h,
+                                        const char* mode) {
+  CHECK(h.is_valid());
+  // Microsoft's documentation for |_open_osfhandle()| only discusses these
+  // flags (and |_O_WTEXT|). Hmmm.
+  int flags = 0;
+  if (strchr(mode, 'a'))
+    flags |= _O_APPEND;
+  if (strchr(mode, 'r'))
+    flags |= _O_RDONLY;
+  if (strchr(mode, 't'))
+    flags |= _O_TEXT;
+  base::ScopedFILE rv(
+      _fdopen(_open_osfhandle(reinterpret_cast<intptr_t>(h.release().handle),
+                              flags),
+              mode));
+  PCHECK(rv) << "_fdopen";
+  return rv.Pass();
+}
+
+base::FilePath GetFilePathForJSResource(const std::string& path) {
+  std::string binding_path = "gen/" + path + ".js";
+  base::ReplaceChars(binding_path, "//", "\\", &binding_path);
+  base::FilePath exe_dir;
+  PathService::Get(base::DIR_EXE, &exe_dir);
+  return exe_dir.AppendASCII(binding_path);
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/environment/BUILD.gn b/mojo/environment/BUILD.gn
new file mode 100644
index 0000000..c860250
--- /dev/null
+++ b/mojo/environment/BUILD.gn
@@ -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.
+
+# GYP version: mojo_base.gyp:mojo_environment_chromium
+source_set("chromium") {
+  output_name = "mojo_environment_chromium"
+
+  sources = [
+    "environment.cc",
+    # TODO(vtl): This is kind of ugly. (See TODO in logging.h.)
+    "../public/cpp/environment/logging.h",
+    "../public/cpp/environment/lib/logging.cc",
+  ]
+
+  public_deps = [
+    ":chromium_impl",
+  ]
+}
+
+# GYP version: mojo_base.gyp:mojo_environment_chromium_impl
+component("chromium_impl") {
+  output_name = "mojo_environment_impl"
+  visibility = [ "//mojo/*" ]
+
+  sources = [
+    "default_async_waiter_impl.cc",
+    "default_async_waiter_impl.h",
+    "default_logger_impl.cc",
+    "default_logger_impl.h",
+  ]
+
+  defines = [
+    "MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//mojo/common",
+  ]
+}
diff --git a/mojo/environment/default_async_waiter_impl.cc b/mojo/environment/default_async_waiter_impl.cc
new file mode 100644
index 0000000..ff7a524
--- /dev/null
+++ b/mojo/environment/default_async_waiter_impl.cc
@@ -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.
+
+#include "mojo/environment/default_async_waiter_impl.h"
+
+#include "base/bind.h"
+#include "mojo/common/handle_watcher.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+void OnHandleReady(common::HandleWatcher* watcher,
+                   MojoAsyncWaitCallback callback,
+                   void* closure,
+                   MojoResult result) {
+  delete watcher;
+  callback(closure, result);
+}
+
+MojoAsyncWaitID AsyncWait(MojoHandle handle,
+                          MojoHandleSignals signals,
+                          MojoDeadline deadline,
+                          MojoAsyncWaitCallback callback,
+                          void* closure) {
+  // This instance will be deleted when done or cancelled.
+  common::HandleWatcher* watcher = new common::HandleWatcher();
+  watcher->Start(Handle(handle), signals, deadline,
+                 base::Bind(&OnHandleReady, watcher, callback, closure));
+  return reinterpret_cast<MojoAsyncWaitID>(watcher);
+}
+
+void CancelWait(MojoAsyncWaitID wait_id) {
+  delete reinterpret_cast<common::HandleWatcher*>(wait_id);
+}
+
+const MojoAsyncWaiter kDefaultAsyncWaiter = {
+  AsyncWait,
+  CancelWait
+};
+
+}  // namespace
+
+const MojoAsyncWaiter* GetDefaultAsyncWaiterImpl() {
+  return &kDefaultAsyncWaiter;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/environment/default_async_waiter_impl.h b/mojo/environment/default_async_waiter_impl.h
new file mode 100644
index 0000000..5140e35
--- /dev/null
+++ b/mojo/environment/default_async_waiter_impl.h
@@ -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.
+
+#ifndef MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
+#define MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
+
+#include "mojo/environment/mojo_environment_impl_export.h"
+#include "mojo/public/c/environment/async_waiter.h"
+
+namespace mojo {
+namespace internal {
+
+MOJO_ENVIRONMENT_IMPL_EXPORT const MojoAsyncWaiter* GetDefaultAsyncWaiterImpl();
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
diff --git a/mojo/environment/default_logger_impl.cc b/mojo/environment/default_logger_impl.cc
new file mode 100644
index 0000000..575feb3
--- /dev/null
+++ b/mojo/environment/default_logger_impl.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium 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/environment/default_logger_impl.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+// We rely on log levels being the same numerically:
+COMPILE_ASSERT(logging::LOG_VERBOSE == MOJO_LOG_LEVEL_VERBOSE,
+               verbose_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_INFO == MOJO_LOG_LEVEL_INFO,
+               info_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_WARNING == MOJO_LOG_LEVEL_WARNING,
+               warning_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_ERROR == MOJO_LOG_LEVEL_ERROR,
+               error_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_FATAL == MOJO_LOG_LEVEL_FATAL,
+               fatal_log_level_value_mismatch);
+
+int MojoToChromiumLogLevel(MojoLogLevel log_level) {
+  // See the compile asserts above.
+  return static_cast<int>(log_level);
+}
+
+MojoLogLevel ChromiumToMojoLogLevel(int chromium_log_level) {
+  // See the compile asserts above.
+  return static_cast<MojoLogLevel>(chromium_log_level);
+}
+
+void LogMessage(MojoLogLevel log_level, const char* message) {
+  int chromium_log_level = MojoToChromiumLogLevel(log_level);
+  int chromium_min_log_level = logging::GetMinLogLevel();
+  // "Fatal" errors aren't suppressable.
+  DCHECK_LE(chromium_min_log_level, logging::LOG_FATAL);
+  if (chromium_log_level < chromium_min_log_level)
+    return;
+
+  // TODO(vtl): Possibly, we should try to pull out the file and line number
+  // from |message|.
+  logging::LogMessage(__FILE__, __LINE__, chromium_log_level).stream()
+      << message;
+}
+
+MojoLogLevel GetMinimumLogLevel() {
+  return ChromiumToMojoLogLevel(logging::GetMinLogLevel());
+}
+
+void SetMinimumLogLevel(MojoLogLevel log_level) {
+  logging::SetMinLogLevel(MojoToChromiumLogLevel(log_level));
+}
+
+const MojoLogger kDefaultLogger = {
+  LogMessage,
+  GetMinimumLogLevel,
+  SetMinimumLogLevel
+};
+
+}  // namespace
+
+const MojoLogger* GetDefaultLoggerImpl() {
+  return &kDefaultLogger;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/environment/default_logger_impl.h b/mojo/environment/default_logger_impl.h
new file mode 100644
index 0000000..c87a727
--- /dev/null
+++ b/mojo/environment/default_logger_impl.h
@@ -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.
+
+#ifndef MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
+#define MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
+
+#include "mojo/environment/mojo_environment_impl_export.h"
+#include "mojo/public/c/environment/logger.h"
+
+namespace mojo {
+namespace internal {
+
+MOJO_ENVIRONMENT_IMPL_EXPORT const MojoLogger* GetDefaultLoggerImpl();
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
diff --git a/mojo/environment/environment.cc b/mojo/environment/environment.cc
new file mode 100644
index 0000000..f861113
--- /dev/null
+++ b/mojo/environment/environment.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/public/cpp/environment/environment.h"
+
+#include "mojo/environment/default_async_waiter_impl.h"
+#include "mojo/environment/default_logger_impl.h"
+
+namespace mojo {
+
+// These methods are intentionally not implemented so that there is a link
+// error if someone uses them in a Chromium-environment.
+#if 0
+Environment::Environment() {
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+                         const MojoLogger* default_logger) {
+}
+
+Environment::~Environment() {
+}
+#endif
+
+// static
+const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() {
+  return internal::GetDefaultAsyncWaiterImpl();
+}
+
+// static
+const MojoLogger* Environment::GetDefaultLogger() {
+  return internal::GetDefaultLoggerImpl();
+}
+
+}  // namespace mojo
diff --git a/mojo/environment/mojo_environment_impl_export.h b/mojo/environment/mojo_environment_impl_export.h
new file mode 100644
index 0000000..0255425
--- /dev/null
+++ b/mojo/environment/mojo_environment_impl_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_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
+#define MOJO_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_ENVIRONMENT_IMPL_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT
+#endif
+
+#endif  // MOJO_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
diff --git a/mojo/examples/BUILD.gn b/mojo/examples/BUILD.gn
new file mode 100644
index 0000000..bb42f4d
--- /dev/null
+++ b/mojo/examples/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("examples") {
+  testonly = true
+
+  deps = [
+    "//mojo/examples/apptest",
+    "//mojo/examples/compositor_app",
+    "//mojo/examples/content_handler_demo",
+    "//mojo/examples/echo",
+    "//mojo/examples/pepper_container_app",
+    "//mojo/examples/png_viewer",
+    "//mojo/examples/sample_app",
+    "//mojo/examples/surfaces_app",
+    "//mojo/examples/wget",
+  ]
+
+  if (use_aura) {
+    deps += [
+      "//mojo/examples/aura_demo:mojo_aura_demo",
+      "//mojo/examples/browser",
+      "//mojo/examples/demo_launcher",
+      "//mojo/examples/embedded_app",
+      "//mojo/examples/media_viewer",
+      "//mojo/examples/nesting_app",
+      "//mojo/examples/keyboard",
+      "//mojo/examples/window_manager",
+      "//mojo/examples/wm_flow",
+    ]
+  }
+}
diff --git a/mojo/examples/apptest/BUILD.gn b/mojo/examples/apptest/BUILD.gn
new file mode 100644
index 0000000..456f0d4
--- /dev/null
+++ b/mojo/examples/apptest/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")
+
+group("apptest") {
+  testonly = true
+
+  deps = [
+    ":apptests",
+    ":service",
+  ]
+}
+
+# GYP version mojo/mojo_examples.gypi:mojo_example_service
+shared_library("service") {
+  output_name = "mojo_example_service"
+
+  sources = [
+    "example_service_application.cc",
+    "example_service_application.h",
+    "example_service_impl.cc",
+    "example_service_impl.h",
+  ]
+
+  deps = [
+    ":bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/utility"
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_example_apptests
+shared_library("apptests") {
+  output_name = "mojo_example_apptests"
+
+  testonly = true
+
+  sources = [
+    "example_apptest.cc",
+    "example_client_application.cc",
+    "example_client_application.h",
+    "example_client_impl.cc",
+    "example_client_impl.h",
+  ]
+
+   deps = [
+    ":bindings",
+    "//testing/gtest",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/utility",
+   ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_example_service_bindings
+mojom("bindings") {
+  sources = [ "example_service.mojom" ]
+}
diff --git a/mojo/examples/apptest/example_apptest.cc b/mojo/examples/apptest/example_apptest.cc
new file mode 100644
index 0000000..ca3fda8
--- /dev/null
+++ b/mojo/examples/apptest/example_apptest.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/examples/apptest/example_client_application.h"
+#include "mojo/examples/apptest/example_client_impl.h"
+#include "mojo/examples/apptest/example_service.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// TODO(msw): Remove this once we can get ApplicationImpl from TLS.
+mojo::ApplicationImpl* g_application_impl_hack = NULL;
+
+}  // namespace
+
+namespace mojo {
+
+namespace {
+
+class ExampleServiceTest : public testing::Test {
+ public:
+  ExampleServiceTest() {
+    g_application_impl_hack->ConnectToService("mojo:mojo_example_service",
+                                              &example_service_);
+    example_service_.set_client(&example_client_);
+  }
+
+  virtual ~ExampleServiceTest() override {}
+
+ protected:
+  ExampleServicePtr example_service_;
+  ExampleClientImpl example_client_;
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceTest);
+};
+
+TEST_F(ExampleServiceTest, Ping) {
+  EXPECT_EQ(0, example_client_.last_pong_value());
+  example_service_->Ping(1);
+  RunLoop::current()->Run();
+  EXPECT_EQ(1, example_client_.last_pong_value());
+}
+
+template <typename T>
+struct SetAndQuit : public Callback<void()>::Runnable {
+  SetAndQuit(T* val, T result) : val_(val), result_(result) {}
+  virtual ~SetAndQuit() {}
+  virtual void Run() const override {
+    *val_ = result_;
+    RunLoop::current()->Quit();
+  }
+  T* val_;
+  T result_;
+};
+
+TEST_F(ExampleServiceTest, RunCallback) {
+  bool was_run = false;
+  example_service_->RunCallback(SetAndQuit<bool>(&was_run, true));
+  RunLoop::current()->Run();
+  EXPECT_TRUE(was_run);
+}
+
+}  // namespace
+
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::Environment env;
+  mojo::RunLoop loop;
+
+  // TODO(tim): Perhaps the delegate should be the thing that provides
+  // the ExampleServiceTest with the ApplicationImpl somehow.
+  mojo::ApplicationDelegate* delegate = new mojo::ExampleClientApplication();
+  mojo::ApplicationImpl app(delegate, shell_handle);
+  g_application_impl_hack = &app;
+
+  // TODO(msw): Get actual commandline arguments.
+  int argc = 0;
+  char** argv = NULL;
+  testing::InitGoogleTest(&argc, argv);
+  mojo_ignore_result(RUN_ALL_TESTS());
+
+  delete delegate;
+  return MOJO_RESULT_OK;
+}
diff --git a/mojo/examples/apptest/example_client_application.cc b/mojo/examples/apptest/example_client_application.cc
new file mode 100644
index 0000000..8ba5739
--- /dev/null
+++ b/mojo/examples/apptest/example_client_application.cc
@@ -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.
+
+#include "mojo/examples/apptest/example_client_application.h"
+
+#include "mojo/examples/apptest/example_client_impl.h"
+
+namespace mojo {
+
+ExampleClientApplication::ExampleClientApplication() {}
+
+ExampleClientApplication::~ExampleClientApplication() {}
+
+void ExampleClientApplication::Initialize(ApplicationImpl* app) {}
+
+}  // namespace mojo
diff --git a/mojo/examples/apptest/example_client_application.h b/mojo/examples/apptest/example_client_application.h
new file mode 100644
index 0000000..c54d2da
--- /dev/null
+++ b/mojo/examples/apptest/example_client_application.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_EXAMPLES_TEST_EXAMPLE_CLIENT_APPLICATION_H_
+#define MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_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 ExampleClientApplication : public ApplicationDelegate {
+ public:
+  ExampleClientApplication();
+  virtual ~ExampleClientApplication();
+
+ private:
+  // ApplicationDelegate implementation.
+  virtual void Initialize(ApplicationImpl* app) override;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleClientApplication);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_APPLICATION_H_
diff --git a/mojo/examples/apptest/example_client_impl.cc b/mojo/examples/apptest/example_client_impl.cc
new file mode 100644
index 0000000..9d539be
--- /dev/null
+++ b/mojo/examples/apptest/example_client_impl.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/examples/apptest/example_client_impl.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+ExampleClientImpl::ExampleClientImpl() : last_pong_value_(0) {}
+ExampleClientImpl::~ExampleClientImpl() {}
+
+void ExampleClientImpl::Pong(uint16_t pong_value) {
+  last_pong_value_ = pong_value;
+  RunLoop::current()->Quit();
+}
+
+}  // namespace mojo
diff --git a/mojo/examples/apptest/example_client_impl.h b/mojo/examples/apptest/example_client_impl.h
new file mode 100644
index 0000000..25d817a
--- /dev/null
+++ b/mojo/examples/apptest/example_client_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_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_
+#define MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_
+
+#include "mojo/examples/apptest/example_service.mojom.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+class ExampleClientImpl : public InterfaceImpl<ExampleClient> {
+ public:
+  explicit ExampleClientImpl();
+  virtual ~ExampleClientImpl();
+
+  int16_t last_pong_value() const { return last_pong_value_; }
+
+ private:
+  // InterfaceImpl<ExampleClient> overrides.
+  virtual void Pong(uint16_t pong_value) override;
+
+  int16_t last_pong_value_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleClientImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_
diff --git a/mojo/examples/apptest/example_service.mojom b/mojo/examples/apptest/example_service.mojom
new file mode 100644
index 0000000..a2226fa
--- /dev/null
+++ b/mojo/examples/apptest/example_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 {
+
+// A simple service used to exemplify application testing within mojo_shell.
+[Client=ExampleClient]
+interface ExampleService {
+  // Calls ExampleClient::Pong with |ping_value|.
+  Ping(uint16 ping_value);
+
+  // Runs the argument callback.
+  RunCallback() => ();
+};
+
+interface ExampleClient {
+  Pong(uint16 pong_value);
+};
+
+}
diff --git a/mojo/examples/apptest/example_service_application.cc b/mojo/examples/apptest/example_service_application.cc
new file mode 100644
index 0000000..a62a510
--- /dev/null
+++ b/mojo/examples/apptest/example_service_application.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/examples/apptest/example_service_application.h"
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+
+namespace mojo {
+
+ExampleServiceApplication::ExampleServiceApplication() {}
+
+ExampleServiceApplication::~ExampleServiceApplication() {}
+
+bool ExampleServiceApplication::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(&example_service_factory_);
+  return true;
+}
+
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new mojo::ExampleServiceApplication());
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/apptest/example_service_application.h b/mojo/examples/apptest/example_service_application.h
new file mode 100644
index 0000000..96843cc
--- /dev/null
+++ b/mojo/examples/apptest/example_service_application.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_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_
+#define MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_
+
+#include "mojo/examples/apptest/example_service_impl.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"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+class ExampleServiceApplication : public ApplicationDelegate {
+ public:
+  ExampleServiceApplication();
+  virtual ~ExampleServiceApplication();
+
+ private:
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+  InterfaceFactoryImpl<ExampleServiceImpl> example_service_factory_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceApplication);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_
diff --git a/mojo/examples/apptest/example_service_impl.cc b/mojo/examples/apptest/example_service_impl.cc
new file mode 100644
index 0000000..b656ce0
--- /dev/null
+++ b/mojo/examples/apptest/example_service_impl.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/examples/apptest/example_service_impl.h"
+
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+ExampleServiceImpl::ExampleServiceImpl() {}
+
+ExampleServiceImpl::~ExampleServiceImpl() {}
+
+void ExampleServiceImpl::Ping(uint16_t ping_value) {
+  client()->Pong(ping_value);
+  RunLoop::current()->Quit();
+}
+
+void ExampleServiceImpl::RunCallback(const Callback<void()>& callback) {
+  callback.Run();
+  RunLoop::current()->Quit();
+}
+
+}  // namespace mojo
diff --git a/mojo/examples/apptest/example_service_impl.h b/mojo/examples/apptest/example_service_impl.h
new file mode 100644
index 0000000..18537fa
--- /dev/null
+++ b/mojo/examples/apptest/example_service_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_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_
+#define MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_
+
+#include "mojo/examples/apptest/example_service.mojom.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+class ExampleServiceImpl : public InterfaceImpl<ExampleService> {
+ public:
+  ExampleServiceImpl();
+  virtual ~ExampleServiceImpl();
+
+ private:
+  // InterfaceImpl<ExampleService> overrides.
+  virtual void Ping(uint16_t ping_value) override;
+  virtual void RunCallback(const Callback<void()>& callback) override;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_
diff --git a/mojo/examples/aura_demo/BUILD.gn b/mojo/examples/aura_demo/BUILD.gn
new file mode 100644
index 0000000..ba2fc37
--- /dev/null
+++ b/mojo/examples/aura_demo/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("//build/config/ui.gni")
+
+assert(use_aura)
+
+# GYP version mojo/mojo_examples.gypi:mojo_aura_demo
+shared_library("mojo_aura_demo") {
+  output_name = "mojo_aura_demo"
+
+  sources = [
+    "aura_demo.cc"
+  ]
+
+  deps = [
+    "//base",
+    "//cc",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/compositor",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/aura",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/view_manager",
+  ]
+
+  datadeps = [ ":init" ]
+}
+
+# GYP version mojo/mojo_examples.gypi:mojo_aura_demo_init
+shared_library("init") {
+  output_name = "mojo_aura_demo_init"
+
+  sources = [
+    "view_manager_init.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/interfaces/view_manager",
+  ]
+}
diff --git a/mojo/examples/aura_demo/DEPS b/mojo/examples/aura_demo/DEPS
new file mode 100644
index 0000000..eb8dd8f
--- /dev/null
+++ b/mojo/examples/aura_demo/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+skia/ext",
+  "+ui/aura",
+  "+ui/base/hit_test.h",
+  "+ui/compositor",
+  "+ui/events",
+  "+ui/gfx",
+]
diff --git a/mojo/examples/aura_demo/aura_demo.cc b/mojo/examples/aura_demo/aura_demo.cc
new file mode 100644
index 0000000..1895761
--- /dev/null
+++ b/mojo/examples/aura_demo/aura_demo.cc
@@ -0,0 +1,209 @@
+// 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 <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/aura/context_factory_mojo.h"
+#include "mojo/aura/screen_mojo.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/aura/window_tree_host_mojo_delegate.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/system/core.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/native_viewport/native_viewport.mojom.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace examples {
+
+// Trivial WindowDelegate implementation that draws a colored background.
+class DemoWindowDelegate : public aura::WindowDelegate {
+ public:
+  explicit DemoWindowDelegate(SkColor color) : color_(color) {}
+
+  // Overridden from WindowDelegate:
+  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 {
+    canvas->DrawColor(color_, SkXfermode::kSrc_Mode);
+  }
+  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 {}
+
+ private:
+  const SkColor color_;
+
+  DISALLOW_COPY_AND_ASSIGN(DemoWindowDelegate);
+};
+
+class DemoWindowTreeClient : public aura::client::WindowTreeClient {
+ public:
+  explicit DemoWindowTreeClient(aura::Window* window) : window_(window) {
+    aura::client::SetWindowTreeClient(window_, this);
+  }
+
+  virtual ~DemoWindowTreeClient() {
+    aura::client::SetWindowTreeClient(window_, NULL);
+  }
+
+  // Overridden from aura::client::WindowTreeClient:
+  virtual aura::Window* GetDefaultParent(aura::Window* context,
+                                         aura::Window* window,
+                                         const gfx::Rect& bounds) override {
+    if (!capture_client_) {
+      capture_client_.reset(
+          new aura::client::DefaultCaptureClient(window_->GetRootWindow()));
+    }
+    return window_;
+  }
+
+ private:
+  aura::Window* window_;
+  scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(DemoWindowTreeClient);
+};
+
+class AuraDemo : public mojo::ApplicationDelegate,
+                 public mojo::WindowTreeHostMojoDelegate,
+                 public mojo::ViewManagerDelegate {
+ public:
+  AuraDemo() : window1_(NULL), window2_(NULL), window21_(NULL) {}
+  virtual ~AuraDemo() {}
+
+ private:
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(
+      mojo::ViewManager* view_manager,
+      mojo::View* root,
+      mojo::ServiceProviderImpl* exported_services,
+      scoped_ptr<mojo::ServiceProvider> imported_services) override {
+    // TODO(beng): this function could be called multiple times!
+    root_ = root;
+
+    window_tree_host_.reset(new mojo::WindowTreeHostMojo(root, this));
+    window_tree_host_->InitHost();
+
+    window_tree_client_.reset(
+        new DemoWindowTreeClient(window_tree_host_->window()));
+
+    delegate1_.reset(new DemoWindowDelegate(SK_ColorBLUE));
+    window1_ = new aura::Window(delegate1_.get());
+    window1_->Init(aura::WINDOW_LAYER_TEXTURED);
+    window1_->SetBounds(gfx::Rect(100, 100, 400, 400));
+    window1_->Show();
+    window_tree_host_->window()->AddChild(window1_);
+
+    delegate2_.reset(new DemoWindowDelegate(SK_ColorRED));
+    window2_ = new aura::Window(delegate2_.get());
+    window2_->Init(aura::WINDOW_LAYER_TEXTURED);
+    window2_->SetBounds(gfx::Rect(200, 200, 350, 350));
+    window2_->Show();
+    window_tree_host_->window()->AddChild(window2_);
+
+    delegate21_.reset(new DemoWindowDelegate(SK_ColorGREEN));
+    window21_ = new aura::Window(delegate21_.get());
+    window21_->Init(aura::WINDOW_LAYER_TEXTURED);
+    window21_->SetBounds(gfx::Rect(10, 10, 50, 50));
+    window21_->Show();
+    window2_->AddChild(window21_);
+
+    window_tree_host_->Show();
+  }
+  virtual void OnViewManagerDisconnected(
+      mojo::ViewManager* view_manager) override {
+    base::MessageLoop::current()->Quit();
+  }
+
+  // WindowTreeHostMojoDelegate:
+  virtual void CompositorContentsChanged(const SkBitmap& bitmap) override {
+    root_->SetContents(bitmap);
+  }
+
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new mojo::ViewManagerClientFactory(app->shell(), this));
+    aura::Env::CreateInstance(true);
+    context_factory_.reset(new mojo::ContextFactoryMojo);
+    aura::Env::GetInstance()->set_context_factory(context_factory_.get());
+    screen_.reset(mojo::ScreenMojo::Create());
+    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+  }
+
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  scoped_ptr<DemoWindowTreeClient> window_tree_client_;
+
+  scoped_ptr<ui::ContextFactory> context_factory_;
+
+  scoped_ptr<mojo::ScreenMojo> screen_;
+
+  scoped_ptr<DemoWindowDelegate> delegate1_;
+  scoped_ptr<DemoWindowDelegate> delegate2_;
+  scoped_ptr<DemoWindowDelegate> delegate21_;
+
+  aura::Window* window1_;
+  aura::Window* window2_;
+  aura::Window* window21_;
+
+  mojo::View* root_;
+
+  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+
+  scoped_ptr<aura::WindowTreeHost> window_tree_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuraDemo);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::AuraDemo);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/aura_demo/view_manager_init.cc b/mojo/examples/aura_demo/view_manager_init.cc
new file mode 100644
index 0000000..bd07e6d
--- /dev/null
+++ b/mojo/examples/aura_demo/view_manager_init.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 "base/basictypes.h"
+#include "base/bind.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/application_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_context.h"
+
+namespace examples {
+
+// ViewManagerInit is responsible for establishing the initial connection to
+// the view manager. When established it loads |mojo_aura_demo|.
+class ViewManagerInit : public mojo::ApplicationDelegate {
+ public:
+  ViewManagerInit() {}
+  virtual ~ViewManagerInit() {}
+
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    context_.reset(new mojo::ViewManagerContext(app));
+    context_->Embed("mojo:mojo_aura_demo");
+  }
+
+ private:
+  scoped_ptr<mojo::ViewManagerContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerInit);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::ViewManagerInit);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/browser/BUILD.gn b/mojo/examples/browser/BUILD.gn
new file mode 100644
index 0000000..2a62913
--- /dev/null
+++ b/mojo/examples/browser/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+assert(use_aura)
+
+# GYP version: mojo/mojo_examples.gypi:mojo_browser
+shared_library("browser") {
+  output_name = "mojo_browser"
+
+  sources = [
+    "browser.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//cc",
+    "//mojo/application",
+    "//mojo/aura",
+    "//mojo/common",
+    "//mojo/examples/window_manager:bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/views",
+    "//third_party/icu",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/compositor",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/resources",
+    "//ui/views",
+    "//url",
+  ]
+}
diff --git a/mojo/examples/browser/DEPS b/mojo/examples/browser/DEPS
new file mode 100644
index 0000000..02dd6ea
--- /dev/null
+++ b/mojo/examples/browser/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+skia/ext",
+  "+ui/aura",
+  "+ui/base/hit_test.h",
+  "+ui/compositor",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/views",
+]
diff --git a/mojo/examples/browser/browser.cc b/mojo/examples/browser/browser.cc
new file mode 100644
index 0000000..2a77f89
--- /dev/null
+++ b/mojo/examples/browser/browser.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium 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/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/examples/window_manager/window_manager.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.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/navigation/navigation.mojom.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "mojo/views/views_init.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/window.h"
+#include "ui/events/event.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/widget/widget_observer.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace examples {
+
+class BrowserLayoutManager : public views::LayoutManager {
+ public:
+  BrowserLayoutManager() {}
+  virtual ~BrowserLayoutManager() {}
+
+ private:
+  // Overridden from views::LayoutManager:
+  virtual void Layout(views::View* host) override {
+    // Browser view has one child, a text input field.
+    DCHECK_EQ(1, host->child_count());
+    views::View* text_field = host->child_at(0);
+    gfx::Size ps = text_field->GetPreferredSize();
+    text_field->SetBoundsRect(gfx::Rect(host->width(), ps.height()));
+  }
+  virtual gfx::Size GetPreferredSize(const views::View* host) const override {
+    return gfx::Size();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserLayoutManager);
+};
+
+// KeyboardManager handles notifying the windowmanager when views are focused.
+// To use create one and KeyboardManager will take care of all other details.
+//
+// TODO(sky): it would be nice if this were put in NativeWidgetViewManager, but
+// that requires NativeWidgetViewManager to take an IWindowManager. That may be
+// desirable anyway...
+class KeyboardManager
+    : public views::FocusChangeListener,
+      public ui::EventHandler,
+      public views::WidgetObserver {
+ public:
+  KeyboardManager(views::Widget* widget,
+                  IWindowManager* window_manager,
+                  View* view)
+      : widget_(widget),
+        window_manager_(window_manager),
+        view_(view),
+        last_view_id_(0),
+        focused_view_(NULL) {
+    widget_->GetFocusManager()->AddFocusChangeListener(this);
+    widget_->AddObserver(this);
+    widget_->GetNativeView()->AddPostTargetHandler(this);
+  }
+
+ private:
+  virtual ~KeyboardManager() {
+    widget_->GetFocusManager()->RemoveFocusChangeListener(this);
+    widget_->GetNativeView()->RemovePostTargetHandler(this);
+    widget_->RemoveObserver(this);
+
+    HideKeyboard();
+  }
+
+  void ShowKeyboard(views::View* view) {
+    if (focused_view_ == view)
+      return;
+
+    const gfx::Rect bounds_in_widget =
+        view->ConvertRectToWidget(gfx::Rect(view->bounds().size()));
+    last_view_id_ = view_->id();
+    window_manager_->ShowKeyboard(last_view_id_,
+                                  Rect::From(bounds_in_widget));
+    // TODO(sky): listen for view to be removed.
+    focused_view_ = view;
+  }
+
+  void HideKeyboard() {
+    if (!focused_view_)
+      return;
+
+    window_manager_->HideKeyboard(last_view_id_);
+    last_view_id_ = 0;
+    focused_view_ = NULL;
+  }
+
+  // views::FocusChangeListener:
+  virtual void OnWillChangeFocus(views::View* focused_before,
+                                 views::View* focused_now) override {
+  }
+  virtual void OnDidChangeFocus(views::View* focused_before,
+                                views::View* focused_now) override {
+    if (focused_view_ && focused_now != focused_view_)
+      HideKeyboard();
+  }
+
+  // ui::EventHandler:
+  virtual void OnMouseEvent(ui::MouseEvent* event) override {
+    views::View* focused_now = widget_->GetFocusManager()->GetFocusedView();
+    if (focused_now &&
+        focused_now->GetClassName() == views::Textfield::kViewClassName &&
+        (event->flags() & ui::EF_FROM_TOUCH) != 0) {
+      ShowKeyboard(focused_now);
+    }
+  }
+
+  // views::WidgetObserver:
+  virtual void OnWidgetDestroying(views::Widget* widget) override {
+    delete this;
+  }
+
+  views::Widget* widget_;
+  IWindowManager* window_manager_;
+  View* view_;
+  Id last_view_id_;
+  views::View* focused_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
+};
+
+// This is the basics of creating a views widget with a textfield.
+// TODO: cleanup!
+class Browser : public ApplicationDelegate,
+                public ViewManagerDelegate,
+                public views::TextfieldController,
+                public ViewObserver {
+ public:
+  Browser()
+      : view_manager_(NULL),
+        root_(NULL),
+        widget_(NULL) {}
+
+  virtual ~Browser() {
+    if (root_)
+      root_->RemoveObserver(this);
+  }
+
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+    views_init_.reset(new ViewsInit);
+    app->ConnectToService("mojo:mojo_window_manager", &window_manager_);
+ }
+
+ virtual bool ConfigureIncomingConnection(
+     ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  void CreateWidget(View* view) {
+    views::Textfield* textfield = new views::Textfield;
+    textfield->set_controller(this);
+
+    views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
+    widget_delegate->GetContentsView()->set_background(
+        views::Background::CreateSolidBackground(SK_ColorBLUE));
+    widget_delegate->GetContentsView()->AddChildView(textfield);
+    widget_delegate->GetContentsView()->SetLayoutManager(
+        new BrowserLayoutManager);
+
+    widget_ = new views::Widget;
+    views::Widget::InitParams params(
+        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.native_widget = new NativeWidgetViewManager(widget_, view);
+    params.delegate = widget_delegate;
+    params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height());
+    widget_->Init(params);
+    // KeyboardManager handles deleting itself when the widget is destroyed.
+    new KeyboardManager(widget_, window_manager_.get(), view);
+    widget_->Show();
+    textfield->RequestFocus();
+  }
+
+  // ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    // TODO: deal with OnEmbed() being invoked multiple times.
+    ConnectToService(imported_services.get(), &navigator_host_);
+    view_manager_ = view_manager;
+    root_ = root;
+    root_->AddObserver(this);
+    root_->SetFocus();
+    CreateWidget(root_);
+  }
+  virtual void OnViewManagerDisconnected(
+      ViewManager* view_manager) override {
+    DCHECK_EQ(view_manager_, view_manager);
+    view_manager_ = NULL;
+    base::MessageLoop::current()->Quit();
+  }
+
+  // views::TextfieldController:
+  virtual bool HandleKeyEvent(views::Textfield* sender,
+                              const ui::KeyEvent& key_event) override {
+    if (key_event.key_code() == ui::VKEY_RETURN) {
+      GURL url(sender->text());
+      printf("User entered this URL: %s\n", url.spec().c_str());
+      URLRequestPtr request(URLRequest::New());
+      request->url = String::From(url);
+      navigator_host_->RequestNavigate(TARGET_NEW_NODE, request.Pass());
+    }
+    return false;
+  }
+
+  // ViewObserver:
+  virtual void OnViewFocusChanged(View* gained_focus,
+                                  View* lost_focus) override {
+    aura::client::FocusClient* focus_client =
+        aura::client::GetFocusClient(widget_->GetNativeView());
+    if (lost_focus == root_)
+      focus_client->FocusWindow(NULL);
+    else if (gained_focus == root_)
+      focus_client->FocusWindow(widget_->GetNativeView());
+  }
+  virtual void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(root_, view);
+    view->RemoveObserver(this);
+    root_ = NULL;
+  }
+
+  scoped_ptr<ViewsInit> views_init_;
+
+  ViewManager* view_manager_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+  View* root_;
+  views::Widget* widget_;
+  NavigatorHostPtr navigator_host_;
+  IWindowManagerPtr window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(Browser);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::Browser);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/compositor_app/BUILD.gn b/mojo/examples/compositor_app/BUILD.gn
new file mode 100644
index 0000000..8ef8ad7
--- /dev/null
+++ b/mojo/examples/compositor_app/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.
+
+# GYP version mojo/mojo_examples.gypi:mojo_compositor_app
+shared_library("compositor_app") {
+  output_name = "mojo_compositor_app"
+
+  sources = [
+    "compositor_app.cc",
+    "compositor_host.cc",
+    "compositor_host.h",
+  ]
+
+  deps = [
+    "//base",
+    "//cc",
+    "//mojo/application",
+    "//mojo/cc",
+    "//mojo/common",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/gpu",
+    "//mojo/services/public/interfaces/native_viewport",
+  ]
+}
diff --git a/mojo/examples/compositor_app/DEPS b/mojo/examples/compositor_app/DEPS
new file mode 100644
index 0000000..70688d6
--- /dev/null
+++ b/mojo/examples/compositor_app/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+ui/gfx",
+  "+gpu/command_buffer/client",
+]
diff --git a/mojo/examples/compositor_app/compositor_app.cc b/mojo/examples/compositor_app/compositor_app.cc
new file mode 100644
index 0000000..279fe75
--- /dev/null
+++ b/mojo/examples/compositor_app/compositor_app.cc
@@ -0,0 +1,78 @@
+// 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 <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/compositor_app/compositor_host.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace examples {
+
+class SampleApp : public ApplicationDelegate, public NativeViewportClient {
+ public:
+  SampleApp() : weak_factory_(this) {}
+  virtual ~SampleApp() {}
+
+  virtual void Initialize(ApplicationImpl* app) override {
+    app->ConnectToService("mojo:mojo_native_viewport_service", &viewport_);
+    viewport_.set_client(this);
+    viewport_->Create(Size::From(gfx::Size(800, 600)),
+                      base::Bind(&SampleApp::OnCreatedNativeViewport,
+                                 weak_factory_.GetWeakPtr()));
+    viewport_->Show();
+
+    // TODO(jamesr): Should be mojo:mojo_gpu_service
+    app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+  }
+
+  virtual void OnDestroyed() override { base::MessageLoop::current()->Quit(); }
+
+  virtual void OnSizeChanged(SizePtr size) override {
+    if (host_)
+      host_->SetSize(size.To<gfx::Size>());
+  }
+
+  virtual void OnEvent(EventPtr event,
+                       const mojo::Callback<void()>& callback) override {
+    callback.Run();
+  }
+
+ private:
+  void OnCreatedNativeViewport(uint64_t native_viewport_id) {
+    CommandBufferPtr cb;
+    // TODO(jamesr): Output to a surface instead.
+    gpu_service_->CreateOnscreenGLES2Context(
+        native_viewport_id, Size::From(gfx::Size(800, 600)), Get(&cb));
+    host_.reset(new CompositorHost(cb.PassMessagePipe()));
+  }
+
+
+  NativeViewportPtr viewport_;
+  GpuPtr gpu_service_;
+  scoped_ptr<CompositorHost> host_;
+  base::WeakPtrFactory<SampleApp> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SampleApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::SampleApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/compositor_app/compositor_host.cc b/mojo/examples/compositor_app/compositor_host.cc
new file mode 100644
index 0000000..0cd6fb9
--- /dev/null
+++ b/mojo/examples/compositor_app/compositor_host.cc
@@ -0,0 +1,97 @@
+// 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/examples/compositor_app/compositor_host.h"
+
+#include "cc/layers/layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/output_surface.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/cc/context_provider_mojo.h"
+
+namespace mojo {
+namespace examples {
+
+CompositorHost::CompositorHost(ScopedMessagePipeHandle command_buffer_handle)
+    : command_buffer_handle_(command_buffer_handle.Pass()),
+      compositor_thread_("compositor") {
+  DCHECK(command_buffer_handle_.is_valid());
+  bool started = compositor_thread_.Start();
+  DCHECK(started);
+
+  cc::LayerTreeSettings settings;
+  tree_ = cc::LayerTreeHost::CreateThreaded(
+      this,
+      NULL,
+      settings,
+      base::MessageLoopProxy::current(),
+      compositor_thread_.message_loop_proxy());
+  SetupScene();
+}
+
+CompositorHost::~CompositorHost() {}
+
+void CompositorHost::SetSize(const gfx::Size& viewport_size) {
+  tree_->SetViewportSize(viewport_size);
+  tree_->SetLayerTreeHostClientReady();
+}
+
+void CompositorHost::SetupScene() {
+  scoped_refptr<cc::Layer> root_layer = cc::SolidColorLayer::Create();
+  root_layer->SetBounds(gfx::Size(500, 500));
+  root_layer->SetBackgroundColor(SK_ColorBLUE);
+  root_layer->SetIsDrawable(true);
+  tree_->SetRootLayer(root_layer);
+
+  child_layer_ = cc::SolidColorLayer::Create();
+  child_layer_->SetBounds(gfx::Size(100, 100));
+  child_layer_->SetBackgroundColor(SK_ColorGREEN);
+  child_layer_->SetIsDrawable(true);
+  gfx::Transform child_transform;
+  child_transform.Translate(200, 200);
+  child_layer_->SetTransform(child_transform);
+  root_layer->AddChild(child_layer_);
+}
+
+void CompositorHost::WillBeginMainFrame(int frame_id) {}
+void CompositorHost::DidBeginMainFrame() {}
+
+void CompositorHost::BeginMainFrame(const cc::BeginFrameArgs& args) {
+  // TODO(jamesr): Should use cc's animation system.
+  static const double kDegreesPerSecond = 70.0;
+  double time_in_seconds = (args.frame_time - base::TimeTicks()).InSecondsF();
+  double child_rotation_degrees = kDegreesPerSecond * time_in_seconds;
+  gfx::Transform child_transform;
+  child_transform.Translate(200, 200);
+  child_transform.Rotate(child_rotation_degrees);
+  child_layer_->SetTransform(child_transform);
+  tree_->SetNeedsAnimate();
+}
+
+void CompositorHost::Layout() {}
+void CompositorHost::ApplyViewportDeltas(const gfx::Vector2d& inner_delta,
+                                         const gfx::Vector2d& outer_delta,
+                                         float page_scale,
+                                         float top_controls_delta) {}
+void CompositorHost::ApplyViewportDeltas(const gfx::Vector2d& scroll_delta,
+                                         float page_scale,
+                                         float top_controls_delta) {}
+
+void CompositorHost::RequestNewOutputSurface(bool fallback) {
+  tree_->SetOutputSurface(make_scoped_ptr(new cc::OutputSurface(
+      new ContextProviderMojo(command_buffer_handle_.Pass()))));
+}
+
+void CompositorHost::DidInitializeOutputSurface() {
+}
+
+void CompositorHost::WillCommit() {}
+void CompositorHost::DidCommit() {}
+void CompositorHost::DidCommitAndDrawFrame() {}
+void CompositorHost::DidCompleteSwapBuffers() {}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/compositor_app/compositor_host.h b/mojo/examples/compositor_app/compositor_host.h
new file mode 100644
index 0000000..3725212
--- /dev/null
+++ b/mojo/examples/compositor_app/compositor_host.h
@@ -0,0 +1,61 @@
+// 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_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
+#define MOJO_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "cc/trees/layer_tree_host_client.h"
+#include "mojo/public/cpp/system/core.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class Layer;
+class LayerTreeHost;
+}  // namespace cc
+
+namespace mojo {
+namespace examples {
+class GLES2ClientImpl;
+
+class CompositorHost : public cc::LayerTreeHostClient {
+ public:
+  explicit CompositorHost(ScopedMessagePipeHandle command_buffer_handle);
+  virtual ~CompositorHost();
+
+  void SetSize(const gfx::Size& viewport_size);
+
+  // 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;
+
+ private:
+  void SetupScene();
+
+  ScopedMessagePipeHandle command_buffer_handle_;
+  scoped_ptr<cc::LayerTreeHost> tree_;
+  scoped_refptr<cc::Layer> child_layer_;
+  base::Thread compositor_thread_;
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
diff --git a/mojo/examples/content_handler_demo/BUILD.gn b/mojo/examples/content_handler_demo/BUILD.gn
new file mode 100644
index 0000000..cd8bcbb
--- /dev/null
+++ b/mojo/examples/content_handler_demo/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_examples.gypi:mojo_content_handler_demo
+shared_library("content_handler_demo") {
+  output_name = "mojo_content_handler_demo"
+
+  sources = [
+    "content_handler_demo.cc"
+  ]
+
+  deps = [
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/public/interfaces/content_handler",
+  ]
+}
+
diff --git a/mojo/examples/content_handler_demo/content_handler_demo.cc b/mojo/examples/content_handler_demo/content_handler_demo.cc
new file mode 100644
index 0000000..a98fea9
--- /dev/null
+++ b/mojo/examples/content_handler_demo/content_handler_demo.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium 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 <stdio.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/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class ContentHandlerApp;
+
+class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
+ public:
+  explicit ContentHandlerImpl(ContentHandlerApp* content_handler_app)
+      : content_handler_app_(content_handler_app) {
+  }
+  virtual ~ContentHandlerImpl() {}
+
+ private:
+  virtual void OnConnect(
+      const mojo::String& url,
+      URLResponsePtr response,
+      InterfaceRequest<ServiceProvider> service_provider) override;
+
+  ContentHandlerApp* content_handler_app_;
+};
+
+class ContentHandlerApp : public ApplicationDelegate {
+ public:
+  ContentHandlerApp() : content_handler_factory_(this) {
+  }
+
+  virtual void Initialize(ApplicationImpl* app) override {}
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(&content_handler_factory_);
+    return true;
+  }
+
+  void PrintResponse(ScopedDataPipeConsumerHandle body) const {
+    for (;;) {
+      char buf[512];
+      uint32_t num_bytes = sizeof(buf);
+      MojoResult result = ReadDataRaw(body.get(), buf, &num_bytes,
+                                      MOJO_READ_DATA_FLAG_NONE);
+      if (result == MOJO_RESULT_SHOULD_WAIT) {
+        Wait(body.get(),
+             MOJO_HANDLE_SIGNAL_READABLE,
+             MOJO_DEADLINE_INDEFINITE);
+      } else if (result == MOJO_RESULT_OK) {
+        if (fwrite(buf, num_bytes, 1, stdout) != 1) {
+          printf("\nUnexpected error writing to file\n");
+          break;
+        }
+      } else {
+        break;
+      }
+
+      printf("\n>>> EOF <<<\n");
+    }
+  }
+
+ private:
+  InterfaceFactoryImplWithContext<ContentHandlerImpl,
+                                  ContentHandlerApp> content_handler_factory_;
+};
+
+void ContentHandlerImpl::OnConnect(
+    const mojo::String& url,
+    URLResponsePtr response,
+    InterfaceRequest<ServiceProvider> service_provider) {
+  printf("ContentHandler::OnConnect - url:%s - body follows\n\n",
+         url.To<std::string>().c_str());
+  content_handler_app_->PrintResponse(response->body.Pass());
+}
+
+}  // namespace examples
+
+// static
+ApplicationDelegate* ApplicationDelegate::Create() {
+  return new examples::ContentHandlerApp();
+}
+
+}  // namespace mojo
diff --git a/mojo/examples/demo_launcher/BUILD.gn b/mojo/examples/demo_launcher/BUILD.gn
new file mode 100644
index 0000000..6ecbbfa
--- /dev/null
+++ b/mojo/examples/demo_launcher/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+assert(use_aura)
+
+# GYP version: mojo/mojo_examples.gypi:mojo_demo_launcher
+shared_library("demo_launcher") {
+  output_name = "mojo_demo_launcher"
+
+  sources = [
+    "demo_launcher.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/view_manager",
+  ]
+}
diff --git a/mojo/examples/demo_launcher/demo_launcher.cc b/mojo/examples/demo_launcher/demo_launcher.cc
new file mode 100644
index 0000000..ec46265
--- /dev/null
+++ b/mojo/examples/demo_launcher/demo_launcher.cc
@@ -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.
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/run_loop.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/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_context.h"
+
+namespace examples {
+
+class DemoLauncher : public mojo::ApplicationDelegate {
+ public:
+  DemoLauncher() {}
+  virtual ~DemoLauncher() {}
+
+ private:
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    context_.reset(new mojo::ViewManagerContext(app));
+    context_->Embed("mojo:mojo_window_manager");
+  }
+
+  scoped_ptr<mojo::ViewManagerContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(DemoLauncher);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::DemoLauncher);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/echo/BUILD.gn b/mojo/examples/echo/BUILD.gn
new file mode 100644
index 0000000..0f518b4
--- /dev/null
+++ b/mojo/examples/echo/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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+group("echo") {
+  deps = [
+    ":client",
+    ":service"
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_echo_client
+shared_library("client") {
+  output_name = "mojo_echo_client"
+
+  deps = [
+    ":bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+  ]
+
+  sources = [ "echo_client.cc" ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_echo_service
+shared_library("service") {
+  output_name = "mojo_echo_service"
+
+  deps = [
+    ":bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+  ]
+
+  sources = [ "echo_service.cc" ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_echo_service_bindings
+mojom("bindings") {
+  sources = [ "echo_service.mojom" ]
+}
diff --git a/mojo/examples/echo/echo_client.cc b/mojo/examples/echo/echo_client.cc
new file mode 100644
index 0000000..2eeae0d
--- /dev/null
+++ b/mojo/examples/echo/echo_client.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "mojo/examples/echo/echo_service.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+namespace examples {
+
+class ResponsePrinter {
+ public:
+  void Run(const String& value) const {
+    printf("Response: \"%s\"\n", value.get().c_str());
+
+    RunLoop::current()->Quit();  // All done!
+  }
+};
+
+class EchoClientDelegate : public ApplicationDelegate {
+ public:
+  virtual void Initialize(ApplicationImpl* app) override {
+    app->ConnectToService("mojo:mojo_echo_service", &echo_service_);
+
+    echo_service_->EchoString("hello world", ResponsePrinter());
+  }
+
+ private:
+  EchoServicePtr echo_service_;
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new mojo::examples::EchoClientDelegate);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/echo/echo_service.cc b/mojo/examples/echo/echo_service.cc
new file mode 100644
index 0000000..5df36ad
--- /dev/null
+++ b/mojo/examples/echo/echo_service.cc
@@ -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.
+
+#include "mojo/examples/echo/echo_service.mojom.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_runner.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+
+namespace mojo {
+namespace examples {
+
+class EchoServiceImpl : public InterfaceImpl<EchoService> {
+ public:
+  virtual void EchoString(const String& value,
+                          const Callback<void(String)>& callback) override {
+    callback.Run(value);
+  }
+};
+
+class EchoServiceDelegate : public ApplicationDelegate {
+ public:
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(&echo_service_factory_);
+    return true;
+  }
+
+ private:
+  InterfaceFactoryImpl<EchoServiceImpl> echo_service_factory_;
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new mojo::examples::EchoServiceDelegate);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/echo/echo_service.mojom b/mojo/examples/echo/echo_service.mojom
new file mode 100644
index 0000000..6e23c6d
--- /dev/null
+++ b/mojo/examples/echo/echo_service.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.examples {
+
+interface EchoService {
+  EchoString(string? value) => (string? value);
+};
+
+}
diff --git a/mojo/examples/embedded_app/BUILD.gn b/mojo/examples/embedded_app/BUILD.gn
new file mode 100644
index 0000000..3e82cbb
--- /dev/null
+++ b/mojo/examples/embedded_app/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.
+
+shared_library("embedded_app") {
+  output_name = "mojo_embedded_app"
+
+  sources = [
+    "embedded_app.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/examples/window_manager:bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/navigation",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//url",
+  ]
+}
diff --git a/mojo/examples/embedded_app/DEPS b/mojo/examples/embedded_app/DEPS
new file mode 100644
index 0000000..fe1d98e
--- /dev/null
+++ b/mojo/examples/embedded_app/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/events",
+]
diff --git a/mojo/examples/embedded_app/embedded_app.cc b/mojo/examples/embedded_app/embedded_app.cc
new file mode 100644
index 0000000..da51fae
--- /dev/null
+++ b/mojo/examples/embedded_app/embedded_app.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 "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/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/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "ui/events/event_constants.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace examples {
+
+const SkColor kColors[] = {SK_ColorYELLOW, SK_ColorRED, SK_ColorGREEN,
+                           SK_ColorMAGENTA};
+
+struct Window {
+  Window(View* root, scoped_ptr<ServiceProvider> embedder_service_provider)
+      : root(root),
+        embedder_service_provider(embedder_service_provider.Pass()) {}
+  View* root;
+  scoped_ptr<ServiceProvider> embedder_service_provider;
+};
+
+class EmbeddedApp
+    : public ApplicationDelegate,
+      public ViewManagerDelegate,
+      public ViewObserver {
+ public:
+  EmbeddedApp() { url::AddStandardScheme("mojo"); }
+  virtual ~EmbeddedApp() {}
+
+ private:
+
+  // 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->AddObserver(this);
+    windows_[root->id()] = new Window(root, imported_services.Pass());
+    root->SetColor(kColors[next_color_++ % arraysize(kColors)]);
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {
+    base::MessageLoop::current()->Quit();
+  }
+
+  // Overridden from ViewObserver:
+  virtual void OnViewDestroyed(View* view) override {
+    DCHECK(windows_.find(view->id()) != windows_.end());
+    windows_.erase(view->id());
+  }
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) override {
+    if (event->action == EVENT_TYPE_MOUSE_RELEASED) {
+      if (event->flags & EVENT_FLAGS_LEFT_MOUSE_BUTTON) {
+        URLRequestPtr request(URLRequest::New());
+        request->url = "http://www.aaronboodman.com/z_dropbox/test.html";
+        NavigatorHostPtr navigator_host;
+        ConnectToService(windows_[view->id()]->embedder_service_provider.get(),
+                         &navigator_host);
+        navigator_host->RequestNavigate(TARGET_SOURCE_NODE, request.Pass());
+      }
+    }
+  }
+
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  typedef std::map<Id, Window*> WindowMap;
+  WindowMap windows_;
+
+  int next_color_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddedApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::EmbeddedApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/keyboard/BUILD.gn b/mojo/examples/keyboard/BUILD.gn
new file mode 100644
index 0000000..fb3e726
--- /dev/null
+++ b/mojo/examples/keyboard/BUILD.gn
@@ -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("//build/config/ui.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(use_aura)
+
+# GYP version: mojo/mojo_examples.gypi:mojo_keyboard
+shared_library("keyboard") {
+  output_name = "mojo_keyboard"
+
+  sources = [
+    "keyboard_delegate.h",
+    "keyboard_view.cc",
+    "keyboard_view.h",
+    "keyboard.cc",
+    "keys.cc",
+    "keys.h",
+  ]
+
+  deps = [
+    ":bindings",
+    "//base",
+    "//cc",
+    "//mojo/application",
+    "//mojo/aura",
+    "//mojo/common",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/views",
+    "//third_party/icu",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/compositor",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/resources",
+    "//ui/resources:ui_test_pak",
+    "//ui/views",
+    "//url",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_keyboard_bindings
+mojom("bindings") {
+  sources = [ "keyboard.mojom" ]
+}
+
diff --git a/mojo/examples/keyboard/DEPS b/mojo/examples/keyboard/DEPS
new file mode 100644
index 0000000..02dd6ea
--- /dev/null
+++ b/mojo/examples/keyboard/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+skia/ext",
+  "+ui/aura",
+  "+ui/base/hit_test.h",
+  "+ui/compositor",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/views",
+]
diff --git a/mojo/examples/keyboard/keyboard.cc b/mojo/examples/keyboard/keyboard.cc
new file mode 100644
index 0000000..8eae684
--- /dev/null
+++ b/mojo/examples/keyboard/keyboard.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium 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/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/keyboard/keyboard.mojom.h"
+#include "mojo/examples/keyboard/keyboard_delegate.h"
+#include "mojo/examples/keyboard/keyboard_view.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/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/navigation/navigation.mojom.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "mojo/views/views_init.h"
+#include "ui/events/event.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace examples {
+
+class Keyboard;
+
+class KeyboardServiceImpl : public InterfaceImpl<KeyboardService> {
+ public:
+  explicit KeyboardServiceImpl(Keyboard* keyboard);
+  virtual ~KeyboardServiceImpl() {}
+
+  // KeyboardService:
+  virtual void SetTarget(uint32_t view_id) override;
+
+ private:
+  Keyboard* keyboard_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardServiceImpl);
+};
+
+class Keyboard : public ApplicationDelegate,
+                 public ViewManagerDelegate,
+                 public KeyboardDelegate {
+ public:
+  Keyboard()
+      : keyboard_service_factory_(this),
+        view_manager_(NULL),
+        keyboard_service_(NULL),
+        target_(0) {}
+
+  virtual ~Keyboard() {
+  }
+
+  void set_target(Id id) { target_ = id; }
+
+  void set_keyboard_service(KeyboardServiceImpl* keyboard) {
+    keyboard_service_ = keyboard;
+  }
+
+ private:
+  // 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 {
+    views_init_.reset(new ViewsInit);
+    connection->AddService(view_manager_client_factory_.get());
+    connection->AddService(&keyboard_service_factory_);
+    return true;
+  }
+
+  void CreateWidget(View* view) {
+    views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
+    widget_delegate->GetContentsView()->AddChildView(new KeyboardView(this));
+    widget_delegate->GetContentsView()->SetLayoutManager(new views::FillLayout);
+
+    views::Widget* widget = new views::Widget;
+    views::Widget::InitParams params(
+        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.native_widget = new NativeWidgetViewManager(widget, view);
+    params.delegate = widget_delegate;
+    params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height());
+    widget->Init(params);
+    widget->Show();
+  }
+
+  // ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    // TODO: deal with OnEmbed() being invoked multiple times.
+    view_manager_ = view_manager;
+    CreateWidget(root);
+  }
+  virtual void OnViewManagerDisconnected(
+      ViewManager* view_manager) override {
+    DCHECK_EQ(view_manager_, view_manager);
+    view_manager_ = NULL;
+    base::MessageLoop::current()->Quit();
+  }
+
+  // KeyboardDelegate:
+  virtual void OnKeyPressed(int key_code, int event_flags) override {
+    if (!target_)
+      return;
+    keyboard_service_->client()->OnKeyboardEvent(target_, key_code,
+                                                 event_flags);
+  }
+
+  InterfaceFactoryImplWithContext<KeyboardServiceImpl, Keyboard>
+      keyboard_service_factory_;
+
+  scoped_ptr<ViewsInit> views_init_;
+
+  ViewManager* view_manager_;
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  KeyboardServiceImpl* keyboard_service_;
+
+  Id target_;
+
+  DISALLOW_COPY_AND_ASSIGN(Keyboard);
+};
+
+KeyboardServiceImpl::KeyboardServiceImpl(Keyboard* keyboard)
+    : keyboard_(keyboard) {
+  keyboard_->set_keyboard_service(this);
+}
+
+void KeyboardServiceImpl::SetTarget(uint32_t view_id) {
+  keyboard_->set_target(view_id);
+}
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::Keyboard);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/keyboard/keyboard.mojom b/mojo/examples/keyboard/keyboard.mojom
new file mode 100644
index 0000000..9377898
--- /dev/null
+++ b/mojo/examples/keyboard/keyboard.mojom
@@ -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.
+
+module mojo {
+
+[Client=KeyboardClient]
+interface KeyboardService {
+  // Sets the view keyboard events are to go to.
+  SetTarget(uint32 view_id);
+};
+
+interface KeyboardClient {
+  // Invoked when the user interacts with the keyboard. |code| is a key code
+  // |flags| is a bitmask of ui::EventFlags.
+  OnKeyboardEvent(uint32 view_id, int32 code, int32 flags);
+};
+
+}
diff --git a/mojo/examples/keyboard/keyboard_delegate.h b/mojo/examples/keyboard/keyboard_delegate.h
new file mode 100644
index 0000000..bf35fc6
--- /dev/null
+++ b/mojo/examples/keyboard/keyboard_delegate.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_EXAMPLES_KEYBOARD_KEYBOARD_DELEGATE_H_
+#define MOJO_EXAMPLES_KEYBOARD_KEYBOARD_DELEGATE_H_
+
+#include "base/basictypes.h"
+
+namespace mojo {
+namespace examples {
+
+class KeyboardDelegate {
+ public:
+  KeyboardDelegate() {}
+
+  virtual void OnKeyPressed(int key_code, int event_flags) = 0;
+
+ protected:
+  virtual ~KeyboardDelegate() {}
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_KEYBOARD_KEYBOARD_DELEGATE_H_
diff --git a/mojo/examples/keyboard/keyboard_view.cc b/mojo/examples/keyboard/keyboard_view.cc
new file mode 100644
index 0000000..3a0d71a
--- /dev/null
+++ b/mojo/examples/keyboard/keyboard_view.cc
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium 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/examples/keyboard/keyboard_view.h"
+
+#include <algorithm>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/examples/keyboard/keyboard_delegate.h"
+#include "mojo/examples/keyboard/keys.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/label_button_border.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const int kHorizontalPadding = 6;
+const int kVerticalPadding = 8;
+
+base::string16 GetDisplayString(int key_code, int flags) {
+  return base::string16(1, ui::GetCharacterFromKeyCode(
+                            static_cast<ui::KeyboardCode>(key_code), flags));
+}
+
+// Returns a font that fits in the space provided. |text| is used as a basis
+// for determing the size.
+gfx::FontList CalculateFont(int width, int height, const base::string16& text) {
+  gfx::FontList font;
+  gfx::FontList last_font;
+  while (gfx::Canvas::GetStringWidth(text, font) < width &&
+         font.GetHeight() < height) {
+    last_font = font;
+    font = font.DeriveWithSizeDelta(2);
+  }
+  return last_font;
+}
+
+// Returns the total number of keys in |rows|.
+int NumKeys(const std::vector<const Row*>& rows) {
+  int result = 0;
+  for (size_t i = 0; i < rows.size(); ++i)
+    result += static_cast<int>(rows[i]->num_keys);
+  return result;
+}
+
+}  // namespace
+
+KeyboardView::KeyboardView(KeyboardDelegate* delegate)
+    : delegate_(delegate),
+      max_keys_in_row_(0),
+      keyboard_layout_(KEYBOARD_LAYOUT_ALPHA) {
+  set_background(views::Background::CreateSolidBackground(SK_ColorBLACK));
+  SetRows(GetQWERTYRows());
+}
+
+KeyboardView::~KeyboardView() {
+}
+
+void KeyboardView::Layout() {
+  if (width() == 0 || height() == 0 || rows_.empty() ||
+      last_layout_size_ == bounds().size())
+    return;
+
+  last_layout_size_ = bounds().size();
+
+  const int button_width =
+      (width() - (max_keys_in_row_ - 1) * kHorizontalPadding) /
+      max_keys_in_row_;
+  const int button_height =
+      (height() - (static_cast<int>(rows_.size() - 1) * kVerticalPadding)) /
+      static_cast<int>(rows_.size());
+  const int initial_x = (width() - button_width * max_keys_in_row_ -
+                         kHorizontalPadding * (max_keys_in_row_ - 1)) / 2;
+  for (size_t i = 0; i < rows_.size(); ++i) {
+    LayoutRow(*(rows_[i]), static_cast<int>(i), initial_x, button_width,
+              button_height);
+  }
+
+  views::LabelButtonBorder border(views::Button::STYLE_TEXTBUTTON);
+  gfx::Insets insets(border.GetInsets());
+  gfx::FontList font = CalculateFont(button_width - insets.width(),
+                                     button_height - insets.height(),
+                                     base::ASCIIToUTF16("W"));
+  gfx::FontList special_font = CalculateFont(button_width - insets.width(),
+                                             button_height - insets.height(),
+                                             base::ASCIIToUTF16("?123"));
+  button_font_ = font;
+  ResetFonts(font, special_font);
+}
+
+void KeyboardView::SetLayout(KeyboardLayout keyboard_layout) {
+  if (keyboard_layout_ == keyboard_layout)
+    return;
+
+  keyboard_layout_ = keyboard_layout;
+  last_layout_size_ = gfx::Size();
+  if (keyboard_layout_ == KEYBOARD_LAYOUT_NUMERIC)
+    SetRows(GetNumericRows());
+  else
+    SetRows(GetQWERTYRows());
+  Layout();
+  SchedulePaint();
+}
+
+void KeyboardView::LayoutRow(const Row& row,
+                             int row_index,
+                             int initial_x,
+                             int button_width,
+                             int button_height) {
+  int x = initial_x + row.padding * (button_width + kHorizontalPadding);
+  const int y = row_index * (button_height + kVerticalPadding);
+  for (size_t i = 0; i < row.num_keys; ++i) {
+    views::View* button = GetButton(row_index, static_cast<int>(i));
+    int actual_width = button_width;
+    if (row.keys[i].size > 1) {
+      actual_width = (button_width + kHorizontalPadding) *
+          row.keys[i].size - kHorizontalPadding;
+    }
+    button->SetBounds(x, y, actual_width, button_height);
+    x += actual_width + kHorizontalPadding;
+  }
+}
+
+void KeyboardView::SetRows(const std::vector<const Row*>& rows) {
+  const int num_keys = NumKeys(rows);
+  while (child_count() > num_keys)
+    delete child_at(child_count() - 1);
+  for (int i = child_count(); i < num_keys; ++i)
+    AddChildView(CreateButton());
+
+  last_layout_size_ = gfx::Size();
+
+  rows_ = rows;
+
+  max_keys_in_row_ = 0;
+  for (size_t i = 0; i < rows_.size(); ++i) {
+    max_keys_in_row_ = std::max(max_keys_in_row_,
+                                static_cast<int>(rows_[i]->num_keys));
+    ConfigureButtonsInRow(static_cast<int>(i), *rows_[i]);
+  }
+}
+
+void KeyboardView::ConfigureButtonsInRow(int row_index, const Row& row) {
+  for (size_t i = 0; i < row.num_keys; ++i) {
+    views::LabelButton* button = GetButton(row_index, static_cast<int>(i));
+    const Key& key(row.keys[i]);
+    switch (key.display_code) {
+      case SPECIAL_KEY_SHIFT:
+        // TODO: need image.
+        button->SetText(base::string16());
+        break;
+      case SPECIAL_KEY_NUMERIC:
+        button->SetText(base::ASCIIToUTF16("?123"));
+        break;
+      case SPECIAL_KEY_ALPHA:
+        button->SetText(base::ASCIIToUTF16("ABC"));
+        break;
+      default:
+        button->SetText(GetDisplayString(key.display_code,
+                                         key.event_flags | event_flags()));
+        break;
+    }
+    button->SetState(views::Button::STATE_NORMAL);
+  }
+}
+
+views::View* KeyboardView::CreateButton() {
+  views::LabelButton* button = new views::LabelButton(this, base::string16());
+  button->SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
+  button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  button->set_background(views::Background::CreateSolidBackground(78, 78, 78));
+  button->SetFontList(button_font_);
+  //  button->SetHaloColor(SK_ColorBLACK);
+  // Turn off animations as we reuse buttons in different layouts. If we didn't
+  // do this and you click a button to change the layout then the button you
+  // clicked on would animate the transition even though it may now represent a
+  // different key.
+  button->SetAnimationDuration(0);
+  return button;
+}
+
+views::LabelButton* KeyboardView::GetButton(int row, int column) {
+  int offset = column;
+  for (int i = 0; i < row; ++i)
+    offset += static_cast<int>(rows_[i]->num_keys);
+  return static_cast<views::LabelButton*>(child_at(offset));
+}
+
+const Key& KeyboardView::GetKeyForButton(views::Button* button) const {
+  int index = GetIndexOf(button);
+  DCHECK_NE(-1, index);
+  int row = 0;
+  while (index >= static_cast<int>(rows_[row]->num_keys)) {
+    index -= static_cast<int>(rows_[row]->num_keys);
+    row++;
+  }
+  return rows_[row]->keys[index];
+}
+
+void KeyboardView::ResetFonts(const gfx::FontList& button_font,
+                              const gfx::FontList& special_font) {
+  for (size_t i = 0; i < rows_.size(); ++i) {
+    for (size_t j = 0; j < rows_[i]->num_keys; ++j) {
+      views::LabelButton* button = GetButton(static_cast<int>(i),
+                                             static_cast<int>(j));
+      const Key& key(GetKeyForButton(button));
+      switch (key.display_code) {
+        case SPECIAL_KEY_ALPHA:
+        case SPECIAL_KEY_NUMERIC:
+          button->SetFontList(special_font);
+          break;
+        default:
+          button->SetFontList(button_font);
+          break;
+      }
+    }
+  }
+}
+
+void KeyboardView::ButtonPressed(views::Button* sender,
+                                 const ui::Event& event) {
+  const Key& key(GetKeyForButton(sender));
+  switch (key.display_code) {
+    case SPECIAL_KEY_SHIFT:
+      SetLayout((keyboard_layout_ == KEYBOARD_LAYOUT_SHIFT) ?
+                KEYBOARD_LAYOUT_ALPHA : KEYBOARD_LAYOUT_SHIFT);
+      return;
+    case SPECIAL_KEY_ALPHA:
+      SetLayout(KEYBOARD_LAYOUT_ALPHA);
+      return;
+    case SPECIAL_KEY_NUMERIC:
+      SetLayout(KEYBOARD_LAYOUT_NUMERIC);
+      return;
+    default:
+      break;
+  }
+
+  // Windows isn't happy if we pass in the flags used to get the display string.
+#if defined(OS_WIN)
+  int key_event_flags = 0;
+#else
+  int key_event_flags = key.event_flags;
+#endif
+  delegate_->OnKeyPressed(key.keyboard_code(), key_event_flags | event_flags());
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/keyboard/keyboard_view.h b/mojo/examples/keyboard/keyboard_view.h
new file mode 100644
index 0000000..4086a2d
--- /dev/null
+++ b/mojo/examples/keyboard/keyboard_view.h
@@ -0,0 +1,104 @@
+// Copyright 2014 The Chromium 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_EXAMPLES_KEYBOARD_KEYBOARD_VIEW_H_
+#define MOJO_EXAMPLES_KEYBOARD_KEYBOARD_VIEW_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/gfx/font_list.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
+
+namespace views {
+class LabelButton;
+}
+
+namespace mojo {
+namespace examples {
+
+class KeyboardDelegate;
+struct Key;
+struct Row;
+
+// Shows a keyboard the user can interact with. The delegate is notified any
+// time the user presses a button.
+class KeyboardView : public views::View, public views::ButtonListener {
+ public:
+  explicit KeyboardView(KeyboardDelegate* delegate);
+  virtual ~KeyboardView();
+
+  // views::View:
+  virtual void Layout() override;
+
+ private:
+  // The type of keys that are shown.
+  enum KeyboardLayout {
+    KEYBOARD_LAYOUT_ALPHA,
+
+    // Uppercase characters.
+    KEYBOARD_LAYOUT_SHIFT,
+
+    // Numeric characters.
+    KEYBOARD_LAYOUT_NUMERIC,
+  };
+
+  int event_flags() const {
+    return (keyboard_layout_ == KEYBOARD_LAYOUT_SHIFT) ?
+      ui::EF_SHIFT_DOWN : ui::EF_NONE;
+  }
+
+  void SetLayout(KeyboardLayout layout);
+
+  // Lays out the buttons for the specified row.
+  void LayoutRow(const Row& row,
+                 int row_index,
+                 int initial_x,
+                 int button_width,
+                 int button_height);
+
+  // Sets the rows to show.
+  void SetRows(const std::vector<const Row*>& rows);
+
+  // Configures the button in a row.
+  void ConfigureButtonsInRow(int row_index, const Row& row);
+
+  // Creates a new button.
+  views::View* CreateButton();
+
+  // Returns the button corresponding to a key at the specified row/column.
+  views::LabelButton* GetButton(int row, int column);
+
+  const Key& GetKeyForButton(views::Button* button) const;
+
+  // Reset the fonts of all the buttons. |special_font| is used for the buttons
+  // that toggle the layout.
+  void ResetFonts(const gfx::FontList& button_font,
+                  const gfx::FontList& special_font);
+
+  // views::ButtonListener:
+  virtual void ButtonPressed(views::Button* sender,
+                             const ui::Event& event) override;
+
+  KeyboardDelegate* delegate_;
+
+  // Maximium number of keys in a row. Determined from |rows_|.
+  int max_keys_in_row_;
+
+  KeyboardLayout keyboard_layout_;
+
+  std::vector<const Row*> rows_;
+
+  gfx::Size last_layout_size_;
+
+  gfx::FontList button_font_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardView);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_KEYBOARD_KEYBOARD_VIEW_H_
diff --git a/mojo/examples/keyboard/keys.cc b/mojo/examples/keyboard/keys.cc
new file mode 100644
index 0000000..adf9094
--- /dev/null
+++ b/mojo/examples/keyboard/keys.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 The Chromium 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/examples/keyboard/keys.h"
+
+#include "base/macros.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+const Key kQWERTYKeysRow1[] =
+{
+  { ui::VKEY_Q, 1, 0, 'q' },
+  { ui::VKEY_W, 1, 0, 'w' },
+  { ui::VKEY_E, 1, 0, 'e' },
+  { ui::VKEY_R, 1, 0, 'r' },
+  { ui::VKEY_T, 1, 0, 't' },
+  { ui::VKEY_Y, 1, 0, 'y' },
+  { ui::VKEY_U, 1, 0, 'u' },
+  { ui::VKEY_I, 1, 0, 'i' },
+  { ui::VKEY_O, 1, 0, 'o' },
+  { ui::VKEY_P, 1, 0, 'p' },
+};
+
+const Key kQWERTYKeysRow2[] =
+{
+  { ui::VKEY_A, 1, 0, 'a' },
+  { ui::VKEY_S, 1, 0, 's' },
+  { ui::VKEY_D, 1, 0, 'd' },
+  { ui::VKEY_F, 1, 0, 'f' },
+  { ui::VKEY_G, 1, 0, 'g' },
+  { ui::VKEY_H, 1, 0, 'h' },
+  { ui::VKEY_J, 1, 0, 'j' },
+  { ui::VKEY_K, 1, 0, 'k' },
+  { ui::VKEY_L, 1, 0, 'l' },
+};
+
+const Key kQWERTYKeysRow3[] =
+{
+  { SPECIAL_KEY_SHIFT, 1.5, 0, 0 },
+  { ui::VKEY_Z, 1, 0, 'z' },
+  { ui::VKEY_X, 1, 0, 'x' },
+  { ui::VKEY_C, 1, 0, 'c' },
+  { ui::VKEY_V, 1, 0, 'v' },
+  { ui::VKEY_B, 1, 0, 'b' },
+  { ui::VKEY_N, 1, 0, 'n' },
+  { ui::VKEY_M, 1, 0, 'm' },
+  { ui::VKEY_BACK, 1.5, 0, 0 },
+};
+
+const Key kQWERTYKeysRow4[] =
+{
+  { SPECIAL_KEY_NUMERIC, 1.5, 0, 0 },
+  { ui::VKEY_DIVIDE, 1, 0, '/' },
+  { ui::VKEY_SPACE, 5, 0, ' ' },
+  { ui::VKEY_DECIMAL, 1, 0, '.' },
+  { ui::VKEY_RETURN, 1.5, 0, 0 },
+};
+
+const Row kQWERTYRow1 = {
+  0,
+  kQWERTYKeysRow1,
+  arraysize(kQWERTYKeysRow1),
+};
+
+const Row kQWERTYRow2 = {
+  .5,
+  kQWERTYKeysRow2,
+  arraysize(kQWERTYKeysRow2),
+};
+
+const Row kQWERTYRow3 = {
+  0,
+  kQWERTYKeysRow3,
+  arraysize(kQWERTYKeysRow3),
+};
+
+const Row kQWERTYRow4 = {
+  0,
+  kQWERTYKeysRow4,
+  arraysize(kQWERTYKeysRow4),
+};
+
+const Key kNumericKeysRow1[] =
+{
+  { ui::VKEY_1, 1, 0, 0 },
+  { ui::VKEY_2, 1, 0, 0 },
+  { ui::VKEY_3, 1, 0, 0 },
+  { ui::VKEY_4, 1, 0, 0 },
+  { ui::VKEY_5, 1, 0, 0 },
+  { ui::VKEY_6, 1, 0, 0 },
+  { ui::VKEY_7, 1, 0, 0 },
+  { ui::VKEY_8, 1, 0, 0 },
+  { ui::VKEY_9, 1, 0, 0 },
+  { ui::VKEY_0, 1, 0, 0 },
+};
+
+const Key kNumericKeysRow2[] =
+{
+  // @#$%&-+()
+  { ui::VKEY_2, 1, ui::EF_SHIFT_DOWN, '@' },
+  { ui::VKEY_3, 1, ui::EF_SHIFT_DOWN, '#' },
+  { ui::VKEY_4, 1, ui::EF_SHIFT_DOWN, '$' },
+  { ui::VKEY_5, 1, ui::EF_SHIFT_DOWN, '%' },
+  { ui::VKEY_7, 1, ui::EF_SHIFT_DOWN, '&' },
+  { ui::VKEY_SUBTRACT, 1, 0, '-' },
+  { ui::VKEY_ADD, 1, 0, '+' },
+  { ui::VKEY_9, 1, ui::EF_SHIFT_DOWN, '(' },
+  { ui::VKEY_0, 1, ui::EF_SHIFT_DOWN, ')' },
+};
+
+const Key kNumericKeysRow3[] =
+{
+  // *"':;!? backspace
+  { ui::VKEY_MULTIPLY, 1, 0, '*' },
+  { ui::VKEY_OEM_7, 1, ui::EF_SHIFT_DOWN, '"' },
+  { ui::VKEY_OEM_7, 1, 0, '\'' },
+  { ui::VKEY_OEM_1, 1, ui::EF_SHIFT_DOWN, ':' },
+  { ui::VKEY_OEM_1, 1, 0, ';' },
+  { ui::VKEY_1, 1, ui::EF_SHIFT_DOWN, '!' },
+  { ui::VKEY_OEM_2, 1, ui::EF_SHIFT_DOWN, '?' },
+  { ui::VKEY_BACK, 1.5, 0, 0 },
+};
+
+const Key kNumericKeysRow4[] =
+{
+  // ABC _ / space (3) ,.enter
+  { SPECIAL_KEY_ALPHA, 1.5, 0, 0 },
+  { ui::VKEY_OEM_MINUS, 1, ui::EF_SHIFT_DOWN, '_' },
+  { ui::VKEY_OEM_2, 1, 0, '/' },
+  { ui::VKEY_SPACE, 3, 0, ' ' },
+  { ui::VKEY_OEM_COMMA, 1, 0, ',' },
+  { ui::VKEY_OEM_PERIOD, 1, 0, '.' },
+  { ui::VKEY_RETURN, 1.5, 0, 0 },
+};
+
+const Row kNumericRow1 = {
+  0,
+  kNumericKeysRow1,
+  arraysize(kNumericKeysRow1),
+};
+
+const Row kNumericRow2 = {
+  .5,
+  kNumericKeysRow2,
+  arraysize(kNumericKeysRow2),
+};
+
+const Row kNumericRow3 = {
+  1.5,
+  kNumericKeysRow3,
+  arraysize(kNumericKeysRow3),
+};
+
+const Row kNumericRow4 = {
+  0,
+  kNumericKeysRow4,
+  arraysize(kNumericKeysRow4),
+};
+
+}  // namespace
+
+std::vector<const Row*> GetQWERTYRows() {
+  std::vector<const Row*> rows;
+  rows.push_back(&kQWERTYRow1);
+  rows.push_back(&kQWERTYRow2);
+  rows.push_back(&kQWERTYRow3);
+  rows.push_back(&kQWERTYRow4);
+  return rows;
+}
+
+std::vector<const Row*> GetNumericRows() {
+  std::vector<const Row*> rows;
+  rows.push_back(&kNumericRow1);
+  rows.push_back(&kNumericRow2);
+  rows.push_back(&kNumericRow3);
+  rows.push_back(&kNumericRow4);
+  return rows;
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/keyboard/keys.h b/mojo/examples/keyboard/keys.h
new file mode 100644
index 0000000..c09f856
--- /dev/null
+++ b/mojo/examples/keyboard/keys.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_EXAMPLES_KEYBOARD_KEYS_H_
+#define MOJO_EXAMPLES_KEYBOARD_KEYS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace mojo {
+namespace examples {
+
+enum SpecialKey {
+  SPECIAL_KEY_SHIFT = -1,
+  SPECIAL_KEY_NUMERIC = -2,
+  SPECIAL_KEY_ALPHA = -3,
+};
+
+struct Key {
+  int keyboard_code() const {
+    // Handling of keycodes differs between in windows and others.
+#if defined(OS_WIN)
+    return generated_code ? generated_code : display_code;
+#else
+    return display_code;
+#endif
+  }
+
+  // Code used to get the value to display in the UI. This is either a
+  // KeyboardCode or a SpecialKey.
+  int display_code;
+
+  // How much space (as a percentage) the key is to take up.
+  float size;
+
+  // Any ui::EventFlags that are required to produce the key.
+  int event_flags;
+
+  // If non-zero KeyboardCode to generate. If 0 use the |display_code|.
+  int generated_code;
+};
+
+struct Row {
+  float padding;
+  const Key* keys;
+  size_t num_keys;
+};
+
+// Returns the rows for a qwerty style keyboard. The returned values are owned
+// by this object and should not be deleted.
+std::vector<const Row*> GetQWERTYRows();
+
+// Returns the rows for a numeric keyboard. The returned values are owned
+// by this object and should not be deleted.
+std::vector<const Row*> GetNumericRows();
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_KEYBOARD_KEYS_H_
diff --git a/mojo/examples/media_viewer/BUILD.gn b/mojo/examples/media_viewer/BUILD.gn
new file mode 100644
index 0000000..b17d2d3
--- /dev/null
+++ b/mojo/examples/media_viewer/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+if (use_aura) {
+
+# GYP version: mojo/mojo_examples.gypi:mojo_media_viewer
+shared_library("media_viewer") {
+  output_name = "mojo_media_viewer"
+
+  sources = [
+    "media_viewer.cc"
+  ]
+
+  deps = [
+    ":bindings",
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/public/interfaces/view_manager",
+    "//mojo/views",
+    "//skia",
+    "//ui/gfx/geometry",
+    "//ui/views",
+  ]
+}
+
+}  # use_aura
+
+# GYP version: mojo/mojo_examples.gypi:mojo_media_viewer_bindings
+mojom("bindings") {
+  sources = [
+    "media_viewer.mojom",
+  ]
+}
diff --git a/mojo/examples/media_viewer/DEPS b/mojo/examples/media_viewer/DEPS
new file mode 100644
index 0000000..44ca77e
--- /dev/null
+++ b/mojo/examples/media_viewer/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+skia/ext",
+  "+third_party/skia/include",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/views",
+]
diff --git a/mojo/examples/media_viewer/media_viewer.cc b/mojo/examples/media_viewer/media_viewer.cc
new file mode 100644
index 0000000..33cf200
--- /dev/null
+++ b/mojo/examples/media_viewer/media_viewer.cc
@@ -0,0 +1,321 @@
+// Copyright 2014 The Chromium 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 <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/media_viewer/media_viewer.mojom.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/public/cpp/bindings/interface_impl.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/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "mojo/views/views_init.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/painter.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace mojo {
+namespace examples {
+
+class MediaViewer;
+
+class CustomButtonBorder: public views::Border {
+ public:
+  CustomButtonBorder()
+      : normal_painter_(CreatePainter(SkColorSetRGB(0x80, 0x80, 0x80),
+                                      SkColorSetRGB(0xC0, 0xC0, 0xC0))),
+        hot_painter_(CreatePainter(SkColorSetRGB(0xA0, 0xA0, 0xA0),
+                                   SkColorSetRGB(0xD0, 0xD0, 0xD0))),
+        pushed_painter_(CreatePainter(SkColorSetRGB(0x80, 0x80, 0x80),
+                                      SkColorSetRGB(0x90, 0x90, 0x90))),
+        insets_(2, 6, 2, 6) {
+  }
+  virtual ~CustomButtonBorder() {}
+
+ private:
+  // Overridden from views::Border:
+  virtual void Paint(const views::View& view, gfx::Canvas* canvas) override {
+    const views::LabelButton* button =
+        static_cast<const views::LabelButton*>(&view);
+    views::Button::ButtonState state = button->state();
+
+    views::Painter* painter = normal_painter_.get();
+    if (state == views::Button::STATE_HOVERED) {
+      painter = hot_painter_.get();
+    } else if (state == views::Button::STATE_PRESSED) {
+      painter = pushed_painter_.get();
+    }
+    painter->Paint(canvas, view.size());
+  }
+
+  virtual gfx::Insets GetInsets() const override {
+    return insets_;
+  }
+
+  virtual gfx::Size GetMinimumSize() const override {
+    gfx::Size size;
+    if (normal_painter_)
+      size.SetToMax(normal_painter_->GetMinimumSize());
+    if (hot_painter_)
+      size.SetToMax(hot_painter_->GetMinimumSize());
+    if (pushed_painter_)
+      size.SetToMax(pushed_painter_->GetMinimumSize());
+    return size;
+  }
+
+  scoped_ptr<views::Painter> CreatePainter(SkColor border, SkColor background) {
+    skia::RefPtr<SkCanvas> canvas(skia::AdoptRef(skia::CreatePlatformCanvas(
+        64, 64, false)));
+    SkPaint paint;
+    paint.setColor(background);
+    canvas->drawRoundRect(SkRect::MakeWH(63, 63), 2, 2, paint);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(border);
+    canvas->drawRoundRect(SkRect::MakeWH(63, 63), 2, 2, paint);
+
+    return scoped_ptr<views::Painter>(
+        views::Painter::CreateImagePainter(
+            gfx::ImageSkia::CreateFrom1xBitmap(
+                skia::GetTopDevice(*canvas)->accessBitmap(true)),
+            gfx::Insets(5, 5, 5, 5)));
+  }
+
+  scoped_ptr<views::Painter> normal_painter_;
+  scoped_ptr<views::Painter> hot_painter_;
+  scoped_ptr<views::Painter> pushed_painter_;
+
+  gfx::Insets insets_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomButtonBorder);
+};
+
+class ControlPanel : public views::ButtonListener {
+ public:
+  enum ControlType {
+    CONTROL_ZOOM_IN,
+    CONTROL_ACTUAL_SIZE,
+    CONTROL_ZOOM_OUT,
+    CONTROL_COUNT,
+  };
+
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    virtual void ButtonPressed(ControlType type) = 0;
+  };
+
+  ControlPanel(Delegate* delegate) : delegate_(delegate), buttons_() {}
+
+  virtual ~ControlPanel() {}
+
+  void Initialize(View* view) {
+    const char* kNames[] = { "Zoom In", "Actual Size", "Zoom Out" };
+
+    views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
+
+    widget_delegate->GetContentsView()->SetLayoutManager(
+        new views::BoxLayout(views::BoxLayout::kHorizontal, 5, 2, 5));
+
+    widget_delegate->GetContentsView()->set_background(
+        views::Background::CreateSolidBackground(SK_ColorLTGRAY));
+
+    for (int type = 0; type < CONTROL_COUNT; ++type) {
+      views::Button* button = new views::LabelButton(
+          this, base::ASCIIToUTF16(kNames[type]));
+      button->SetBorder(scoped_ptr<views::Border>(new CustomButtonBorder));
+      buttons_[type] = button;
+      widget_delegate->GetContentsView()->AddChildView(button);
+    }
+
+    views::Widget* widget = new views::Widget;
+    views::Widget::InitParams params(
+        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.native_widget = new NativeWidgetViewManager(widget, view);
+    params.delegate = widget_delegate;
+    params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height());
+    params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
+    widget->Init(params);
+    widget->Show();
+  }
+
+ private:
+  // Overridden from views::ButtonListener:
+  virtual void ButtonPressed(views::Button* sender,
+                             const ui::Event& event) override {
+    for (int i = 0; i < CONTROL_COUNT; ++i) {
+      if (sender == buttons_[i]) {
+        delegate_->ButtonPressed(static_cast<ControlType>(i));
+        return;
+      }
+    }
+  }
+
+  Delegate* delegate_;
+  views::Button* buttons_[CONTROL_COUNT];
+
+  DISALLOW_COPY_AND_ASSIGN(ControlPanel);
+};
+
+class MediaViewer
+    : public ApplicationDelegate,
+      public ViewManagerDelegate,
+      public ControlPanel::Delegate,
+      public ViewObserver {
+ public:
+  MediaViewer()
+      : app_(NULL),
+        view_manager_(NULL),
+        root_view_(NULL),
+        control_view_(NULL),
+        content_view_(NULL),
+        control_panel_(this) {
+    handler_map_["image/png"] = "mojo:mojo_png_viewer";
+  }
+
+  virtual ~MediaViewer() {
+    if (root_view_)
+      root_view_->RemoveObserver(this);
+  }
+
+ private:
+  typedef std::map<std::string, std::string> HandlerMap;
+
+
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+    app_ = app;
+    views_init_.reset(new ViewsInit);
+  }
+
+  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+      override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  void LayoutViews() {
+    View* root = content_view_->parent();
+    gfx::Rect control_bounds(root->bounds().width(), 28);
+    control_view_->SetBounds(control_bounds);
+    gfx::Rect content_bounds(0, control_bounds.height(), root->bounds().width(),
+                             root->bounds().height() - control_bounds.height());
+    content_view_->SetBounds(content_bounds);
+  }
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    root_view_ = root;
+    view_manager_ = view_manager;
+
+    control_view_ = View::Create(view_manager_);
+    root_view_->AddChild(control_view_);
+
+    content_view_ = View::Create(view_manager_);
+    root_view_->AddChild(content_view_);
+
+    control_panel_.Initialize(control_view_);
+
+    LayoutViews();
+    root_view_->AddObserver(this);
+
+    content_view_->Embed("TODO");
+  }
+
+  virtual void OnViewManagerDisconnected(
+      ViewManager* view_manager) override {
+    DCHECK_EQ(view_manager_, view_manager);
+    view_manager_ = NULL;
+    base::MessageLoop::current()->Quit();
+  }
+
+  // Overridden from ControlPanel::Delegate:
+  virtual void ButtonPressed(ControlPanel::ControlType type) override {
+    switch (type) {
+      case ControlPanel::CONTROL_ZOOM_IN:
+        zoomable_media_->ZoomIn();
+        break;
+      case ControlPanel::CONTROL_ACTUAL_SIZE:
+       zoomable_media_->ZoomToActualSize();
+        break;
+      case ControlPanel::CONTROL_ZOOM_OUT:
+        zoomable_media_->ZoomOut();
+        break;
+      default:
+        NOTIMPLEMENTED();
+    }
+  }
+
+  // ViewObserver:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override {
+    LayoutViews();
+  }
+  virtual void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(view, root_view_);
+    view->RemoveObserver(this);
+    root_view_ = NULL;
+  }
+
+  std::string GetHandlerForContentType(const std::string& content_type) {
+    HandlerMap::const_iterator it = handler_map_.find(content_type);
+    return it != handler_map_.end() ? it->second : std::string();
+  }
+
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  ApplicationImpl* app_;
+  scoped_ptr<ViewsInit> views_init_;
+  ViewManager* view_manager_;
+  View* root_view_;
+  View* control_view_;
+  View* content_view_;
+  ControlPanel control_panel_;
+  ZoomableMediaPtr zoomable_media_;
+  HandlerMap handler_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaViewer);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::MediaViewer);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/media_viewer/media_viewer.mojom b/mojo/examples/media_viewer/media_viewer.mojom
new file mode 100644
index 0000000..773d775
--- /dev/null
+++ b/mojo/examples/media_viewer/media_viewer.mojom
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo {
+
+interface ZoomableMedia {
+  ZoomIn();
+  ZoomOut();
+  ZoomToActualSize();
+};
+
+}
diff --git a/mojo/examples/nesting_app/BUILD.gn b/mojo/examples/nesting_app/BUILD.gn
new file mode 100644
index 0000000..e25263f
--- /dev/null
+++ b/mojo/examples/nesting_app/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_examples.gypi:mojo_nesting_app
+shared_library("nesting_app") {
+  output_name = "mojo_nesting_app"
+
+  sources = [
+    "nesting_app.cc"
+  ]
+
+  deps = [
+    "//base",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//url",
+    "//mojo/application",
+    "//mojo/examples/window_manager:bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/navigation",
+  ]
+}
diff --git a/mojo/examples/nesting_app/DEPS b/mojo/examples/nesting_app/DEPS
new file mode 100644
index 0000000..fe1d98e
--- /dev/null
+++ b/mojo/examples/nesting_app/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/events",
+]
diff --git a/mojo/examples/nesting_app/nesting_app.cc b/mojo/examples/nesting_app/nesting_app.cc
new file mode 100644
index 0000000..a7fb74e
--- /dev/null
+++ b/mojo/examples/nesting_app/nesting_app.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium 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/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/window_manager/window_manager.mojom.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/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/cpp/view_manager/view_observer.h"
+#include "ui/events/event_constants.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+const char kEmbeddedAppURL[] = "mojo:mojo_embedded_app";
+}
+
+class NestingApp;
+
+// An app that embeds another app.
+// TODO(davemoore): Is this the right name?
+class NestingApp
+    : public ApplicationDelegate,
+      public ViewManagerDelegate,
+      public ViewObserver {
+ public:
+  NestingApp() : nested_(NULL) {}
+  virtual ~NestingApp() {}
+
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new ViewManagerClientFactory(app->shell(), this));
+  }
+
+  // Overridden from ApplicationImpl:
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->ConnectToService(&window_manager_);
+    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->AddObserver(this);
+    root->SetColor(SK_ColorCYAN);
+
+    nested_ = View::Create(view_manager);
+    root->AddChild(nested_);
+    nested_->SetBounds(gfx::Rect(20, 20, 50, 50));
+    nested_->Embed(kEmbeddedAppURL);
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {
+    base::MessageLoop::current()->Quit();
+  }
+
+  // Overridden from ViewObserver:
+  virtual void OnViewDestroyed(View* view) override {
+    // TODO(beng): reap views & child Views.
+    nested_ = NULL;
+  }
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) override {
+    if (event->action == EVENT_TYPE_MOUSE_RELEASED)
+      window_manager_->CloseWindow(view->id());
+  }
+
+  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+  View* nested_;
+  IWindowManagerPtr window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(NestingApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::NestingApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/pepper_container_app/BUILD.gn b/mojo/examples/pepper_container_app/BUILD.gn
new file mode 100644
index 0000000..aa9d7af
--- /dev/null
+++ b/mojo/examples/pepper_container_app/BUILD.gn
@@ -0,0 +1,103 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP version: mojo/mojo_examples.gypi:mojo_pepper_container_app
+shared_library("pepper_container_app") {
+  output_name = "mojo_pepper_container_app"
+
+  sources = [
+    # Source files from ppapi/.
+    # An alternative is to depend on
+    # "//ppapi/ppapi_shared', but that target includes
+    # a lot of things that we don't need.
+    # TODO(yzshen): Consider extracting these files into a separate target
+    # which mojo_pepper_container_app and ppapi_shared both depend on.
+    "//ppapi/shared_impl/api_id.h",
+    "//ppapi/shared_impl/callback_tracker.cc",
+    "//ppapi/shared_impl/callback_tracker.h",
+    "//ppapi/shared_impl/host_resource.cc",
+    "//ppapi/shared_impl/host_resource.h",
+    "//ppapi/shared_impl/id_assignment.cc",
+    "//ppapi/shared_impl/id_assignment.h",
+    "//ppapi/shared_impl/ppapi_globals.cc",
+    "//ppapi/shared_impl/ppapi_globals.h",
+    "//ppapi/shared_impl/ppapi_shared_export.h",
+    "//ppapi/shared_impl/ppb_message_loop_shared.cc",
+    "//ppapi/shared_impl/ppb_message_loop_shared.h",
+    "//ppapi/shared_impl/ppb_view_shared.cc",
+    "//ppapi/shared_impl/ppb_view_shared.h",
+    "//ppapi/shared_impl/proxy_lock.cc",
+    "//ppapi/shared_impl/proxy_lock.h",
+    "//ppapi/shared_impl/resource.cc",
+    "//ppapi/shared_impl/resource.h",
+    "//ppapi/shared_impl/resource_tracker.cc",
+    "//ppapi/shared_impl/resource_tracker.h",
+    "//ppapi/shared_impl/scoped_pp_resource.cc",
+    "//ppapi/shared_impl/scoped_pp_resource.h",
+    "//ppapi/shared_impl/singleton_resource_id.h",
+    "//ppapi/shared_impl/tracked_callback.cc",
+    "//ppapi/shared_impl/tracked_callback.h",
+    "//ppapi/thunk/enter.cc",
+    "//ppapi/thunk/enter.h",
+    "//ppapi/thunk/interfaces_ppb_private.h",
+    "//ppapi/thunk/interfaces_ppb_private_flash.h",
+    "//ppapi/thunk/interfaces_ppb_private_no_permissions.h",
+    "//ppapi/thunk/interfaces_ppb_public_dev.h",
+    "//ppapi/thunk/interfaces_ppb_public_dev_channel.h",
+    "//ppapi/thunk/interfaces_ppb_public_stable.h",
+    "//ppapi/thunk/interfaces_preamble.h",
+    "//ppapi/thunk/ppapi_thunk_export.h",
+    "//ppapi/thunk/ppb_graphics_3d_api.h",
+    "//ppapi/thunk/ppb_graphics_3d_thunk.cc",
+    "//ppapi/thunk/ppb_instance_api.h",
+    "//ppapi/thunk/ppb_instance_thunk.cc",
+    "//ppapi/thunk/ppb_message_loop_api.h",
+    "//ppapi/thunk/ppb_view_api.h",
+    "//ppapi/thunk/ppb_view_thunk.cc",
+    "//ppapi/thunk/resource_creation_api.h",
+    "//ppapi/thunk/thunk.h",
+
+    "graphics_3d_resource.cc",
+    "graphics_3d_resource.h",
+    "interface_list.cc",
+    "interface_list.h",
+    "mojo_ppapi_globals.cc",
+    "mojo_ppapi_globals.h",
+    "pepper_container_app.cc",
+    "plugin_instance.cc",
+    "plugin_instance.h",
+    "plugin_module.cc",
+    "plugin_module.h",
+    "ppb_core_thunk.cc",
+    "ppb_opengles2_thunk.cc",
+    "resource_creation_impl.cc",
+    "resource_creation_impl.h",
+    "thunk.h",
+    "type_converters.h",
+  ]
+
+  defines = [
+    # We don't really want to export. We could change how
+    # ppapi_{shared,thunk}_export.h are defined to avoid this.
+    "PPAPI_SHARED_IMPLEMENTATION",
+    "PPAPI_THUNK_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//gpu/command_buffer/common",
+    "//ppapi:ppapi_c",
+    # TODO(GYP):
+    # "//ppapi:ppapi_example_gles2_spinning_cube",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/gpu",
+    "//mojo/services/public/interfaces/native_viewport",
+    "//ui/events:events_base",
+  ]
+}
diff --git a/mojo/examples/pepper_container_app/DEPS b/mojo/examples/pepper_container_app/DEPS
new file mode 100644
index 0000000..b1e8ef3
--- /dev/null
+++ b/mojo/examples/pepper_container_app/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+base",
+  "+ppapi/c",
+  "+ppapi/shared_impl",
+  "+ppapi/thunk",
+]
diff --git a/mojo/examples/pepper_container_app/OWNERS b/mojo/examples/pepper_container_app/OWNERS
new file mode 100644
index 0000000..4523b30
--- /dev/null
+++ b/mojo/examples/pepper_container_app/OWNERS
@@ -0,0 +1,5 @@
+bbudge@chromium.org
+dmichael@chromium.org
+raymes@chromium.org
+teravest@chromium.org
+yzshen@chromium.org
diff --git a/mojo/examples/pepper_container_app/graphics_3d_resource.cc b/mojo/examples/pepper_container_app/graphics_3d_resource.cc
new file mode 100644
index 0000000..ef6bd76
--- /dev/null
+++ b/mojo/examples/pepper_container_app/graphics_3d_resource.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium 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/examples/pepper_container_app/graphics_3d_resource.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "ppapi/c/pp_errors.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+gpu::CommandBuffer::State GetErrorState() {
+  gpu::CommandBuffer::State error_state;
+  error_state.error = gpu::error::kGenericError;
+  return error_state;
+}
+
+}  // namespace
+
+Graphics3DResource::Graphics3DResource(PP_Instance instance)
+    : Resource(ppapi::OBJECT_IS_IMPL, instance) {
+  ScopedMessagePipeHandle pipe = MojoPpapiGlobals::Get()->CreateGLES2Context();
+  context_ = MojoGLES2CreateContext(pipe.release().value(),
+                                    &ContextLostThunk,
+                                    this,
+                                    Environment::GetDefaultAsyncWaiter());
+}
+
+bool Graphics3DResource::IsBoundGraphics() const {
+  PluginInstance* plugin_instance =
+      MojoPpapiGlobals::Get()->GetInstance(pp_instance());
+  return plugin_instance && plugin_instance->IsBoundGraphics(pp_resource());
+}
+
+void Graphics3DResource::BindGraphics() {
+  MojoGLES2MakeCurrent(context_);
+}
+
+ppapi::thunk::PPB_Graphics3D_API* Graphics3DResource::AsPPB_Graphics3D_API() {
+  return this;
+}
+
+int32_t Graphics3DResource::GetAttribs(int32_t attrib_list[]) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::SetAttribs(const int32_t attrib_list[]) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::GetError() {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::ResizeBuffers(int32_t width, int32_t height) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::SwapBuffers(
+    scoped_refptr<ppapi::TrackedCallback> callback) {
+  if (!IsBoundGraphics())
+    return PP_ERROR_FAILED;
+
+  MojoGLES2SwapBuffers();
+  return PP_OK;
+}
+
+int32_t Graphics3DResource::GetAttribMaxValue(int32_t attribute,
+                                              int32_t* value) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+PP_Bool Graphics3DResource::SetGetBuffer(int32_t shm_id) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+scoped_refptr<gpu::Buffer> Graphics3DResource::CreateTransferBuffer(
+    uint32_t size,
+    int32* id) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+PP_Bool Graphics3DResource::DestroyTransferBuffer(int32_t id) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Bool Graphics3DResource::Flush(int32_t put_offset) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+gpu::CommandBuffer::State Graphics3DResource::WaitForTokenInRange(int32_t start,
+                                                                  int32_t end) {
+  NOTIMPLEMENTED();
+  return GetErrorState();
+}
+
+gpu::CommandBuffer::State Graphics3DResource::WaitForGetOffsetInRange(
+    int32_t start, int32_t end) {
+  NOTIMPLEMENTED();
+  return GetErrorState();
+}
+
+void* Graphics3DResource::MapTexSubImage2DCHROMIUM(GLenum target,
+                                                   GLint level,
+                                                   GLint xoffset,
+                                                   GLint yoffset,
+                                                   GLsizei width,
+                                                   GLsizei height,
+                                                   GLenum format,
+                                                   GLenum type,
+                                                   GLenum access) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+void Graphics3DResource::UnmapTexSubImage2DCHROMIUM(const void* mem) {
+  NOTIMPLEMENTED();
+}
+
+uint32_t Graphics3DResource::InsertSyncPoint() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+uint32_t Graphics3DResource::InsertFutureSyncPoint() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void Graphics3DResource::RetireSyncPoint(uint32_t sync_point) {
+  NOTIMPLEMENTED();
+}
+
+Graphics3DResource::~Graphics3DResource() {
+  MojoGLES2DestroyContext(context_);
+}
+
+void Graphics3DResource::ContextLostThunk(void* closure) {
+  static_cast<Graphics3DResource*>(closure)->ContextLost();
+}
+
+void Graphics3DResource::ContextLost() {
+  PluginInstance* plugin_instance =
+      MojoPpapiGlobals::Get()->GetInstance(pp_instance());
+  if (plugin_instance)
+    plugin_instance->Graphics3DContextLost();
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/graphics_3d_resource.h b/mojo/examples/pepper_container_app/graphics_3d_resource.h
new file mode 100644
index 0000000..b6ad3b5
--- /dev/null
+++ b/mojo/examples/pepper_container_app/graphics_3d_resource.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_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
+
+#include "base/macros.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "ppapi/shared_impl/resource.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+class Graphics3DResource : public ppapi::Resource,
+                           public ppapi::thunk::PPB_Graphics3D_API {
+ public:
+  explicit Graphics3DResource(PP_Instance instance);
+
+  bool IsBoundGraphics() const;
+  void BindGraphics();
+
+  // ppapi::Resource overrides.
+  virtual ppapi::thunk::PPB_Graphics3D_API* AsPPB_Graphics3D_API() override;
+
+  // ppapi::thunk::PPB_Graphics3D_API implementation.
+  virtual int32_t GetAttribs(int32_t attrib_list[]) override;
+  virtual int32_t SetAttribs(const int32_t attrib_list[]) override;
+  virtual int32_t GetError() override;
+  virtual int32_t ResizeBuffers(int32_t width, int32_t height) override;
+  virtual int32_t SwapBuffers(
+      scoped_refptr<ppapi::TrackedCallback> callback) override;
+  virtual int32_t GetAttribMaxValue(int32_t attribute, int32_t* value) override;
+  virtual PP_Bool SetGetBuffer(int32_t shm_id) override;
+  virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
+                                                          int32* id) override;
+  virtual PP_Bool DestroyTransferBuffer(int32_t id) override;
+  virtual PP_Bool Flush(int32_t put_offset) override;
+  virtual gpu::CommandBuffer::State WaitForTokenInRange(int32_t start,
+                                                        int32_t end) override;
+  virtual gpu::CommandBuffer::State WaitForGetOffsetInRange(
+      int32_t start, int32_t end) override;
+  virtual void* MapTexSubImage2DCHROMIUM(GLenum target,
+                                         GLint level,
+                                         GLint xoffset,
+                                         GLint yoffset,
+                                         GLsizei width,
+                                         GLsizei height,
+                                         GLenum format,
+                                         GLenum type,
+                                         GLenum access) override;
+  virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) override;
+  virtual uint32_t InsertSyncPoint() override;
+  virtual uint32_t InsertFutureSyncPoint() override;
+  virtual void RetireSyncPoint(uint32_t sync_point) override;
+
+ private:
+  virtual ~Graphics3DResource();
+
+  static void ContextLostThunk(void* closure);
+  void ContextLost();
+
+  MojoGLES2Context context_;
+  DISALLOW_COPY_AND_ASSIGN(Graphics3DResource);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
diff --git a/mojo/examples/pepper_container_app/interface_list.cc b/mojo/examples/pepper_container_app/interface_list.cc
new file mode 100644
index 0000000..7f4d2ea
--- /dev/null
+++ b/mojo/examples/pepper_container_app/interface_list.cc
@@ -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.
+
+#include "mojo/examples/pepper_container_app/interface_list.h"
+
+#include "base/memory/singleton.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/c/ppb_graphics_3d.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_opengles2.h"
+#include "ppapi/c/ppb_view.h"
+#include "ppapi/thunk/thunk.h"
+
+namespace mojo {
+namespace examples {
+
+InterfaceList::InterfaceList() {
+  browser_interfaces_[PPB_CORE_INTERFACE_1_0] = GetPPB_Core_1_0_Thunk();
+  browser_interfaces_[PPB_GRAPHICS_3D_INTERFACE_1_0] =
+      ppapi::thunk::GetPPB_Graphics3D_1_0_Thunk();
+  browser_interfaces_[PPB_OPENGLES2_INTERFACE_1_0] =
+      GetPPB_OpenGLES2_Thunk();
+  browser_interfaces_[PPB_INSTANCE_INTERFACE_1_0] =
+      ppapi::thunk::GetPPB_Instance_1_0_Thunk();
+  browser_interfaces_[PPB_VIEW_INTERFACE_1_0] =
+      ppapi::thunk::GetPPB_View_1_0_Thunk();
+  browser_interfaces_[PPB_VIEW_INTERFACE_1_1] =
+      ppapi::thunk::GetPPB_View_1_1_Thunk();
+}
+
+InterfaceList::~InterfaceList() {}
+
+// static
+InterfaceList* InterfaceList::GetInstance() {
+  return Singleton<InterfaceList>::get();
+}
+
+const void* InterfaceList::GetBrowserInterface(const std::string& name) const {
+  NameToInterfaceMap::const_iterator iter = browser_interfaces_.find(name);
+
+  if (iter == browser_interfaces_.end())
+    return NULL;
+
+  return iter->second;
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/interface_list.h b/mojo/examples/pepper_container_app/interface_list.h
new file mode 100644
index 0000000..5cff06c
--- /dev/null
+++ b/mojo/examples/pepper_container_app/interface_list.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_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+
+namespace mojo {
+namespace examples {
+
+// InterfaceList maintains the mapping from Pepper interface names to
+// interface pointers.
+class InterfaceList {
+ public:
+  InterfaceList();
+  ~InterfaceList();
+
+  static InterfaceList* GetInstance();
+
+  const void* GetBrowserInterface(const std::string& name) const;
+
+ private:
+  typedef std::map<std::string, const void*> NameToInterfaceMap;
+  NameToInterfaceMap browser_interfaces_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfaceList);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
diff --git a/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc b/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc
new file mode 100644
index 0000000..c9e7a2b
--- /dev/null
+++ b/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc
@@ -0,0 +1,189 @@
+// Copyright 2014 The Chromium 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/examples/pepper_container_app/mojo_ppapi_globals.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/ppb_message_loop_shared.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const PP_Instance kInstanceId = 1;
+
+}  // namespace
+
+// A non-abstract subclass of ppapi::MessageLoopShared that represents the
+// message loop of the main thread.
+// TODO(yzshen): Build a more general ppapi::MessageLoopShared subclass to fully
+// support PPB_MessageLoop.
+class MojoPpapiGlobals::MainThreadMessageLoopResource
+    : public ppapi::MessageLoopShared {
+ public:
+  explicit MainThreadMessageLoopResource(
+      base::MessageLoopProxy* main_thread_message_loop)
+      : MessageLoopShared(ForMainThread()),
+        main_thread_message_loop_(main_thread_message_loop) {}
+
+  // ppapi::MessageLoopShared implementation.
+  virtual void PostClosure(const tracked_objects::Location& from_here,
+                           const base::Closure& closure,
+                           int64 delay_ms) override {
+    main_thread_message_loop_->PostDelayedTask(
+        from_here, closure, base::TimeDelta::FromMilliseconds(delay_ms));
+  }
+
+  virtual base::MessageLoopProxy* GetMessageLoopProxy() override {
+    return main_thread_message_loop_.get();
+  }
+
+  virtual bool CurrentlyHandlingBlockingMessage() override {
+    return false;
+  }
+
+  // ppapi::thunk::PPB_MessageLoop_API implementation.
+  virtual int32_t AttachToCurrentThread() override {
+    NOTIMPLEMENTED();
+    return PP_ERROR_FAILED;
+  }
+
+  virtual int32_t Run() override {
+    NOTIMPLEMENTED();
+    return PP_ERROR_FAILED;
+  }
+
+  virtual int32_t PostWork(PP_CompletionCallback callback,
+                           int64_t delay_ms) override {
+    NOTIMPLEMENTED();
+    return PP_ERROR_FAILED;
+  }
+
+  virtual int32_t PostQuit(PP_Bool should_destroy) override {
+    NOTIMPLEMENTED();
+    return PP_ERROR_FAILED;
+  }
+
+ private:
+  virtual ~MainThreadMessageLoopResource() {}
+
+  scoped_refptr<base::MessageLoopProxy> main_thread_message_loop_;
+  DISALLOW_COPY_AND_ASSIGN(MainThreadMessageLoopResource);
+};
+
+MojoPpapiGlobals::MojoPpapiGlobals(Delegate* delegate)
+    : delegate_(delegate),
+      plugin_instance_(NULL),
+      resource_tracker_(ppapi::ResourceTracker::THREAD_SAFE) {}
+
+MojoPpapiGlobals::~MojoPpapiGlobals() {}
+
+PP_Instance MojoPpapiGlobals::AddInstance(PluginInstance* instance) {
+  DCHECK(!plugin_instance_);
+  plugin_instance_ = instance;
+  resource_tracker_.DidCreateInstance(kInstanceId);
+  return kInstanceId;
+}
+
+void MojoPpapiGlobals::InstanceDeleted(PP_Instance instance) {
+  DCHECK_EQ(instance, kInstanceId);
+  DCHECK(plugin_instance_);
+  resource_tracker_.DidDeleteInstance(instance);
+  plugin_instance_ = NULL;
+}
+
+PluginInstance* MojoPpapiGlobals::GetInstance(PP_Instance instance) {
+  if (instance == kInstanceId)
+    return plugin_instance_;
+  return NULL;
+}
+
+ScopedMessagePipeHandle MojoPpapiGlobals::CreateGLES2Context() {
+  return delegate_->CreateGLES2Context();
+}
+
+ppapi::ResourceTracker* MojoPpapiGlobals::GetResourceTracker() {
+  return &resource_tracker_;
+}
+
+ppapi::VarTracker* MojoPpapiGlobals::GetVarTracker() {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+ppapi::CallbackTracker* MojoPpapiGlobals::GetCallbackTrackerForInstance(
+    PP_Instance instance) {
+  if (instance == kInstanceId && plugin_instance_)
+    return plugin_instance_->plugin_module()->callback_tracker();
+  return NULL;
+}
+
+void MojoPpapiGlobals::LogWithSource(PP_Instance instance,
+                                     PP_LogLevel level,
+                                     const std::string& source,
+                                     const std::string& value) {
+  NOTIMPLEMENTED();
+}
+
+void MojoPpapiGlobals::BroadcastLogWithSource(PP_Module module,
+                                              PP_LogLevel level,
+                                              const std::string& source,
+                                              const std::string& value) {
+  NOTIMPLEMENTED();
+}
+
+ppapi::thunk::PPB_Instance_API* MojoPpapiGlobals::GetInstanceAPI(
+    PP_Instance instance) {
+  if (instance == kInstanceId && plugin_instance_)
+    return plugin_instance_;
+  return NULL;
+}
+
+ppapi::thunk::ResourceCreationAPI* MojoPpapiGlobals::GetResourceCreationAPI(
+    PP_Instance instance) {
+  if (instance == kInstanceId && plugin_instance_)
+    return plugin_instance_->resource_creation();
+  return NULL;
+}
+
+PP_Module MojoPpapiGlobals::GetModuleForInstance(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+ppapi::MessageLoopShared* MojoPpapiGlobals::GetCurrentMessageLoop() {
+  if (base::MessageLoopProxy::current().get() == GetMainThreadMessageLoop()) {
+    if (!main_thread_message_loop_resource_.get()) {
+      main_thread_message_loop_resource_ = new MainThreadMessageLoopResource(
+          GetMainThreadMessageLoop());
+    }
+    return main_thread_message_loop_resource_.get();
+  }
+
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+base::TaskRunner* MojoPpapiGlobals::GetFileTaskRunner() {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+std::string MojoPpapiGlobals::GetCmdLine() {
+  NOTIMPLEMENTED();
+  return std::string();
+}
+
+void MojoPpapiGlobals::PreCacheFontForFlash(const void* logfontw) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/mojo_ppapi_globals.h b/mojo/examples/pepper_container_app/mojo_ppapi_globals.h
new file mode 100644
index 0000000..037d161
--- /dev/null
+++ b/mojo/examples/pepper_container_app/mojo_ppapi_globals.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium 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_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/core.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance;
+
+class MojoPpapiGlobals : public ppapi::PpapiGlobals {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    virtual ScopedMessagePipeHandle CreateGLES2Context() = 0;
+  };
+
+  // |delegate| must live longer than this object.
+  explicit MojoPpapiGlobals(Delegate* delegate);
+  virtual ~MojoPpapiGlobals();
+
+  inline static MojoPpapiGlobals* Get() {
+    return static_cast<MojoPpapiGlobals*>(PpapiGlobals::Get());
+  }
+
+  PP_Instance AddInstance(PluginInstance* instance);
+  void InstanceDeleted(PP_Instance instance);
+  PluginInstance* GetInstance(PP_Instance instance);
+
+  ScopedMessagePipeHandle CreateGLES2Context();
+
+  // ppapi::PpapiGlobals implementation.
+  virtual ppapi::ResourceTracker* GetResourceTracker() override;
+  virtual ppapi::VarTracker* GetVarTracker() override;
+  virtual ppapi::CallbackTracker* GetCallbackTrackerForInstance(
+      PP_Instance instance) override;
+  virtual void LogWithSource(PP_Instance instance,
+                             PP_LogLevel level,
+                             const std::string& source,
+                             const std::string& value) override;
+  virtual void BroadcastLogWithSource(PP_Module module,
+                                      PP_LogLevel level,
+                                      const std::string& source,
+                                      const std::string& value) override;
+  virtual ppapi::thunk::PPB_Instance_API* GetInstanceAPI(
+      PP_Instance instance) override;
+  virtual ppapi::thunk::ResourceCreationAPI* GetResourceCreationAPI(
+      PP_Instance instance) override;
+  virtual PP_Module GetModuleForInstance(PP_Instance instance) override;
+  virtual ppapi::MessageLoopShared* GetCurrentMessageLoop() override;
+  virtual base::TaskRunner* GetFileTaskRunner() override;
+  virtual std::string GetCmdLine() override;
+  virtual void PreCacheFontForFlash(const void* logfontw) override;
+
+ private:
+  class MainThreadMessageLoopResource;
+
+  // Non-owning pointer.
+  Delegate* const delegate_;
+
+  // Non-owning pointer.
+  PluginInstance* plugin_instance_;
+
+  ppapi::ResourceTracker resource_tracker_;
+
+  scoped_refptr<MainThreadMessageLoopResource>
+      main_thread_message_loop_resource_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoPpapiGlobals);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
diff --git a/mojo/examples/pepper_container_app/pepper_container_app.cc b/mojo/examples/pepper_container_app/pepper_container_app.cc
new file mode 100644
index 0000000..4b64441
--- /dev/null
+++ b/mojo/examples/pepper_container_app/pepper_container_app.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 "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "build/build_config.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "mojo/examples/pepper_container_app/plugin_module.h"
+#include "mojo/examples/pepper_container_app/type_converters.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+
+namespace mojo {
+namespace examples {
+
+class PepperContainerApp: public ApplicationDelegate,
+                          public NativeViewportClient,
+                          public MojoPpapiGlobals::Delegate {
+ public:
+  PepperContainerApp()
+      : ppapi_globals_(this),
+        plugin_module_(new PluginModule),
+        weak_factory_(this) {}
+
+  virtual ~PepperContainerApp() {}
+
+  virtual void Initialize(ApplicationImpl* app) override {
+    app->ConnectToService("mojo:mojo_native_viewport_service", &viewport_);
+    viewport_.set_client(this);
+
+    // TODO(jamesr): Should be mojo:mojo_gpu_service
+    app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+
+    SizePtr size(Size::New());
+    size->width = 800;
+    size->height = 600;
+    viewport_->Create(size.Pass(),
+                      base::Bind(&PepperContainerApp::OnCreatedNativeViewport,
+                                 weak_factory_.GetWeakPtr()));
+    viewport_->Show();
+  }
+
+  // NativeViewportClient implementation.
+  virtual void OnDestroyed() override {
+    ppapi::ProxyAutoLock lock;
+
+    if (plugin_instance_) {
+      plugin_instance_->DidDestroy();
+      plugin_instance_.reset();
+    }
+
+    base::MessageLoop::current()->Quit();
+  }
+
+  virtual void OnSizeChanged(SizePtr size) override {
+    ppapi::ProxyAutoLock lock;
+
+    if (plugin_instance_) {
+      PP_Rect pp_rect = {{0, 0}, {size->width, size->height}};
+      plugin_instance_->DidChangeView(pp_rect);
+    }
+  }
+
+  virtual void OnEvent(EventPtr event,
+                       const mojo::Callback<void()>& callback) override {
+    if (!event->location_data.is_null()) {
+      ppapi::ProxyAutoLock lock;
+
+      // TODO(yzshen): Handle events.
+    }
+    callback.Run();
+  }
+
+  // MojoPpapiGlobals::Delegate implementation.
+  virtual ScopedMessagePipeHandle CreateGLES2Context() override {
+    CommandBufferPtr command_buffer;
+    SizePtr size = Size::New();
+    size->width = 800;
+    size->width = 600;
+    // TODO(jamesr): Output a surface to the native viewport instead.
+    gpu_service_->CreateOnscreenGLES2Context(
+        native_viewport_id_, size.Pass(), Get(&command_buffer));
+    return command_buffer.PassMessagePipe();
+  }
+
+ private:
+  void OnCreatedNativeViewport(uint64_t native_viewport_id) {
+    native_viewport_id_ = native_viewport_id;
+    ppapi::ProxyAutoLock lock;
+
+    plugin_instance_ = plugin_module_->CreateInstance().Pass();
+    if (!plugin_instance_->DidCreate())
+      plugin_instance_.reset();
+  }
+
+  MojoPpapiGlobals ppapi_globals_;
+
+  uint64_t native_viewport_id_;
+  NativeViewportPtr viewport_;
+  GpuPtr gpu_service_;
+  scoped_refptr<PluginModule> plugin_module_;
+  scoped_ptr<PluginInstance> plugin_instance_;
+
+  base::WeakPtrFactory<PepperContainerApp> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperContainerApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(
+      new mojo::examples::PepperContainerApp);
+  return runner.Run(shell_handle);
+}
+
diff --git a/mojo/examples/pepper_container_app/plugin_instance.cc b/mojo/examples/pepper_container_app/plugin_instance.cc
new file mode 100644
index 0000000..e0b08ef
--- /dev/null
+++ b/mojo/examples/pepper_container_app/plugin_instance.cc
@@ -0,0 +1,444 @@
+// Copyright 2014 The Chromium 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/examples/pepper_container_app/plugin_instance.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppp_graphics_3d.h"
+#include "ppapi/c/ppp_instance.h"
+#include "ppapi/shared_impl/ppb_view_shared.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+PluginInstance::PluginInstance(scoped_refptr<PluginModule> plugin_module)
+    : pp_instance_(0),
+      plugin_module_(plugin_module) {
+  pp_instance_ = MojoPpapiGlobals::Get()->AddInstance(this);
+}
+
+PluginInstance::~PluginInstance() {
+  MojoPpapiGlobals::Get()->InstanceDeleted(pp_instance_);
+}
+
+bool PluginInstance::DidCreate() {
+  ppapi::ProxyAutoUnlock unlock;
+  const PPP_Instance_1_1* instance_interface =
+      static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+          PPP_INSTANCE_INTERFACE_1_1));
+  return !!instance_interface->DidCreate(pp_instance(), 0, NULL, NULL);
+}
+
+void PluginInstance::DidDestroy() {
+  ppapi::ProxyAutoUnlock unlock;
+  const PPP_Instance_1_1* instance_interface =
+      static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+          PPP_INSTANCE_INTERFACE_1_1));
+  instance_interface->DidDestroy(pp_instance());
+}
+
+void PluginInstance::DidChangeView(const PP_Rect& bounds) {
+  ppapi::ViewData view_data;
+  view_data.rect = bounds;
+  view_data.is_fullscreen = false;
+  view_data.is_page_visible = true;
+  view_data.clip_rect = bounds;
+  view_data.device_scale = 1.0f;
+  view_data.css_scale = 1.0f;
+
+  ppapi::ScopedPPResource resource(ppapi::ScopedPPResource::PassRef(),
+      (new ppapi::PPB_View_Shared(
+          ppapi::OBJECT_IS_IMPL, pp_instance(), view_data))->GetReference());
+  {
+    ppapi::ProxyAutoUnlock unlock;
+    const PPP_Instance_1_1* instance_interface =
+        static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+            PPP_INSTANCE_INTERFACE_1_1));
+    instance_interface->DidChangeView(pp_instance(), resource);
+  }
+}
+
+void PluginInstance::Graphics3DContextLost() {
+  ppapi::ProxyAutoUnlock unlock;
+  const PPP_Graphics3D_1_0* graphic_3d_interface =
+      static_cast<const PPP_Graphics3D_1_0*>(plugin_module_->GetPluginInterface(
+          PPP_GRAPHICS_3D_INTERFACE_1_0));
+  // TODO(yzshen): Maybe we only need to notify for the bound graphics context?
+  graphic_3d_interface->Graphics3DContextLost(pp_instance());
+}
+
+bool PluginInstance::IsBoundGraphics(PP_Resource device) const {
+  return device != 0 && device == bound_graphics_.get();
+}
+
+PP_Bool PluginInstance::BindGraphics(PP_Instance instance, PP_Resource device) {
+  if (bound_graphics_.get() == device)
+    return PP_TRUE;
+
+  ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_Graphics3D_API>
+      enter(device, false);
+  if (enter.failed())
+    return PP_FALSE;
+
+  bound_graphics_ = device;
+  static_cast<Graphics3DResource*>(enter.object())->BindGraphics();
+
+  return PP_TRUE;
+}
+
+PP_Bool PluginInstance::IsFullFrame(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+const ppapi::ViewData* PluginInstance::GetViewData(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+PP_Bool PluginInstance::FlashIsFullscreen(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Var PluginInstance::GetWindowObject(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::GetOwnerElementObject(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::ExecuteScript(PP_Instance instance,
+                                     PP_Var script,
+                                     PP_Var* exception) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+uint32_t PluginInstance::GetAudioHardwareOutputSampleRate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+uint32_t PluginInstance::GetAudioHardwareOutputBufferSize(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Var PluginInstance::GetDefaultCharSet(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+void PluginInstance::Log(PP_Instance instance,
+                         PP_LogLevel log_level,
+                         PP_Var value) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::LogWithSource(PP_Instance instance,
+                                   PP_LogLevel log_level,
+                                   PP_Var source,
+                                   PP_Var value) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetPluginToHandleFindRequests(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::NumberOfFindResultsChanged(PP_Instance instance,
+                                                int32_t total,
+                                                PP_Bool final_result) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SelectedFindResultChanged(PP_Instance instance,
+                                               int32_t index) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetTickmarks(PP_Instance instance,
+                                  const PP_Rect* tickmarks,
+                                  uint32_t count) {
+  NOTIMPLEMENTED();
+}
+
+PP_Bool PluginInstance::IsFullscreen(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Bool PluginInstance::SetFullscreen(PP_Instance instance,
+                                      PP_Bool fullscreen) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Bool PluginInstance::GetScreenSize(PP_Instance instance, PP_Size* size) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+ppapi::Resource* PluginInstance::GetSingletonResource(
+    PP_Instance instance,
+    ppapi::SingletonResourceID id) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+int32_t PluginInstance::RequestInputEvents(PP_Instance instance,
+                                           uint32_t event_classes) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+int32_t PluginInstance::RequestFilteringInputEvents(PP_Instance instance,
+                                                    uint32_t event_classes) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+void PluginInstance::ClearInputEventRequest(PP_Instance instance,
+                                            uint32_t event_classes) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::StartTrackingLatency(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::PostMessage(PP_Instance instance, PP_Var message) {
+  NOTIMPLEMENTED();
+}
+
+int32_t PluginInstance::RegisterMessageHandler(
+    PP_Instance instance,
+    void* user_data,
+    const PPP_MessageHandler_0_2* handler,
+    PP_Resource message_loop) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+// TODO(dmichael): Remove this. crbug.com/414398
+int32_t PluginInstance::RegisterMessageHandler_1_1_Deprecated(
+    PP_Instance instance,
+    void* user_data,
+    const PPP_MessageHandler_0_1_Deprecated* handler,
+    PP_Resource message_loop) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+void PluginInstance::UnregisterMessageHandler(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+PP_Bool PluginInstance::SetCursor(PP_Instance instance,
+                                  PP_MouseCursor_Type type,
+                                  PP_Resource image,
+                                  const PP_Point* hot_spot) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+int32_t PluginInstance::LockMouse(
+    PP_Instance instance,
+    scoped_refptr<ppapi::TrackedCallback> callback) {
+  NOTIMPLEMENTED();
+  return PP_ERROR_FAILED;
+}
+
+void PluginInstance::UnlockMouse(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetTextInputType(PP_Instance instance,
+                                      PP_TextInput_Type type) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::UpdateCaretPosition(PP_Instance instance,
+                                         const PP_Rect& caret,
+                                         const PP_Rect& bounding_box) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::CancelCompositionText(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SelectionChanged(PP_Instance instance) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::UpdateSurroundingText(PP_Instance instance,
+                                           const char* text,
+                                           uint32_t caret,
+                                           uint32_t anchor) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::ZoomChanged(PP_Instance instance, double factor) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::ZoomLimitsChanged(PP_Instance instance,
+                                       double minimum_factor,
+                                       double maximum_factor) {
+  NOTIMPLEMENTED();
+}
+
+PP_Var PluginInstance::GetDocumentURL(PP_Instance instance,
+                                      PP_URLComponents_Dev* components) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+void PluginInstance::PromiseResolved(PP_Instance instance, uint32 promise_id) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::PromiseResolvedWithSession(PP_Instance instance,
+                                                uint32 promise_id,
+                                                PP_Var web_session_id_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::PromiseResolvedWithKeyIds(PP_Instance instance,
+                                               uint32 promise_id,
+                                               PP_Var key_ids_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::PromiseRejected(PP_Instance instance,
+                                     uint32 promise_id,
+                                     PP_CdmExceptionCode exception_code,
+                                     uint32 system_code,
+                                     PP_Var error_description_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionMessage(PP_Instance instance,
+                                    PP_Var web_session_id_var,
+                                    PP_Var message_var,
+                                    PP_Var destination_url_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionKeysChange(PP_Instance instance,
+                                       PP_Var web_session_id_var,
+                                       PP_Bool has_additional_usable_key) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionExpirationChange(PP_Instance instance,
+                                             PP_Var web_session_id_var,
+                                             PP_Time new_expiry_time) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionReady(PP_Instance instance,
+                                  PP_Var web_session_id_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionClosed(PP_Instance instance,
+                                   PP_Var web_session_id_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionError(PP_Instance instance,
+                                  PP_Var web_session_id_var,
+                                  PP_CdmExceptionCode exception_code,
+                                  uint32 system_code,
+                                  PP_Var error_description_var) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverBlock(PP_Instance instance,
+                                  PP_Resource decrypted_block,
+                                  const PP_DecryptedBlockInfo* block_info) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderInitializeDone(PP_Instance instance,
+                                           PP_DecryptorStreamType decoder_type,
+                                           uint32_t request_id,
+                                           PP_Bool success) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderDeinitializeDone(
+    PP_Instance instance,
+    PP_DecryptorStreamType decoder_type,
+    uint32_t request_id) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderResetDone(PP_Instance instance,
+                                      PP_DecryptorStreamType decoder_type,
+                                      uint32_t request_id) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverFrame(PP_Instance instance,
+                                  PP_Resource decrypted_frame,
+                                  const PP_DecryptedFrameInfo* frame_info) {
+  NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverSamples(PP_Instance instance,
+                                    PP_Resource audio_frames,
+                                    const PP_DecryptedSampleInfo* sample_info) {
+  NOTIMPLEMENTED();
+}
+
+PP_Var PluginInstance::ResolveRelativeToDocument(
+    PP_Instance instance,
+    PP_Var relative,
+    PP_URLComponents_Dev* components) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+PP_Bool PluginInstance::DocumentCanRequest(PP_Instance instance, PP_Var url) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Bool PluginInstance::DocumentCanAccessDocument(PP_Instance instance,
+                                                  PP_Instance target) {
+  NOTIMPLEMENTED();
+  return PP_FALSE;
+}
+
+PP_Var PluginInstance::GetPluginInstanceURL(PP_Instance instance,
+                                            PP_URLComponents_Dev* components) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::GetPluginReferrerURL(PP_Instance instance,
+                                            PP_URLComponents_Dev* components) {
+  NOTIMPLEMENTED();
+  return PP_MakeUndefined();
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/plugin_instance.h b/mojo/examples/pepper_container_app/plugin_instance.h
new file mode 100644
index 0000000..6a3240f
--- /dev/null
+++ b/mojo/examples/pepper_container_app/plugin_instance.h
@@ -0,0 +1,199 @@
+// Copyright 2014 The Chromium 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_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/examples/pepper_container_app/plugin_module.h"
+#include "mojo/examples/pepper_container_app/resource_creation_impl.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/shared_impl/scoped_pp_resource.h"
+#include "ppapi/thunk/ppb_instance_api.h"
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance : public ppapi::thunk::PPB_Instance_API {
+ public:
+  explicit PluginInstance(scoped_refptr<PluginModule> plugin_module);
+  virtual ~PluginInstance();
+
+  // Notifies the plugin that a new instance has been created.
+  bool DidCreate();
+  // Notifies the plugin that the instance has been destroyed.
+  void DidDestroy();
+  // Notifies the plugin that the position or size of the instance has changed.
+  void DidChangeView(const PP_Rect& bounds);
+  // Notifies the plugin that the Graphics 3D context has been invalidated.
+  void Graphics3DContextLost();
+
+  // Returns true if |device| has been bound as the current display surface.
+  bool IsBoundGraphics(PP_Resource device) const;
+
+  PP_Instance pp_instance() const { return pp_instance_; }
+  ResourceCreationImpl* resource_creation() { return &resource_creation_; }
+  PluginModule* plugin_module() { return plugin_module_.get(); }
+
+  // ppapi::thunk::PPB_Instance_API implementation.
+  virtual PP_Bool BindGraphics(PP_Instance instance,
+                               PP_Resource device) override;
+  virtual PP_Bool IsFullFrame(PP_Instance instance) override;
+  virtual const ppapi::ViewData* GetViewData(PP_Instance instance) override;
+  virtual PP_Bool FlashIsFullscreen(PP_Instance instance) override;
+  virtual PP_Var GetWindowObject(PP_Instance instance) override;
+  virtual PP_Var GetOwnerElementObject(PP_Instance instance) override;
+  virtual PP_Var ExecuteScript(PP_Instance instance,
+                               PP_Var script,
+                               PP_Var* exception) override;
+  virtual uint32_t GetAudioHardwareOutputSampleRate(
+      PP_Instance instance) override;
+  virtual uint32_t GetAudioHardwareOutputBufferSize(
+      PP_Instance instance) override;
+  virtual PP_Var GetDefaultCharSet(PP_Instance instance) override;
+  virtual void Log(PP_Instance instance,
+                   PP_LogLevel log_level,
+                   PP_Var value) override;
+  virtual void LogWithSource(PP_Instance instance,
+                             PP_LogLevel log_level,
+                             PP_Var source,
+                             PP_Var value) override;
+  virtual void SetPluginToHandleFindRequests(PP_Instance instance) override;
+  virtual void NumberOfFindResultsChanged(PP_Instance instance,
+                                          int32_t total,
+                                          PP_Bool final_result) override;
+  virtual void SelectedFindResultChanged(PP_Instance instance,
+                                         int32_t index) override;
+  virtual void SetTickmarks(PP_Instance instance,
+                            const PP_Rect* tickmarks,
+                            uint32_t count) override;
+  virtual PP_Bool IsFullscreen(PP_Instance instance) override;
+  virtual PP_Bool SetFullscreen(PP_Instance instance,
+                                PP_Bool fullscreen) override;
+  virtual PP_Bool GetScreenSize(PP_Instance instance, PP_Size* size) override;
+  virtual ppapi::Resource* GetSingletonResource(
+      PP_Instance instance, ppapi::SingletonResourceID id) override;
+  virtual int32_t RequestInputEvents(PP_Instance instance,
+                                     uint32_t event_classes) override;
+  virtual int32_t RequestFilteringInputEvents(PP_Instance instance,
+                                              uint32_t event_classes) override;
+  virtual void ClearInputEventRequest(PP_Instance instance,
+                                      uint32_t event_classes) override;
+  virtual void StartTrackingLatency(PP_Instance instance) override;
+  virtual void PostMessage(PP_Instance instance, PP_Var message) override;
+  virtual int32_t RegisterMessageHandler(PP_Instance instance,
+                                         void* user_data,
+                                         const PPP_MessageHandler_0_2* handler,
+                                         PP_Resource message_loop) override;
+  virtual int32_t RegisterMessageHandler_1_1_Deprecated(
+      PP_Instance instance,
+      void* user_data,
+      const PPP_MessageHandler_0_1_Deprecated* handler,
+      PP_Resource message_loop) override;
+  virtual void UnregisterMessageHandler(PP_Instance instance) override;
+  virtual PP_Bool SetCursor(PP_Instance instance,
+                            PP_MouseCursor_Type type,
+                            PP_Resource image,
+                            const PP_Point* hot_spot) override;
+  virtual int32_t LockMouse(
+      PP_Instance instance,
+      scoped_refptr<ppapi::TrackedCallback> callback) override;
+  virtual void UnlockMouse(PP_Instance instance) override;
+  virtual void SetTextInputType(PP_Instance instance,
+                                PP_TextInput_Type type) override;
+  virtual void UpdateCaretPosition(PP_Instance instance,
+                                   const PP_Rect& caret,
+                                   const PP_Rect& bounding_box) override;
+  virtual void CancelCompositionText(PP_Instance instance) override;
+  virtual void SelectionChanged(PP_Instance instance) override;
+  virtual void UpdateSurroundingText(PP_Instance instance,
+                                     const char* text,
+                                     uint32_t caret,
+                                     uint32_t anchor) override;
+  virtual void ZoomChanged(PP_Instance instance, double factor) override;
+  virtual void ZoomLimitsChanged(PP_Instance instance,
+                                 double minimum_factor,
+                                 double maximum_factor) override;
+  virtual PP_Var GetDocumentURL(PP_Instance instance,
+                                PP_URLComponents_Dev* components) override;
+  virtual void PromiseResolved(PP_Instance instance,
+                               uint32 promise_id) override;
+  virtual void PromiseResolvedWithSession(PP_Instance instance,
+                                          uint32 promise_id,
+                                          PP_Var web_session_id_var) override;
+  virtual void PromiseResolvedWithKeyIds(PP_Instance instance,
+                                         uint32 promise_id,
+                                         PP_Var key_ids_var) override;
+  virtual void PromiseRejected(PP_Instance instance,
+                               uint32 promise_id,
+                               PP_CdmExceptionCode exception_code,
+                               uint32 system_code,
+                               PP_Var error_description_var) override;
+  virtual void SessionMessage(PP_Instance instance,
+                              PP_Var web_session_id_var,
+                              PP_Var message_var,
+                              PP_Var destination_url_var) override;
+  virtual void SessionKeysChange(PP_Instance instance,
+                                 PP_Var web_session_id_var,
+                                 PP_Bool has_additional_usable_key) override;
+  virtual void SessionExpirationChange(PP_Instance instance,
+                                       PP_Var web_session_id_var,
+                                       PP_Time new_expiry_time) override;
+  virtual void SessionReady(PP_Instance instance,
+                            PP_Var web_session_id_var) override;
+  virtual void SessionClosed(PP_Instance instance,
+                             PP_Var web_session_id_var) override;
+  virtual void SessionError(PP_Instance instance,
+                            PP_Var web_session_id_var,
+                            PP_CdmExceptionCode exception_code,
+                            uint32 system_code,
+                            PP_Var error_description_var) override;
+  virtual void DeliverBlock(PP_Instance instance,
+                            PP_Resource decrypted_block,
+                            const PP_DecryptedBlockInfo* block_info) override;
+  virtual void DecoderInitializeDone(PP_Instance instance,
+                                     PP_DecryptorStreamType decoder_type,
+                                     uint32_t request_id,
+                                     PP_Bool success) override;
+  virtual void DecoderDeinitializeDone(PP_Instance instance,
+                                       PP_DecryptorStreamType decoder_type,
+                                       uint32_t request_id) override;
+  virtual void DecoderResetDone(PP_Instance instance,
+                                PP_DecryptorStreamType decoder_type,
+                                uint32_t request_id) override;
+  virtual void DeliverFrame(PP_Instance instance,
+                            PP_Resource decrypted_frame,
+                            const PP_DecryptedFrameInfo* frame_info) override;
+  virtual void DeliverSamples(
+      PP_Instance instance,
+      PP_Resource audio_frames,
+      const PP_DecryptedSampleInfo* sample_info) override;
+  virtual PP_Var ResolveRelativeToDocument(
+      PP_Instance instance,
+      PP_Var relative,
+      PP_URLComponents_Dev* components) override;
+  virtual PP_Bool DocumentCanRequest(PP_Instance instance, PP_Var url) override;
+  virtual PP_Bool DocumentCanAccessDocument(PP_Instance instance,
+                                            PP_Instance target) override;
+  virtual PP_Var GetPluginInstanceURL(
+      PP_Instance instance,
+      PP_URLComponents_Dev* components) override;
+  virtual PP_Var GetPluginReferrerURL(
+      PP_Instance instance,
+      PP_URLComponents_Dev* components) override;
+
+ private:
+  PP_Instance pp_instance_;
+  ResourceCreationImpl resource_creation_;
+  scoped_refptr<PluginModule> plugin_module_;
+  ppapi::ScopedPPResource bound_graphics_;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginInstance);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
diff --git a/mojo/examples/pepper_container_app/plugin_module.cc b/mojo/examples/pepper_container_app/plugin_module.cc
new file mode 100644
index 0000000..d09d42d
--- /dev/null
+++ b/mojo/examples/pepper_container_app/plugin_module.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/examples/pepper_container_app/plugin_module.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "mojo/examples/pepper_container_app/interface_list.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/callback_tracker.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const void* GetInterface(const char* name) {
+  const void* interface =
+      InterfaceList::GetInstance()->GetBrowserInterface(name);
+
+  if (!interface)
+    LOG(WARNING) << "Interface requested " << name;
+
+  return interface;
+}
+
+}  // namespace
+
+PluginModule::EntryPoints::EntryPoints() : get_interface(NULL),
+                                           initialize_module(NULL),
+                                           shutdown_module(NULL) {}
+
+PluginModule::PluginModule() : callback_tracker_(new ppapi::CallbackTracker) {
+  Initialize();
+}
+
+PluginModule::~PluginModule() {
+  callback_tracker_->AbortAll();
+
+  if (entry_points_.shutdown_module)
+    entry_points_.shutdown_module();
+}
+
+scoped_ptr<PluginInstance> PluginModule::CreateInstance() {
+  return make_scoped_ptr(new PluginInstance(this));
+}
+
+const void* PluginModule::GetPluginInterface(const char* name) const {
+  if (entry_points_.get_interface)
+    return entry_points_.get_interface(name);
+  return NULL;
+}
+
+void PluginModule::Initialize() {
+  // Platform-specific filename.
+  // TODO(yzshen): Don't hard-code it.
+#if defined(OS_WIN)
+  static const wchar_t plugin_name[] = L"ppapi_example_gles2_spinning_cube.dll";
+#elif defined(OS_MACOSX)
+  static const char plugin_name[] = "ppapi_example_gles2_spinning_cube.plugin";
+#elif defined(OS_POSIX)
+  static const char plugin_name[] = "libppapi_example_gles2_spinning_cube.so";
+#endif
+
+  base::FilePath plugin_path(plugin_name);
+
+  base::NativeLibraryLoadError error;
+  plugin_module_.Reset(base::LoadNativeLibrary(plugin_path, &error));
+
+  if (!plugin_module_.is_valid()) {
+    LOG(WARNING) << "Cannot load " << plugin_path.AsUTF8Unsafe()
+                 << ". Error: " << error.ToString();
+    return;
+  }
+
+  entry_points_.get_interface =
+      reinterpret_cast<PP_GetInterface_Func>(
+          plugin_module_.GetFunctionPointer("PPP_GetInterface"));
+  if (!entry_points_.get_interface) {
+    LOG(WARNING) << "No PPP_GetInterface in plugin library";
+    return;
+  }
+
+  entry_points_.initialize_module =
+      reinterpret_cast<PP_InitializeModule_Func>(
+          plugin_module_.GetFunctionPointer("PPP_InitializeModule"));
+  if (!entry_points_.initialize_module) {
+    LOG(WARNING) << "No PPP_InitializeModule in plugin library";
+    return;
+  }
+
+  // It's okay for PPP_ShutdownModule to not be defined and |shutdown_module| to
+  // be NULL.
+  entry_points_.shutdown_module =
+      reinterpret_cast<PP_ShutdownModule_Func>(
+          plugin_module_.GetFunctionPointer("PPP_ShutdownModule"));
+
+  int32_t result = entry_points_.initialize_module(pp_module(),
+                                                   &GetInterface);
+  if (result != PP_OK)
+    LOG(WARNING) << "Initializing module failed with error " << result;
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/plugin_module.h b/mojo/examples/pepper_container_app/plugin_module.h
new file mode 100644
index 0000000..05aad3b
--- /dev/null
+++ b/mojo/examples/pepper_container_app/plugin_module.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/native_library.h"
+#include "base/scoped_native_library.h"
+#include "ppapi/c/pp_module.h"
+#include "ppapi/c/ppp.h"
+
+namespace ppapi {
+class CallbackTracker;
+}
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance;
+
+class PluginModule : public base::RefCounted<PluginModule> {
+ public:
+  PluginModule();
+
+  scoped_ptr<PluginInstance> CreateInstance();
+
+  const void* GetPluginInterface(const char* name) const;
+
+  PP_Module pp_module() const { return 1; }
+  ppapi::CallbackTracker* callback_tracker() { return callback_tracker_.get(); }
+
+ private:
+  friend class base::RefCounted<PluginModule>;
+
+  struct EntryPoints {
+    EntryPoints();
+
+    PP_GetInterface_Func get_interface;
+    PP_InitializeModule_Func initialize_module;
+    PP_ShutdownModule_Func shutdown_module;  // Optional, may be NULL.
+  };
+
+  ~PluginModule();
+
+  void Initialize();
+
+  base::ScopedNativeLibrary plugin_module_;
+  EntryPoints entry_points_;
+  scoped_refptr<ppapi::CallbackTracker> callback_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginModule);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
diff --git a/mojo/examples/pepper_container_app/ppb_core_thunk.cc b/mojo/examples/pepper_container_app/ppb_core_thunk.cc
new file mode 100644
index 0000000..a76a976
--- /dev/null
+++ b/mojo/examples/pepper_container_app/ppb_core_thunk.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+void AddRefResource(PP_Resource resource) {
+  ppapi::ProxyAutoLock lock;
+  ppapi::PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(resource);
+}
+
+void ReleaseResource(PP_Resource resource) {
+  ppapi::ProxyAutoLock lock;
+  ppapi::PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(resource);
+}
+
+PP_Time GetTime() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_TimeTicks GetTimeTicks() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void CallOnMainThread(int32_t delay_in_milliseconds,
+                      PP_CompletionCallback callback,
+                      int32_t result) {
+  NOTIMPLEMENTED();
+}
+
+PP_Bool IsMainThread() {
+  NOTIMPLEMENTED();
+  return PP_TRUE;
+}
+
+}  // namespace
+
+const PPB_Core_1_0 g_ppb_core_thunk_1_0 = {
+  &AddRefResource,
+  &ReleaseResource,
+  &GetTime,
+  &GetTimeTicks,
+  &CallOnMainThread,
+  &IsMainThread
+};
+
+const PPB_Core_1_0* GetPPB_Core_1_0_Thunk() {
+  return &g_ppb_core_thunk_1_0;
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc b/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc
new file mode 100644
index 0000000..c5097bb
--- /dev/null
+++ b/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc
@@ -0,0 +1,1454 @@
+// Copyright 2014 The Chromium 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/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+typedef ppapi::thunk::EnterResource<ppapi::thunk::PPB_Graphics3D_API> Enter3D;
+
+bool IsBoundGraphics(Enter3D* enter) {
+  return enter->succeeded() &&
+         static_cast<Graphics3DResource*>(enter->object())->IsBoundGraphics();
+}
+
+void ActiveTexture(PP_Resource context_id, GLenum texture) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glActiveTexture(texture);
+  }
+}
+
+void AttachShader(PP_Resource context_id, GLuint program, GLuint shader) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glAttachShader(program, shader);
+  }
+}
+
+void BindAttribLocation(PP_Resource context_id,
+                        GLuint program,
+                        GLuint index,
+                        const char* name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBindAttribLocation(program, index, name);
+  }
+}
+
+void BindBuffer(PP_Resource context_id, GLenum target, GLuint buffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBindBuffer(target, buffer);
+  }
+}
+
+void BindFramebuffer(PP_Resource context_id,
+                     GLenum target,
+                     GLuint framebuffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBindFramebuffer(target, framebuffer);
+  }
+}
+
+void BindRenderbuffer(PP_Resource context_id,
+                      GLenum target,
+                      GLuint renderbuffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBindRenderbuffer(target, renderbuffer);
+  }
+}
+
+void BindTexture(PP_Resource context_id, GLenum target, GLuint texture) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBindTexture(target, texture);
+  }
+}
+
+void BlendColor(PP_Resource context_id,
+                GLclampf red,
+                GLclampf green,
+                GLclampf blue,
+                GLclampf alpha) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBlendColor(red, green, blue, alpha);
+  }
+}
+
+void BlendEquation(PP_Resource context_id, GLenum mode) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBlendEquation(mode);
+  }
+}
+
+void BlendEquationSeparate(PP_Resource context_id,
+                           GLenum modeRGB,
+                           GLenum modeAlpha) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBlendEquationSeparate(modeRGB, modeAlpha);
+  }
+}
+
+void BlendFunc(PP_Resource context_id, GLenum sfactor, GLenum dfactor) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBlendFunc(sfactor, dfactor);
+  }
+}
+
+void BlendFuncSeparate(PP_Resource context_id,
+                       GLenum srcRGB,
+                       GLenum dstRGB,
+                       GLenum srcAlpha,
+                       GLenum dstAlpha) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+  }
+}
+
+void BufferData(PP_Resource context_id,
+                GLenum target,
+                GLsizeiptr size,
+                const void* data,
+                GLenum usage) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBufferData(target, size, data, usage);
+  }
+}
+
+void BufferSubData(PP_Resource context_id,
+                   GLenum target,
+                   GLintptr offset,
+                   GLsizeiptr size,
+                   const void* data) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glBufferSubData(target, offset, size, data);
+  }
+}
+
+GLenum CheckFramebufferStatus(PP_Resource context_id, GLenum target) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glCheckFramebufferStatus(target);
+  } else {
+    return 0;
+  }
+}
+
+void Clear(PP_Resource context_id, GLbitfield mask) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glClear(mask);
+  }
+}
+
+void ClearColor(PP_Resource context_id,
+                GLclampf red,
+                GLclampf green,
+                GLclampf blue,
+                GLclampf alpha) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glClearColor(red, green, blue, alpha);
+  }
+}
+
+void ClearDepthf(PP_Resource context_id, GLclampf depth) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glClearDepthf(depth);
+  }
+}
+
+void ClearStencil(PP_Resource context_id, GLint s) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glClearStencil(s);
+  }
+}
+
+void ColorMask(PP_Resource context_id,
+               GLboolean red,
+               GLboolean green,
+               GLboolean blue,
+               GLboolean alpha) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glColorMask(red, green, blue, alpha);
+  }
+}
+
+void CompileShader(PP_Resource context_id, GLuint shader) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCompileShader(shader);
+  }
+}
+
+void CompressedTexImage2D(PP_Resource context_id,
+                          GLenum target,
+                          GLint level,
+                          GLenum internalformat,
+                          GLsizei width,
+                          GLsizei height,
+                          GLint border,
+                          GLsizei imageSize,
+                          const void* data) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCompressedTexImage2D(
+        target, level, internalformat, width, height, border, imageSize, data);
+  }
+}
+
+void CompressedTexSubImage2D(PP_Resource context_id,
+                             GLenum target,
+                             GLint level,
+                             GLint xoffset,
+                             GLint yoffset,
+                             GLsizei width,
+                             GLsizei height,
+                             GLenum format,
+                             GLsizei imageSize,
+                             const void* data) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCompressedTexSubImage2D(target,
+                              level,
+                              xoffset,
+                              yoffset,
+                              width,
+                              height,
+                              format,
+                              imageSize,
+                              data);
+  }
+}
+
+void CopyTexImage2D(PP_Resource context_id,
+                    GLenum target,
+                    GLint level,
+                    GLenum internalformat,
+                    GLint x,
+                    GLint y,
+                    GLsizei width,
+                    GLsizei height,
+                    GLint border) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCopyTexImage2D(
+        target, level, internalformat, x, y, width, height, border);
+  }
+}
+
+void CopyTexSubImage2D(PP_Resource context_id,
+                       GLenum target,
+                       GLint level,
+                       GLint xoffset,
+                       GLint yoffset,
+                       GLint x,
+                       GLint y,
+                       GLsizei width,
+                       GLsizei height) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+  }
+}
+
+GLuint CreateProgram(PP_Resource context_id) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glCreateProgram();
+  } else {
+    return 0;
+  }
+}
+
+GLuint CreateShader(PP_Resource context_id, GLenum type) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glCreateShader(type);
+  } else {
+    return 0;
+  }
+}
+
+void CullFace(PP_Resource context_id, GLenum mode) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glCullFace(mode);
+  }
+}
+
+void DeleteBuffers(PP_Resource context_id, GLsizei n, const GLuint* buffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteBuffers(n, buffers);
+  }
+}
+
+void DeleteFramebuffers(PP_Resource context_id,
+                        GLsizei n,
+                        const GLuint* framebuffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteFramebuffers(n, framebuffers);
+  }
+}
+
+void DeleteProgram(PP_Resource context_id, GLuint program) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteProgram(program);
+  }
+}
+
+void DeleteRenderbuffers(PP_Resource context_id,
+                         GLsizei n,
+                         const GLuint* renderbuffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteRenderbuffers(n, renderbuffers);
+  }
+}
+
+void DeleteShader(PP_Resource context_id, GLuint shader) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteShader(shader);
+  }
+}
+
+void DeleteTextures(PP_Resource context_id, GLsizei n, const GLuint* textures) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDeleteTextures(n, textures);
+  }
+}
+
+void DepthFunc(PP_Resource context_id, GLenum func) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDepthFunc(func);
+  }
+}
+
+void DepthMask(PP_Resource context_id, GLboolean flag) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDepthMask(flag);
+  }
+}
+
+void DepthRangef(PP_Resource context_id, GLclampf zNear, GLclampf zFar) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDepthRangef(zNear, zFar);
+  }
+}
+
+void DetachShader(PP_Resource context_id, GLuint program, GLuint shader) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDetachShader(program, shader);
+  }
+}
+
+void Disable(PP_Resource context_id, GLenum cap) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDisable(cap);
+  }
+}
+
+void DisableVertexAttribArray(PP_Resource context_id, GLuint index) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDisableVertexAttribArray(index);
+  }
+}
+
+void DrawArrays(PP_Resource context_id,
+                GLenum mode,
+                GLint first,
+                GLsizei count) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDrawArrays(mode, first, count);
+  }
+}
+
+void DrawElements(PP_Resource context_id,
+                  GLenum mode,
+                  GLsizei count,
+                  GLenum type,
+                  const void* indices) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glDrawElements(mode, count, type, indices);
+  }
+}
+
+void Enable(PP_Resource context_id, GLenum cap) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glEnable(cap);
+  }
+}
+
+void EnableVertexAttribArray(PP_Resource context_id, GLuint index) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glEnableVertexAttribArray(index);
+  }
+}
+
+void Finish(PP_Resource context_id) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glFinish();
+  }
+}
+
+void Flush(PP_Resource context_id) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glFlush();
+  }
+}
+
+void FramebufferRenderbuffer(PP_Resource context_id,
+                             GLenum target,
+                             GLenum attachment,
+                             GLenum renderbuffertarget,
+                             GLuint renderbuffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glFramebufferRenderbuffer(
+        target, attachment, renderbuffertarget, renderbuffer);
+  }
+}
+
+void FramebufferTexture2D(PP_Resource context_id,
+                          GLenum target,
+                          GLenum attachment,
+                          GLenum textarget,
+                          GLuint texture,
+                          GLint level) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glFramebufferTexture2D(target, attachment, textarget, texture, level);
+  }
+}
+
+void FrontFace(PP_Resource context_id, GLenum mode) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glFrontFace(mode);
+  }
+}
+
+void GenBuffers(PP_Resource context_id, GLsizei n, GLuint* buffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGenBuffers(n, buffers);
+  }
+}
+
+void GenerateMipmap(PP_Resource context_id, GLenum target) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGenerateMipmap(target);
+  }
+}
+
+void GenFramebuffers(PP_Resource context_id, GLsizei n, GLuint* framebuffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGenFramebuffers(n, framebuffers);
+  }
+}
+
+void GenRenderbuffers(PP_Resource context_id,
+                      GLsizei n,
+                      GLuint* renderbuffers) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGenRenderbuffers(n, renderbuffers);
+  }
+}
+
+void GenTextures(PP_Resource context_id, GLsizei n, GLuint* textures) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGenTextures(n, textures);
+  }
+}
+
+void GetActiveAttrib(PP_Resource context_id,
+                     GLuint program,
+                     GLuint index,
+                     GLsizei bufsize,
+                     GLsizei* length,
+                     GLint* size,
+                     GLenum* type,
+                     char* name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetActiveAttrib(program, index, bufsize, length, size, type, name);
+  }
+}
+
+void GetActiveUniform(PP_Resource context_id,
+                      GLuint program,
+                      GLuint index,
+                      GLsizei bufsize,
+                      GLsizei* length,
+                      GLint* size,
+                      GLenum* type,
+                      char* name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetActiveUniform(program, index, bufsize, length, size, type, name);
+  }
+}
+
+void GetAttachedShaders(PP_Resource context_id,
+                        GLuint program,
+                        GLsizei maxcount,
+                        GLsizei* count,
+                        GLuint* shaders) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetAttachedShaders(program, maxcount, count, shaders);
+  }
+}
+
+GLint GetAttribLocation(PP_Resource context_id,
+                        GLuint program,
+                        const char* name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glGetAttribLocation(program, name);
+  } else {
+    return -1;
+  }
+}
+
+void GetBooleanv(PP_Resource context_id, GLenum pname, GLboolean* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetBooleanv(pname, params);
+  }
+}
+
+void GetBufferParameteriv(PP_Resource context_id,
+                          GLenum target,
+                          GLenum pname,
+                          GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetBufferParameteriv(target, pname, params);
+  }
+}
+
+GLenum GetError(PP_Resource context_id) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glGetError();
+  } else {
+    return 0;
+  }
+}
+
+void GetFloatv(PP_Resource context_id, GLenum pname, GLfloat* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetFloatv(pname, params);
+  }
+}
+
+void GetFramebufferAttachmentParameteriv(PP_Resource context_id,
+                                         GLenum target,
+                                         GLenum attachment,
+                                         GLenum pname,
+                                         GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+  }
+}
+
+void GetIntegerv(PP_Resource context_id, GLenum pname, GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetIntegerv(pname, params);
+  }
+}
+
+void GetProgramiv(PP_Resource context_id,
+                  GLuint program,
+                  GLenum pname,
+                  GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetProgramiv(program, pname, params);
+  }
+}
+
+void GetProgramInfoLog(PP_Resource context_id,
+                       GLuint program,
+                       GLsizei bufsize,
+                       GLsizei* length,
+                       char* infolog) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetProgramInfoLog(program, bufsize, length, infolog);
+  }
+}
+
+void GetRenderbufferParameteriv(PP_Resource context_id,
+                                GLenum target,
+                                GLenum pname,
+                                GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetRenderbufferParameteriv(target, pname, params);
+  }
+}
+
+void GetShaderiv(PP_Resource context_id,
+                 GLuint shader,
+                 GLenum pname,
+                 GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetShaderiv(shader, pname, params);
+  }
+}
+
+void GetShaderInfoLog(PP_Resource context_id,
+                      GLuint shader,
+                      GLsizei bufsize,
+                      GLsizei* length,
+                      char* infolog) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetShaderInfoLog(shader, bufsize, length, infolog);
+  }
+}
+
+void GetShaderPrecisionFormat(PP_Resource context_id,
+                              GLenum shadertype,
+                              GLenum precisiontype,
+                              GLint* range,
+                              GLint* precision) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+  }
+}
+
+void GetShaderSource(PP_Resource context_id,
+                     GLuint shader,
+                     GLsizei bufsize,
+                     GLsizei* length,
+                     char* source) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetShaderSource(shader, bufsize, length, source);
+  }
+}
+
+const GLubyte* GetString(PP_Resource context_id, GLenum name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glGetString(name);
+  } else {
+    return NULL;
+  }
+}
+
+void GetTexParameterfv(PP_Resource context_id,
+                       GLenum target,
+                       GLenum pname,
+                       GLfloat* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetTexParameterfv(target, pname, params);
+  }
+}
+
+void GetTexParameteriv(PP_Resource context_id,
+                       GLenum target,
+                       GLenum pname,
+                       GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetTexParameteriv(target, pname, params);
+  }
+}
+
+void GetUniformfv(PP_Resource context_id,
+                  GLuint program,
+                  GLint location,
+                  GLfloat* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetUniformfv(program, location, params);
+  }
+}
+
+void GetUniformiv(PP_Resource context_id,
+                  GLuint program,
+                  GLint location,
+                  GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetUniformiv(program, location, params);
+  }
+}
+
+GLint GetUniformLocation(PP_Resource context_id,
+                         GLuint program,
+                         const char* name) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glGetUniformLocation(program, name);
+  } else {
+    return -1;
+  }
+}
+
+void GetVertexAttribfv(PP_Resource context_id,
+                       GLuint index,
+                       GLenum pname,
+                       GLfloat* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetVertexAttribfv(index, pname, params);
+  }
+}
+
+void GetVertexAttribiv(PP_Resource context_id,
+                       GLuint index,
+                       GLenum pname,
+                       GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetVertexAttribiv(index, pname, params);
+  }
+}
+
+void GetVertexAttribPointerv(PP_Resource context_id,
+                             GLuint index,
+                             GLenum pname,
+                             void** pointer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glGetVertexAttribPointerv(index, pname, pointer);
+  }
+}
+
+void Hint(PP_Resource context_id, GLenum target, GLenum mode) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glHint(target, mode);
+  }
+}
+
+GLboolean IsBuffer(PP_Resource context_id, GLuint buffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsBuffer(buffer);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsEnabled(PP_Resource context_id, GLenum cap) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsEnabled(cap);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsFramebuffer(PP_Resource context_id, GLuint framebuffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsFramebuffer(framebuffer);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsProgram(PP_Resource context_id, GLuint program) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsProgram(program);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsRenderbuffer(PP_Resource context_id, GLuint renderbuffer) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsRenderbuffer(renderbuffer);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsShader(PP_Resource context_id, GLuint shader) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsShader(shader);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+GLboolean IsTexture(PP_Resource context_id, GLuint texture) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    return glIsTexture(texture);
+  } else {
+    return GL_FALSE;
+  }
+}
+
+void LineWidth(PP_Resource context_id, GLfloat width) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glLineWidth(width);
+  }
+}
+
+void LinkProgram(PP_Resource context_id, GLuint program) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glLinkProgram(program);
+  }
+}
+
+void PixelStorei(PP_Resource context_id, GLenum pname, GLint param) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glPixelStorei(pname, param);
+  }
+}
+
+void PolygonOffset(PP_Resource context_id, GLfloat factor, GLfloat units) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glPolygonOffset(factor, units);
+  }
+}
+
+void ReadPixels(PP_Resource context_id,
+                GLint x,
+                GLint y,
+                GLsizei width,
+                GLsizei height,
+                GLenum format,
+                GLenum type,
+                void* pixels) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glReadPixels(x, y, width, height, format, type, pixels);
+  }
+}
+
+void ReleaseShaderCompiler(PP_Resource context_id) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glReleaseShaderCompiler();
+  }
+}
+
+void RenderbufferStorage(PP_Resource context_id,
+                         GLenum target,
+                         GLenum internalformat,
+                         GLsizei width,
+                         GLsizei height) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glRenderbufferStorage(target, internalformat, width, height);
+  }
+}
+
+void SampleCoverage(PP_Resource context_id, GLclampf value, GLboolean invert) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glSampleCoverage(value, invert);
+  }
+}
+
+void Scissor(PP_Resource context_id,
+             GLint x,
+             GLint y,
+             GLsizei width,
+             GLsizei height) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glScissor(x, y, width, height);
+  }
+}
+
+void ShaderBinary(PP_Resource context_id,
+                  GLsizei n,
+                  const GLuint* shaders,
+                  GLenum binaryformat,
+                  const void* binary,
+                  GLsizei length) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glShaderBinary(n, shaders, binaryformat, binary, length);
+  }
+}
+
+void ShaderSource(PP_Resource context_id,
+                  GLuint shader,
+                  GLsizei count,
+                  const char** str,
+                  const GLint* length) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glShaderSource(shader, count, str, length);
+  }
+}
+
+void StencilFunc(PP_Resource context_id, GLenum func, GLint ref, GLuint mask) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilFunc(func, ref, mask);
+  }
+}
+
+void StencilFuncSeparate(PP_Resource context_id,
+                         GLenum face,
+                         GLenum func,
+                         GLint ref,
+                         GLuint mask) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilFuncSeparate(face, func, ref, mask);
+  }
+}
+
+void StencilMask(PP_Resource context_id, GLuint mask) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilMask(mask);
+  }
+}
+
+void StencilMaskSeparate(PP_Resource context_id, GLenum face, GLuint mask) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilMaskSeparate(face, mask);
+  }
+}
+
+void StencilOp(PP_Resource context_id,
+               GLenum fail,
+               GLenum zfail,
+               GLenum zpass) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilOp(fail, zfail, zpass);
+  }
+}
+
+void StencilOpSeparate(PP_Resource context_id,
+                       GLenum face,
+                       GLenum fail,
+                       GLenum zfail,
+                       GLenum zpass) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glStencilOpSeparate(face, fail, zfail, zpass);
+  }
+}
+
+void TexImage2D(PP_Resource context_id,
+                GLenum target,
+                GLint level,
+                GLint internalformat,
+                GLsizei width,
+                GLsizei height,
+                GLint border,
+                GLenum format,
+                GLenum type,
+                const void* pixels) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexImage2D(target,
+                 level,
+                 internalformat,
+                 width,
+                 height,
+                 border,
+                 format,
+                 type,
+                 pixels);
+  }
+}
+
+void TexParameterf(PP_Resource context_id,
+                   GLenum target,
+                   GLenum pname,
+                   GLfloat param) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexParameterf(target, pname, param);
+  }
+}
+
+void TexParameterfv(PP_Resource context_id,
+                    GLenum target,
+                    GLenum pname,
+                    const GLfloat* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexParameterfv(target, pname, params);
+  }
+}
+
+void TexParameteri(PP_Resource context_id,
+                   GLenum target,
+                   GLenum pname,
+                   GLint param) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexParameteri(target, pname, param);
+  }
+}
+
+void TexParameteriv(PP_Resource context_id,
+                    GLenum target,
+                    GLenum pname,
+                    const GLint* params) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexParameteriv(target, pname, params);
+  }
+}
+
+void TexSubImage2D(PP_Resource context_id,
+                   GLenum target,
+                   GLint level,
+                   GLint xoffset,
+                   GLint yoffset,
+                   GLsizei width,
+                   GLsizei height,
+                   GLenum format,
+                   GLenum type,
+                   const void* pixels) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glTexSubImage2D(
+        target, level, xoffset, yoffset, width, height, format, type, pixels);
+  }
+}
+
+void Uniform1f(PP_Resource context_id, GLint location, GLfloat x) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform1f(location, x);
+  }
+}
+
+void Uniform1fv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLfloat* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform1fv(location, count, v);
+  }
+}
+
+void Uniform1i(PP_Resource context_id, GLint location, GLint x) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform1i(location, x);
+  }
+}
+
+void Uniform1iv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLint* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform1iv(location, count, v);
+  }
+}
+
+void Uniform2f(PP_Resource context_id, GLint location, GLfloat x, GLfloat y) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform2f(location, x, y);
+  }
+}
+
+void Uniform2fv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLfloat* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform2fv(location, count, v);
+  }
+}
+
+void Uniform2i(PP_Resource context_id, GLint location, GLint x, GLint y) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform2i(location, x, y);
+  }
+}
+
+void Uniform2iv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLint* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform2iv(location, count, v);
+  }
+}
+
+void Uniform3f(PP_Resource context_id,
+               GLint location,
+               GLfloat x,
+               GLfloat y,
+               GLfloat z) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform3f(location, x, y, z);
+  }
+}
+
+void Uniform3fv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLfloat* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform3fv(location, count, v);
+  }
+}
+
+void Uniform3i(PP_Resource context_id,
+               GLint location,
+               GLint x,
+               GLint y,
+               GLint z) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform3i(location, x, y, z);
+  }
+}
+
+void Uniform3iv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLint* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform3iv(location, count, v);
+  }
+}
+
+void Uniform4f(PP_Resource context_id,
+               GLint location,
+               GLfloat x,
+               GLfloat y,
+               GLfloat z,
+               GLfloat w) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform4f(location, x, y, z, w);
+  }
+}
+
+void Uniform4fv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLfloat* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform4fv(location, count, v);
+  }
+}
+
+void Uniform4i(PP_Resource context_id,
+               GLint location,
+               GLint x,
+               GLint y,
+               GLint z,
+               GLint w) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform4i(location, x, y, z, w);
+  }
+}
+
+void Uniform4iv(PP_Resource context_id,
+                GLint location,
+                GLsizei count,
+                const GLint* v) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniform4iv(location, count, v);
+  }
+}
+
+void UniformMatrix2fv(PP_Resource context_id,
+                      GLint location,
+                      GLsizei count,
+                      GLboolean transpose,
+                      const GLfloat* value) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniformMatrix2fv(location, count, transpose, value);
+  }
+}
+
+void UniformMatrix3fv(PP_Resource context_id,
+                      GLint location,
+                      GLsizei count,
+                      GLboolean transpose,
+                      const GLfloat* value) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniformMatrix3fv(location, count, transpose, value);
+  }
+}
+
+void UniformMatrix4fv(PP_Resource context_id,
+                      GLint location,
+                      GLsizei count,
+                      GLboolean transpose,
+                      const GLfloat* value) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUniformMatrix4fv(location, count, transpose, value);
+  }
+}
+
+void UseProgram(PP_Resource context_id, GLuint program) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glUseProgram(program);
+  }
+}
+
+void ValidateProgram(PP_Resource context_id, GLuint program) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glValidateProgram(program);
+  }
+}
+
+void VertexAttrib1f(PP_Resource context_id, GLuint indx, GLfloat x) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib1f(indx, x);
+  }
+}
+
+void VertexAttrib1fv(PP_Resource context_id,
+                     GLuint indx,
+                     const GLfloat* values) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib1fv(indx, values);
+  }
+}
+
+void VertexAttrib2f(PP_Resource context_id, GLuint indx, GLfloat x, GLfloat y) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib2f(indx, x, y);
+  }
+}
+
+void VertexAttrib2fv(PP_Resource context_id,
+                     GLuint indx,
+                     const GLfloat* values) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib2fv(indx, values);
+  }
+}
+
+void VertexAttrib3f(PP_Resource context_id,
+                    GLuint indx,
+                    GLfloat x,
+                    GLfloat y,
+                    GLfloat z) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib3f(indx, x, y, z);
+  }
+}
+
+void VertexAttrib3fv(PP_Resource context_id,
+                     GLuint indx,
+                     const GLfloat* values) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib3fv(indx, values);
+  }
+}
+
+void VertexAttrib4f(PP_Resource context_id,
+                    GLuint indx,
+                    GLfloat x,
+                    GLfloat y,
+                    GLfloat z,
+                    GLfloat w) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib4f(indx, x, y, z, w);
+  }
+}
+
+void VertexAttrib4fv(PP_Resource context_id,
+                     GLuint indx,
+                     const GLfloat* values) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttrib4fv(indx, values);
+  }
+}
+
+void VertexAttribPointer(PP_Resource context_id,
+                         GLuint indx,
+                         GLint size,
+                         GLenum type,
+                         GLboolean normalized,
+                         GLsizei stride,
+                         const void* ptr) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
+  }
+}
+
+void Viewport(PP_Resource context_id,
+              GLint x,
+              GLint y,
+              GLsizei width,
+              GLsizei height) {
+  Enter3D enter(context_id, true);
+  if (IsBoundGraphics(&enter)) {
+    glViewport(x, y, width, height);
+  }
+}
+
+}  // namespace
+
+const PPB_OpenGLES2* GetPPB_OpenGLES2_Thunk() {
+  static const struct PPB_OpenGLES2 ppb_opengles2 = {
+      &ActiveTexture,                       &AttachShader,
+      &BindAttribLocation,                  &BindBuffer,
+      &BindFramebuffer,                     &BindRenderbuffer,
+      &BindTexture,                         &BlendColor,
+      &BlendEquation,                       &BlendEquationSeparate,
+      &BlendFunc,                           &BlendFuncSeparate,
+      &BufferData,                          &BufferSubData,
+      &CheckFramebufferStatus,              &Clear,
+      &ClearColor,                          &ClearDepthf,
+      &ClearStencil,                        &ColorMask,
+      &CompileShader,                       &CompressedTexImage2D,
+      &CompressedTexSubImage2D,             &CopyTexImage2D,
+      &CopyTexSubImage2D,                   &CreateProgram,
+      &CreateShader,                        &CullFace,
+      &DeleteBuffers,                       &DeleteFramebuffers,
+      &DeleteProgram,                       &DeleteRenderbuffers,
+      &DeleteShader,                        &DeleteTextures,
+      &DepthFunc,                           &DepthMask,
+      &DepthRangef,                         &DetachShader,
+      &Disable,                             &DisableVertexAttribArray,
+      &DrawArrays,                          &DrawElements,
+      &Enable,                              &EnableVertexAttribArray,
+      &Finish,                              &Flush,
+      &FramebufferRenderbuffer,             &FramebufferTexture2D,
+      &FrontFace,                           &GenBuffers,
+      &GenerateMipmap,                      &GenFramebuffers,
+      &GenRenderbuffers,                    &GenTextures,
+      &GetActiveAttrib,                     &GetActiveUniform,
+      &GetAttachedShaders,                  &GetAttribLocation,
+      &GetBooleanv,                         &GetBufferParameteriv,
+      &GetError,                            &GetFloatv,
+      &GetFramebufferAttachmentParameteriv, &GetIntegerv,
+      &GetProgramiv,                        &GetProgramInfoLog,
+      &GetRenderbufferParameteriv,          &GetShaderiv,
+      &GetShaderInfoLog,                    &GetShaderPrecisionFormat,
+      &GetShaderSource,                     &GetString,
+      &GetTexParameterfv,                   &GetTexParameteriv,
+      &GetUniformfv,                        &GetUniformiv,
+      &GetUniformLocation,                  &GetVertexAttribfv,
+      &GetVertexAttribiv,                   &GetVertexAttribPointerv,
+      &Hint,                                &IsBuffer,
+      &IsEnabled,                           &IsFramebuffer,
+      &IsProgram,                           &IsRenderbuffer,
+      &IsShader,                            &IsTexture,
+      &LineWidth,                           &LinkProgram,
+      &PixelStorei,                         &PolygonOffset,
+      &ReadPixels,                          &ReleaseShaderCompiler,
+      &RenderbufferStorage,                 &SampleCoverage,
+      &Scissor,                             &ShaderBinary,
+      &ShaderSource,                        &StencilFunc,
+      &StencilFuncSeparate,                 &StencilMask,
+      &StencilMaskSeparate,                 &StencilOp,
+      &StencilOpSeparate,                   &TexImage2D,
+      &TexParameterf,                       &TexParameterfv,
+      &TexParameteri,                       &TexParameteriv,
+      &TexSubImage2D,                       &Uniform1f,
+      &Uniform1fv,                          &Uniform1i,
+      &Uniform1iv,                          &Uniform2f,
+      &Uniform2fv,                          &Uniform2i,
+      &Uniform2iv,                          &Uniform3f,
+      &Uniform3fv,                          &Uniform3i,
+      &Uniform3iv,                          &Uniform4f,
+      &Uniform4fv,                          &Uniform4i,
+      &Uniform4iv,                          &UniformMatrix2fv,
+      &UniformMatrix3fv,                    &UniformMatrix4fv,
+      &UseProgram,                          &ValidateProgram,
+      &VertexAttrib1f,                      &VertexAttrib1fv,
+      &VertexAttrib2f,                      &VertexAttrib2fv,
+      &VertexAttrib3f,                      &VertexAttrib3fv,
+      &VertexAttrib4f,                      &VertexAttrib4fv,
+      &VertexAttribPointer,                 &Viewport};
+  return &ppb_opengles2;
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/resource_creation_impl.cc b/mojo/examples/pepper_container_app/resource_creation_impl.cc
new file mode 100644
index 0000000..96cf770
--- /dev/null
+++ b/mojo/examples/pepper_container_app/resource_creation_impl.cc
@@ -0,0 +1,410 @@
+// Copyright 2014 The Chromium 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/examples/pepper_container_app/resource_creation_impl.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+
+namespace mojo {
+namespace examples {
+
+ResourceCreationImpl::ResourceCreationImpl() {}
+
+ResourceCreationImpl::~ResourceCreationImpl() {}
+
+PP_Resource ResourceCreationImpl::CreateFileIO(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileRef(
+    PP_Instance instance,
+    const ppapi::FileRefCreateInfo& create_info) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileSystem(
+    PP_Instance instance,
+    PP_FileSystemType type) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateIMEInputEvent(
+    PP_Instance instance,
+    PP_InputEvent_Type type,
+    PP_TimeTicks time_stamp,
+    struct PP_Var text,
+    uint32_t segment_number,
+    const uint32_t* segment_offsets,
+    int32_t target_segment,
+    uint32_t selection_start,
+    uint32_t selection_end) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent_1_0(
+    PP_Instance instance,
+    PP_InputEvent_Type type,
+    PP_TimeTicks time_stamp,
+    uint32_t modifiers,
+    uint32_t key_code,
+    struct PP_Var character_text) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent_1_2(
+    PP_Instance instance,
+    PP_InputEvent_Type type,
+    PP_TimeTicks time_stamp,
+    uint32_t modifiers,
+    uint32_t key_code,
+    struct PP_Var character_text,
+    struct PP_Var code) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateMouseInputEvent(
+    PP_Instance instance,
+    PP_InputEvent_Type type,
+    PP_TimeTicks time_stamp,
+    uint32_t modifiers,
+    PP_InputEvent_MouseButton mouse_button,
+    const PP_Point* mouse_position,
+    int32_t click_count,
+    const PP_Point* mouse_movement) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTouchInputEvent(
+    PP_Instance instance,
+    PP_InputEvent_Type type,
+    PP_TimeTicks time_stamp,
+    uint32_t modifiers) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTrueTypeFont(
+    PP_Instance instance,
+    const PP_TrueTypeFontDesc_Dev* desc) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateURLLoader(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateURLRequestInfo(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateWheelInputEvent(
+    PP_Instance instance,
+    PP_TimeTicks time_stamp,
+    uint32_t modifiers,
+    const PP_FloatPoint* wheel_delta,
+    const PP_FloatPoint* wheel_ticks,
+    PP_Bool scroll_by_page) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudio1_0(
+    PP_Instance instance,
+    PP_Resource config_id,
+    PPB_Audio_Callback_1_0 audio_callback,
+    void* user_data) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudio(
+    PP_Instance instance,
+    PP_Resource config_id,
+    PPB_Audio_Callback audio_callback,
+    void* user_data) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudioTrusted(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudioConfig(
+    PP_Instance instance,
+    PP_AudioSampleRate sample_rate,
+    uint32_t sample_frame_count) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateCompositor(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileChooser(
+    PP_Instance instance,
+    PP_FileChooserMode_Dev mode,
+    const PP_Var& accept_types) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics2D(PP_Instance instance,
+                                                   const PP_Size* size,
+                                                   PP_Bool is_always_opaque) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics3D(
+    PP_Instance instance,
+    PP_Resource share_context,
+    const int32_t* attrib_list) {
+  return (new Graphics3DResource(instance))->GetReference();
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics3DRaw(
+    PP_Instance instance,
+    PP_Resource share_context,
+    const int32_t* attrib_list,
+    base::SharedMemoryHandle* shared_state) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateHostResolver(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateHostResolverPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateImageData(
+    PP_Instance instance,
+    PP_ImageDataFormat format,
+    const PP_Size* size,
+    PP_Bool init_to_zero) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateImageDataSimple(
+    PP_Instance instance,
+    PP_ImageDataFormat format,
+    const PP_Size* size,
+    PP_Bool init_to_zero) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateMediaStreamVideoTrack(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv4Address(
+    PP_Instance instance,
+    const PP_NetAddress_IPv4* ipv4_addr) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv6Address(
+    PP_Instance instance,
+    const PP_NetAddress_IPv6* ipv6_addr) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromNetAddressPrivate(
+    PP_Instance instance,
+    const PP_NetAddress_Private& private_addr) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetworkMonitor(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateOutputProtectionPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreatePrinting(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPServerSocketPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocket1_0(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocket(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocketPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateUDPSocket(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateUDPSocketPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDecoder(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDestination(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoSource(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateWebSocket(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateX509CertificatePrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+#if !defined(OS_NACL)
+PP_Resource ResourceCreationImpl::CreateAudioInput(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBroker(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBrowserFont(
+    PP_Instance instance,
+    const PP_BrowserFont_Trusted_Description* description) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBuffer(PP_Instance instance,
+                                                uint32_t size) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashDRM(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashFontFile(
+    PP_Instance instance,
+    const PP_BrowserFont_Trusted_Description* description,
+    PP_PrivateFontCharset charset) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashMenu(
+    PP_Instance instance,
+    const PP_Flash_Menu* menu_data) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashMessageLoop(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreatePlatformVerificationPrivate(
+    PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateScrollbar(PP_Instance instance,
+                                                   PP_Bool vertical) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTalk(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoCapture(PP_Instance instance) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDecoderDev(
+    PP_Instance instance,
+    PP_Resource context3d_id,
+    PP_VideoDecoder_Profile profile) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+#endif  // !defined(OS_NACL)
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/pepper_container_app/resource_creation_impl.h b/mojo/examples/pepper_container_app/resource_creation_impl.h
new file mode 100644
index 0000000..e150964
--- /dev/null
+++ b/mojo/examples/pepper_container_app/resource_creation_impl.h
@@ -0,0 +1,178 @@
+// Copyright 2014 The Chromium 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_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "ppapi/thunk/resource_creation_api.h"
+
+namespace mojo {
+namespace examples {
+
+class ResourceCreationImpl : public ppapi::thunk::ResourceCreationAPI {
+ public:
+  ResourceCreationImpl();
+  virtual ~ResourceCreationImpl();
+
+  // ppapi::thunk::ResourceCreationAPI implementation.
+  virtual PP_Resource CreateFileIO(PP_Instance instance) override;
+  virtual PP_Resource CreateFileRef(
+      PP_Instance instance,
+      const ppapi::FileRefCreateInfo& create_info) override;
+  virtual PP_Resource CreateFileSystem(PP_Instance instance,
+                                       PP_FileSystemType type) override;
+  virtual PP_Resource CreateIMEInputEvent(PP_Instance instance,
+                                          PP_InputEvent_Type type,
+                                          PP_TimeTicks time_stamp,
+                                          struct PP_Var text,
+                                          uint32_t segment_number,
+                                          const uint32_t* segment_offsets,
+                                          int32_t target_segment,
+                                          uint32_t selection_start,
+                                          uint32_t selection_end) override;
+  virtual PP_Resource CreateKeyboardInputEvent_1_0(
+      PP_Instance instance,
+      PP_InputEvent_Type type,
+      PP_TimeTicks time_stamp,
+      uint32_t modifiers,
+      uint32_t key_code,
+      PP_Var character_text) override;
+  virtual PP_Resource CreateKeyboardInputEvent_1_2(
+      PP_Instance instance,
+      PP_InputEvent_Type type,
+      PP_TimeTicks time_stamp,
+      uint32_t modifiers,
+      uint32_t key_code,
+      PP_Var character_text,
+      PP_Var code) override;
+  virtual PP_Resource CreateMouseInputEvent(
+      PP_Instance instance,
+      PP_InputEvent_Type type,
+      PP_TimeTicks time_stamp,
+      uint32_t modifiers,
+      PP_InputEvent_MouseButton mouse_button,
+      const PP_Point* mouse_position,
+      int32_t click_count,
+      const PP_Point* mouse_movement) override;
+  virtual PP_Resource CreateTouchInputEvent(
+      PP_Instance instance,
+      PP_InputEvent_Type type,
+      PP_TimeTicks time_stamp,
+      uint32_t modifiers) override;
+  virtual PP_Resource CreateTrueTypeFont(
+      PP_Instance instance,
+      const PP_TrueTypeFontDesc_Dev* desc) override;
+  virtual PP_Resource CreateURLLoader(PP_Instance instance) override;
+  virtual PP_Resource CreateURLRequestInfo(
+      PP_Instance instance) override;
+  virtual PP_Resource CreateWheelInputEvent(
+      PP_Instance instance,
+      PP_TimeTicks time_stamp,
+      uint32_t modifiers,
+      const PP_FloatPoint* wheel_delta,
+      const PP_FloatPoint* wheel_ticks,
+      PP_Bool scroll_by_page) override;
+  virtual PP_Resource CreateAudio1_0(PP_Instance instance,
+                                     PP_Resource config_id,
+                                     PPB_Audio_Callback_1_0 audio_callback,
+                                     void* user_data) override;
+  virtual PP_Resource CreateAudio(PP_Instance instance,
+                                  PP_Resource config_id,
+                                  PPB_Audio_Callback audio_callback,
+                                  void* user_data) override;
+  virtual PP_Resource CreateAudioTrusted(PP_Instance instance) override;
+  virtual PP_Resource CreateAudioConfig(PP_Instance instance,
+                                        PP_AudioSampleRate sample_rate,
+                                        uint32_t sample_frame_count) override;
+  virtual PP_Resource CreateCompositor(PP_Instance instance) override;
+  virtual PP_Resource CreateFileChooser(PP_Instance instance,
+                                        PP_FileChooserMode_Dev mode,
+                                        const PP_Var& accept_types) override;
+  virtual PP_Resource CreateGraphics2D(PP_Instance pp_instance,
+                                       const PP_Size* size,
+                                       PP_Bool is_always_opaque) override;
+  virtual PP_Resource CreateGraphics3D(PP_Instance instance,
+                                       PP_Resource share_context,
+                                       const int32_t* attrib_list) override;
+  virtual PP_Resource CreateGraphics3DRaw(
+      PP_Instance instance,
+      PP_Resource share_context,
+      const int32_t* attrib_list,
+      base::SharedMemoryHandle* shared_state) override;
+  virtual PP_Resource CreateHostResolver(PP_Instance instance) override;
+  virtual PP_Resource CreateHostResolverPrivate(PP_Instance instance) override;
+  virtual PP_Resource CreateImageData(PP_Instance instance,
+                                      PP_ImageDataFormat format,
+                                      const PP_Size* size,
+                                      PP_Bool init_to_zero) override;
+  virtual PP_Resource CreateImageDataSimple(PP_Instance instance,
+                                            PP_ImageDataFormat format,
+                                            const PP_Size* size,
+                                            PP_Bool init_to_zero) override;
+  virtual PP_Resource CreateMediaStreamVideoTrack(
+      PP_Instance instance) override;
+  virtual PP_Resource CreateNetAddressFromIPv4Address(
+      PP_Instance instance,
+      const PP_NetAddress_IPv4* ipv4_addr) override;
+  virtual PP_Resource CreateNetAddressFromIPv6Address(
+      PP_Instance instance,
+      const PP_NetAddress_IPv6* ipv6_addr) override;
+  virtual PP_Resource CreateNetAddressFromNetAddressPrivate(
+      PP_Instance instance,
+      const PP_NetAddress_Private& private_addr) override;
+  virtual PP_Resource CreateNetworkMonitor(PP_Instance instance) override;
+  virtual PP_Resource CreateOutputProtectionPrivate(
+      PP_Instance instance) override;
+  virtual PP_Resource CreatePrinting(PP_Instance) override;
+  virtual PP_Resource CreateTCPServerSocketPrivate(
+      PP_Instance instance) override;
+  virtual PP_Resource CreateTCPSocket1_0(PP_Instance instance) override;
+  virtual PP_Resource CreateTCPSocket(PP_Instance instance) override;
+  virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) override;
+  virtual PP_Resource CreateUDPSocket(PP_Instance instance) override;
+  virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instance) override;
+  virtual PP_Resource CreateVideoDecoder(PP_Instance instance) override;
+  virtual PP_Resource CreateVideoDestination(PP_Instance instance) override;
+  virtual PP_Resource CreateVideoSource(PP_Instance instance) override;
+  virtual PP_Resource CreateWebSocket(PP_Instance instance) override;
+  virtual PP_Resource CreateX509CertificatePrivate(
+      PP_Instance instance) override;
+#if !defined(OS_NACL)
+  virtual PP_Resource CreateAudioInput(PP_Instance instance) override;
+  virtual PP_Resource CreateBroker(PP_Instance instance) override;
+  virtual PP_Resource CreateBrowserFont(
+      PP_Instance instance,
+      const PP_BrowserFont_Trusted_Description* description) override;
+  virtual PP_Resource CreateBuffer(PP_Instance instance,
+                                   uint32_t size) override;
+  virtual PP_Resource CreateFlashDRM(PP_Instance instance) override;
+  virtual PP_Resource CreateFlashFontFile(
+      PP_Instance instance,
+      const PP_BrowserFont_Trusted_Description* description,
+      PP_PrivateFontCharset charset) override;
+  virtual PP_Resource CreateFlashMenu(PP_Instance instance,
+                                      const PP_Flash_Menu* menu_data) override;
+  virtual PP_Resource CreateFlashMessageLoop(PP_Instance instance) override;
+  virtual PP_Resource CreatePlatformVerificationPrivate(
+      PP_Instance instance) override;
+  virtual PP_Resource CreateScrollbar(PP_Instance instance,
+                                      PP_Bool vertical) override;
+  virtual PP_Resource CreateTalk(PP_Instance instance) override;
+  virtual PP_Resource CreateVideoCapture(PP_Instance instance) override;
+  virtual PP_Resource CreateVideoDecoderDev(
+      PP_Instance instance,
+      PP_Resource context3d_id,
+      PP_VideoDecoder_Profile profile) override;
+#endif  // !defined(OS_NACL)
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResourceCreationImpl);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
diff --git a/mojo/examples/pepper_container_app/thunk.h b/mojo/examples/pepper_container_app/thunk.h
new file mode 100644
index 0000000..f3b3d54
--- /dev/null
+++ b/mojo/examples/pepper_container_app/thunk.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
+
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/c/ppb_opengles2.h"
+
+namespace mojo {
+namespace examples {
+
+const PPB_Core_1_0* GetPPB_Core_1_0_Thunk();
+const PPB_OpenGLES2* GetPPB_OpenGLES2_Thunk();
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
diff --git a/mojo/examples/pepper_container_app/type_converters.h b/mojo/examples/pepper_container_app/type_converters.h
new file mode 100644
index 0000000..6384f5b
--- /dev/null
+++ b/mojo/examples/pepper_container_app/type_converters.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ppapi/c/pp_point.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/c/pp_size.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<PointPtr, PP_Point> {
+  static PointPtr Convert(const PP_Point& input) {
+    PointPtr point(Point::New());
+    point->x = input.x;
+    point->y = input.y;
+    return point.Pass();
+  }
+};
+
+template <>
+struct TypeConverter<PP_Point, PointPtr> {
+  static PP_Point Convert(const PointPtr& input) {
+    if (!input)
+      return PP_MakePoint(0, 0);
+    return PP_MakePoint(static_cast<int32_t>(input->x),
+                        static_cast<int32_t>(input->y));
+  }
+};
+
+template <>
+struct TypeConverter<SizePtr, PP_Size> {
+  static SizePtr Convert(const PP_Size& input) {
+    SizePtr size(Size::New());
+    size->width = input.width;
+    size->height = input.height;
+    return size.Pass();
+  }
+};
+
+template <>
+struct TypeConverter<PP_Size, SizePtr> {
+  static PP_Size Convert(const SizePtr& input) {
+    if (!input)
+      return PP_MakeSize(0, 0);
+    return PP_MakeSize(static_cast<int32_t>(input->width),
+                       static_cast<int32_t>(input->height));
+  }
+};
+
+template <>
+struct TypeConverter<RectPtr, PP_Rect> {
+  static RectPtr Convert(const PP_Rect& input) {
+    RectPtr rect(Rect::New());
+    rect->x = input.point.x;
+    rect->y = input.point.y;
+    rect->width = input.size.width;
+    rect->height = input.size.height;
+    return rect.Pass();
+  }
+};
+
+template <>
+struct TypeConverter<PP_Rect, SizePtr> {
+  static PP_Rect Convert(const SizePtr& input) {
+    if (!input)
+      return PP_MakeRectFromXYWH(0, 0, 0, 0);
+    return PP_MakeRectFromXYWH(0, 0, input->width, input->height);
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
diff --git a/mojo/examples/png_viewer/BUILD.gn b/mojo/examples/png_viewer/BUILD.gn
new file mode 100644
index 0000000..9fc2315
--- /dev/null
+++ b/mojo/examples/png_viewer/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_examples.gypi:mojo_png_viewer
+shared_library("png_viewer") {
+  output_name = "mojo_png_viewer"
+
+  sources = [
+    "png_viewer.cc"
+  ]
+
+  deps = [
+    "//mojo/application",
+    "//mojo/examples/media_viewer:bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/content_handler",
+    "//mojo/services/public/interfaces/network",
+    "//skia",
+    "//ui/gfx",
+  ]
+}
diff --git a/mojo/examples/png_viewer/DEPS b/mojo/examples/png_viewer/DEPS
new file mode 100644
index 0000000..23b2bf6
--- /dev/null
+++ b/mojo/examples/png_viewer/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+skia/ext",
+  "+third_party/skia/include",
+  "+ui/gfx",
+]
diff --git a/mojo/examples/png_viewer/png_viewer.cc b/mojo/examples/png_viewer/png_viewer.cc
new file mode 100644
index 0000000..792879a
--- /dev/null
+++ b/mojo/examples/png_viewer/png_viewer.cc
@@ -0,0 +1,242 @@
+// Copyright 2014 The Chromium 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 <algorithm>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_tokenizer.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/media_viewer/media_viewer.mojom.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/public/cpp/application/service_provider_impl.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/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkScalar.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+namespace examples {
+
+class PNGViewer;
+
+// TODO(aa): Hook up ZoomableMedia interface again.
+class PNGView : public ViewManagerDelegate, public ViewObserver {
+ public:
+  static void Spawn(URLResponsePtr response,
+                    ServiceProviderImpl* exported_services,
+                    scoped_ptr<ServiceProvider> imported_services,
+                    Shell* shell) {
+    // PNGView deletes itself when its View is destroyed.
+    new PNGView(
+        response.Pass(), exported_services, imported_services.Pass(), shell);
+  }
+
+ private:
+  static const uint16_t kMaxZoomPercentage = 400;
+  static const uint16_t kMinZoomPercentage = 20;
+  static const uint16_t kDefaultZoomPercentage = 100;
+  static const uint16_t kZoomStep = 20;
+
+  PNGView(URLResponsePtr response,
+          ServiceProviderImpl* exported_services,
+          scoped_ptr<ServiceProvider> imported_services,
+          Shell* shell)
+      : imported_services_(imported_services.Pass()),
+        root_(NULL),
+        view_manager_client_factory_(shell, this),
+        zoom_percentage_(kDefaultZoomPercentage) {
+    exported_services->AddService(&view_manager_client_factory_);
+    DecodePNG(response.Pass());
+  }
+
+  virtual ~PNGView() {
+    if (root_)
+      root_->RemoveObserver(this);
+  }
+
+  // Overridden from ViewManagerDelegate:
+  virtual void OnEmbed(ViewManager* view_manager,
+                       View* root,
+                       ServiceProviderImpl* exported_services,
+                       scoped_ptr<ServiceProvider> imported_services) override {
+    root_ = root;
+    root_->AddObserver(this);
+    root_->SetColor(SK_ColorGRAY);
+    if (!bitmap_.isNull())
+      DrawBitmap();
+  }
+
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {
+    // TODO(aa): Need to figure out how shutdown works.
+  }
+
+  // Overridden from ViewObserver:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override {
+    DCHECK_EQ(view, root_);
+    DrawBitmap();
+  }
+
+  virtual void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(view, root_);
+    delete this;
+  }
+
+  void DecodePNG(URLResponsePtr response) {
+    int content_length = GetContentLength(response->headers);
+    scoped_ptr<unsigned char[]> data(new unsigned char[content_length]);
+    unsigned char* buf = data.get();
+    uint32_t bytes_remaining = content_length;
+    uint32_t num_bytes = bytes_remaining;
+    while (bytes_remaining > 0) {
+      MojoResult result = ReadDataRaw(
+          response->body.get(), buf, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+      if (result == MOJO_RESULT_SHOULD_WAIT) {
+        Wait(response->body.get(),
+             MOJO_HANDLE_SIGNAL_READABLE,
+             MOJO_DEADLINE_INDEFINITE);
+      } else if (result == MOJO_RESULT_OK) {
+        buf += num_bytes;
+        num_bytes = bytes_remaining -= num_bytes;
+      } else {
+        break;
+      }
+    }
+
+    gfx::PNGCodec::Decode(static_cast<const unsigned char*>(data.get()),
+                          content_length,
+                          &bitmap_);
+  }
+
+  void DrawBitmap() {
+    if (!root_)
+      return;
+
+    skia::RefPtr<SkCanvas> canvas(skia::AdoptRef(skia::CreatePlatformCanvas(
+        root_->bounds().width(), root_->bounds().height(), true)));
+    canvas->drawColor(SK_ColorGRAY);
+    SkPaint paint;
+    SkScalar scale =
+        SkFloatToScalar(zoom_percentage_ * 1.0f / kDefaultZoomPercentage);
+    canvas->scale(scale, scale);
+    canvas->drawBitmap(bitmap_, 0, 0, &paint);
+    root_->SetContents(skia::GetTopDevice(*canvas)->accessBitmap(true));
+  }
+
+  void ZoomIn() {
+    if (zoom_percentage_ >= kMaxZoomPercentage)
+      return;
+    zoom_percentage_ += kZoomStep;
+    DrawBitmap();
+  }
+
+  void ZoomOut() {
+    if (zoom_percentage_ <= kMinZoomPercentage)
+      return;
+    zoom_percentage_ -= kZoomStep;
+    DrawBitmap();
+  }
+
+  void ZoomToActualSize() {
+    if (zoom_percentage_ == kDefaultZoomPercentage)
+      return;
+    zoom_percentage_ = kDefaultZoomPercentage;
+    DrawBitmap();
+  }
+
+  int GetContentLength(const Array<String>& headers) {
+    for (size_t i = 0; i < headers.size(); ++i) {
+      base::StringTokenizer t(headers[i], ": ;=");
+      while (t.GetNext()) {
+        if (!t.token_is_delim() && t.token() == "Content-Length") {
+          while (t.GetNext()) {
+            if (!t.token_is_delim())
+              return atoi(t.token().c_str());
+          }
+        }
+      }
+    }
+    return 0;
+  }
+
+  SkBitmap bitmap_;
+  scoped_ptr<ServiceProvider> imported_services_;
+  View* root_;
+  ViewManagerClientFactory view_manager_client_factory_;
+  uint16_t zoom_percentage_;
+
+  DISALLOW_COPY_AND_ASSIGN(PNGView);
+};
+
+class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
+ public:
+  explicit ContentHandlerImpl(Shell* shell) : shell_(shell) {}
+  virtual ~ContentHandlerImpl() {}
+
+ private:
+  // Overridden from ContentHandler:
+  virtual void OnConnect(
+      const mojo::String& url,
+      URLResponsePtr response,
+      InterfaceRequest<ServiceProvider> service_provider) override {
+    ServiceProviderImpl* exported_services = new ServiceProviderImpl();
+    BindToRequest(exported_services, &service_provider);
+    scoped_ptr<ServiceProvider> remote(
+        exported_services->CreateRemoteServiceProvider());
+    PNGView::Spawn(response.Pass(), exported_services, remote.Pass(), shell_);
+  }
+
+  Shell* shell_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl);
+};
+
+class PNGViewer : public ApplicationDelegate {
+ public:
+  PNGViewer() {}
+ private:
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    content_handler_factory_.reset(
+        new InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell>(
+            app->shell()));
+  }
+
+  // Overridden from ApplicationDelegate:
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(content_handler_factory_.get());
+    return true;
+  }
+
+  scoped_ptr<InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell> >
+      content_handler_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PNGViewer);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::PNGViewer);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/sample_app/BUILD.gn b/mojo/examples/sample_app/BUILD.gn
new file mode 100644
index 0000000..d44e6e4
--- /dev/null
+++ b/mojo/examples/sample_app/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_examples.gypi:mojo_sample_app
+shared_library("sample_app") {
+  output_name = "mojo_sample_app"
+
+  sources = [
+    "gles2_client_impl.cc",
+    "gles2_client_impl.h",
+    "sample_app.cc",
+  ]
+
+  deps = [
+    ":spinning_cube",
+    "//base",
+    "//gpu/command_buffer/client:gles2_interface",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/native_viewport",
+  ]
+}
+
+source_set("spinning_cube") {
+  sources = [
+    "spinning_cube.cc",
+    "spinning_cube.h"
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/gles2:for_shared_library",
+  ]
+}
diff --git a/mojo/examples/sample_app/DEPS b/mojo/examples/sample_app/DEPS
new file mode 100644
index 0000000..08992c3
--- /dev/null
+++ b/mojo/examples/sample_app/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+gpu/command_buffer/client",
+]
diff --git a/mojo/examples/sample_app/gles2_client_impl.cc b/mojo/examples/sample_app/gles2_client_impl.cc
new file mode 100644
index 0000000..6cdd87a
--- /dev/null
+++ b/mojo/examples/sample_app/gles2_client_impl.cc
@@ -0,0 +1,140 @@
+// 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/examples/sample_app/gles2_client_impl.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace examples {
+namespace {
+
+float CalculateDragDistance(const mojo::Point& start, const mojo::Point& end) {
+  return hypot(static_cast<float>(start.x - end.x),
+               static_cast<float>(start.y - end.y));
+}
+
+float GetRandomColor() {
+  return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+}
+
+}
+
+GLES2ClientImpl::GLES2ClientImpl(mojo::CommandBufferPtr command_buffer)
+    : last_time_(mojo::GetTimeTicksNow()), waiting_to_draw_(false) {
+  context_ =
+      MojoGLES2CreateContext(command_buffer.PassMessagePipe().release().value(),
+                             &ContextLostThunk,
+                             this,
+                             mojo::Environment::GetDefaultAsyncWaiter());
+  MojoGLES2MakeCurrent(context_);
+}
+
+GLES2ClientImpl::~GLES2ClientImpl() {
+  MojoGLES2DestroyContext(context_);
+}
+
+void GLES2ClientImpl::SetSize(const mojo::Size& size) {
+  size_ = size;
+  if (size_.width == 0 || size_.height == 0)
+    return;
+  static_cast<gpu::gles2::GLES2Interface*>(
+      MojoGLES2GetGLES2Interface(context_))->ResizeCHROMIUM(size_.width,
+                                                            size_.height,
+                                                            1);
+  cube_.Init(size_.width, size_.height);
+  WantToDraw();
+}
+
+void GLES2ClientImpl::HandleInputEvent(const mojo::Event& event) {
+  switch (event.action) {
+  case mojo::EVENT_TYPE_MOUSE_PRESSED:
+  case mojo::EVENT_TYPE_TOUCH_PRESSED:
+    if (event.flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+      break;
+    capture_point_ = *event.location_data->in_view_location;
+    last_drag_point_ = capture_point_;
+    drag_start_time_ = mojo::GetTimeTicksNow();
+    break;
+  case mojo::EVENT_TYPE_MOUSE_DRAGGED:
+  case mojo::EVENT_TYPE_TOUCH_MOVED: {
+    if (event.flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+      break;
+    int direction =
+        (event.location_data->in_view_location->y < last_drag_point_.y ||
+         event.location_data->in_view_location->x > last_drag_point_.x)
+        ? 1 : -1;
+    cube_.set_direction(direction);
+    cube_.UpdateForDragDistance(CalculateDragDistance(
+        last_drag_point_, *event.location_data->in_view_location));
+    WantToDraw();
+
+    last_drag_point_ = *event.location_data->in_view_location;
+    break;
+  }
+  case mojo::EVENT_TYPE_MOUSE_RELEASED:
+  case mojo::EVENT_TYPE_TOUCH_RELEASED: {
+    if (event.flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON) {
+      cube_.set_color(GetRandomColor(), GetRandomColor(), GetRandomColor());
+      break;
+    }
+    MojoTimeTicks offset = mojo::GetTimeTicksNow() - drag_start_time_;
+    float delta = static_cast<float>(offset) / 1000000.;
+    cube_.SetFlingMultiplier(CalculateDragDistance(
+        capture_point_, *event.location_data->in_view_location), delta);
+
+    capture_point_ = last_drag_point_ = mojo::Point();
+    WantToDraw();
+    break;
+  }
+  default:
+    break;
+  }
+}
+
+void GLES2ClientImpl::ContextLost() {
+}
+
+void GLES2ClientImpl::ContextLostThunk(void* closure) {
+  static_cast<GLES2ClientImpl*>(closure)->ContextLost();
+}
+
+struct DrawRunnable {
+  explicit DrawRunnable(GLES2ClientImpl* impl) : impl(impl) {}
+  virtual ~DrawRunnable() {}
+
+  void Run() const { impl->Draw(); }
+
+  GLES2ClientImpl* impl;
+};
+
+void GLES2ClientImpl::WantToDraw() {
+  if (waiting_to_draw_)
+    return;
+  waiting_to_draw_ = true;
+  mojo::RunLoop::current()->PostDelayedTask(mojo::Closure(DrawRunnable(this)),
+                                            MojoTimeTicks(16667));
+}
+
+void GLES2ClientImpl::Draw() {
+  waiting_to_draw_ = false;
+  MojoTimeTicks now = mojo::GetTimeTicksNow();
+  MojoTimeTicks offset = now - last_time_;
+  float delta = static_cast<float>(offset) / 1000000.;
+  last_time_ = now;
+  cube_.UpdateForTimeDelta(delta);
+  cube_.Draw();
+
+  MojoGLES2SwapBuffers();
+  WantToDraw();
+}
+
+}  // namespace examples
diff --git a/mojo/examples/sample_app/gles2_client_impl.h b/mojo/examples/sample_app/gles2_client_impl.h
new file mode 100644
index 0000000..1a968a0
--- /dev/null
+++ b/mojo/examples/sample_app/gles2_client_impl.h
@@ -0,0 +1,44 @@
+// 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_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
+#define MOJO_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
+
+#include "mojo/examples/sample_app/spinning_cube.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+
+namespace examples {
+
+class GLES2ClientImpl {
+ public:
+  explicit GLES2ClientImpl(mojo::CommandBufferPtr command_buffer);
+  virtual ~GLES2ClientImpl();
+
+  void SetSize(const mojo::Size& size);
+  void HandleInputEvent(const mojo::Event& event);
+  void Draw();
+
+ private:
+  void ContextLost();
+  static void ContextLostThunk(void* closure);
+  void WantToDraw();
+
+  MojoTimeTicks last_time_;
+  mojo::Size size_;
+  SpinningCube cube_;
+  mojo::Point capture_point_;
+  mojo::Point last_drag_point_;
+  MojoTimeTicks drag_start_time_;
+  bool waiting_to_draw_;
+
+  MojoGLES2Context context_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(GLES2ClientImpl);
+};
+
+}  // namespace examples
+
+#endif  // MOJO_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
diff --git a/mojo/examples/sample_app/sample_app.cc b/mojo/examples/sample_app/sample_app.cc
new file mode 100644
index 0000000..6ffdb59
--- /dev/null
+++ b/mojo/examples/sample_app/sample_app.cc
@@ -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.
+
+#include <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/examples/sample_app/gles2_client_impl.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/application_runner.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+
+namespace examples {
+
+class SampleApp : public mojo::ApplicationDelegate,
+                  public mojo::NativeViewportClient {
+ public:
+  SampleApp() : weak_factory_(this) {}
+
+  virtual ~SampleApp() {
+    // TODO(darin): Fix shutdown so we don't need to leak this.
+    MOJO_ALLOW_UNUSED GLES2ClientImpl* leaked = gles2_client_.release();
+  }
+
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    app->ConnectToService("mojo:mojo_native_viewport_service", &viewport_);
+    viewport_.set_client(this);
+
+    // TODO(jamesr): Should be mojo:mojo_gpu_service
+    app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+
+    mojo::SizePtr size(mojo::Size::New());
+    size->width = 800;
+    size->height = 600;
+    viewport_->Create(size.Pass(),
+                      base::Bind(&SampleApp::OnCreatedNativeViewport,
+                                 weak_factory_.GetWeakPtr()));
+    viewport_->Show();
+  }
+
+  virtual void OnDestroyed() override { mojo::RunLoop::current()->Quit(); }
+
+  virtual void OnSizeChanged(mojo::SizePtr size) override {
+    assert(size);
+    if (gles2_client_)
+      gles2_client_->SetSize(*size);
+  }
+
+  virtual void OnEvent(mojo::EventPtr event,
+                       const mojo::Callback<void()>& callback) override {
+    assert(event);
+    if (event->location_data && event->location_data->in_view_location)
+      gles2_client_->HandleInputEvent(*event);
+    callback.Run();
+  }
+
+ private:
+  void OnCreatedNativeViewport(uint64_t native_viewport_id) {
+    mojo::SizePtr size = mojo::Size::New();
+    size->width = 800;
+    size->height = 600;
+    mojo::CommandBufferPtr command_buffer;
+    // TODO(jamesr): Output to a surface instead.
+    gpu_service_->CreateOnscreenGLES2Context(
+        native_viewport_id, size.Pass(), Get(&command_buffer));
+    gles2_client_.reset(new GLES2ClientImpl(command_buffer.Pass()));
+  }
+
+  scoped_ptr<GLES2ClientImpl> gles2_client_;
+  mojo::NativeViewportPtr viewport_;
+  mojo::GpuPtr gpu_service_;
+  base::WeakPtrFactory<SampleApp> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SampleApp);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new examples::SampleApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/sample_app/spinning_cube.cc b/mojo/examples/sample_app/spinning_cube.cc
new file mode 100644
index 0000000..e417984
--- /dev/null
+++ b/mojo/examples/sample_app/spinning_cube.cc
@@ -0,0 +1,470 @@
+// 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.
+
+// This example program is based on Simple_VertexShader.c from:
+
+//
+// Book:      OpenGL(R) ES 2.0 Programming Guide
+// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10:   0321502795
+// ISBN-13:   9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs:      http://safari.informit.com/9780321563835
+//            http://www.opengles-book.com
+//
+
+#include "mojo/examples/sample_app/spinning_cube.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace examples {
+
+namespace {
+
+const float kPi = 3.14159265359f;
+
+int GenerateCube(GLuint *vbo_vertices,
+                 GLuint *vbo_indices) {
+  const int num_indices = 36;
+
+  const GLfloat cube_vertices[] = {
+    -0.5f, -0.5f, -0.5f,
+    -0.5f, -0.5f,  0.5f,
+    0.5f, -0.5f,  0.5f,
+    0.5f, -0.5f, -0.5f,
+    -0.5f,  0.5f, -0.5f,
+    -0.5f,  0.5f,  0.5f,
+    0.5f,  0.5f,  0.5f,
+    0.5f,  0.5f, -0.5f,
+    -0.5f, -0.5f, -0.5f,
+    -0.5f,  0.5f, -0.5f,
+    0.5f,  0.5f, -0.5f,
+    0.5f, -0.5f, -0.5f,
+    -0.5f, -0.5f, 0.5f,
+    -0.5f,  0.5f, 0.5f,
+    0.5f,  0.5f, 0.5f,
+    0.5f, -0.5f, 0.5f,
+    -0.5f, -0.5f, -0.5f,
+    -0.5f, -0.5f,  0.5f,
+    -0.5f,  0.5f,  0.5f,
+    -0.5f,  0.5f, -0.5f,
+    0.5f, -0.5f, -0.5f,
+    0.5f, -0.5f,  0.5f,
+    0.5f,  0.5f,  0.5f,
+    0.5f,  0.5f, -0.5f,
+  };
+
+  const GLushort cube_indices[] = {
+    0, 2, 1,
+    0, 3, 2,
+    4, 5, 6,
+    4, 6, 7,
+    8, 9, 10,
+    8, 10, 11,
+    12, 15, 14,
+    12, 14, 13,
+    16, 17, 18,
+    16, 18, 19,
+    20, 23, 22,
+    20, 22, 21
+  };
+
+  if (vbo_vertices) {
+    glGenBuffers(1, vbo_vertices);
+    glBindBuffer(GL_ARRAY_BUFFER, *vbo_vertices);
+    glBufferData(GL_ARRAY_BUFFER,
+                 sizeof(cube_vertices),
+                 cube_vertices,
+                 GL_STATIC_DRAW);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+  }
+
+  if (vbo_indices) {
+    glGenBuffers(1, vbo_indices);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *vbo_indices);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+                 sizeof(cube_indices),
+                 cube_indices,
+                 GL_STATIC_DRAW);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  }
+
+  return num_indices;
+}
+
+GLuint LoadShader(GLenum type,
+                  const char* shader_source) {
+  GLuint shader = glCreateShader(type);
+  glShaderSource(shader, 1, &shader_source, NULL);
+  glCompileShader(shader);
+
+  GLint compiled = 0;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+
+  if (!compiled) {
+    glDeleteShader(shader);
+    return 0;
+  }
+
+  return shader;
+}
+
+GLuint LoadProgram(const char* vertext_shader_source,
+                   const char* fragment_shader_source) {
+  GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER,
+                                    vertext_shader_source);
+  if (!vertex_shader)
+    return 0;
+
+  GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER,
+                                      fragment_shader_source);
+  if (!fragment_shader) {
+    glDeleteShader(vertex_shader);
+    return 0;
+  }
+
+  GLuint program_object = glCreateProgram();
+  glAttachShader(program_object, vertex_shader);
+  glAttachShader(program_object, fragment_shader);
+
+  glLinkProgram(program_object);
+
+  glDeleteShader(vertex_shader);
+  glDeleteShader(fragment_shader);
+
+  GLint linked = 0;
+  glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
+
+  if (!linked) {
+    glDeleteProgram(program_object);
+    return 0;
+  }
+
+  return program_object;
+}
+
+class ESMatrix {
+ public:
+  GLfloat m[4][4];
+
+  ESMatrix() {
+    LoadZero();
+  }
+
+  void LoadZero() {
+    memset(this, 0x0, sizeof(ESMatrix));
+  }
+
+  void LoadIdentity() {
+    LoadZero();
+    m[0][0] = 1.0f;
+    m[1][1] = 1.0f;
+    m[2][2] = 1.0f;
+    m[3][3] = 1.0f;
+  }
+
+  void Multiply(ESMatrix* a, ESMatrix* b) {
+    ESMatrix result;
+    for (int i = 0; i < 4; ++i) {
+      result.m[i][0] = (a->m[i][0] * b->m[0][0]) +
+                       (a->m[i][1] * b->m[1][0]) +
+                       (a->m[i][2] * b->m[2][0]) +
+                       (a->m[i][3] * b->m[3][0]);
+
+      result.m[i][1] = (a->m[i][0] * b->m[0][1]) +
+                       (a->m[i][1] * b->m[1][1]) +
+                       (a->m[i][2] * b->m[2][1]) +
+                       (a->m[i][3] * b->m[3][1]);
+
+      result.m[i][2] = (a->m[i][0] * b->m[0][2]) +
+                       (a->m[i][1] * b->m[1][2]) +
+                       (a->m[i][2] * b->m[2][2]) +
+                       (a->m[i][3] * b->m[3][2]);
+
+      result.m[i][3] = (a->m[i][0] * b->m[0][3]) +
+                       (a->m[i][1] * b->m[1][3]) +
+                       (a->m[i][2] * b->m[2][3]) +
+                       (a->m[i][3] * b->m[3][3]);
+    }
+    *this = result;
+  }
+
+  void Frustum(float left,
+               float right,
+               float bottom,
+               float top,
+               float near_z,
+               float far_z) {
+    float delta_x = right - left;
+    float delta_y = top - bottom;
+    float delta_z = far_z - near_z;
+
+    if ((near_z <= 0.0f) ||
+        (far_z <= 0.0f) ||
+        (delta_z <= 0.0f) ||
+        (delta_y <= 0.0f) ||
+        (delta_y <= 0.0f))
+      return;
+
+    ESMatrix frust;
+    frust.m[0][0] = 2.0f * near_z / delta_x;
+    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
+
+    frust.m[1][1] = 2.0f * near_z / delta_y;
+    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
+
+    frust.m[2][0] = (right + left) / delta_x;
+    frust.m[2][1] = (top + bottom) / delta_y;
+    frust.m[2][2] = -(near_z + far_z) / delta_z;
+    frust.m[2][3] = -1.0f;
+
+    frust.m[3][2] = -2.0f * near_z * far_z / delta_z;
+    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
+
+    Multiply(&frust, this);
+  }
+
+  void Perspective(float fov_y, float aspect, float near_z, float far_z) {
+    GLfloat frustum_h = tanf(fov_y / 360.0f * kPi) * near_z;
+    GLfloat frustum_w = frustum_h * aspect;
+    Frustum(-frustum_w, frustum_w, -frustum_h, frustum_h, near_z, far_z);
+  }
+
+  void Translate(GLfloat tx, GLfloat ty, GLfloat tz) {
+    m[3][0] += m[0][0] * tx + m[1][0] * ty + m[2][0] * tz;
+    m[3][1] += m[0][1] * tx + m[1][1] * ty + m[2][1] * tz;
+    m[3][2] += m[0][2] * tx + m[1][2] * ty + m[2][2] * tz;
+    m[3][3] += m[0][3] * tx + m[1][3] * ty + m[2][3] * tz;
+  }
+
+  void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
+    GLfloat mag = sqrtf(x * x + y * y + z * z);
+
+    GLfloat sin_angle = sinf(angle * kPi / 180.0f);
+    GLfloat cos_angle = cosf(angle * kPi / 180.0f);
+    if (mag > 0.0f) {
+      GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
+      GLfloat one_minus_cos;
+      ESMatrix rotation;
+
+      x /= mag;
+      y /= mag;
+      z /= mag;
+
+      xx = x * x;
+      yy = y * y;
+      zz = z * z;
+      xy = x * y;
+      yz = y * z;
+      zx = z * x;
+      xs = x * sin_angle;
+      ys = y * sin_angle;
+      zs = z * sin_angle;
+      one_minus_cos = 1.0f - cos_angle;
+
+      rotation.m[0][0] = (one_minus_cos * xx) + cos_angle;
+      rotation.m[0][1] = (one_minus_cos * xy) - zs;
+      rotation.m[0][2] = (one_minus_cos * zx) + ys;
+      rotation.m[0][3] = 0.0F;
+
+      rotation.m[1][0] = (one_minus_cos * xy) + zs;
+      rotation.m[1][1] = (one_minus_cos * yy) + cos_angle;
+      rotation.m[1][2] = (one_minus_cos * yz) - xs;
+      rotation.m[1][3] = 0.0F;
+
+      rotation.m[2][0] = (one_minus_cos * zx) - ys;
+      rotation.m[2][1] = (one_minus_cos * yz) + xs;
+      rotation.m[2][2] = (one_minus_cos * zz) + cos_angle;
+      rotation.m[2][3] = 0.0F;
+
+      rotation.m[3][0] = 0.0F;
+      rotation.m[3][1] = 0.0F;
+      rotation.m[3][2] = 0.0F;
+      rotation.m[3][3] = 1.0F;
+
+      Multiply(&rotation, this);
+    }
+  }
+};
+
+float RotationForTimeDelta(float delta_time) {
+  return delta_time * 40.0f;
+}
+
+float RotationForDragDistance(float drag_distance) {
+  return drag_distance / 5; // Arbitrary damping.
+}
+
+}  // namespace
+
+class SpinningCube::GLState {
+ public:
+  GLState();
+
+  void OnGLContextLost();
+
+  GLfloat angle_;  // Survives losing the GL context.
+
+  GLuint program_object_;
+  GLint position_location_;
+  GLint mvp_location_;
+  GLint color_location_;
+  GLuint vbo_vertices_;
+  GLuint vbo_indices_;
+  int num_indices_;
+  ESMatrix mvp_matrix_;
+};
+
+SpinningCube::GLState::GLState()
+    : angle_(0) {
+  OnGLContextLost();
+}
+
+void SpinningCube::GLState::OnGLContextLost() {
+  program_object_ = 0;
+  position_location_ = 0;
+  mvp_location_ = 0;
+  color_location_ = 0;
+  vbo_vertices_ = 0;
+  vbo_indices_ = 0;
+  num_indices_ = 0;
+}
+
+SpinningCube::SpinningCube()
+    : initialized_(false),
+      width_(0),
+      height_(0),
+      state_(new GLState()),
+      fling_multiplier_(1.0f),
+      direction_(1),
+      color_() {
+  state_->angle_ = 45.0f;
+  set_color(0.0, 1.0, 0.0);
+}
+
+SpinningCube::~SpinningCube() {
+  if (!initialized_)
+    return;
+  if (state_->vbo_vertices_)
+    glDeleteBuffers(1, &state_->vbo_vertices_);
+  if (state_->vbo_indices_)
+    glDeleteBuffers(1, &state_->vbo_indices_);
+  if (state_->program_object_)
+    glDeleteProgram(state_->program_object_);
+}
+
+void SpinningCube::Init(uint32_t width, uint32_t height) {
+  width_ = width;
+  height_ = height;
+
+  const char vertext_shader_source[] =
+      "uniform mat4 u_mvpMatrix;                   \n"
+      "attribute vec4 a_position;                  \n"
+      "void main()                                 \n"
+      "{                                           \n"
+      "   gl_Position = u_mvpMatrix * a_position;  \n"
+      "}                                           \n";
+
+  const char fragment_shader_source[] =
+      "precision mediump float;                            \n"
+      "uniform vec4 u_color;                               \n"
+      "void main()                                         \n"
+      "{                                                   \n"
+      "  gl_FragColor = u_color;                           \n"
+      "}                                                   \n";
+
+  state_->program_object_ = LoadProgram(
+      vertext_shader_source, fragment_shader_source);
+  state_->position_location_ = glGetAttribLocation(
+      state_->program_object_, "a_position");
+  state_->mvp_location_ = glGetUniformLocation(
+      state_->program_object_, "u_mvpMatrix");
+  state_->color_location_ = glGetUniformLocation(
+      state_->program_object_, "u_color");
+  state_->num_indices_ = GenerateCube(
+      &state_->vbo_vertices_, &state_->vbo_indices_);
+
+  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+  initialized_ = true;
+}
+
+void SpinningCube::OnGLContextLost() {
+  initialized_ = false;
+  height_ = 0;
+  width_ = 0;
+  state_->OnGLContextLost();
+}
+
+void SpinningCube::SetFlingMultiplier(float drag_distance,
+                                      float drag_time) {
+  fling_multiplier_ = RotationForDragDistance(drag_distance) /
+      RotationForTimeDelta(drag_time);
+
+}
+
+void SpinningCube::UpdateForTimeDelta(float delta_time) {
+  state_->angle_ += RotationForTimeDelta(delta_time) * fling_multiplier_;
+  if (state_->angle_ >= 360.0f)
+    state_->angle_ -= 360.0f;
+
+  // Arbitrary 50-step linear reduction in spin speed.
+  if (fling_multiplier_ > 1.0f) {
+    fling_multiplier_ =
+        std::max(1.0f, fling_multiplier_ - (fling_multiplier_ - 1.0f) / 50);
+  }
+
+  Update();
+}
+
+void SpinningCube::UpdateForDragDistance(float distance) {
+  state_->angle_ += RotationForDragDistance(distance);
+  if (state_->angle_ >= 360.0f )
+    state_->angle_ -= 360.0f;
+
+  Update();
+}
+
+void SpinningCube::Draw() {
+  glViewport(0, 0, width_, height_);
+  glClear(GL_COLOR_BUFFER_BIT);
+  glUseProgram(state_->program_object_);
+  glBindBuffer(GL_ARRAY_BUFFER, state_->vbo_vertices_);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state_->vbo_indices_);
+  glVertexAttribPointer(state_->position_location_,
+                           3,
+                           GL_FLOAT,
+                           GL_FALSE, 3 * sizeof(GLfloat),
+                           0);
+  glEnableVertexAttribArray(state_->position_location_);
+  glUniformMatrix4fv(state_->mvp_location_,
+                        1,
+                        GL_FALSE,
+                        (GLfloat*) &state_->mvp_matrix_.m[0][0]);
+  glUniform4f(state_->color_location_, color_[0], color_[1], color_[2], 1.0);
+  glDrawElements(GL_TRIANGLES,
+                    state_->num_indices_,
+                    GL_UNSIGNED_SHORT,
+                    0);
+}
+
+void SpinningCube::Update() {
+  float aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
+
+  ESMatrix perspective;
+  perspective.LoadIdentity();
+  perspective.Perspective(60.0f, aspect, 1.0f, 20.0f );
+
+  ESMatrix modelview;
+  modelview.LoadIdentity();
+  modelview.Translate(0.0, 0.0, -2.0);
+  modelview.Rotate(state_->angle_ * direction_, 1.0, 0.0, 1.0);
+
+  state_->mvp_matrix_.Multiply(&modelview, &perspective);
+}
+
+}  // namespace examples
diff --git a/mojo/examples/sample_app/spinning_cube.h b/mojo/examples/sample_app/spinning_cube.h
new file mode 100644
index 0000000..f8d506d
--- /dev/null
+++ b/mojo/examples/sample_app/spinning_cube.h
@@ -0,0 +1,49 @@
+// 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_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
+#define MOJO_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
+
+#include <stdint.h>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace examples {
+
+class SpinningCube {
+ public:
+  SpinningCube();
+  ~SpinningCube();
+
+  void Init(uint32_t width, uint32_t height);
+  void set_direction(int direction) { direction_ = direction; }
+  void set_color(float r, float g, float b) {
+    color_[0] = r;
+    color_[1] = g;
+    color_[2] = b;
+  }
+  void SetFlingMultiplier(float drag_distance, float drag_time);
+  void UpdateForTimeDelta(float delta_time);
+  void UpdateForDragDistance(float distance);
+  void Draw();
+
+  void OnGLContextLost();
+
+ private:
+  class GLState;
+
+  void Update();
+
+  bool initialized_;
+  uint32_t width_;
+  uint32_t height_;
+  scoped_ptr<GLState> state_;
+  float fling_multiplier_;
+  int direction_;
+  float color_[3];
+};
+
+}  // namespace examples
+
+#endif  // MOJO_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
diff --git a/mojo/examples/surfaces_app/BUILD.gn b/mojo/examples/surfaces_app/BUILD.gn
new file mode 100644
index 0000000..dc7df4d
--- /dev/null
+++ b/mojo/examples/surfaces_app/BUILD.gn
@@ -0,0 +1,129 @@
+# Copyright 2014 The Chromium 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")
+
+group("surfaces_app") {
+  deps = [
+    ":child_app",
+    ":child_gl_app",
+    ":parent_app",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_surfaces_app
+shared_library("parent_app") {
+  output_name = "mojo_surfaces_app"
+
+  deps = [
+    ":bindings",
+    ":util",
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//skia",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/cpp/surfaces",
+  ]
+
+  sources = [
+    "embedder.cc",
+    "embedder.h",
+    "surfaces_app.cc",
+  ]
+}
+
+
+# GYP version: mojo/mojo_examples.gypi:mojo_surfaces_child_app
+shared_library("child_app") {
+  output_name = "mojo_surfaces_child_app"
+
+  deps = [
+    ":bindings",
+    ":util",
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//skia",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/cpp/surfaces",
+  ]
+
+  sources = [
+    "child_app.cc",
+    "child_impl.cc",
+    "child_impl.h",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_surfaces_child_gl_app
+shared_library("child_gl_app") {
+  output_name = "mojo_surfaces_child_gl_app"
+
+  deps = [
+    ":bindings",
+    ":util",
+    "//base",
+    "//cc",
+    "//cc/surfaces",
+    "//skia",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//mojo/common",
+    "//mojo/application",
+    "//mojo/environment:chromium",
+    "//mojo/examples/sample_app:spinning_cube",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/cpp/surfaces",
+  ]
+
+  sources = [
+    "child_gl_app.cc",
+    "child_gl_impl.cc",
+    "child_gl_impl.h",
+  ]
+}
+
+source_set("util") {
+  deps = [
+    "//cc",
+    "//skia",
+  ]
+
+  sources = [
+    "surfaces_util.cc",
+    "surfaces_util.h",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_surfaces_app_bindings
+mojom("bindings") {
+  deps = [
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/surfaces",
+    "//mojo/services/public/interfaces/surfaces:surface_id",
+  ]
+
+  sources = [ "child.mojom" ]
+}
diff --git a/mojo/examples/surfaces_app/DEPS b/mojo/examples/surfaces_app/DEPS
new file mode 100644
index 0000000..23379ad
--- /dev/null
+++ b/mojo/examples/surfaces_app/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+  "+cc",
+  "-cc/blink",
+  "+gpu/GLES2",
+  "+gpu/command_buffer/client",
+  "+third_party/khronos/GLES2",
+  "+third_party/skia/include",
+  "+ui/gfx",
+]
diff --git a/mojo/examples/surfaces_app/child.mojom b/mojo/examples/surfaces_app/child.mojom
new file mode 100644
index 0000000..2fcb00d
--- /dev/null
+++ b/mojo/examples/surfaces_app/child.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.
+
+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"
+
+interface Child {
+  ProduceFrame(mojo.Color color, mojo.Size size) =>
+      (mojo.SurfaceId id);
+};
diff --git a/mojo/examples/surfaces_app/child_app.cc b/mojo/examples/surfaces_app/child_app.cc
new file mode 100644
index 0000000..04c1fba
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_app.cc
@@ -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.
+
+#include "base/macros.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/surfaces_app/child_impl.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/bindings/string.h"
+
+namespace mojo {
+namespace examples {
+
+class ChildApp : public ApplicationDelegate, public InterfaceFactory<Child> {
+ public:
+  ChildApp() {}
+  virtual ~ChildApp() {}
+
+  virtual void Initialize(ApplicationImpl* app) override {
+    surfaces_service_connection_ =
+        app->ConnectToApplication("mojo:mojo_surfaces_service");
+  }
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(this);
+    return true;
+  }
+
+  // InterfaceFactory<Child> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Child> request) override {
+    BindToRequest(new ChildImpl(surfaces_service_connection_), &request);
+  }
+
+ private:
+  ApplicationConnection* surfaces_service_connection_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::ChildApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/surfaces_app/child_gl_app.cc b/mojo/examples/surfaces_app/child_gl_app.cc
new file mode 100644
index 0000000..9646db4
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_gl_app.cc
@@ -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.
+
+#include "base/macros.h"
+#include "base/threading/platform_thread.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/surfaces_app/child_gl_impl.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/bindings/string.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class ChildGLApp : public ApplicationDelegate, public InterfaceFactory<Child> {
+ public:
+  ChildGLApp() {}
+  virtual ~ChildGLApp() {}
+
+  virtual void Initialize(ApplicationImpl* app) override {
+    surfaces_service_connection_ =
+        app->ConnectToApplication("mojo:mojo_surfaces_service");
+    // TODO(jamesr): Should be mojo:mojo_gpu_service
+    app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+  }
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(this);
+    return true;
+  }
+
+  // InterfaceFactory<Child> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Child> request) override {
+    CommandBufferPtr command_buffer;
+    gpu_service_->CreateOffscreenGLES2Context(Get(&command_buffer));
+    BindToRequest(
+        new ChildGLImpl(surfaces_service_connection_, command_buffer.Pass()),
+        &request);
+  }
+
+ private:
+  ApplicationConnection* surfaces_service_connection_;
+  GpuPtr gpu_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildGLApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::ChildGLApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/surfaces_app/child_gl_impl.cc b/mojo/examples/surfaces_app/child_gl_impl.cc
new file mode 100644
index 0000000..72374fb
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_gl_impl.cc
@@ -0,0 +1,187 @@
+// Copyright 2014 The Chromium 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/examples/surfaces_app/child_gl_impl.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "mojo/examples/surfaces_app/surfaces_util.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/environment/environment.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/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+namespace examples {
+
+using cc::RenderPass;
+using cc::RenderPassId;
+using cc::DrawQuad;
+using cc::TextureDrawQuad;
+using cc::DelegatedFrameData;
+using cc::CompositorFrame;
+
+static void ContextLostThunk(void*) {
+  LOG(FATAL) << "Context lost";
+}
+
+ChildGLImpl::ChildGLImpl(ApplicationConnection* surfaces_service_connection,
+                         CommandBufferPtr command_buffer)
+    : start_time_(base::TimeTicks::Now()),
+      next_resource_id_(1),
+      weak_factory_(this) {
+  surfaces_service_connection->ConnectToService(&surfaces_service_);
+  surfaces_service_->CreateSurfaceConnection(base::Bind(
+      &ChildGLImpl::SurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+  context_ =
+      MojoGLES2CreateContext(command_buffer.PassMessagePipe().release().value(),
+                             &ContextLostThunk,
+                             this,
+                             Environment::GetDefaultAsyncWaiter());
+  DCHECK(context_);
+  MojoGLES2MakeCurrent(context_);
+}
+
+ChildGLImpl::~ChildGLImpl() {
+  MojoGLES2DestroyContext(context_);
+  surface_->DestroySurface(mojo::SurfaceId::From(id_));
+}
+
+void ChildGLImpl::ProduceFrame(
+    ColorPtr color,
+    SizePtr size,
+    const mojo::Callback<void(SurfaceIdPtr id)>& callback) {
+  color_ = color.To<SkColor>();
+  size_ = size.To<gfx::Size>();
+  cube_.Init(size_.width(), size_.height());
+  cube_.set_color(
+      SkColorGetR(color_), SkColorGetG(color_), SkColorGetB(color_));
+  produce_callback_ = callback;
+  AllocateSurface();
+}
+
+void ChildGLImpl::SurfaceConnectionCreated(SurfacePtr surface,
+                                           uint32_t id_namespace) {
+  surface_ = surface.Pass();
+  surface_.set_client(this);
+  allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+  AllocateSurface();
+}
+
+void ChildGLImpl::ReturnResources(Array<ReturnedResourcePtr> resources) {
+  for (size_t i = 0; i < resources.size(); ++i) {
+    cc::ReturnedResource res = resources[i].To<cc::ReturnedResource>();
+    GLuint returned_texture = id_to_tex_map_[res.id];
+    glDeleteTextures(1, &returned_texture);
+  }
+}
+
+void ChildGLImpl::AllocateSurface() {
+  if (produce_callback_.is_null() || !allocator_)
+    return;
+
+  id_ = allocator_->GenerateId();
+  surface_->CreateSurface(mojo::SurfaceId::From(id_), mojo::Size::From(size_));
+  produce_callback_.Run(SurfaceId::From(id_));
+  Draw();
+}
+
+void ChildGLImpl::Draw() {
+  // First, generate a GL texture and draw the cube into it.
+  GLuint texture = 0u;
+  glGenTextures(1, &texture);
+  glBindTexture(GL_TEXTURE_2D, texture);
+  glTexImage2D(GL_TEXTURE_2D,
+               0,
+               GL_RGBA,
+               size_.width(),
+               size_.height(),
+               0,
+               GL_RGBA,
+               GL_UNSIGNED_BYTE,
+               0);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  GLuint fbo = 0u;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2D(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+  DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+            glCheckFramebufferStatus(GL_FRAMEBUFFER));
+  glClearColor(1, 0, 0, 0.5);
+  cube_.UpdateForTimeDelta(0.16f);
+  cube_.Draw();
+
+  // Then, put the texture into a mailbox.
+  gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+  glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+  GLuint sync_point = glInsertSyncPointCHROMIUM();
+  gpu::MailboxHolder holder(mailbox, GL_TEXTURE_2D, sync_point);
+
+  // Then, put the mailbox into a TransferableResource
+  cc::TransferableResource resource;
+  resource.id = next_resource_id_++;
+  id_to_tex_map_[resource.id] = texture;
+  resource.format = cc::RGBA_8888;
+  resource.filter = GL_LINEAR;
+  resource.size = size_;
+  resource.mailbox_holder = holder;
+  resource.is_repeated = false;
+  resource.is_software = false;
+
+  gfx::Rect rect(size_);
+  RenderPassId id(1, 1);
+  scoped_ptr<RenderPass> pass = RenderPass::Create();
+  pass->SetNew(id, rect, rect, gfx::Transform());
+
+  CreateAndAppendSimpleSharedQuadState(pass.get(), gfx::Transform(), size_);
+
+  TextureDrawQuad* texture_quad =
+      pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
+  float vertex_opacity[4] = {1.0f, 1.0f, 0.2f, 1.0f};
+  texture_quad->SetNew(pass->shared_quad_state_list.back(),
+                       rect,
+                       rect,
+                       rect,
+                       resource.id,
+                       true,
+                       gfx::PointF(),
+                       gfx::PointF(1.f, 1.f),
+                       SK_ColorBLUE,
+                       vertex_opacity,
+                       false);
+
+  scoped_ptr<DelegatedFrameData> delegated_frame_data(new DelegatedFrameData);
+  delegated_frame_data->render_pass_list.push_back(pass.Pass());
+  delegated_frame_data->resource_list.push_back(resource);
+
+  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+  frame->delegated_frame_data = delegated_frame_data.Pass();
+
+  surface_->SubmitFrame(mojo::SurfaceId::From(id_), mojo::Frame::From(*frame));
+
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ChildGLImpl::Draw, base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(50));
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/surfaces_app/child_gl_impl.h b/mojo/examples/surfaces_app/child_gl_impl.h
new file mode 100644
index 0000000..4c73e00
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_gl_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_EXAMPLES_SURFACES_APP_CHILD_GL_IMPL_H_
+#define MOJO_EXAMPLES_SURFACES_APP_CHILD_GL_IMPL_H_
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/examples/sample_app/spinning_cube.h"
+#include "mojo/examples/surfaces_app/child.mojom.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.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/SkColor.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class CompositorFrame;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace examples {
+
+// Simple example of a child app using surfaces + GL.
+class ChildGLImpl : public InterfaceImpl<Child>, public SurfaceClient {
+ public:
+  ChildGLImpl(ApplicationConnection* surfaces_service_connection,
+              CommandBufferPtr command_buffer);
+  virtual ~ChildGLImpl();
+
+  // SurfaceClient implementation
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) override;
+
+ private:
+  // Child implementation.
+  virtual void ProduceFrame(
+      ColorPtr color,
+      SizePtr size,
+      const mojo::Callback<void(SurfaceIdPtr id)>& callback) override;
+
+  void SurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  void AllocateSurface();
+  void Draw();
+
+  SkColor color_;
+  gfx::Size size_;
+  scoped_ptr<cc::SurfaceIdAllocator> allocator_;
+  SurfacesServicePtr surfaces_service_;
+  SurfacePtr surface_;
+  MojoGLES2Context context_;
+  cc::SurfaceId id_;
+  ::examples::SpinningCube cube_;
+  Callback<void(SurfaceIdPtr id)> produce_callback_;
+  base::TimeTicks start_time_;
+  uint32_t next_resource_id_;
+  base::hash_map<uint32_t, GLuint> id_to_tex_map_;
+  base::WeakPtrFactory<ChildGLImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildGLImpl);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_SURFACES_APP_CHILD_GL_IMPL_H_
diff --git a/mojo/examples/surfaces_app/child_impl.cc b/mojo/examples/surfaces_app/child_impl.cc
new file mode 100644
index 0000000..963afb6
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_impl.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/examples/surfaces_app/child_impl.h"
+
+#include "base/bind.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "mojo/examples/surfaces_app/surfaces_util.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/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+namespace examples {
+
+using cc::RenderPass;
+using cc::RenderPassId;
+using cc::DrawQuad;
+using cc::SolidColorDrawQuad;
+using cc::DelegatedFrameData;
+using cc::CompositorFrame;
+
+ChildImpl::ChildImpl(ApplicationConnection* surfaces_service_connection)
+    : weak_factory_(this) {
+  surfaces_service_connection->ConnectToService(&surfaces_service_);
+  surfaces_service_->CreateSurfaceConnection(base::Bind(
+      &ChildImpl::SurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+}
+
+ChildImpl::~ChildImpl() {
+  if (surface_)
+    surface_->DestroySurface(mojo::SurfaceId::From(id_));
+}
+
+void ChildImpl::ProduceFrame(
+    ColorPtr color,
+    SizePtr size,
+    const mojo::Callback<void(SurfaceIdPtr id)>& callback) {
+  color_ = color.To<SkColor>();
+  size_ = size.To<gfx::Size>();
+  produce_callback_ = callback;
+  if (allocator_)
+    Draw();
+}
+
+void ChildImpl::SurfaceConnectionCreated(SurfacePtr surface,
+                                         uint32_t id_namespace) {
+  surface_ = surface.Pass();
+  surface_.set_client(this);
+  allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+  if (!produce_callback_.is_null())
+    Draw();
+}
+
+void ChildImpl::ReturnResources(
+    Array<ReturnedResourcePtr> resources) {
+  DCHECK(!resources.size());
+}
+
+void ChildImpl::Draw() {
+  id_ = allocator_->GenerateId();
+  surface_->CreateSurface(mojo::SurfaceId::From(id_),
+                          mojo::Size::From(size_));
+  gfx::Rect rect(size_);
+  RenderPassId id(1, 1);
+  scoped_ptr<RenderPass> pass = RenderPass::Create();
+  pass->SetNew(id, rect, rect, gfx::Transform());
+
+  CreateAndAppendSimpleSharedQuadState(pass.get(), gfx::Transform(), size_);
+
+  SolidColorDrawQuad* color_quad =
+      pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  bool force_anti_aliasing_off = false;
+  color_quad->SetNew(pass->shared_quad_state_list.back(),
+                     rect,
+                     rect,
+                     color_,
+                     force_anti_aliasing_off);
+
+  scoped_ptr<DelegatedFrameData> delegated_frame_data(new DelegatedFrameData);
+  delegated_frame_data->render_pass_list.push_back(pass.Pass());
+
+  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+  frame->delegated_frame_data = delegated_frame_data.Pass();
+
+  surface_->SubmitFrame(mojo::SurfaceId::From(id_),
+                        mojo::Frame::From(*frame));
+  produce_callback_.Run(SurfaceId::From(id_));
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/surfaces_app/child_impl.h b/mojo/examples/surfaces_app/child_impl.h
new file mode 100644
index 0000000..1cb887e
--- /dev/null
+++ b/mojo/examples/surfaces_app/child_impl.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_SURFACES_APP_CHILD_IMPL_H_
+#define MOJO_EXAMPLES_SURFACES_APP_CHILD_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/examples/surfaces_app/child.mojom.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.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/SkColor.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class CompositorFrame;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace surfaces {
+class Surface;
+}
+
+namespace examples {
+
+// Simple example of a child app using surfaces.
+class ChildImpl : public InterfaceImpl<Child>, public SurfaceClient {
+ public:
+  class Context {
+   public:
+    virtual ApplicationConnection* ShellConnection(
+        const mojo::String& application_url) = 0;
+  };
+  explicit ChildImpl(ApplicationConnection* surfaces_service_connection);
+  virtual ~ChildImpl();
+
+  // SurfaceClient implementation
+  virtual void ReturnResources(
+      Array<ReturnedResourcePtr> resources) override;
+
+ private:
+  // Child implementation.
+  virtual void ProduceFrame(
+      ColorPtr color,
+      SizePtr size,
+      const mojo::Callback<void(SurfaceIdPtr id)>& callback) override;
+
+  void SurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+  void Draw();
+
+  SkColor color_;
+  gfx::Size size_;
+  scoped_ptr<cc::SurfaceIdAllocator> allocator_;
+  SurfacesServicePtr surfaces_service_;
+  SurfacePtr surface_;
+  cc::SurfaceId id_;
+  mojo::Callback<void(SurfaceIdPtr id)> produce_callback_;
+  base::WeakPtrFactory<ChildImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildImpl);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_SURFACES_APP_CHILD_IMPL_H_
diff --git a/mojo/examples/surfaces_app/embedder.cc b/mojo/examples/surfaces_app/embedder.cc
new file mode 100644
index 0000000..4a5ff27
--- /dev/null
+++ b/mojo/examples/surfaces_app/embedder.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/examples/surfaces_app/embedder.h"
+
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "mojo/examples/surfaces_app/surfaces_util.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+namespace examples {
+
+using cc::RenderPass;
+using cc::RenderPassId;
+using cc::SurfaceDrawQuad;
+using cc::DrawQuad;
+using cc::SolidColorDrawQuad;
+using cc::DelegatedFrameData;
+using cc::CompositorFrame;
+
+Embedder::Embedder(Surface* surface) : surface_(surface) {
+}
+
+Embedder::~Embedder() {
+}
+
+void Embedder::ProduceFrame(cc::SurfaceId child_one,
+                            cc::SurfaceId child_two,
+                            const gfx::Size& child_size,
+                            const gfx::Size& size,
+                            int offset) {
+  gfx::Rect rect(size);
+  RenderPassId pass_id(1, 1);
+  scoped_ptr<RenderPass> pass = RenderPass::Create();
+  pass->SetNew(pass_id, rect, rect, gfx::Transform());
+
+  if (!child_one.is_null()) {
+    gfx::Transform one_transform;
+    one_transform.Translate(10 + child_size.width() / 2,
+                            50 + child_size.height() / 2);
+    one_transform.Translate(0, offset);
+    one_transform.Translate(-child_size.width() / 2, -child_size.height() / 2);
+    CreateAndAppendSimpleSharedQuadState(pass.get(), one_transform, size);
+
+    SurfaceDrawQuad* surface_one_quad =
+        pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
+    gfx::Rect one_rect(child_size);
+    surface_one_quad->SetNew(
+        pass->shared_quad_state_list.back(), one_rect, one_rect, child_one);
+  }
+
+  if (!child_two.is_null()) {
+    gfx::Transform two_transform;
+    two_transform.Translate(10 + size.width() / 2 + child_size.width() / 2,
+                            50 + child_size.height() / 2);
+    two_transform.Translate(0, 200 - offset);
+    two_transform.Translate(-child_size.width() / 2, -child_size.height() / 2);
+    CreateAndAppendSimpleSharedQuadState(pass.get(), two_transform, size);
+
+    SurfaceDrawQuad* surface_two_quad =
+        pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
+    gfx::Rect two_rect(child_size);
+    surface_two_quad->SetNew(
+        pass->shared_quad_state_list.back(), two_rect, two_rect, child_two);
+  }
+
+  CreateAndAppendSimpleSharedQuadState(pass.get(), gfx::Transform(), size);
+  SolidColorDrawQuad* color_quad =
+      pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  bool force_anti_aliasing_off = false;
+  color_quad->SetNew(pass->shared_quad_state_list.back(),
+                     rect,
+                     rect,
+                     SK_ColorYELLOW,
+                     force_anti_aliasing_off);
+
+  scoped_ptr<DelegatedFrameData> delegated_frame_data(new DelegatedFrameData);
+  delegated_frame_data->render_pass_list.push_back(pass.Pass());
+
+  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+  frame->delegated_frame_data = delegated_frame_data.Pass();
+
+  surface_->SubmitFrame(mojo::SurfaceId::From(id_),
+                        mojo::Frame::From(*frame));
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/surfaces_app/embedder.h b/mojo/examples/surfaces_app/embedder.h
new file mode 100644
index 0000000..3be6a42
--- /dev/null
+++ b/mojo/examples/surfaces_app/embedder.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EXAMPLES_SURFACES_APP_EMBEDDER_H_
+#define MOJO_EXAMPLES_SURFACES_APP_EMBEDDER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class CompositorFrame;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace examples {
+
+// Simple example of a surface embedder that embeds two other surfaces.
+class Embedder {
+ public:
+  explicit Embedder(Surface* surface);
+  ~Embedder();
+
+  void SetSurfaceId(cc::SurfaceId id) { id_ = id; }
+  void ProduceFrame(cc::SurfaceId child_one,
+                    cc::SurfaceId child_two,
+                    const gfx::Size& child_size,
+                    const gfx::Size& size,
+                    int offset);
+
+ private:
+  cc::SurfaceId id_;
+  Surface* surface_;
+
+  DISALLOW_COPY_AND_ASSIGN(Embedder);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+#endif  // MOJO_EXAMPLES_SURFACES_APP_EMBEDDER_H_
diff --git a/mojo/examples/surfaces_app/surfaces_app.cc b/mojo/examples/surfaces_app/surfaces_app.cc
new file mode 100644
index 0000000..a779c16
--- /dev/null
+++ b/mojo/examples/surfaces_app/surfaces_app.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 "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/surfaces_app/child.mojom.h"
+#include "mojo/examples/surfaces_app/embedder.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/system/core.h"
+#include "mojo/services/gles2/command_buffer.mojom.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/interfaces/gpu/gpu.mojom.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 "ui/gfx/rect.h"
+
+namespace mojo {
+namespace examples {
+
+class SurfacesApp : public ApplicationDelegate,
+                    public SurfaceClient,
+                    public NativeViewportClient {
+ public:
+  SurfacesApp() : weak_factory_(this) {}
+  virtual ~SurfacesApp() {}
+
+  // ApplicationDelegate implementation
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->ConnectToService("mojo:mojo_native_viewport_service",
+                                 &viewport_);
+    viewport_.set_client(this);
+
+    connection->ConnectToService("mojo:mojo_surfaces_service",
+                                 &surfaces_service_);
+    surfaces_service_->CreateSurfaceConnection(base::Bind(
+        &SurfacesApp::SurfaceConnectionCreated, base::Unretained(this)));
+
+    size_ = gfx::Size(800, 600);
+
+    viewport_->Create(Size::From(size_),
+                      base::Bind(&SurfacesApp::OnCreatedNativeViewport,
+                                 weak_factory_.GetWeakPtr()));
+    viewport_->Show();
+
+    child_size_ = gfx::Size(size_.width() / 3, size_.height() / 2);
+    connection->ConnectToService("mojo:mojo_surfaces_child_app", &child_one_);
+    connection->ConnectToService("mojo:mojo_surfaces_child_gl_app",
+                                 &child_two_);
+    child_one_->ProduceFrame(Color::From(SK_ColorBLUE),
+                             Size::From(child_size_),
+                             base::Bind(&SurfacesApp::ChildOneProducedFrame,
+                                        base::Unretained(this)));
+    child_two_->ProduceFrame(Color::From(SK_ColorGREEN),
+                             Size::From(child_size_),
+                             base::Bind(&SurfacesApp::ChildTwoProducedFrame,
+                                        base::Unretained(this)));
+    return true;
+  }
+
+  void ChildOneProducedFrame(SurfaceIdPtr id) {
+    child_one_id_ = id.To<cc::SurfaceId>();
+  }
+
+  void ChildTwoProducedFrame(SurfaceIdPtr id) {
+    child_two_id_ = id.To<cc::SurfaceId>();
+  }
+
+  void Draw(int offset) {
+    int bounced_offset = offset;
+    if (offset > 200)
+      bounced_offset = 400 - offset;
+    embedder_->ProduceFrame(
+        child_one_id_, child_two_id_, child_size_, size_, bounced_offset);
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(
+            &SurfacesApp::Draw, base::Unretained(this), (offset + 2) % 400),
+        base::TimeDelta::FromMilliseconds(50));
+  }
+
+  void SurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace) {
+    surface_ = surface.Pass();
+    surface_.set_client(this);
+    embedder_.reset(new Embedder(surface_.get()));
+    allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+
+    onscreen_id_ = allocator_->GenerateId();
+    embedder_->SetSurfaceId(onscreen_id_);
+    surface_->CreateSurface(SurfaceId::From(onscreen_id_), Size::From(size_));
+    viewport_->SubmittedFrame(SurfaceId::From(onscreen_id_));
+    Draw(10);
+  }
+
+  // SurfaceClient implementation.
+  virtual void ReturnResources(Array<ReturnedResourcePtr> resources) override {
+    DCHECK(!resources.size());
+  }
+  // NativeViewportClient implementation.
+  virtual void OnSizeChanged(mojo::SizePtr size) override {}
+  virtual void OnDestroyed() override {}
+  virtual void OnEvent(mojo::EventPtr event,
+                       const mojo::Callback<void()>& callback) override {
+    callback.Run();
+  }
+
+ private:
+  void OnCreatedNativeViewport(uint64_t native_viewport_id) {}
+
+  SurfacesServicePtr surfaces_service_;
+  SurfacePtr surface_;
+  cc::SurfaceId onscreen_id_;
+  scoped_ptr<cc::SurfaceIdAllocator> allocator_;
+  scoped_ptr<Embedder> embedder_;
+  ChildPtr child_one_;
+  cc::SurfaceId child_one_id_;
+  ChildPtr child_two_;
+  cc::SurfaceId child_two_id_;
+  gfx::Size size_;
+  gfx::Size child_size_;
+
+  NativeViewportPtr viewport_;
+
+  base::WeakPtrFactory<SurfacesApp> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfacesApp);
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::SurfacesApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/surfaces_app/surfaces_util.cc b/mojo/examples/surfaces_app/surfaces_util.cc
new file mode 100644
index 0000000..50bfbff
--- /dev/null
+++ b/mojo/examples/surfaces_app/surfaces_util.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/examples/surfaces_app/surfaces_util.h"
+
+#include "cc/quads/render_pass.h"
+#include "cc/quads/shared_quad_state.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+namespace examples {
+
+using cc::SharedQuadState;
+
+void CreateAndAppendSimpleSharedQuadState(cc::RenderPass* render_pass,
+                                          const gfx::Transform& transform,
+                                          const gfx::Size& size) {
+  const gfx::Size content_bounds = size;
+  const gfx::Rect visible_content_rect = gfx::Rect(size);
+  const gfx::Rect clip_rect = gfx::Rect(size);
+  bool is_clipped = false;
+  float opacity = 1.f;
+  const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
+  SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
+  shared_state->SetAll(transform,
+                       content_bounds,
+                       visible_content_rect,
+                       clip_rect,
+                       is_clipped,
+                       opacity,
+                       blend_mode,
+                       0);
+}
+
+}  // namespace mojo
+}  // namespace examples
diff --git a/mojo/examples/surfaces_app/surfaces_util.h b/mojo/examples/surfaces_app/surfaces_util.h
new file mode 100644
index 0000000..3109258
--- /dev/null
+++ b/mojo/examples/surfaces_app/surfaces_util.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_EXAMPLES_SURFACES_APP_SURFACES_UTIL_H_
+#define MOJO_EXAMPLES_SURFACES_APP_SURFACES_UTIL_H_
+
+namespace cc {
+class RenderPass;
+}
+
+namespace gfx {
+class Transform;
+class Size;
+}
+
+namespace mojo {
+namespace examples {
+
+void CreateAndAppendSimpleSharedQuadState(cc::RenderPass* render_pass,
+                                          const gfx::Transform& transform,
+                                          const gfx::Size& size);
+
+}  // namespace mojo
+}  // namespace examples
+
+#endif  // MOJO_EXAMPLES_SURFACES_APP_SURFACES_UTIL_H_
diff --git a/mojo/examples/wget/BUILD.gn b/mojo/examples/wget/BUILD.gn
new file mode 100644
index 0000000..448e4ec
--- /dev/null
+++ b/mojo/examples/wget/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.
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wget
+shared_library("wget") {
+  output_name = "mojo_wget"
+
+  deps = [
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/public/interfaces/network",
+  ]
+
+  sources = [
+    "wget.cc"
+  ]
+}
diff --git a/mojo/examples/wget/wget.cc b/mojo/examples/wget/wget.cc
new file mode 100644
index 0000000..f7c03bb
--- /dev/null
+++ b/mojo/examples/wget/wget.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 <stdio.h>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+class ResponsePrinter {
+ public:
+  void Run(URLResponsePtr response) const {
+    if (response->error) {
+      printf("Got error: %d (%s)\n",
+          response->error->code, response->error->description.get().c_str());
+    } else {
+      PrintResponse(response);
+      PrintResponseBody(response->body.Pass());
+    }
+
+    RunLoop::current()->Quit();  // All done!
+  }
+
+  void PrintResponse(const URLResponsePtr& response) const {
+    printf(">>> Headers <<< \n");
+    printf("  %s\n", response->status_line.get().c_str());
+    if (response->headers) {
+      for (size_t i = 0; i < response->headers.size(); ++i)
+        printf("  %s\n", response->headers[i].get().c_str());
+    }
+  }
+
+  void PrintResponseBody(ScopedDataPipeConsumerHandle body) const {
+    // Read response body in blocking fashion.
+    printf(">>> Body <<<\n");
+
+    for (;;) {
+      char buf[512];
+      uint32_t num_bytes = sizeof(buf);
+      MojoResult result = ReadDataRaw(body.get(), buf, &num_bytes,
+                                      MOJO_READ_DATA_FLAG_NONE);
+      if (result == MOJO_RESULT_SHOULD_WAIT) {
+        Wait(body.get(),
+             MOJO_HANDLE_SIGNAL_READABLE,
+             MOJO_DEADLINE_INDEFINITE);
+      } else if (result == MOJO_RESULT_OK) {
+        if (fwrite(buf, num_bytes, 1, stdout) != 1) {
+          printf("\nUnexpected error writing to file\n");
+          break;
+        }
+      } else {
+        break;
+      }
+    }
+
+    printf("\n>>> EOF <<<\n");
+  }
+};
+
+}  // namespace
+
+class WGetApp : public ApplicationDelegate {
+ public:
+  virtual void Initialize(ApplicationImpl* app) override {
+    app->ConnectToService("mojo:mojo_network_service", &network_service_);
+    Start(app->args());
+  }
+
+ private:
+  void Start(const Array<String>& args) {
+    std::string url((args.size() > 1) ? args[1].get() : PromptForURL());
+    printf("Loading: %s\n", url.c_str());
+
+    network_service_->CreateURLLoader(Get(&url_loader_));
+
+    URLRequestPtr request(URLRequest::New());
+    request->url = url;
+    request->method = "GET";
+    request->auto_follow_redirects = true;
+
+    url_loader_->Start(request.Pass(), ResponsePrinter());
+  }
+
+  std::string PromptForURL() {
+    printf("Enter URL> ");
+    char buf[1024];
+    if (scanf("%1023s", buf) != 1)
+      buf[0] = '\0';
+    return buf;
+  }
+
+  NetworkServicePtr network_service_;
+  URLLoaderPtr url_loader_;
+};
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new mojo::examples::WGetApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/window_manager/BUILD.gn b/mojo/examples/window_manager/BUILD.gn
new file mode 100644
index 0000000..8c89c04
--- /dev/null
+++ b/mojo/examples/window_manager/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(use_aura)
+
+# GYP version: mojo/mojo_examples.gypi:mojo_window_manager
+shared_library("window_manager") {
+  output_name = "mojo_window_manager"
+
+  sources = [
+    "debug_panel.cc",
+    "debug_panel.h",
+    "window_manager.cc",
+  ]
+
+  deps = [
+    ":bindings",
+    "//base",
+    "//mojo/application",
+    "//mojo/aura",
+    "//mojo/examples/keyboard:bindings",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/gles2:for_shared_library",
+    "//mojo/services/public/cpp/geometry",
+    "//mojo/services/public/cpp/input_events",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/geometry",
+    "//mojo/services/public/interfaces/input_events",
+    "//mojo/services/public/interfaces/navigation",
+    "//mojo/services/window_manager:lib",
+    "//mojo/views",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//ui/resources",
+    "//ui/views",
+    "//ui/wm",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_window_manager_bindings
+mojom("bindings") {
+  sources = [
+    "window_manager.mojom",
+  ]
+  deps = [
+    "//mojo/services/public/interfaces/geometry",
+  ]
+}
+
diff --git a/mojo/examples/window_manager/DEPS b/mojo/examples/window_manager/DEPS
new file mode 100644
index 0000000..372b6a8
--- /dev/null
+++ b/mojo/examples/window_manager/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+ui/aura",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/views",
+  "+ui/wm/core",
+]
diff --git a/mojo/examples/window_manager/debug_panel.cc b/mojo/examples/window_manager/debug_panel.cc
new file mode 100644
index 0000000..c65785c
--- /dev/null
+++ b/mojo/examples/window_manager/debug_panel.cc
@@ -0,0 +1,134 @@
+// Copyright 2014 The Chromium 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/examples/window_manager/debug_panel.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/blue_button.h"
+#include "ui/views/controls/button/radio_button.h"
+#include "ui/views/widget/widget.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const int kControlBorderInset = 5;
+const int kNavigationTargetGroupId = 1;
+
+}  // namespace
+
+DebugPanel::DebugPanel(Delegate* delegate, View* view)
+    : delegate_(delegate),
+      view_(view),
+      navigation_target_label_(new views::Label(
+          base::ASCIIToUTF16("Navigation target:"))),
+      navigation_target_new_(new views::RadioButton(
+          base::ASCIIToUTF16("New window"), kNavigationTargetGroupId)),
+      navigation_target_source_(new views::RadioButton(
+          base::ASCIIToUTF16("Source window"), kNavigationTargetGroupId)),
+      navigation_target_default_(new views::RadioButton(
+          base::ASCIIToUTF16("Default"), kNavigationTargetGroupId)),
+      colored_square_(new views::BlueButton(
+          this, base::ASCIIToUTF16("Local nav test"))),
+      close_last_(new views::BlueButton(
+          this, base::ASCIIToUTF16("Close last window"))),
+      cross_app_(new views::BlueButton(
+          this, base::ASCIIToUTF16("Cross-app nav test"))) {
+  navigation_target_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  navigation_target_default_->SetChecked(true);
+
+  views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView();
+  widget_delegate->GetContentsView()->set_background(
+      views::Background::CreateSolidBackground(0xFFDDDDDD));
+  widget_delegate->GetContentsView()->AddChildView(navigation_target_label_);
+  widget_delegate->GetContentsView()->AddChildView(navigation_target_default_);
+  widget_delegate->GetContentsView()->AddChildView(navigation_target_new_);
+  widget_delegate->GetContentsView()->AddChildView(navigation_target_source_);
+  widget_delegate->GetContentsView()->AddChildView(colored_square_);
+  widget_delegate->GetContentsView()->AddChildView(close_last_);
+  widget_delegate->GetContentsView()->AddChildView(cross_app_);
+  widget_delegate->GetContentsView()->SetLayoutManager(this);
+
+  views::Widget* widget = new views::Widget();
+  views::Widget::InitParams params(
+      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+  params.native_widget = new NativeWidgetViewManager(widget, view);
+  params.delegate = widget_delegate;
+  params.bounds = gfx::Rect(view->bounds().size());
+  widget->Init(params);
+  widget->Show();
+}
+
+DebugPanel::~DebugPanel() {
+}
+
+gfx::Size DebugPanel::GetPreferredSize(const views::View* view) const {
+  return gfx::Size();
+}
+
+Target DebugPanel::navigation_target() const {
+  if (navigation_target_new_->checked())
+    return TARGET_NEW_NODE;
+  if (navigation_target_source_->checked())
+    return TARGET_SOURCE_NODE;
+  return TARGET_DEFAULT;
+}
+
+void DebugPanel::Layout(views::View* view) {
+  int y = kControlBorderInset;
+  int w = view->width() - kControlBorderInset * 2;
+
+  navigation_target_label_->SetBounds(
+      kControlBorderInset, y, w,
+      navigation_target_label_->GetPreferredSize().height());
+  y += navigation_target_label_->height();
+
+  views::RadioButton* radios[] = {
+    navigation_target_default_,
+    navigation_target_new_,
+    navigation_target_source_,
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(radios); ++i) {
+    radios[i]->SetBounds(kControlBorderInset, y, w,
+                         radios[i]->GetPreferredSize().height());
+    y += radios[i]->height();
+  }
+
+  y += kControlBorderInset;
+  views::Button* buttons[] = {
+    colored_square_,
+    close_last_,
+    cross_app_,
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(buttons); ++i) {
+    buttons[i]->SetBounds(kControlBorderInset, y, w,
+                          buttons[i]->GetPreferredSize().height());
+    y += buttons[i]->height();
+  }
+}
+
+void DebugPanel::ButtonPressed(views::Button* sender, const ui::Event& event) {
+  if (sender == colored_square_) {
+    Navigate("mojo://mojo_embedded_app/");
+  } else if (sender == close_last_) {
+    delegate_->CloseTopWindow();
+  } else if (sender == cross_app_) {
+    Navigate("http://www.aaronboodman.com/z_dropbox/test.html");
+  }
+}
+
+void DebugPanel::Navigate(const std::string& url) {
+  URLRequestPtr request(URLRequest::New());
+  request->url = url;
+  delegate_->RequestNavigate(view_->id(), TARGET_NEW_NODE, request.Pass());
+}
+
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/examples/window_manager/debug_panel.h b/mojo/examples/window_manager/debug_panel.h
new file mode 100644
index 0000000..160e400
--- /dev/null
+++ b/mojo/examples/window_manager/debug_panel.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_WINDOW_MANAGER_DEBUG_PANEL_H_
+#define MOJO_EXAMPLES_WINDOW_MANAGER_DEBUG_PANEL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+class Label;
+class RadioButton;
+}
+
+namespace mojo {
+
+class View;
+
+namespace examples {
+
+namespace {
+}
+
+// A panel of controls intended to demonstrate the functionality of the window
+// manager.
+class DebugPanel : public views::LayoutManager, public views::ButtonListener {
+ public:
+  class Delegate {
+   public:
+    virtual void CloseTopWindow() = 0;
+    virtual void RequestNavigate(uint32 source_view_id,
+                                 Target target,
+                                 URLRequestPtr url_request) = 0;
+
+   protected:
+    virtual ~Delegate(){}
+  };
+
+  DebugPanel(Delegate* delegate, View* view);
+  virtual ~DebugPanel();
+
+  Target navigation_target() const;
+
+ private:
+  // LayoutManager overrides:
+  virtual gfx::Size GetPreferredSize(const views::View* view) const override;
+  virtual void Layout(views::View* host) override;
+  virtual void ButtonPressed(views::Button* sender,
+                             const ui::Event& event) override;
+
+  void Navigate(const std::string& url);
+
+  Delegate* delegate_;
+  View* view_;
+
+  views::Label* navigation_target_label_;
+  views::RadioButton* navigation_target_new_;
+  views::RadioButton* navigation_target_source_;
+  views::RadioButton* navigation_target_default_;
+
+  views::Button* colored_square_;
+  views::Button* close_last_;
+  views::Button* cross_app_;
+
+  DISALLOW_COPY_AND_ASSIGN(DebugPanel);
+};
+
+}  // examples
+}  // mojo
+
+#endif  //  MOJO_EXAMPLES_WINDOW_MANAGER_DEBUG_PANEL_H_
diff --git a/mojo/examples/window_manager/window_manager.cc b/mojo/examples/window_manager/window_manager.cc
new file mode 100644
index 0000000..2cb5458
--- /dev/null
+++ b/mojo/examples/window_manager/window_manager.cc
@@ -0,0 +1,623 @@
+// Copyright 2014 The Chromium 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/macros.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/keyboard/keyboard.mojom.h"
+#include "mojo/examples/window_manager/debug_panel.h"
+#include "mojo/examples/window_manager/window_manager.mojom.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/public/cpp/application/service_provider_impl.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/view_manager/view.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/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "mojo/views/views_init.h"
+#include "ui/aura/window.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/wm/core/focus_rules.h"
+
+#if defined CreateWindow
+#undef CreateWindow
+#endif
+
+namespace mojo {
+namespace examples {
+
+class WindowManager;
+
+namespace {
+
+const int kBorderInset = 25;
+const int kControlPanelWidth = 200;
+const int kTextfieldHeight = 25;
+
+class WMFocusRules : public wm::FocusRules {
+ public:
+  WMFocusRules(mojo::WindowManagerApp* window_manager_app,
+               mojo::View* window_container)
+      : window_container_(window_container),
+        window_manager_app_(window_manager_app) {}
+  virtual ~WMFocusRules() {}
+
+ private:
+  // Overridden from wm::FocusRules:
+  virtual bool IsToplevelWindow(aura::Window* window) const override {
+    return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
+        window_container_;
+  }
+  virtual bool CanActivateWindow(aura::Window* window) const override {
+    return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
+        window_container_;
+  }
+  virtual bool CanFocusWindow(aura::Window* window) const override {
+    return true;
+  }
+  virtual aura::Window* GetToplevelWindow(aura::Window* window) const override {
+    mojo::View* view = mojo::WindowManagerApp::GetViewForWindow(window);
+    while (view->parent() != window_container_) {
+      view = view->parent();
+      // Unparented hierarchy, there is no "top level" window.
+      if (!view)
+        return NULL;
+    }
+
+    return window_manager_app_->GetWindowForViewId(view->id());
+  }
+  virtual aura::Window* GetActivatableWindow(
+      aura::Window* window) const override {
+    return GetToplevelWindow(window);
+  }
+  virtual aura::Window* GetFocusableWindow(
+      aura::Window* window) const override {
+    return window;
+  }
+  virtual aura::Window* GetNextActivatableWindow(
+      aura::Window* ignore) const override {
+    aura::Window* activatable = GetActivatableWindow(ignore);
+    const aura::Window::Windows& children = activatable->parent()->children();
+    for (aura::Window::Windows::const_reverse_iterator it = children.rbegin();
+         it != children.rend(); ++it) {
+      if (*it != ignore)
+        return *it;
+    }
+    return NULL;
+  }
+
+  mojo::View* window_container_;
+  mojo::WindowManagerApp* window_manager_app_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMFocusRules);
+};
+
+}  // namespace
+
+class WindowManagerConnection : public InterfaceImpl<IWindowManager> {
+ public:
+  explicit WindowManagerConnection(WindowManager* window_manager)
+      : window_manager_(window_manager) {}
+  virtual ~WindowManagerConnection() {}
+
+ private:
+  // Overridden from IWindowManager:
+  virtual void CloseWindow(Id view_id) override;
+  virtual void ShowKeyboard(Id view_id, RectPtr bounds) override;
+  virtual void HideKeyboard(Id view_id) override;
+
+  WindowManager* window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection);
+};
+
+class NavigatorHostImpl : public InterfaceImpl<NavigatorHost> {
+ public:
+  explicit NavigatorHostImpl(WindowManager* window_manager, Id view_id)
+      : window_manager_(window_manager), view_id_(view_id) {}
+  virtual ~NavigatorHostImpl() {
+  }
+
+ private:
+  virtual void DidNavigateLocally(const mojo::String& url) override;
+  virtual void RequestNavigate(Target target, URLRequestPtr request) override;
+
+  WindowManager* window_manager_;
+  Id view_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl);
+};
+
+class KeyboardManager : public KeyboardClient,
+                        public ViewObserver {
+ public:
+  KeyboardManager() : view_manager_(NULL), view_(NULL) {
+  }
+  virtual ~KeyboardManager() {
+    if (view_)
+      view_->parent()->RemoveObserver(this);
+  }
+
+  View* view() { return view_; }
+
+  void Init(ApplicationImpl* application,
+            ViewManager* view_manager,
+            View* parent,
+            const gfx::Rect& bounds) {
+    view_manager_ = view_manager;
+    view_ = View::Create(view_manager);
+    view_->SetBounds(bounds);
+    parent->AddChild(view_);
+    view_->Embed("mojo:mojo_keyboard");
+    application->ConnectToService("mojo:mojo_keyboard", &keyboard_service_);
+    keyboard_service_.set_client(this);
+    parent->AddObserver(this);
+  }
+
+  void Show(Id view_id, const gfx::Rect& bounds) {
+    keyboard_service_->SetTarget(view_id);
+    view_->SetVisible(true);
+  }
+
+  void Hide(Id view_id) {
+    keyboard_service_->SetTarget(0);
+    view_->SetVisible(false);
+  }
+
+ private:
+  // KeyboardClient:
+  virtual void OnKeyboardEvent(Id view_id,
+                               int32_t code,
+                               int32_t flags) override {
+    View* view = view_manager_->GetViewById(view_id);
+    if (!view)
+      return;
+#if defined(OS_WIN)
+    const bool is_char = code != ui::VKEY_BACK && code != ui::VKEY_RETURN;
+#else
+    const bool is_char = false;
+#endif
+    if (is_char) {
+      view_manager_->DispatchEvent(
+          view,
+          Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED,
+                                   static_cast<ui::KeyboardCode>(code),
+                                   flags)));
+    } else {
+      view_manager_->DispatchEvent(
+          view,
+          Event::From(ui::KeyEvent(static_cast<base::char16>(code),
+                                   static_cast<ui::KeyboardCode>(code),
+                                   flags)));
+    }
+    view_manager_->DispatchEvent(
+        view,
+        Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED,
+                                 static_cast<ui::KeyboardCode>(code),
+                                 flags)));
+  }
+
+  // Overridden from ViewObserver:
+  virtual void OnViewBoundsChanged(View* parent,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override {
+    gfx::Rect keyboard_bounds(view_->bounds());
+    keyboard_bounds.set_y(new_bounds.bottom() - keyboard_bounds.height());
+    keyboard_bounds.set_width(keyboard_bounds.width() +
+                              new_bounds.width() - old_bounds.width());
+    view_->SetBounds(keyboard_bounds);
+  }
+  virtual void OnViewDestroyed(View* parent) override {
+    DCHECK_EQ(parent, view_->parent());
+    parent->RemoveObserver(this);
+    view_ = NULL;
+  }
+
+  KeyboardServicePtr keyboard_service_;
+  ViewManager* view_manager_;
+
+  // View the keyboard is attached to.
+  View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
+};
+
+class RootLayoutManager : public ViewObserver {
+ public:
+  RootLayoutManager(ViewManager* view_manager,
+                    View* root,
+                    Id content_view_id,
+                    Id launcher_ui_view_id,
+                    Id control_panel_view_id)
+      : root_(root),
+        view_manager_(view_manager),
+        content_view_id_(content_view_id),
+        launcher_ui_view_id_(launcher_ui_view_id),
+        control_panel_view_id_(control_panel_view_id) {}
+  virtual ~RootLayoutManager() {
+    if (root_)
+      root_->RemoveObserver(this);
+  }
+
+ private:
+  // Overridden from ViewObserver:
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) override {
+    DCHECK_EQ(view, root_);
+
+    View* content_view = view_manager_->GetViewById(content_view_id_);
+    content_view->SetBounds(new_bounds);
+
+    int delta_width = new_bounds.width() - old_bounds.width();
+    int delta_height = new_bounds.height() - old_bounds.height();
+
+    View* launcher_ui_view =
+        view_manager_->GetViewById(launcher_ui_view_id_);
+    gfx::Rect launcher_ui_bounds(launcher_ui_view->bounds());
+    launcher_ui_bounds.set_width(launcher_ui_bounds.width() + delta_width);
+    launcher_ui_view->SetBounds(launcher_ui_bounds);
+
+    View* control_panel_view =
+        view_manager_->GetViewById(control_panel_view_id_);
+    gfx::Rect control_panel_bounds(control_panel_view->bounds());
+    control_panel_bounds.set_x(control_panel_bounds.x() + delta_width);
+    control_panel_view->SetBounds(control_panel_bounds);
+
+    const View::Children& content_views = content_view->children();
+    View::Children::const_iterator iter = content_views.begin();
+    for(; iter != content_views.end(); ++iter) {
+      View* view = *iter;
+      if (view->id() == control_panel_view->id() ||
+          view->id() == launcher_ui_view->id())
+        continue;
+      gfx::Rect view_bounds(view->bounds());
+      view_bounds.set_width(view_bounds.width() + delta_width);
+      view_bounds.set_height(view_bounds.height() + delta_height);
+      view->SetBounds(view_bounds);
+    }
+  }
+  virtual void OnViewDestroyed(View* view) override {
+    DCHECK_EQ(view, root_);
+    root_->RemoveObserver(this);
+    root_ = NULL;
+  }
+
+  View* root_;
+  ViewManager* view_manager_;
+  const Id content_view_id_;
+  const Id launcher_ui_view_id_;
+  const Id control_panel_view_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootLayoutManager);
+};
+
+class Window : public InterfaceFactory<NavigatorHost> {
+ public:
+  Window(WindowManager* window_manager, View* view)
+      : window_manager_(window_manager), view_(view) {}
+
+  virtual ~Window() {}
+
+  View* view() const { return view_; }
+
+  void Embed(const std::string& url) {
+    scoped_ptr<ServiceProviderImpl> service_provider_impl(
+        new ServiceProviderImpl());
+    service_provider_impl->AddService<NavigatorHost>(this);
+    view_->Embed(url, service_provider_impl.Pass());
+  }
+
+ private:
+  // InterfaceFactory<NavigatorHost>
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NavigatorHost> request) override {
+    BindToRequest(new NavigatorHostImpl(window_manager_, view_->id()),
+                  &request);
+  }
+
+  WindowManager* window_manager_;
+  View* view_;
+};
+
+class WindowManager
+    : public ApplicationDelegate,
+      public DebugPanel::Delegate,
+      public ViewManagerDelegate,
+      public WindowManagerDelegate,
+      public ui::EventHandler {
+ public:
+  WindowManager()
+      : window_manager_factory_(this),
+        launcher_ui_(NULL),
+        view_manager_(NULL),
+        window_manager_app_(new WindowManagerApp(this, this)),
+        app_(NULL) {}
+
+  virtual ~WindowManager() {
+    // host() may be destroyed by the time we get here.
+    // TODO: figure out a way to always cleanly remove handler.
+    if (window_manager_app_->host())
+      window_manager_app_->host()->window()->RemovePreTargetHandler(this);
+  }
+
+  void CloseWindow(Id view_id) {
+    WindowVector::iterator iter = GetWindowByViewId(view_id);
+    DCHECK(iter != windows_.end());
+    Window* window = *iter;
+    windows_.erase(iter);
+    window->view()->Destroy();
+  }
+
+  void ShowKeyboard(Id view_id, const gfx::Rect& bounds) {
+    // TODO: this needs to validate |view_id|. That is, it shouldn't assume
+    // |view_id| is valid and it also needs to make sure the client that sent
+    // this really owns |view_id|.
+    // TODO: honor |bounds|.
+    if (!keyboard_manager_) {
+      keyboard_manager_.reset(new KeyboardManager);
+      View* parent = view_manager_->GetRoots().back();
+      int ideal_height = 200;
+      // TODO(sky): 10 is a bit of a hack here. There is a bug that causes
+      // white strips to appear when 0 is used. Figure this out!
+      const gfx::Rect keyboard_bounds(
+          10, parent->bounds().height() - ideal_height,
+          parent->bounds().width() - 20, ideal_height);
+      keyboard_manager_->Init(app_, view_manager_, parent, keyboard_bounds);
+    }
+    keyboard_manager_->Show(view_id, bounds);
+  }
+
+  void HideKeyboard(Id view_id) {
+    // See comment in ShowKeyboard() about validating args.
+    if (keyboard_manager_)
+      keyboard_manager_->Hide(view_id);
+  }
+
+  void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) {
+    LOG(ERROR) << "DidNavigateLocally: source_view_id: " << source_view_id
+               << " url: " << url.To<std::string>();
+  }
+
+  // Overridden from DebugPanel::Delegate:
+  virtual void CloseTopWindow() override {
+    if (!windows_.empty())
+      CloseWindow(windows_.back()->view()->id());
+  }
+
+  virtual void RequestNavigate(uint32 source_view_id,
+                               Target target,
+                               URLRequestPtr request) override {
+    OnLaunch(source_view_id, target, request->url);
+  }
+
+ private:
+  typedef std::vector<Window*> WindowVector;
+
+  // Overridden from ApplicationDelegate:
+  virtual void Initialize(ApplicationImpl* app) override {
+    app_ = app;
+    views_init_.reset(new ViewsInit);
+    window_manager_app_->Initialize(app);
+  }
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    connection->AddService(&window_manager_factory_);
+    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 {
+    DCHECK(!view_manager_);
+    view_manager_ = view_manager;
+
+    View* view = View::Create(view_manager_);
+    root->AddChild(view);
+    view->SetBounds(gfx::Rect(root->bounds().size()));
+    content_view_id_ = view->id();
+
+    Id launcher_ui_id = CreateLauncherUI();
+    Id control_panel_id = CreateControlPanel(view);
+
+    root_layout_manager_.reset(
+        new RootLayoutManager(view_manager, root,
+                              content_view_id_,
+                              launcher_ui_id,
+                              control_panel_id));
+    root->AddObserver(root_layout_manager_.get());
+
+    window_manager_app_->host()->window()->AddPreTargetHandler(this);
+
+    window_manager_app_->InitFocus(new WMFocusRules(window_manager_app_.get(),
+                                                    view));
+  }
+  virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {
+    DCHECK_EQ(view_manager_, view_manager);
+    view_manager_ = NULL;
+    base::MessageLoop::current()->Quit();
+  }
+
+  // Overridden from WindowManagerDelegate:
+  virtual void Embed(
+      const String& url,
+      InterfaceRequest<ServiceProvider> service_provider) override {
+    const Id kInvalidSourceViewId = 0;
+    OnLaunch(kInvalidSourceViewId, TARGET_DEFAULT, url);
+  }
+  virtual void DispatchEvent(EventPtr event) override {}
+
+  // Overridden from ui::EventHandler:
+  virtual void OnEvent(ui::Event* event) override {
+    View* view = WindowManagerApp::GetViewForWindow(
+        static_cast<aura::Window*>(event->target()));
+    if (event->type() == ui::ET_MOUSE_PRESSED &&
+        !IsDescendantOfKeyboard(view)) {
+      view->SetFocus();
+    }
+  }
+
+  void OnLaunch(uint32 source_view_id,
+                Target requested_target,
+                const mojo::String& url) {
+    Target target = debug_panel_->navigation_target();
+    if (target == TARGET_DEFAULT) {
+      if (requested_target != TARGET_DEFAULT) {
+        target = requested_target;
+      } else {
+        // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
+        // are different?
+        target = TARGET_SOURCE_NODE;
+      }
+    }
+
+    Window* dest_view = NULL;
+    if (target == TARGET_SOURCE_NODE) {
+      WindowVector::iterator source_view = GetWindowByViewId(source_view_id);
+      bool app_initiated = source_view != windows_.end();
+      if (app_initiated)
+        dest_view = *source_view;
+      else if (!windows_.empty())
+        dest_view = windows_.back();
+    }
+
+    if (!dest_view) {
+      dest_view = CreateWindow();
+      windows_.push_back(dest_view);
+    }
+
+    dest_view->Embed(url);
+  }
+
+  // TODO(beng): proper layout manager!!
+  Id CreateLauncherUI() {
+    View* view = view_manager_->GetViewById(content_view_id_);
+    gfx::Rect bounds = view->bounds();
+    bounds.Inset(kBorderInset, kBorderInset);
+    bounds.set_height(kTextfieldHeight);
+    launcher_ui_ = CreateWindow(bounds);
+    launcher_ui_->Embed("mojo:mojo_browser");
+    return launcher_ui_->view()->id();
+  }
+
+  Window* CreateWindow() {
+    View* view = view_manager_->GetViewById(content_view_id_);
+    gfx::Rect bounds(kBorderInset,
+                     2 * kBorderInset + kTextfieldHeight,
+                     view->bounds().width() - 3 * kBorderInset -
+                         kControlPanelWidth,
+                     view->bounds().height() -
+                         (3 * kBorderInset + kTextfieldHeight));
+    if (!windows_.empty()) {
+      gfx::Point position = windows_.back()->view()->bounds().origin();
+      position.Offset(35, 35);
+      bounds.set_origin(position);
+    }
+    return CreateWindow(bounds);
+  }
+
+  Window* CreateWindow(const gfx::Rect& bounds) {
+    View* content = view_manager_->GetViewById(content_view_id_);
+    View* view = View::Create(view_manager_);
+    content->AddChild(view);
+    view->SetBounds(bounds);
+    view->SetFocus();
+    return new Window(this, view);
+  }
+
+  bool IsDescendantOfKeyboard(View* target) {
+    return keyboard_manager_.get() &&
+        keyboard_manager_->view()->Contains(target);
+  }
+
+  Id CreateControlPanel(View* root) {
+    View* view = View::Create(view_manager_);
+    root->AddChild(view);
+
+    gfx::Rect bounds(root->bounds().width() - kControlPanelWidth -
+                         kBorderInset,
+                     kBorderInset * 2 + kTextfieldHeight,
+                     kControlPanelWidth,
+                     root->bounds().height() - kBorderInset * 3 -
+                         kTextfieldHeight);
+    view->SetBounds(bounds);
+
+    debug_panel_ = new DebugPanel(this, view);
+    return view->id();
+  }
+
+  WindowVector::iterator GetWindowByViewId(Id view_id) {
+    for (std::vector<Window*>::iterator iter = windows_.begin();
+         iter != windows_.end();
+         ++iter) {
+      if ((*iter)->view()->id() == view_id) {
+        return iter;
+      }
+    }
+    return windows_.end();
+  }
+
+  InterfaceFactoryImplWithContext<WindowManagerConnection, WindowManager>
+      window_manager_factory_;
+
+  scoped_ptr<ViewsInit> views_init_;
+  DebugPanel* debug_panel_;
+  Window* launcher_ui_;
+  WindowVector windows_;
+  ViewManager* view_manager_;
+  scoped_ptr<RootLayoutManager> root_layout_manager_;
+
+  scoped_ptr<WindowManagerApp> window_manager_app_;
+
+  // Id of the view most content is added to. The keyboard is NOT added here.
+  Id content_view_id_;
+
+  scoped_ptr<KeyboardManager> keyboard_manager_;
+  ApplicationImpl* app_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManager);
+};
+
+void WindowManagerConnection::CloseWindow(Id view_id) {
+  window_manager_->CloseWindow(view_id);
+}
+
+void WindowManagerConnection::ShowKeyboard(Id view_id, RectPtr bounds) {
+  window_manager_->ShowKeyboard(view_id, bounds.To<gfx::Rect>());
+}
+
+void WindowManagerConnection::HideKeyboard(Id view_id) {
+  window_manager_->HideKeyboard(view_id);
+}
+
+void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) {
+  window_manager_->DidNavigateLocally(view_id_, url);
+}
+
+void NavigatorHostImpl::RequestNavigate(Target target, URLRequestPtr request) {
+  window_manager_->RequestNavigate(view_id_, target, request.Pass());
+}
+
+}  // namespace examples
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::examples::WindowManager);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/window_manager/window_manager.mojom b/mojo/examples/window_manager/window_manager.mojom
new file mode 100644
index 0000000..c6b4187
--- /dev/null
+++ b/mojo/examples/window_manager/window_manager.mojom
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+
+module mojo {
+
+interface IWindowManager {
+  CloseWindow(uint32 node_id);
+
+  // Shows the keyboard for the specified view. |bounds| is the bounds of the
+  // view that is showing focus. |bounds| is relative to the bounds of the node.
+  // Events from the keyboard are routed to the view with id |view_id|.
+  ShowKeyboard(uint32 view_id, mojo.Rect? bounds);
+
+  // Hides the keyboard. This is ignored if |view_id| is not the view that was
+  // last passed to ShowKeyboard().
+  HideKeyboard(uint32 view_id);
+};
+
+}
diff --git a/mojo/examples/wm_flow/BUILD.gn b/mojo/examples/wm_flow/BUILD.gn
new file mode 100644
index 0000000..ef665c0
--- /dev/null
+++ b/mojo/examples/wm_flow/BUILD.gn
@@ -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("//build/config/ui.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(use_aura)
+
+group("wm_flow" ) {
+  deps = [
+    ":app",
+    ":embedded",
+    ":init",
+    ":wm",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_wm
+shared_library("wm") {
+  output_name = "mojo_wm_flow_wm"
+
+  sources = [
+    "wm/wm.cc",
+    "wm/frame_controller.cc",
+    "wm/frame_controller.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/window_manager:lib",
+    "//mojo/views",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_init
+shared_library("init") {
+  output_name = "mojo_wm_flow_init"
+
+  sources = [
+    "init/init.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/public/interfaces/view_manager",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_app
+shared_library("app") {
+  output_name = "mojo_wm_flow_app"
+
+  sources = [
+    "app/app.cc",
+  ]
+
+  deps = [
+    ":embedder_bindings",
+    ":embeddee_bindings",
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/window_manager:lib",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_embedded
+shared_library("embedded") {
+  output_name = "mojo_wm_flow_embedded"
+
+  sources = [
+    "embedded/embedded.cc",
+  ]
+
+  deps = [
+    ":embedder_bindings",
+    ":embeddee_bindings",
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/services/public/cpp/view_manager",
+    "//mojo/services/window_manager:lib",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_embedder_bindings
+mojom("embedder_bindings") {
+  sources = [
+    "app/embedder.mojom",
+  ]
+}
+
+# GYP version: mojo/mojo_examples.gypi:mojo_wm_flow_embeddee_bindings
+mojom("embeddee_bindings") {
+  sources = [
+    "embedded/embeddee.mojom",
+  ]
+}
diff --git a/mojo/examples/wm_flow/app/DEPS b/mojo/examples/wm_flow/app/DEPS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/examples/wm_flow/app/DEPS
diff --git a/mojo/examples/wm_flow/app/app.cc b/mojo/examples/wm_flow/app/app.cc
new file mode 100644
index 0000000..f2bea10
--- /dev/null
+++ b/mojo/examples/wm_flow/app/app.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium 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/macros.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/wm_flow/app/embedder.mojom.h"
+#include "mojo/examples/wm_flow/embedded/embeddee.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.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_context.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+
+namespace examples {
+namespace {
+
+const SkColor kColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorYELLOW };
+
+class EmbedderImpl : public mojo::InterfaceImpl<Embedder> {
+ public:
+  EmbedderImpl() {}
+  virtual ~EmbedderImpl() {}
+
+ private:
+  // Overridden from Embedder:
+  virtual void HelloWorld(const mojo::Callback<void()>& callback) override {
+    callback.Run();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(EmbedderImpl);
+};
+
+}  // namespace
+
+// This app starts its life via Connect() rather than by being embed, so it does
+// not start with a connection to the ViewManager service. It has to obtain a
+// connection by connecting to the ViewManagerInit service and asking to be
+// embed without a view context.
+class WMFlowApp : public mojo::ApplicationDelegate,
+                  public mojo::ViewManagerDelegate,
+                  public mojo::ViewObserver {
+ public:
+  WMFlowApp() : embed_count_(0) {}
+  virtual ~WMFlowApp() {}
+
+ private:
+  // Overridden from Application:
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new mojo::ViewManagerClientFactory(app->shell(), this));
+    view_manager_context_.reset(new mojo::ViewManagerContext(app));
+    OpenNewWindow();
+    OpenNewWindow();
+    OpenNewWindow();
+  }
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  void OnConnect(bool success) {}
+
+  // Overridden from mojo::ViewManagerDelegate:
+  virtual void OnEmbed(
+      mojo::ViewManager* view_manager,
+      mojo::View* root,
+      mojo::ServiceProviderImpl* exported_services,
+      scoped_ptr<mojo::ServiceProvider> imported_services) override {
+    root->AddObserver(this);
+    root->SetColor(kColors[embed_count_++ % arraysize(kColors)]);
+
+    mojo::View* embed = mojo::View::Create(view_manager);
+    root->AddChild(embed);
+    gfx::Rect bounds = gfx::Rect(root->bounds().size());
+    bounds.Inset(25, 25);
+    embed->SetBounds(bounds);
+
+    scoped_ptr<mojo::ServiceProviderImpl> registry(
+        new mojo::ServiceProviderImpl);
+    // Expose some services to the embeddee...
+    registry->AddService(&embedder_factory_);
+    scoped_ptr<mojo::ServiceProvider> imported =
+        embed->Embed("mojo:mojo_wm_flow_embedded", registry.Pass());
+    mojo::ConnectToService(imported.get(), &embeddee_);
+    embeddee_->HelloBack(base::Bind(&WMFlowApp::HelloBackAck,
+                                    base::Unretained(this)));
+  }
+  virtual void OnViewManagerDisconnected(
+      mojo::ViewManager* view_manager) override {}
+
+  // Overridden from mojo::ViewObserver:
+  virtual void OnViewInputEvent(mojo::View* view,
+                                const mojo::EventPtr& event) override {
+    if (event->action == mojo::EVENT_TYPE_MOUSE_RELEASED &&
+        event->flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON) {
+      OpenNewWindow();
+    }
+  }
+  virtual void OnViewDestroyed(mojo::View* view) override {
+    --embed_count_;
+    view->RemoveObserver(this);
+  }
+
+  void HelloBackAck() {
+    printf("HelloBack() ack'ed\n");
+  }
+
+  void OpenNewWindow() {
+    view_manager_context_->Embed("mojo:mojo_wm_flow_app");
+  }
+
+  int embed_count_;
+  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+  mojo::InterfaceFactoryImpl<EmbedderImpl> embedder_factory_;
+  scoped_ptr<mojo::ViewManagerContext> view_manager_context_;
+  EmbeddeePtr embeddee_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMFlowApp);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::WMFlowApp);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/wm_flow/app/embedder.mojom b/mojo/examples/wm_flow/app/embedder.mojom
new file mode 100644
index 0000000..aae7870
--- /dev/null
+++ b/mojo/examples/wm_flow/app/embedder.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 examples {
+
+interface Embedder {
+  HelloWorld() => ();
+};
+
+}
diff --git a/mojo/examples/wm_flow/embedded/embedded.cc b/mojo/examples/wm_flow/embedded/embedded.cc
new file mode 100644
index 0000000..f69aa90
--- /dev/null
+++ b/mojo/examples/wm_flow/embedded/embedded.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 "base/bind.h"
+#include "base/macros.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/wm_flow/app/embedder.mojom.h"
+#include "mojo/examples/wm_flow/embedded/embeddee.mojom.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.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"
+
+namespace examples {
+
+namespace {
+
+class EmbeddeeImpl : public mojo::InterfaceImpl<Embeddee> {
+ public:
+  EmbeddeeImpl() {}
+  virtual ~EmbeddeeImpl() {}
+
+ private:
+  // Overridden from Embeddee:
+  virtual void HelloBack(const mojo::Callback<void()>& callback) override {
+    callback.Run();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddeeImpl);
+};
+
+}  // namespace
+
+class WMFlowEmbedded : public mojo::ApplicationDelegate,
+                       public mojo::ViewManagerDelegate {
+ public:
+  WMFlowEmbedded() {}
+  virtual ~WMFlowEmbedded() {}
+
+ private:
+  // Overridden from Application:
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    view_manager_client_factory_.reset(
+        new mojo::ViewManagerClientFactory(app->shell(), this));
+  }
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService(view_manager_client_factory_.get());
+    return true;
+  }
+
+  // Overridden from mojo::ViewManagerDelegate:
+  virtual void OnEmbed(
+      mojo::ViewManager* view_manager,
+      mojo::View* root,
+      mojo::ServiceProviderImpl* exported_services,
+      scoped_ptr<mojo::ServiceProvider> imported_services) override {
+    root->SetColor(SK_ColorMAGENTA);
+
+    exported_services->AddService(&embeddee_factory_);
+    mojo::ConnectToService(imported_services.get(), &embedder_);
+    embedder_->HelloWorld(base::Bind(&WMFlowEmbedded::HelloWorldAck,
+                                     base::Unretained(this)));
+  }
+  virtual void OnViewManagerDisconnected(
+      mojo::ViewManager* view_manager) override {}
+
+  void HelloWorldAck() {
+    printf("HelloWorld() ack'ed\n");
+  }
+
+  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+  EmbedderPtr embedder_;
+  mojo::InterfaceFactoryImpl<EmbeddeeImpl> embeddee_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMFlowEmbedded);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::WMFlowEmbedded);
+  return runner.Run(shell_handle);
+}
+
diff --git a/mojo/examples/wm_flow/embedded/embeddee.mojom b/mojo/examples/wm_flow/embedded/embeddee.mojom
new file mode 100644
index 0000000..b989da2
--- /dev/null
+++ b/mojo/examples/wm_flow/embedded/embeddee.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 examples {
+
+interface Embeddee {
+  HelloBack() => ();
+};
+
+}
diff --git a/mojo/examples/wm_flow/init/init.cc b/mojo/examples/wm_flow/init/init.cc
new file mode 100644
index 0000000..ba17b44
--- /dev/null
+++ b/mojo/examples/wm_flow/init/init.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 "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/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/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_context.h"
+
+namespace examples {
+
+// This application starts the view manager, embeds the window manager and then
+// starts another app (wm_flow_app) which also connects to the view manager and
+// asks to be embedded without context.
+class WMFlowInit : public mojo::ApplicationDelegate {
+ public:
+  WMFlowInit() {}
+  virtual ~WMFlowInit() {}
+
+ private:
+  // Overridden from Application:
+  virtual void Initialize(mojo::ApplicationImpl* app) override {
+    context_.reset(new mojo::ViewManagerContext(app));
+    context_->Embed("mojo:mojo_wm_flow_wm");
+    app->ConnectToApplication("mojo:mojo_wm_flow_app");
+  }
+
+  scoped_ptr<mojo::ViewManagerContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMFlowInit);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new examples::WMFlowInit);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/examples/wm_flow/wm/DEPS b/mojo/examples/wm_flow/wm/DEPS
new file mode 100644
index 0000000..1486b9f
--- /dev/null
+++ b/mojo/examples/wm_flow/wm/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+ui/aura",
+  "+ui/gfx/geometry",
+  "+ui/views",
+  "+ui/wm/core",
+  "+ui/wm/public",
+]
diff --git a/mojo/examples/wm_flow/wm/frame_controller.cc b/mojo/examples/wm_flow/wm/frame_controller.cc
new file mode 100644
index 0000000..7ed1dac
--- /dev/null
+++ b/mojo/examples/wm_flow/wm/frame_controller.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium 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/examples/wm_flow/wm/frame_controller.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/widget/widget.h"
+#include "ui/wm/public/activation_client.h"
+
+class FrameController::LayoutManager : public views::LayoutManager,
+                                       public views::ButtonListener {
+ public:
+  explicit LayoutManager(FrameController* controller)
+      : controller_(controller),
+        close_button_(
+            new views::LabelButton(this, base::ASCIIToUTF16("Begone"))),
+        maximize_button_(
+            new views::LabelButton(this, base::ASCIIToUTF16("Embiggen"))) {}
+  virtual ~LayoutManager() {}
+
+ private:
+  static const int kButtonFrameMargin = 5;
+  static const int kButtonFrameSpacing = 2;
+  static const int kFrameSize = 10;
+
+  // Overridden from views::LayoutManager:
+  virtual void Installed(views::View* host) override {
+    host->AddChildView(close_button_);
+    host->AddChildView(maximize_button_);
+  }
+  virtual void Layout(views::View* host) override {
+    gfx::Size ps = close_button_->GetPreferredSize();
+    gfx::Rect bounds = host->GetLocalBounds();
+    close_button_->SetBounds(bounds.right() - kButtonFrameMargin - ps.width(),
+                             kButtonFrameMargin, ps.width(), ps.height());
+
+    ps = maximize_button_->GetPreferredSize();
+    maximize_button_->SetBounds(
+        close_button_->x() - kButtonFrameSpacing - ps.width(),
+        kButtonFrameMargin, ps.width(), ps.height());
+
+    bounds.Inset(kFrameSize,
+                 close_button_->bounds().bottom() + kButtonFrameMargin,
+                 kFrameSize, kFrameSize);
+    controller_->app_view_->SetBounds(bounds);
+  }
+  virtual gfx::Size GetPreferredSize(const views::View* host) const override {
+    return gfx::Size();
+  }
+
+  // Overridden from views::ButtonListener:
+  virtual void ButtonPressed(views::Button* sender,
+                             const ui::Event& event) override {
+    if (sender == close_button_)
+      controller_->CloseWindow();
+    else if (sender == maximize_button_)
+      controller_->ToggleMaximize();
+  }
+
+  FrameController* controller_;
+  views::Button* close_button_;
+  views::Button* maximize_button_;
+
+  DISALLOW_COPY_AND_ASSIGN(LayoutManager);
+};
+
+class FrameController::FrameEventHandler : public ui::EventHandler {
+ public:
+  explicit FrameEventHandler(FrameController* frame_controller)
+      : frame_controller_(frame_controller) {}
+  virtual ~FrameEventHandler() {}
+
+ private:
+
+  // Overriden from ui::EventHandler:
+  virtual void OnMouseEvent(ui::MouseEvent* event) override {
+    if (event->type() == ui::ET_MOUSE_PRESSED)
+      frame_controller_->ActivateWindow();
+  }
+
+  FrameController* frame_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameEventHandler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// FrameController, public:
+
+FrameController::FrameController(
+    mojo::View* view,
+    mojo::View** app_view,
+    aura::client::ActivationClient* activation_client,
+    mojo::WindowManagerApp* window_manager_app)
+    : view_(view),
+      app_view_(mojo::View::Create(view->view_manager())),
+      frame_view_(new views::View),
+      frame_view_layout_manager_(new LayoutManager(this)),
+      widget_(new views::Widget),
+      maximized_(false),
+      activation_client_(activation_client),
+      window_manager_app_(window_manager_app) {
+  view_->AddChild(app_view_);
+  view_->AddObserver(this);
+  *app_view = app_view_;
+  frame_view_->set_background(
+      views::Background::CreateSolidBackground(SK_ColorBLUE));
+  frame_view_->SetLayoutManager(frame_view_layout_manager_);
+  frame_event_handler_.reset(new FrameEventHandler(this));
+  frame_view_->AddPreTargetHandler(frame_event_handler_.get());
+  views::Widget::InitParams params(
+      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+  params.native_widget = new mojo::NativeWidgetViewManager(widget_, view_);
+  params.bounds = gfx::Rect(view_->bounds().size());
+  widget_->Init(params);
+  widget_->SetContentsView(frame_view_);
+  widget_->Show();
+}
+
+FrameController::~FrameController() {}
+
+void FrameController::CloseWindow() {
+  app_view_->Destroy();
+  view_->Destroy();
+}
+
+void FrameController::ToggleMaximize() {
+  if (!maximized_)
+    restored_bounds_ = view_->bounds();
+  maximized_ = !maximized_;
+  if (maximized_)
+    view_->SetBounds(view_->parent()->bounds());
+  else
+    view_->SetBounds(restored_bounds_);
+}
+
+void FrameController::ActivateWindow() {
+  aura::Window* window = window_manager_app_->GetWindowForViewId(view_->id());
+  activation_client_->ActivateWindow(window);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FrameController, mojo::ViewObserver implementation:
+
+void FrameController::OnViewDestroyed(mojo::View* view) {
+  view_->RemoveObserver(this);
+  delete this;
+}
diff --git a/mojo/examples/wm_flow/wm/frame_controller.h b/mojo/examples/wm_flow/wm/frame_controller.h
new file mode 100644
index 0000000..2c7d9e9
--- /dev/null
+++ b/mojo/examples/wm_flow/wm/frame_controller.h
@@ -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.
+
+#ifndef MOJO_EXAMPLES_WM_FLOW_WM_FRAME_CONTROLLER_H_
+#define MOJO_EXAMPLES_WM_FLOW_WM_FRAME_CONTROLLER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace aura {
+namespace client {
+class ActivationClient;
+}
+}
+
+namespace mojo {
+class NativeWidgetViewManager;
+class View;
+class WindowManagerApp;
+}
+
+namespace views {
+class View;
+class Widget;
+}
+
+// FrameController encapsulates the window manager's frame additions to a window
+// created by an application. It renders the content of the frame and responds
+// to any events targeted at it.
+class FrameController : mojo::ViewObserver {
+ public:
+  FrameController(mojo::View* view,
+                  mojo::View** app_view,
+                  aura::client::ActivationClient* activation_client,
+                  mojo::WindowManagerApp* window_manager_app);
+  virtual ~FrameController();
+
+  void CloseWindow();
+  void ToggleMaximize();
+
+  void ActivateWindow();
+
+ private:
+  class LayoutManager;
+  friend class LayoutManager;
+  class FrameEventHandler;
+
+  virtual void OnViewDestroyed(mojo::View* view) override;
+
+  mojo::View* view_;
+  mojo::View* app_view_;
+  views::View* frame_view_;
+  LayoutManager* frame_view_layout_manager_;
+  views::Widget* widget_;
+  bool maximized_;
+  gfx::Rect restored_bounds_;
+  aura::client::ActivationClient* activation_client_;
+  mojo::WindowManagerApp* window_manager_app_;
+  scoped_ptr<FrameEventHandler> frame_event_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameController);
+};
+
+#endif  // MOJO_EXAMPLES_WM_FLOW_WM_FRAME_CONTROLLER_H_
diff --git a/mojo/examples/wm_flow/wm/wm.cc b/mojo/examples/wm_flow/wm/wm.cc
new file mode 100644
index 0000000..f2787fc
--- /dev/null
+++ b/mojo/examples/wm_flow/wm/wm.cc
@@ -0,0 +1,200 @@
+// Copyright 2014 The Chromium 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 <vector>
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/examples/wm_flow/wm/frame_controller.h"
+#include "mojo/public/c/system/main.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/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/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "mojo/views/views_init.h"
+#include "ui/aura/window.h"
+#include "ui/wm/core/focus_rules.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace examples {
+
+namespace {
+
+class WMFocusRules : public wm::FocusRules {
+ public:
+  WMFocusRules(mojo::WindowManagerApp* window_manager_app,
+               mojo::View* window_container)
+      : window_container_(window_container),
+        window_manager_app_(window_manager_app) {}
+  virtual ~WMFocusRules() {}
+
+ private:
+  // Overridden from wm::FocusRules:
+  virtual bool IsToplevelWindow(aura::Window* window) const override {
+    return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
+        window_container_;
+  }
+  virtual bool CanActivateWindow(aura::Window* window) const override {
+    return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
+        window_container_;
+  }
+  virtual bool CanFocusWindow(aura::Window* window) const override {
+    return true;
+  }
+  virtual aura::Window* GetToplevelWindow(aura::Window* window) const override {
+    mojo::View* view = mojo::WindowManagerApp::GetViewForWindow(window);
+    while (view->parent() != window_container_) {
+      view = view->parent();
+      // Unparented hierarchy, there is no "top level" window.
+      if (!view)
+        return NULL;
+    }
+
+    return window_manager_app_->GetWindowForViewId(view->id());
+  }
+  virtual aura::Window* GetActivatableWindow(
+      aura::Window* window) const override {
+    return GetToplevelWindow(window);
+  }
+  virtual aura::Window* GetFocusableWindow(
+      aura::Window* window) const override {
+    return window;
+  }
+  virtual aura::Window* GetNextActivatableWindow(
+      aura::Window* ignore) const override {
+    aura::Window* activatable = GetActivatableWindow(ignore);
+    const aura::Window::Windows& children = activatable->parent()->children();
+    for (aura::Window::Windows::const_reverse_iterator it = children.rbegin();
+         it != children.rend(); ++it) {
+      if (*it != ignore)
+        return *it;
+    }
+    return NULL;
+  }
+
+  mojo::View* window_container_;
+  mojo::WindowManagerApp* window_manager_app_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMFocusRules);
+};
+
+}  // namespace
+
+class SimpleWM : public mojo::ApplicationDelegate,
+                 public mojo::ViewManagerDelegate,
+                 public mojo::WindowManagerDelegate,
+                 public mojo::ViewObserver {
+ public:
+  SimpleWM()
+      : window_manager_app_(new mojo::WindowManagerApp(this, this)),
+        view_manager_(NULL),
+        root_(NULL),
+        window_container_(NULL),
+        next_window_origin_(10, 10) {}
+  virtual ~SimpleWM() {}
+
+ private:
+  // Overridden from mojo::ApplicationDelegate:
+  virtual void Initialize(mojo::ApplicationImpl* impl) override {
+    window_manager_app_->Initialize(impl);
+  }
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    window_manager_app_->ConfigureIncomingConnection(connection);
+    return true;
+  }
+
+  // Overridden from mojo::ViewManagerDelegate:
+  virtual void OnEmbed(
+      mojo::ViewManager* view_manager,
+      mojo::View* root,
+      mojo::ServiceProviderImpl* exported_services,
+      scoped_ptr<mojo::ServiceProvider> remote_service_provider) override {
+    view_manager_ = view_manager;
+    root_ = root;
+
+    window_container_ = mojo::View::Create(view_manager_);
+    window_container_->SetBounds(root_->bounds());
+    root_->AddChild(window_container_);
+
+    window_manager_app_->InitFocus(new WMFocusRules(window_manager_app_.get(),
+                                                    window_container_));
+  }
+  virtual void OnViewManagerDisconnected(
+      mojo::ViewManager* view_manager) override {
+    view_manager_ = NULL;
+    root_ = NULL;
+  }
+
+  // Overridden from mojo::WindowManagerDelegate:
+  virtual void Embed(
+      const mojo::String& url,
+      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) override {
+    mojo::View* app_view = NULL;
+    mojo::View* frame_view = CreateTopLevelWindow(&app_view);
+    window_container_->AddChild(frame_view);
+
+    // TODO(beng): We're dropping the |service_provider| passed from the client
+    //             on the floor here and passing our own. Seems like we should
+    //             be sending both. I'm not yet sure how this sould work for
+    //             N levels of proxying.
+    app_view->Embed(url, scoped_ptr<mojo::ServiceProviderImpl>(
+        new mojo::ServiceProviderImpl).Pass());
+  }
+  virtual void DispatchEvent(mojo::EventPtr event) override {}
+
+  // Overridden from mojo::ViewObserver:
+  virtual void OnViewInputEvent(mojo::View* view,
+                                const mojo::EventPtr& event) override {
+    if (event->action == mojo::EVENT_TYPE_MOUSE_RELEASED &&
+        event->flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON &&
+        view->parent() == window_container_) {
+      CloseWindow(view);
+    }
+  }
+  virtual void OnViewDestroyed(mojo::View* view) override {
+    view->RemoveObserver(this);
+  }
+
+  void CloseWindow(mojo::View* view) {
+    mojo::View* first_child = view->children().front();
+    first_child->Destroy();
+    view->Destroy();
+    next_window_origin_.Offset(-50, -50);
+  }
+
+  mojo::View* CreateTopLevelWindow(mojo::View** app_view) {
+    mojo::View* frame_view = mojo::View::Create(view_manager_);
+    frame_view->SetBounds(gfx::Rect(next_window_origin_, gfx::Size(400, 400)));
+    next_window_origin_.Offset(50, 50);
+
+    aura::client::ActivationClient* client = aura::client::GetActivationClient(
+        window_manager_app_->host()->window());
+    new FrameController(frame_view, app_view, client,
+                        window_manager_app_.get());
+    return frame_view;
+  }
+
+  scoped_ptr<mojo::WindowManagerApp> window_manager_app_;
+
+  mojo::ViewManager* view_manager_;
+  mojo::View* root_;
+  mojo::View* window_container_;
+
+  gfx::Point next_window_origin_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleWM);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ViewsInit views_init;
+  mojo::ApplicationRunnerChromium runner(new examples::SimpleWM);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/gles2/BUILD.gn b/mojo/gles2/BUILD.gn
new file mode 100644
index 0000000..14e1b8e
--- /dev/null
+++ b/mojo/gles2/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+config("mojo_use_gles2") {
+  defines = [ "MOJO_USE_GLES2_IMPL" ]
+}
+
+config("gles2_use_mojo") {
+  defines = [ "GLES2_USE_MOJO" ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_gles2_impl
+component("gles2") {
+  output_name = "mojo_gles2_impl"
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//gpu/command_buffer/client",
+    "//gpu/command_buffer/common",
+    "//gpu/command_buffer/client:gles2_implementation",
+    "//gpu/command_buffer/client:gles2_interface",
+    "//mojo/public/c/system:for_component",
+    "//mojo/services/gles2:bindings",
+    "//mojo/services/gles2:interfaces",
+    "//mojo/environment:chromium",
+  ]
+
+  defines = [
+    "GL_GLEXT_PROTOTYPES",
+    "MOJO_GLES2_IMPL_IMPLEMENTATION",
+    "MOJO_GLES2_IMPLEMENTATION",
+  ]
+
+  configs += [
+    ":gles2_use_mojo",
+    ":mojo_use_gles2",
+  ]
+  public_configs = [ ":gles2_use_mojo" ]
+  all_dependent_configs = [ ":mojo_use_gles2" ]
+
+  sources = [
+    "command_buffer_client_impl.cc",
+    "command_buffer_client_impl.h",
+    "gles2_impl_export.h",
+    "gles2_impl.cc",
+    "gles2_context.cc",
+    "gles2_context.h",
+  ]
+}
diff --git a/mojo/gles2/DEPS b/mojo/gles2/DEPS
new file mode 100644
index 0000000..db90643
--- /dev/null
+++ b/mojo/gles2/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+gpu/command_buffer/client",
+  "+gpu/command_buffer/common",
+  "+gpu/GLES2",
+]
diff --git a/mojo/gles2/README.md b/mojo/gles2/README.md
new file mode 100644
index 0000000..94b3997
--- /dev/null
+++ b/mojo/gles2/README.md
@@ -0,0 +1,5 @@
+Mojo GLES2
+==========
+
+We export this dynamically linked library via mojo/public/gles2 in order to
+hide the gpu/command_buffer/client dependency from clients of the Mojo API.
diff --git a/mojo/gles2/command_buffer_client_impl.cc b/mojo/gles2/command_buffer_client_impl.cc
new file mode 100644
index 0000000..5c317a0
--- /dev/null
+++ b/mojo/gles2/command_buffer_client_impl.cc
@@ -0,0 +1,286 @@
+// Copyright 2014 The Chromium 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/gles2/command_buffer_client_impl.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/process/process_handle.h"
+#include "mojo/services/gles2/command_buffer_type_conversions.h"
+#include "mojo/services/gles2/mojo_buffer_backing.h"
+
+namespace mojo {
+namespace gles2 {
+
+namespace {
+
+bool CreateMapAndDupSharedBuffer(size_t size,
+                                 void** memory,
+                                 mojo::ScopedSharedBufferHandle* handle,
+                                 mojo::ScopedSharedBufferHandle* duped) {
+  MojoResult result = mojo::CreateSharedBuffer(NULL, size, handle);
+  if (result != MOJO_RESULT_OK)
+    return false;
+  DCHECK(handle->is_valid());
+
+  result = mojo::DuplicateBuffer(handle->get(), NULL, duped);
+  if (result != MOJO_RESULT_OK)
+    return false;
+  DCHECK(duped->is_valid());
+
+  result = mojo::MapBuffer(
+      handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return false;
+  DCHECK(*memory);
+
+  return true;
+}
+
+}  // namespace
+
+CommandBufferDelegate::~CommandBufferDelegate() {}
+
+void CommandBufferDelegate::ContextLost() {}
+
+class CommandBufferClientImpl::SyncClientImpl
+    : public InterfaceImpl<CommandBufferSyncClient> {
+ public:
+  SyncClientImpl() : initialized_successfully_(false) {}
+
+  bool WaitForInitialization() {
+    if (!WaitForIncomingMethodCall())
+      return false;
+    return initialized_successfully_;
+  }
+
+  CommandBufferStatePtr WaitForProgress() {
+    if (!WaitForIncomingMethodCall())
+      return CommandBufferStatePtr();
+    return command_buffer_state_.Pass();
+  }
+
+ private:
+  // CommandBufferSyncClient methods:
+  virtual void DidInitialize(bool success) override {
+    initialized_successfully_ = success;
+  }
+  virtual void DidMakeProgress(CommandBufferStatePtr state) override {
+    command_buffer_state_ = state.Pass();
+  }
+
+  bool initialized_successfully_;
+  CommandBufferStatePtr command_buffer_state_;
+};
+
+CommandBufferClientImpl::CommandBufferClientImpl(
+    CommandBufferDelegate* delegate,
+    const MojoAsyncWaiter* async_waiter,
+    ScopedMessagePipeHandle command_buffer_handle)
+    : delegate_(delegate),
+      shared_state_(NULL),
+      last_put_offset_(-1),
+      next_transfer_buffer_id_(0),
+      async_waiter_(async_waiter) {
+  command_buffer_.Bind(command_buffer_handle.Pass(), async_waiter);
+  command_buffer_.set_error_handler(this);
+  command_buffer_.set_client(this);
+}
+
+CommandBufferClientImpl::~CommandBufferClientImpl() {}
+
+bool CommandBufferClientImpl::Initialize() {
+  const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState);
+  void* memory = NULL;
+  mojo::ScopedSharedBufferHandle duped;
+  bool result = CreateMapAndDupSharedBuffer(
+      kSharedStateSize, &memory, &shared_state_handle_, &duped);
+  if (!result)
+    return false;
+
+  shared_state_ = static_cast<gpu::CommandBufferSharedState*>(memory);
+
+  shared_state()->Initialize();
+
+  CommandBufferSyncClientPtr sync_client;
+  sync_client_impl_.reset(
+      WeakBindToProxy(new SyncClientImpl(), &sync_client, async_waiter_));
+
+  command_buffer_->Initialize(sync_client.Pass(), duped.Pass());
+
+  // Wait for DidInitialize to come on the sync client pipe.
+  if (!sync_client_impl_->WaitForInitialization()) {
+    VLOG(1) << "Channel encountered error while creating command buffer";
+    return false;
+  }
+  return true;
+}
+
+gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() {
+  return last_state_;
+}
+
+int32 CommandBufferClientImpl::GetLastToken() {
+  TryUpdateState();
+  return last_state_.token;
+}
+
+void CommandBufferClientImpl::Flush(int32 put_offset) {
+  if (last_put_offset_ == put_offset)
+    return;
+
+  last_put_offset_ = put_offset;
+  command_buffer_->Flush(put_offset);
+}
+
+void CommandBufferClientImpl::WaitForTokenInRange(int32 start, int32 end) {
+  TryUpdateState();
+  while (!InRange(start, end, last_state_.token) &&
+         last_state_.error == gpu::error::kNoError) {
+    MakeProgressAndUpdateState();
+    TryUpdateState();
+  }
+}
+
+void CommandBufferClientImpl::WaitForGetOffsetInRange(int32 start, int32 end) {
+  TryUpdateState();
+  while (!InRange(start, end, last_state_.get_offset) &&
+         last_state_.error == gpu::error::kNoError) {
+    MakeProgressAndUpdateState();
+    TryUpdateState();
+  }
+}
+
+void CommandBufferClientImpl::SetGetBuffer(int32 shm_id) {
+  command_buffer_->SetGetBuffer(shm_id);
+  last_put_offset_ = -1;
+}
+
+scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer(
+    size_t size,
+    int32* id) {
+  if (size >= std::numeric_limits<uint32_t>::max())
+    return NULL;
+
+  void* memory = NULL;
+  mojo::ScopedSharedBufferHandle handle;
+  mojo::ScopedSharedBufferHandle duped;
+  if (!CreateMapAndDupSharedBuffer(size, &memory, &handle, &duped))
+    return NULL;
+
+  *id = ++next_transfer_buffer_id_;
+
+  command_buffer_->RegisterTransferBuffer(
+      *id, duped.Pass(), static_cast<uint32_t>(size));
+
+  scoped_ptr<gpu::BufferBacking> backing(
+      new MojoBufferBacking(handle.Pass(), memory, size));
+  scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(backing.Pass()));
+  return buffer;
+}
+
+void CommandBufferClientImpl::DestroyTransferBuffer(int32 id) {
+  command_buffer_->DestroyTransferBuffer(id);
+}
+
+gpu::Capabilities CommandBufferClientImpl::GetCapabilities() {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+  return gpu::Capabilities();
+}
+
+gfx::GpuMemoryBuffer* CommandBufferClientImpl::CreateGpuMemoryBuffer(
+    size_t width,
+    size_t height,
+    unsigned internalformat,
+    unsigned usage,
+    int32* id) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+void CommandBufferClientImpl::DestroyGpuMemoryBuffer(int32 id) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+}
+
+uint32 CommandBufferClientImpl::InsertSyncPoint() {
+  // TODO(jamesr): Optimize this.
+  WaitForGetOffsetInRange(last_put_offset_, last_put_offset_);
+  return 0;
+}
+
+uint32 CommandBufferClientImpl::InsertFutureSyncPoint() {
+  // TODO(jamesr): Optimize this.
+  WaitForGetOffsetInRange(last_put_offset_, last_put_offset_);
+  return 0;
+}
+
+void CommandBufferClientImpl::RetireSyncPoint(uint32 sync_point) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::SignalSyncPoint(uint32 sync_point,
+                                              const base::Closure& callback) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::SignalQuery(uint32 query,
+                                          const base::Closure& callback) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::SetSurfaceVisible(bool visible) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+}
+
+uint32 CommandBufferClientImpl::CreateStreamTexture(uint32 texture_id) {
+  // TODO(piman)
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void CommandBufferClientImpl::DidDestroy() {
+  LostContext(gpu::error::kUnknown);
+}
+
+void CommandBufferClientImpl::LostContext(int32_t lost_reason) {
+  last_state_.error = gpu::error::kLostContext;
+  last_state_.context_lost_reason =
+      static_cast<gpu::error::ContextLostReason>(lost_reason);
+  delegate_->ContextLost();
+}
+
+void CommandBufferClientImpl::OnConnectionError() {
+  LostContext(gpu::error::kUnknown);
+}
+
+void CommandBufferClientImpl::TryUpdateState() {
+  if (last_state_.error == gpu::error::kNoError)
+    shared_state()->Read(&last_state_);
+}
+
+void CommandBufferClientImpl::MakeProgressAndUpdateState() {
+  command_buffer_->MakeProgress(last_state_.get_offset);
+
+  CommandBufferStatePtr state = sync_client_impl_->WaitForProgress();
+  if (!state) {
+    VLOG(1) << "Channel encountered error while waiting for command buffer";
+    // TODO(piman): is it ok for this to re-enter?
+    DidDestroy();
+    return;
+  }
+
+  if (state->generation - last_state_.generation < 0x80000000U)
+    last_state_ = state.To<State>();
+}
+
+}  // namespace gles2
+}  // namespace mojo
diff --git a/mojo/gles2/command_buffer_client_impl.h b/mojo/gles2/command_buffer_client_impl.h
new file mode 100644
index 0000000..1c8ca53
--- /dev/null
+++ b/mojo/gles2/command_buffer_client_impl.h
@@ -0,0 +1,104 @@
+// Copyright 2014 The Chromium 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_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
+#define MOJO_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gpu_control.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer_shared.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace base {
+class RunLoop;
+}
+
+namespace mojo {
+namespace gles2 {
+class CommandBufferClientImpl;
+
+class CommandBufferDelegate {
+ public:
+  virtual ~CommandBufferDelegate();
+  virtual void ContextLost();
+};
+
+class CommandBufferClientImpl : public CommandBufferClient,
+                                public ErrorHandler,
+                                public gpu::CommandBuffer,
+                                public gpu::GpuControl {
+ public:
+  explicit CommandBufferClientImpl(
+      CommandBufferDelegate* delegate,
+      const MojoAsyncWaiter* async_waiter,
+      ScopedMessagePipeHandle command_buffer_handle);
+  virtual ~CommandBufferClientImpl();
+
+  // CommandBuffer implementation:
+  virtual bool Initialize() override;
+  virtual State GetLastState() override;
+  virtual int32 GetLastToken() override;
+  virtual void Flush(int32 put_offset) override;
+  virtual void WaitForTokenInRange(int32 start, int32 end) override;
+  virtual void WaitForGetOffsetInRange(int32 start, int32 end) override;
+  virtual void SetGetBuffer(int32 shm_id) override;
+  virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+                                                          int32* id) override;
+  virtual void DestroyTransferBuffer(int32 id) override;
+
+  // gpu::GpuControl implementation:
+  virtual gpu::Capabilities GetCapabilities() override;
+  virtual gfx::GpuMemoryBuffer* CreateGpuMemoryBuffer(size_t width,
+                                                      size_t height,
+                                                      unsigned internalformat,
+                                                      unsigned usage,
+                                                      int32* id) override;
+  virtual void DestroyGpuMemoryBuffer(int32 id) override;
+  virtual uint32 InsertSyncPoint() override;
+  virtual uint32 InsertFutureSyncPoint() override;
+  virtual void RetireSyncPoint(uint32 sync_point) override;
+  virtual void SignalSyncPoint(uint32 sync_point,
+                               const base::Closure& callback) override;
+  virtual void SignalQuery(uint32 query,
+                           const base::Closure& callback) override;
+  virtual void SetSurfaceVisible(bool visible) override;
+  virtual uint32 CreateStreamTexture(uint32 texture_id) override;
+
+ private:
+  class SyncClientImpl;
+
+  // CommandBufferClient implementation:
+  virtual void DidDestroy() override;
+  virtual void LostContext(int32_t lost_reason) override;
+
+  // ErrorHandler implementation:
+  virtual void OnConnectionError() override;
+
+  void TryUpdateState();
+  void MakeProgressAndUpdateState();
+
+  gpu::CommandBufferSharedState* shared_state() const { return shared_state_; }
+
+  CommandBufferDelegate* delegate_;
+  CommandBufferPtr command_buffer_;
+  scoped_ptr<SyncClientImpl> sync_client_impl_;
+
+  State last_state_;
+  mojo::ScopedSharedBufferHandle shared_state_handle_;
+  gpu::CommandBufferSharedState* shared_state_;
+  int32 last_put_offset_;
+  int32 next_transfer_buffer_id_;
+
+  const MojoAsyncWaiter* async_waiter_;
+};
+
+}  // gles2
+}  // mojo
+
+#endif  // MOJO_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
diff --git a/mojo/gles2/gles2_context.cc b/mojo/gles2/gles2_context.cc
new file mode 100644
index 0000000..c454aed
--- /dev/null
+++ b/mojo/gles2/gles2_context.cc
@@ -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.
+
+#include "mojo/gles2/gles2_context.h"
+
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace gles2 {
+
+namespace {
+const size_t kDefaultCommandBufferSize = 1024 * 1024;
+const size_t kDefaultStartTransferBufferSize = 1 * 1024 * 1024;
+const size_t kDefaultMinTransferBufferSize = 1 * 256 * 1024;
+const size_t kDefaultMaxTransferBufferSize = 16 * 1024 * 1024;
+}
+
+GLES2Context::GLES2Context(const MojoAsyncWaiter* async_waiter,
+                           ScopedMessagePipeHandle command_buffer_handle,
+                           MojoGLES2ContextLost lost_callback,
+                           void* closure)
+    : command_buffer_(this, async_waiter, command_buffer_handle.Pass()),
+      lost_callback_(lost_callback),
+      closure_(closure) {}
+
+GLES2Context::~GLES2Context() {}
+
+bool GLES2Context::Initialize() {
+  if (!command_buffer_.Initialize())
+    return false;
+  gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(&command_buffer_));
+  if (!gles2_helper_->Initialize(kDefaultCommandBufferSize))
+    return false;
+  gles2_helper_->SetAutomaticFlushes(false);
+  transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get()));
+  bool bind_generates_resource = true;
+  // TODO(piman): Some contexts (such as compositor) want this to be true, so
+  // this needs to be a public parameter.
+  bool lose_context_when_out_of_memory = false;
+  implementation_.reset(
+      new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
+                                          NULL,
+                                          transfer_buffer_.get(),
+                                          bind_generates_resource,
+                                          lose_context_when_out_of_memory,
+                                          &command_buffer_));
+  return implementation_->Initialize(kDefaultStartTransferBufferSize,
+                                     kDefaultMinTransferBufferSize,
+                                     kDefaultMaxTransferBufferSize,
+                                     gpu::gles2::GLES2Implementation::kNoLimit);
+}
+
+void GLES2Context::ContextLost() { lost_callback_(closure_); }
+
+}  // namespace gles2
+}  // namespace mojo
diff --git a/mojo/gles2/gles2_context.h b/mojo/gles2/gles2_context.h
new file mode 100644
index 0000000..0bbb754
--- /dev/null
+++ b/mojo/gles2/gles2_context.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_GLES2_GLES2_CONTEXT_H_
+#define MOJO_GLES2_GLES2_CONTEXT_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "mojo/gles2/command_buffer_client_impl.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+struct MojoGLES2ContextPrivate {};
+
+namespace gpu {
+class TransferBuffer;
+namespace gles2 {
+class GLES2CmdHelper;
+class GLES2Implementation;
+}
+}
+
+namespace mojo {
+namespace gles2 {
+
+class GLES2Context : public CommandBufferDelegate,
+                     public MojoGLES2ContextPrivate {
+ public:
+  explicit GLES2Context(const MojoAsyncWaiter* async_waiter,
+                        ScopedMessagePipeHandle command_buffer_handle,
+                        MojoGLES2ContextLost lost_callback,
+                        void* closure);
+  virtual ~GLES2Context();
+  bool Initialize();
+
+  gpu::gles2::GLES2Interface* interface() const {
+    return implementation_.get();
+  }
+  gpu::ContextSupport* context_support() const { return implementation_.get(); }
+
+ private:
+  virtual void ContextLost() override;
+
+  CommandBufferClientImpl command_buffer_;
+  scoped_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
+  scoped_ptr<gpu::TransferBuffer> transfer_buffer_;
+  scoped_ptr<gpu::gles2::GLES2Implementation> implementation_;
+  MojoGLES2ContextLost lost_callback_;
+  void* closure_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(GLES2Context);
+};
+
+}  // namespace gles2
+}  // namespace mojo
+
+#endif  // MOJO_GLES2_GLES2_CONTEXT_H_
diff --git a/mojo/gles2/gles2_impl.cc b/mojo/gles2/gles2_impl.cc
new file mode 100644
index 0000000..8cfcff1
--- /dev/null
+++ b/mojo/gles2/gles2_impl.cc
@@ -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.
+
+#include "mojo/public/c/gles2/gles2.h"
+
+#include "base/lazy_instance.h"
+#include "base/threading/thread_local.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "mojo/gles2/gles2_context.h"
+#include "mojo/gles2/gles2_impl_export.h"
+
+using mojo::gles2::GLES2Context;
+
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<gpu::gles2::GLES2Interface> >::Leaky
+    g_gpu_interface;
+
+}  // namespace
+
+extern "C" {
+MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle,
+                                        MojoGLES2ContextLost lost_callback,
+                                        void* closure,
+                                        const MojoAsyncWaiter* async_waiter) {
+  mojo::MessagePipeHandle mph(handle);
+  mojo::ScopedMessagePipeHandle scoped_handle(mph);
+  scoped_ptr<GLES2Context> client(new GLES2Context(
+      async_waiter, scoped_handle.Pass(), lost_callback, closure));
+  if (!client->Initialize())
+    client.reset();
+  return client.release();
+}
+
+void MojoGLES2DestroyContext(MojoGLES2Context context) {
+  delete static_cast<GLES2Context*>(context);
+}
+
+void MojoGLES2MakeCurrent(MojoGLES2Context context) {
+  gpu::gles2::GLES2Interface* interface = NULL;
+  if (context) {
+    GLES2Context* client = static_cast<GLES2Context*>(context);
+    interface = client->interface();
+    DCHECK(interface);
+  }
+  g_gpu_interface.Get().Set(interface);
+}
+
+void MojoGLES2SwapBuffers() {
+  DCHECK(g_gpu_interface.Get().Get());
+  g_gpu_interface.Get().Get()->SwapBuffers();
+}
+
+void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) {
+  return static_cast<GLES2Context*>(context)->interface();
+}
+
+void* MojoGLES2GetContextSupport(MojoGLES2Context context) {
+  return static_cast<GLES2Context*>(context)->context_support();
+}
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType gl##Function PARAMETERS {                             \
+    DCHECK(g_gpu_interface.Get().Get());                           \
+    return g_gpu_interface.Get().Get()->Function ARGUMENTS;        \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+
+}  // extern "C"
diff --git a/mojo/gles2/gles2_impl_export.h b/mojo/gles2/gles2_impl_export.h
new file mode 100644
index 0000000..3299ced
--- /dev/null
+++ b/mojo/gles2/gles2_impl_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_GLES2_GLES2_IMPL_EXPORT_H_
+#define MOJO_GLES2_GLES2_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPL_IMPLEMENTATION)
+#define MOJO_GLES2_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GLES2_IMPL_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPL_IMPLEMENTATION)
+#define MOJO_GLES2_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GLES2_IMPL_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_GLES2_IMPL_EXPORT
+#endif
+
+#endif  // MOJO_GLES2_GLES2_IMPL_EXPORT_H_
diff --git a/mojo/go/BUILD.gn b/mojo/go/BUILD.gn
new file mode 100644
index 0000000..e190379
--- /dev/null
+++ b/mojo/go/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/go/rules.gni")
+
+group("go") {
+  deps = [
+    ":system_test",
+  ]
+}
+
+go_test_binary("system_test") {
+  sources = [
+    "tests/system_test.go",
+  ]
+  static_library_sources = [
+    "c_embedder/c_embedder.cc",
+    "c_embedder/c_embedder.h",
+  ]
+  deps = [
+    "//mojo/edk/system",
+  ]
+}
diff --git a/mojo/go/c_embedder/c_embedder.cc b/mojo/go/c_embedder/c_embedder.cc
new file mode 100644
index 0000000..7d6a2e0
--- /dev/null
+++ b/mojo/go/c_embedder/c_embedder.cc
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/go/c_embedder/c_embedder.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void InitializeMojoEmbedder() {
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/mojo/go/c_embedder/c_embedder.h b/mojo/go/c_embedder/c_embedder.h
new file mode 100644
index 0000000..f2961d6
--- /dev/null
+++ b/mojo/go/c_embedder/c_embedder.h
@@ -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.
+
+#ifndef MOJO_GO_C_EMBEDDER_C_EMBEDDER_H_
+#define MOJO_GO_C_EMBEDDER_C_EMBEDDER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+__attribute__((visibility("default"))) void InitializeMojoEmbedder();
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_GO_C_EMBEDDER_C_EMBEDDER_H__
diff --git a/mojo/go/system/embedder/embedder.go b/mojo/go/system/embedder/embedder.go
new file mode 100644
index 0000000..6f605df
--- /dev/null
+++ b/mojo/go/system/embedder/embedder.go
@@ -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.
+
+package embedder;
+
+/*
+#include "mojo/go/c_embedder/c_embedder.h"
+*/
+import "C"
+
+func InitializeMojoEmbedder() {
+	C.InitializeMojoEmbedder();
+}
diff --git a/mojo/go/system/impl/core_impl.go b/mojo/go/system/impl/core_impl.go
new file mode 100644
index 0000000..9341a06
--- /dev/null
+++ b/mojo/go/system/impl/core_impl.go
@@ -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.
+
+package impl
+
+/*
+#include "mojo/public/platform/native/system_thunks.h"
+*/
+import "C"
+import "mojo/public/go/mojo/edk/system"
+
+type CoreImpl struct {
+}
+
+func (c CoreImpl) GetTimeTicksNow() int64 {
+	return (int64)(C.MojoGetTimeTicksNow())
+}
+
+var lazyInstance *CoreImpl = nil
+
+func GetCore() system.Core {
+	if lazyInstance == nil {
+		lazyInstance = new(CoreImpl)
+	}
+	return lazyInstance
+}
diff --git a/mojo/go/tests/system_test.go b/mojo/go/tests/system_test.go
new file mode 100644
index 0000000..549d01d
--- /dev/null
+++ b/mojo/go/tests/system_test.go
@@ -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.
+
+package tests
+
+import "mojo/go/system/embedder"
+import "mojo/go/system/impl"
+import "mojo/public/go/mojo/edk/system"
+import "testing"
+
+func Init() {
+	embedder.InitializeMojoEmbedder()
+}
+
+func TestGetTimeTicksNow(t *testing.T) {
+	Init()
+	var c system.Core = impl.GetCore()
+	x := c.GetTimeTicksNow()
+	if x < 10 {
+		t.Error("Invalid GetTimeTicksNow return value")
+	}
+}
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
new file mode 100644
index 0000000..30ffcf2
--- /dev/null
+++ b/mojo/mojo.gyp
@@ -0,0 +1,704 @@
+# 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.
+
+{
+  'target_defaults': {
+    'conditions': [
+      ['mojo_shell_debug_url != ""', {
+        'defines': [
+          'MOJO_SHELL_DEBUG=1',
+          'MOJO_SHELL_DEBUG_URL="<(mojo_shell_debug_url)"',
+         ],
+      }],
+    ],
+  },
+  'includes': [
+    'mojo_apps.gypi',
+    'mojo_examples.gypi',
+    'mojo_services.gypi',
+    'mojo_variables.gypi',
+  ],
+  'targets': [
+    {
+      # GN version: //mojo
+      'target_name': 'mojo',
+      'type': 'none',
+      'dependencies': [
+        'mojo_application_manager',
+        'mojo_application_manager_unittests',
+        'mojo_apps_js_unittests',
+        'mojo_base.gyp:mojo_base',
+        'mojo_clipboard',
+        'mojo_clipboard_unittests',
+        'mojo_compositor_app',
+        'mojo_content_handler_demo',
+        'mojo_echo_client',
+        'mojo_echo_service',
+        'mojo_example_apptests',
+        'mojo_example_service',
+        'mojo_geometry_lib',
+        'mojo_html_viewer',
+        'mojo_js_content_handler',
+        'mojo_js_standalone',
+        'mojo_js_unittests',
+        'mojo_native_viewport_service',
+        'mojo_network_service',
+        'mojo_pepper_container_app',
+        'mojo_png_viewer',
+        'mojo_sample_app',
+        'mojo_shell',
+        'mojo_shell_lib',
+        'mojo_shell_tests',
+        'mojo_surfaces_app',
+        'mojo_surfaces_app',
+        'mojo_surfaces_child_app',
+        'mojo_surfaces_child_gl_app',
+        'mojo_surfaces_lib',
+        'mojo_surfaces_lib_unittests',
+        'mojo_surfaces_service',
+        'mojo_test_app',
+        'mojo_test_request_tracker_app',
+        'mojo_view_manager_lib',
+        'mojo_view_manager_lib_unittests',
+        'mojo_wget',
+      ],
+      'conditions': [
+        ['use_aura==1', {
+          'dependencies': [
+            'mojo_aura_demo',
+            'mojo_aura_demo_init',
+            'mojo_browser',
+            'mojo_core_window_manager',
+            'mojo_core_window_manager_unittests',
+            'mojo_demo_launcher',
+            'mojo_embedded_app',
+            'mojo_keyboard',
+            'mojo_media_viewer',
+            'mojo_nesting_app',
+            'mojo_window_manager',
+            'mojo_wm_flow_app',
+            'mojo_wm_flow_embedded',
+            'mojo_wm_flow_init',
+            'mojo_wm_flow_wm',
+            'mojo_view_manager',
+            'mojo_view_manager_unittests',
+          ],
+        }],
+        ['OS == "linux"', {
+          'dependencies': [
+            'mojo_external_application_tests',
+          ],
+        }],
+        ['component != "shared_library" and OS == "linux"', {
+          'dependencies': [
+            'mojo_python_bindings',
+            'mojo_python_embedder',
+            'mojo_python_system',
+            'mojo_python',
+          ],
+        }],
+      ]
+    },
+    {
+      # GN version: //mojo/shell:external_service_bindings
+      'target_name': 'mojo_external_service_bindings',
+      'type': 'static_library',
+      'sources': [
+        'shell/external_service.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/spy
+      'target_name': 'mojo_spy',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../net/net.gyp:http_server',
+        '../url/url.gyp:url_lib',
+        'mojo_application_manager',
+      ],
+      'variables': {
+        'mojom_base_output_dir': 'mojo',
+      },
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'sources': [
+        'spy/public/spy.mojom',
+        'spy/common.h',
+        'spy/spy.cc',
+        'spy/spy.h',
+        'spy/spy_server_impl.h',
+        'spy/spy_server_impl.cc',
+        'spy/websocket_server.cc',
+        'spy/websocket_server.h',
+      ],
+    },
+    {
+      # GN version: //mojo/shell:lib
+      'target_name': 'mojo_shell_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:base_static',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        'mojo_application_manager',
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_gles2_impl',
+        'mojo_base.gyp:mojo_system_impl',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_external_service_bindings',
+        'mojo_network_bindings',
+        'mojo_spy',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'sources': [
+        'shell/app_child_process.cc',
+        'shell/app_child_process.h',
+        'shell/app_child_process.mojom',
+        'shell/app_child_process_host.cc',
+        'shell/app_child_process_host.h',
+        'shell/child_process.cc',
+        'shell/child_process.h',
+        'shell/child_process_host.cc',
+        'shell/child_process_host.h',
+        'shell/context.cc',
+        'shell/context.h',
+        'shell/dynamic_application_loader.cc',
+        'shell/dynamic_application_loader.h',
+        'shell/dynamic_service_runner.h',
+        'shell/external_application_listener_posix.cc',
+        'shell/external_application_listener_win.cc',
+        'shell/external_application_listener.h',
+        'shell/external_application_registrar.mojom',
+        'shell/incoming_connection_listener_posix.cc',
+        'shell/incoming_connection_listener_posix.h',
+        'shell/init.cc',
+        'shell/init.h',
+        'shell/in_process_dynamic_service_runner.cc',
+        'shell/in_process_dynamic_service_runner.h',
+        'shell/mojo_url_resolver.cc',
+        'shell/mojo_url_resolver.h',
+        'shell/out_of_process_dynamic_service_runner.cc',
+        'shell/out_of_process_dynamic_service_runner.h',
+        'shell/switches.cc',
+        'shell/switches.h',
+        'shell/task_runners.cc',
+        'shell/task_runners.h',
+        'shell/test_child_process.cc',
+        'shell/test_child_process.h',
+        'shell/ui_application_loader_android.cc',
+        'shell/ui_application_loader_android.h',
+      ],
+      'conditions': [
+        ['component=="shared_library"', {
+          'dependencies': [
+            '../ui/gl/gl.gyp:gl',
+          ],
+        }],
+        ['OS=="linux"', {
+          'sources': [
+            'shell/external_application_registrar_connection.cc',
+            'shell/external_application_registrar_connection.h',
+          ],
+        }],
+        ['OS=="android"', {
+          'dependencies': [
+            'mojo_network_service_lib',
+            'mojo_native_viewport_service_lib',
+          ],
+          'sources': [
+            'shell/network_application_loader.cc',
+            'shell/network_application_loader.h',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/shell:test_support
+      'target_name': 'mojo_shell_test_support',
+      'type': 'static_library',
+      'dependencies': [
+        'mojo_base.gyp:mojo_system_impl',
+        'mojo_shell_lib',
+      ],
+      'sources': [
+        'shell/shell_test_helper.cc',
+        'shell/shell_test_helper.h',
+      ],
+    },
+    {
+      # GN version: //mojo/shell
+      'target_name': 'mojo_shell',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_shell_lib',
+      ],
+      'sources': [
+        'shell/desktop/mojo_main.cc',
+      ],
+      'conditions': [
+        ['component=="shared_library"', {
+          'dependencies': [
+            '../ui/gfx/gfx.gyp:gfx',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/shell:mojo_shell_tests
+      'target_name': 'mojo_shell_tests',
+      'type': '<(gtest_target_type)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:base_i18n',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        '../net/net.gyp:net_test_support',
+        '../url/url.gyp:url_lib',
+        'mojo_application_manager',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_base.gyp:mojo_system_impl',
+        'mojo_shell_lib',
+        'mojo_test_app',
+        'mojo_test_request_tracker_app',
+        'mojo_test_service_bindings',
+      ],
+      'sources': [
+        'shell/child_process_host_unittest.cc',
+        'shell/dynamic_application_loader_unittest.cc',
+        'shell/in_process_dynamic_service_runner_unittest.cc',
+        'shell/shell_test_base.cc',
+        'shell/shell_test_base.h',
+        'shell/shell_test_base_unittest.cc',
+        'shell/shell_test_main.cc',
+      ],
+      'conditions': [
+        ['OS == "android"', {
+          'dependencies': [
+            '../testing/android/native_test.gyp:native_test_native_code',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/application_manager
+      'target_name': 'mojo_application_manager',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_APPLICATION_MANAGER_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        '../url/url.gyp:url_lib',
+        'mojo_content_handler_bindings',
+        'mojo_network_bindings',
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_environment_chromium',
+        '<(mojo_system_for_component)',
+      ],
+      'sources': [
+        'application_manager/application_loader.cc',
+        'application_manager/application_loader.h',
+        'application_manager/application_manager.cc',
+        'application_manager/application_manager.h',
+        'application_manager/application_manager_export.h',
+        'application_manager/background_shell_application_loader.cc',
+        'application_manager/background_shell_application_loader.h',
+      ],
+      'export_dependent_settings': [
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_network_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/application_manager:mojo_application_manager_unittests
+      'target_name': 'mojo_application_manager_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        '../url/url.gyp:url_lib',
+        'mojo_application_manager',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_base.gyp:mojo_run_all_unittests',
+        'mojo_base.gyp:mojo_application_chromium',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'sources': [
+        'application_manager/application_manager_unittest.cc',
+        'application_manager/background_shell_application_loader_unittest.cc',
+        'application_manager/test.mojom',
+      ],
+    },
+    {
+      # GN version: //mojo/cc
+      'target_name': 'mojo_cc_support',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../gpu/gpu.gyp:gles2_implementation',
+        'mojo_geometry_lib',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'export_dependent_settings': [
+        'mojo_surfaces_bindings',
+      ],
+      'sources': [
+        'cc/context_provider_mojo.cc',
+        'cc/context_provider_mojo.h',
+        'cc/output_surface_mojo.cc',
+        'cc/output_surface_mojo.h',
+      ],
+    },
+    {
+      # GN version: //mojo/bindings/js/tests:mojo_js_unittests
+      'target_name': 'mojo_js_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../gin/gin.gyp:gin_test',
+        'mojo_base.gyp:mojo_common_test_support',
+        'mojo_base.gyp:mojo_environment_standalone',
+        'mojo_base.gyp:mojo_js_bindings_lib',
+        'mojo_base.gyp:mojo_public_test_interfaces',
+        'mojo_base.gyp:mojo_run_all_unittests',
+        'mojo_base.gyp:mojo_utility',
+      ],
+      'sources': [
+        'bindings/js/tests/run_js_tests.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="android"', {
+      'targets': [
+        {
+          'target_name': 'mojo_native_viewport_java',
+          'type': 'none',
+          'dependencies': [
+            '../base/base.gyp:base_java',
+          ],
+          'variables': {
+            'java_in_dir': '<(DEPTH)/mojo/services/native_viewport/android',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+        {
+          'target_name': 'libmojo_shell',
+          'type': 'shared_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/gl/gl.gyp:gl',
+            'mojo_base.gyp:mojo_application_bindings',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_base.gyp:mojo_environment_chromium',
+            'mojo_base.gyp:mojo_jni_headers',
+            'mojo_shell_lib',
+          ],
+          'sources': [
+            'shell/android/library_loader.cc',
+            'shell/android/mojo_main.cc',
+            'shell/android/mojo_main.h',
+          ],
+        },
+        {
+          'target_name': 'mojo_shell_apk',
+          'type': 'none',
+          'dependencies': [
+            '../base/base.gyp:base_java',
+            '../net/net.gyp:net_java',
+            'mojo_native_viewport_java',
+            'libmojo_shell',
+          ],
+          'variables': {
+            'apk_name': 'MojoShell',
+            'java_in_dir': '<(DEPTH)/mojo/shell/android/apk',
+            'resource_dir': '<(DEPTH)/mojo/shell/android/apk/res',
+            'native_lib_target': 'libmojo_shell',
+          },
+          'includes': [ '../build/java_apk.gypi' ],
+        }
+      ],
+    }],
+    ['OS=="linux"', {
+      'targets': [
+        {
+          # GN version: //mojo/shell:mojo_external_application_tests
+          'target_name': 'mojo_external_application_tests',
+          'type': '<(gtest_target_type)',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:test_support_base',
+            '../testing/gtest.gyp:gtest',
+            '../net/net.gyp:net_test_support',
+            '../url/url.gyp:url_lib',
+            'mojo_application_manager',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_base.gyp:mojo_environment_chromium',
+            'mojo_base.gyp:mojo_system_impl',
+            'mojo_shell_lib',
+          ],
+          'sources': [
+            'shell/incoming_connection_listener_unittest.cc',
+            'shell/external_application_listener_unittest.cc',
+            'shell/external_application_test_main.cc',
+          ],
+        },
+      ],
+    }],
+    ['use_aura==1', {
+      'targets': [
+        {
+          # GN version: //mojo/aura
+          'target_name': 'mojo_aura_support',
+          'type': 'static_library',
+          'dependencies': [
+            '../cc/cc.gyp:cc',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/compositor/compositor.gyp:compositor',
+            '../ui/events/events.gyp:events',
+            '../ui/events/events.gyp:events_base',
+            'mojo_cc_support',
+            'mojo_native_viewport_bindings',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'sources': [
+            'aura/aura_init.cc',
+            'aura/aura_init.h',
+            'aura/context_factory_mojo.cc',
+            'aura/context_factory_mojo.h',
+            'aura/screen_mojo.cc',
+            'aura/screen_mojo.h',
+            'aura/window_tree_host_mojo.cc',
+            'aura/window_tree_host_mojo.h',
+            'aura/window_tree_host_mojo_delegate.h',
+          ],
+        },
+        {
+          # GN version: //mojo/views
+          'target_name': 'mojo_views_support',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:base_i18n',
+            '../skia/skia.gyp:skia',
+            '../third_party/icu/icu.gyp:icui18n',
+            '../third_party/icu/icu.gyp:icuuc',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/views/views.gyp:views',
+            '../ui/wm/wm.gyp:wm',
+            'mojo_aura_support',
+            'mojo_views_support_internal',
+            'mojo_view_manager_bindings',
+          ],
+          'sources': [
+            'views/input_method_mojo_linux.cc',
+            'views/input_method_mojo_linux.h',
+            'views/native_widget_view_manager.cc',
+            'views/native_widget_view_manager.h',
+            'views/views_init.cc',
+            'views/views_init.h',
+          ],
+        },
+        {
+          # GN version: //mojo/views:views_internal
+          'target_name': 'mojo_views_support_internal',
+          'type': '<(component)',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:base_i18n',
+            '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+            '../skia/skia.gyp:skia',
+            '../third_party/icu/icu.gyp:icui18n',
+            '../third_party/icu/icu.gyp:icuuc',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/gfx/gfx.gyp:gfx',
+          ],
+          'sources': [
+            'views/mojo_views_export.h',
+            'views/views_init_internal.cc',
+            'views/views_init_internal.h',
+          ],
+          'defines': [
+            'MOJO_VIEWS_IMPLEMENTATION',
+          ],
+        },
+      ],
+    }],
+    ['component!="shared_library" and OS=="linux"', {
+      'targets': [
+        {
+          # GN version: //mojo/public/python:system
+          'target_name': 'mojo_python_system',
+          'variables': {
+            'python_base_module': 'mojo',
+            'python_cython_module': 'system',
+          },
+          'sources': [
+            'public/python/mojo/c_core.pxd',
+            'public/python/mojo/c_environment.pxd',
+            'public/python/mojo/system.pyx',
+            'public/python/src/python_system_helper.cc',
+            'public/python/src/python_system_helper.h',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_environment_standalone',
+            'mojo_base.gyp:mojo_system',
+            'mojo_base.gyp:mojo_utility',
+          ],
+          'includes': [ '../third_party/cython/cython_compiler.gypi' ],
+        },
+        {
+          # GN version: //mojo/python:embedder
+          'target_name': 'mojo_python_embedder',
+          'type': 'loadable_module',
+          'variables': {
+            'python_base_module': 'mojo',
+            'python_cython_module': 'embedder',
+          },
+          'sources': [
+            'python/system/mojo/embedder.pyx',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_system_impl',
+          ],
+          'includes': [ '../third_party/cython/cython_compiler.gypi' ],
+        },
+        {
+          # GN version: //mojo/public/python:bindings
+          'target_name': 'mojo_python_bindings',
+          'type': 'none',
+          'variables': {
+            'python_base_module': 'mojo/bindings',
+          },
+          'sources': [
+            'public/python/mojo/bindings/__init__.py',
+            'public/python/mojo/bindings/descriptor.py',
+            'public/python/mojo/bindings/messaging.py',
+            'public/python/mojo/bindings/promise.py',
+            'public/python/mojo/bindings/reflection.py',
+            'public/python/mojo/bindings/serialization.py',
+          ],
+          'dependencies': [
+            'mojo_python_system',
+          ],
+          'includes': [ '../third_party/cython/python_module.gypi' ],
+        },
+        {
+          # GN version: //mojo/python
+          'target_name': 'mojo_python',
+          'type': 'none',
+          'variables': {
+            'python_base_module': 'mojo',
+          },
+          'sources': [
+            'public/python/mojo/__init__.py',
+          ],
+          'dependencies': [
+            'mojo_python_bindings',
+            'mojo_python_embedder',
+            'mojo_python_system',
+          ],
+          # The python module need to be copied to their destinations
+          'actions': [
+            {
+              'action_name': 'Copy system module.',
+              'inputs': [
+                '<(DEPTH)/build/cp.py',
+                '<(PRODUCT_DIR)/libmojo_python_system.so',
+              ],
+              'outputs': [
+                '<(PRODUCT_DIR)/python/mojo/system.so',
+              ],
+              'action': [
+                'python',
+                '<@(_inputs)',
+                '<@(_outputs)',
+              ]
+            },
+            {
+              'action_name': 'Copy embedder module.',
+              'inputs': [
+                '<(DEPTH)/build/cp.py',
+                '<(PRODUCT_DIR)/libmojo_python_embedder.so',
+              ],
+              'outputs': [
+                '<(PRODUCT_DIR)/python/mojo/embedder.so',
+              ],
+              'action': [
+                'python',
+                '<@(_inputs)',
+                '<@(_outputs)',
+              ]
+            },
+          ],
+          'includes': [ '../third_party/cython/python_module.gypi' ],
+        },
+      ],
+    }],
+    ['component!="shared_library" and OS=="linux" and test_isolation_mode!="noop"', {
+      'targets': [
+        {
+          'target_name': 'mojo_python_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_python',
+            'mojo_base.gyp:mojo_public_test_interfaces',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_python_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+    ['test_isolation_mode != "noop"', {
+      'targets': [
+        {
+          'target_name': 'mojo_js_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_js_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_js_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_apps.gypi b/mojo/mojo_apps.gypi
new file mode 100644
index 0000000..de1919a
--- /dev/null
+++ b/mojo/mojo_apps.gypi
@@ -0,0 +1,148 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo/apps/js
+      #             //mojo/apps/js/bindings
+      #             //mojo/apps/js/bindings/gl
+      'target_name': 'mojo_js_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:base_i18n',
+        '../gin/gin.gyp:gin',
+        '../ui/gl/gl.gyp:gl',
+        '../v8/tools/gyp/v8.gyp:v8',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_base.gyp:mojo_js_bindings_lib',
+        'mojo_native_viewport_bindings',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'export_dependent_settings': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_native_viewport_bindings',
+      ],
+      'sources': [
+        'apps/js/mojo_runner_delegate.cc',
+        'apps/js/mojo_runner_delegate.h',
+        'apps/js/bindings/threading.cc',
+        'apps/js/bindings/threading.h',
+        'apps/js/bindings/gl/context.cc',
+        'apps/js/bindings/gl/context.h',
+        'apps/js/bindings/gl/module.cc',
+        'apps/js/bindings/gl/module.h',
+        'apps/js/bindings/monotonic_clock.cc',
+        'apps/js/bindings/monotonic_clock.h',
+      ],
+    },
+    {
+      # GN version: //mojo/apps/js/test:js_to_cpp_bindings
+      'target_name': 'mojo_apps_js_bindings',
+      'type': 'static_library',
+      'sources': [
+        'apps/js/test/js_to_cpp.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/apps/js/test/mojo_apps_js_unittests
+      'target_name': 'mojo_apps_js_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../gin/gin.gyp:gin_test',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_common_test_support',
+        'mojo_base.gyp:mojo_public_test_interfaces',
+        'mojo_base.gyp:mojo_run_all_unittests',
+        'mojo_apps_js_bindings',
+        'mojo_js_lib',
+      ],
+      'sources': [
+        'apps/js/test/handle_unittest.cc',
+        'apps/js/test/js_to_cpp_unittest.cc',
+        'apps/js/test/run_apps_js_tests.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/apps/js/test:mojo_js_apps_lib
+      'target_name': 'mojo_js_apps_lib',
+      'type': 'static_library',
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_apps_js_bindings',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_js_lib',
+      ],
+      'sources': [
+        'apps/js/application_delegate_impl.cc',
+        'apps/js/js_app.cc',
+        'apps/js/mojo_module.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/apps/js:mojo_js_content_handler
+      'target_name': 'mojo_js_content_handler',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_content_handler_bindings',
+        'mojo_js_apps_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'apps/js/content_handler_impl.cc',
+        'apps/js/content_handler_main.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/apps/js:mojo_js_standalone
+      'target_name': 'mojo_js_standalone',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_js_apps_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'apps/js/standalone_main.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['test_isolation_mode != "noop"', {
+      'targets': [
+        {
+          'target_name': 'mojo_apps_js_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_apps_js_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_apps_js_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_apps_js_unittests.isolate b/mojo/mojo_apps_js_unittests.isolate
new file mode 100644
index 0000000..6921a46
--- /dev/null
+++ b/mojo/mojo_apps_js_unittests.isolate
@@ -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.
+{
+  'includes': [
+    '../third_party/icu/icu.isolate',
+  ],
+  'conditions': [
+    ['OS=="win" or OS=="mac" or OS=="linux"', {
+      'variables': {
+        'command': [
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/mojo_apps_js_unittests<(EXECUTABLE_SUFFIX)',
+          '--brave-new-test-launcher',
+          '--test-launcher-bot-mode',
+        ],
+        'files': [
+          '../gin/test/expect.js',
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/',
+          '<(PRODUCT_DIR)/mojo_apps_js_unittests<(EXECUTABLE_SUFFIX)',
+          'apps/js/',
+          'bindings/js/',
+          'public/js/bindings/',
+        ],
+      },
+    }],
+    ['OS=="win"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/mojo_test_support.dll',
+        ],
+      },
+    }],
+    ['OS=="linux"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/lib/libmojo_test_support.so',
+        ],
+      },
+    }],
+    ['OS=="mac"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/libmojo_test_support.dylib',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp
new file mode 100644
index 0000000..0cf8766
--- /dev/null
+++ b/mojo/mojo_base.gyp
@@ -0,0 +1,642 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Essential components (and their tests) that are needed to build
+# Chrome should be here.  Other components that are useful only in
+# Mojo land like mojo_shell should be in mojo.gyp.
+{
+  'includes': [
+    'mojo_public.gypi',
+    'mojo_public_tests.gypi',
+    'mojo_variables.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'mojo_base',
+      'type': 'none',
+      'dependencies': [
+        # NOTE: If adding a new dependency here, please consider whether it
+        # should also be added to the list of Mojo-related dependencies of
+        # build/all.gyp:All on iOS, as All cannot depend on the mojo_base
+        # target on iOS due to the presence of the js targets, which cause v8
+        # to be built.
+        'mojo_common_lib',
+        'mojo_common_unittests',
+        'mojo_cpp_bindings',
+        'mojo_js_bindings',
+        'mojo_message_generator',
+        'mojo_message_pipe_perftests',
+        'mojo_public_application_unittests',
+        'mojo_public_test_utils',
+        'mojo_public_bindings_unittests',
+        'mojo_public_environment_unittests',
+        'mojo_public_system_perftests',
+        'mojo_public_system_unittests',
+        'mojo_public_utility_unittests',
+        'mojo_system',
+        'mojo_system_impl',
+        'mojo_system_unittests',
+        'mojo_utility',
+      ],
+      'conditions': [
+        ['OS == "android"', {
+          'dependencies': [
+            'mojo_bindings_java',
+            'mojo_public_java',
+            'mojo_system_java',
+            'libmojo_system_java',
+            'mojo_test_apk',
+          ],
+        }],
+      ]
+    },
+    {
+      'target_name': 'mojo_none',
+      'type': 'none',
+    },
+    {
+      # GN version: //mojo/common/test:run_all_unittests
+      'target_name': 'mojo_run_all_unittests',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_system_impl',
+        'mojo_test_support',
+        'mojo_test_support_impl',
+      ],
+      'sources': [
+        'common/test/run_all_unittests.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/common/test:run_all_perftests
+      'target_name': 'mojo_run_all_perftests',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:test_support_base',
+        'mojo_system_impl',
+        'mojo_test_support',
+        'mojo_test_support_impl',
+      ],
+      'sources': [
+        'common/test/run_all_perftests.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/system
+      'target_name': 'mojo_system_impl',
+      'type': '<(component)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+      ],
+      'defines': [
+        'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+        'MOJO_SYSTEM_IMPLEMENTATION',
+        'MOJO_USE_SYSTEM_IMPL',
+      ],
+      'sources': [
+        'edk/embedder/channel_init.cc',
+        'edk/embedder/channel_init.h',
+        'edk/embedder/embedder.cc',
+        'edk/embedder/embedder.h',
+        'edk/embedder/platform_channel_pair.cc',
+        'edk/embedder/platform_channel_pair.h',
+        'edk/embedder/platform_channel_pair_posix.cc',
+        'edk/embedder/platform_channel_pair_win.cc',
+        'edk/embedder/platform_channel_utils_posix.cc',
+        'edk/embedder/platform_channel_utils_posix.h',
+        'edk/embedder/platform_handle.cc',
+        'edk/embedder/platform_handle.h',
+        'edk/embedder/platform_handle_utils.h',
+        'edk/embedder/platform_handle_utils_posix.cc',
+        'edk/embedder/platform_handle_utils_win.cc',
+        'edk/embedder/platform_handle_vector.h',
+        'edk/embedder/platform_shared_buffer.h',
+        'edk/embedder/platform_support.h',
+        'edk/embedder/scoped_platform_handle.h',
+        'edk/embedder/simple_platform_shared_buffer.cc',
+        'edk/embedder/simple_platform_shared_buffer.h',
+        'edk/embedder/simple_platform_shared_buffer_posix.cc',
+        'edk/embedder/simple_platform_shared_buffer_win.cc',
+        'edk/embedder/simple_platform_support.cc',
+        'edk/embedder/simple_platform_support.h',
+        'edk/system/channel.cc',
+        'edk/system/channel.h',
+        'edk/system/channel_endpoint.cc',
+        'edk/system/channel_endpoint.h',
+        'edk/system/constants.h',
+        'edk/system/core.cc',
+        'edk/system/core.h',
+        'edk/system/data_pipe.cc',
+        'edk/system/data_pipe.h',
+        'edk/system/data_pipe_consumer_dispatcher.cc',
+        'edk/system/data_pipe_consumer_dispatcher.h',
+        'edk/system/data_pipe_producer_dispatcher.cc',
+        'edk/system/data_pipe_producer_dispatcher.h',
+        'edk/system/dispatcher.cc',
+        'edk/system/dispatcher.h',
+        'edk/system/entrypoints.cc',
+        'edk/system/handle_signals_state.h',
+        'edk/system/handle_table.cc',
+        'edk/system/handle_table.h',
+        'edk/system/local_data_pipe.cc',
+        'edk/system/local_data_pipe.h',
+        'edk/system/local_message_pipe_endpoint.cc',
+        'edk/system/local_message_pipe_endpoint.h',
+        'edk/system/mapping_table.cc',
+        'edk/system/mapping_table.h',
+        'edk/system/memory.cc',
+        'edk/system/memory.h',
+        'edk/system/message_in_transit.cc',
+        'edk/system/message_in_transit.h',
+        'edk/system/message_in_transit_queue.cc',
+        'edk/system/message_in_transit_queue.h',
+        'edk/system/message_pipe.cc',
+        'edk/system/message_pipe.h',
+        'edk/system/message_pipe_dispatcher.cc',
+        'edk/system/message_pipe_dispatcher.h',
+        'edk/system/message_pipe_endpoint.cc',
+        'edk/system/message_pipe_endpoint.h',
+        'edk/system/options_validation.h',
+        'edk/system/platform_handle_dispatcher.cc',
+        'edk/system/platform_handle_dispatcher.h',
+        'edk/system/proxy_message_pipe_endpoint.cc',
+        'edk/system/proxy_message_pipe_endpoint.h',
+        'edk/system/raw_channel.cc',
+        'edk/system/raw_channel.h',
+        'edk/system/raw_channel_posix.cc',
+        'edk/system/raw_channel_win.cc',
+        'edk/system/shared_buffer_dispatcher.cc',
+        'edk/system/shared_buffer_dispatcher.h',
+        'edk/system/simple_dispatcher.cc',
+        'edk/system/simple_dispatcher.h',
+        'edk/system/transport_data.cc',
+        'edk/system/transport_data.h',
+        'edk/system/waiter.cc',
+        'edk/system/waiter.h',
+        'edk/system/waiter_list.cc',
+        'edk/system/waiter_list.h',
+        # Test-only code:
+        # TODO(vtl): It's a little unfortunate that these end up in the same
+        # component as non-test-only code. In the static build, this code should
+        # hopefully be dead-stripped.
+        'edk/embedder/test_embedder.cc',
+        'edk/embedder/test_embedder.h',
+      ],
+      'all_dependent_settings': {
+        # Ensures that dependent projects import the core functions on Windows.
+        'defines': ['MOJO_USE_SYSTEM_IMPL'],
+      }
+    },
+    {
+      # GN version: //mojo/edk/system:mojo_system_unittests
+      'target_name': 'mojo_system_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_common_test_support',
+        'mojo_system_impl',
+      ],
+      'sources': [
+        'edk/embedder/embedder_unittest.cc',
+        'edk/embedder/platform_channel_pair_posix_unittest.cc',
+        'edk/embedder/simple_platform_shared_buffer_unittest.cc',
+        'edk/system/channel_unittest.cc',
+        'edk/system/core_unittest.cc',
+        'edk/system/core_test_base.cc',
+        'edk/system/core_test_base.h',
+        'edk/system/data_pipe_unittest.cc',
+        'edk/system/dispatcher_unittest.cc',
+        'edk/system/local_data_pipe_unittest.cc',
+        'edk/system/memory_unittest.cc',
+        'edk/system/message_pipe_dispatcher_unittest.cc',
+        'edk/system/message_pipe_test_utils.h',
+        'edk/system/message_pipe_test_utils.cc',
+        'edk/system/message_pipe_unittest.cc',
+        'edk/system/multiprocess_message_pipe_unittest.cc',
+        'edk/system/options_validation_unittest.cc',
+        'edk/system/platform_handle_dispatcher_unittest.cc',
+        'edk/system/raw_channel_unittest.cc',
+        'edk/system/remote_message_pipe_unittest.cc',
+        'edk/system/run_all_unittests.cc',
+        'edk/system/shared_buffer_dispatcher_unittest.cc',
+        'edk/system/simple_dispatcher_unittest.cc',
+        'edk/system/test_utils.cc',
+        'edk/system/test_utils.h',
+        'edk/system/waiter_list_unittest.cc',
+        'edk/system/waiter_test_utils.cc',
+        'edk/system/waiter_test_utils.h',
+        'edk/system/waiter_unittest.cc',
+      ],
+      'conditions': [
+        ['OS=="ios"', {
+          'sources!': [
+            'edk/embedder/embedder_unittest.cc',
+            'edk/system/multiprocess_message_pipe_unittest.cc',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/edk/system:mojo_message_pipe_perftests
+      'target_name': 'mojo_message_pipe_perftests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../base/base.gyp:test_support_perf',
+        '../testing/gtest.gyp:gtest',
+        'mojo_common_test_support',
+        'mojo_environment_chromium',
+        'mojo_system_impl',
+      ],
+      'sources': [
+        'edk/system/message_pipe_perftest.cc',
+        'edk/system/message_pipe_test_utils.h',
+        'edk/system/message_pipe_test_utils.cc',
+        'edk/system/test_utils.cc',
+        'edk/system/test_utils.h',
+      ],
+    },
+    {
+      # GN version: //mojo/common/test:test_support_impl
+      'target_name': 'mojo_test_support_impl',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+      ],
+      'sources': [
+        'common/test/test_support_impl.cc',
+        'common/test/test_support_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/common
+      'target_name': 'mojo_common_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_COMMON_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../url/url.gyp:url_lib',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        '<(mojo_system_for_component)',
+      ],
+      'export_dependent_settings': [
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+      ],
+      'sources': [
+        'common/common_type_converters.cc',
+        'common/common_type_converters.h',
+        'common/data_pipe_utils.cc',
+        'common/data_pipe_utils.h',
+        'common/handle_watcher.cc',
+        'common/handle_watcher.h',
+        'common/message_pump_mojo.cc',
+        'common/message_pump_mojo.h',
+        'common/message_pump_mojo_handler.h',
+        'common/time_helper.cc',
+        'common/time_helper.h',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:test_support
+      'target_name': 'mojo_common_test_support',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_system_impl',
+      ],
+      'sources': [
+        'edk/test/multiprocess_test_helper.cc',
+        'edk/test/multiprocess_test_helper.h',
+        'edk/test/test_utils.h',
+        'edk/test/test_utils_posix.cc',
+        'edk/test/test_utils_win.cc',
+      ],
+      'conditions': [
+        ['OS=="ios"', {
+          'sources!': [
+            'edk/test/multiprocess_test_helper.cc',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/common:mojo_common_unittests
+      'target_name': 'mojo_common_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:base_message_loop_tests',
+        '../testing/gtest.gyp:gtest',
+        '../url/url.gyp:url_lib',
+        'mojo_cpp_bindings',
+        'mojo_environment_chromium',
+        'mojo_common_lib',
+        'mojo_common_test_support',
+        'mojo_public_test_utils',
+        'mojo_run_all_unittests',
+      ],
+      'sources': [
+        'common/common_type_converters_unittest.cc',
+        'common/handle_watcher_unittest.cc',
+        'common/message_pump_mojo_unittest.cc',
+        'edk/test/multiprocess_test_helper_unittest.cc',
+      ],
+      'conditions': [
+        ['OS=="ios"', {
+          'sources!': [
+            'edk/test/multiprocess_test_helper_unittest.cc',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/environment:chromium
+      'target_name': 'mojo_environment_chromium',
+      'type': 'static_library',
+      'dependencies': [
+        'mojo_environment_chromium_impl',
+      ],
+      'sources': [
+        'environment/environment.cc',
+        # TODO(vtl): This is kind of ugly. (See TODO in logging.h.)
+        "public/cpp/environment/logging.h",
+        "public/cpp/environment/lib/logging.cc",
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'export_dependent_settings': [
+        'mojo_environment_chromium_impl',
+      ],
+    },
+    {
+      # GN version: //mojo/environment:chromium_impl
+      'target_name': 'mojo_environment_chromium_impl',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        'mojo_common_lib',
+        '<(mojo_system_for_component)',
+      ],
+      'sources': [
+        'environment/default_async_waiter_impl.cc',
+        'environment/default_async_waiter_impl.h',
+        'environment/default_logger_impl.cc',
+        'environment/default_logger_impl.h',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+    },
+    {
+      # GN version: //mojo/services/gles2:interfaces (for files generated from
+      # the mojom file)
+      # GN version: //mojo/services/gles2:bindings
+      'target_name': 'mojo_gles2_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/gles2/command_buffer.mojom',
+        'services/gles2/command_buffer_type_conversions.cc',
+        'services/gles2/command_buffer_type_conversions.h',
+        'services/gles2/mojo_buffer_backing.cc',
+        'services/gles2/mojo_buffer_backing.h',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_cpp_bindings',
+        '../gpu/gpu.gyp:command_buffer_common',
+      ],
+    },
+    {
+      # GN version: //mojo/gles2
+      'target_name': 'mojo_gles2_impl',
+      'type': '<(component)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        '../gpu/gpu.gyp:command_buffer_client',
+        '../gpu/gpu.gyp:command_buffer_common',
+        '../gpu/gpu.gyp:gles2_cmd_helper',
+        '../gpu/gpu.gyp:gles2_implementation',
+        'mojo_environment_chromium',
+        'mojo_gles2_bindings',
+        '<(mojo_system_for_component)',
+      ],
+      'defines': [
+        'GLES2_USE_MOJO',
+        'GL_GLEXT_PROTOTYPES',
+        'MOJO_GLES2_IMPLEMENTATION',
+        'MOJO_GLES2_IMPL_IMPLEMENTATION',
+        'MOJO_USE_GLES2_IMPL'
+      ],
+      'direct_dependent_settings': {
+        'defines': [
+          'GLES2_USE_MOJO',
+        ],
+      },
+      'export_dependent_settings': [
+        'mojo_gles2_bindings',
+      ],
+      'sources': [
+        'gles2/command_buffer_client_impl.cc',
+        'gles2/command_buffer_client_impl.h',
+        'gles2/gles2_impl_export.h',
+        'gles2/gles2_impl.cc',
+        'gles2/gles2_context.cc',
+        'gles2/gles2_context.h',
+      ],
+      'all_dependent_settings': {
+        # Ensures that dependent projects import the core functions on Windows.
+        'defines': ['MOJO_USE_GLES2_IMPL'],
+      }
+    },
+    {
+     # GN version: //mojo/application
+     'target_name': 'mojo_application_chromium',
+     'type': 'static_library',
+     'sources': [
+       'application/application_runner_chromium.cc',
+       'application/application_runner_chromium.h',
+      ],
+      'dependencies': [
+        'mojo_application_base',
+        'mojo_common_lib',
+        'mojo_environment_chromium',
+       ],
+      'export_dependent_settings': [
+        'mojo_application_base',
+       ],
+    },
+    {
+      # GN version: //mojo/bindings/js
+      'target_name': 'mojo_js_bindings_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin',
+        '../v8/tools/gyp/v8.gyp:v8',
+        'mojo_common_lib',
+      ],
+      'export_dependent_settings': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin',
+        'mojo_common_lib',
+      ],
+      'sources': [
+        # Sources list duplicated in GN build.
+        'bindings/js/core.cc',
+        'bindings/js/core.h',
+        'bindings/js/drain_data.cc',
+        'bindings/js/drain_data.h',
+        'bindings/js/handle.cc',
+        'bindings/js/handle.h',
+        'bindings/js/handle_close_observer.h',
+        'bindings/js/support.cc',
+        'bindings/js/support.h',
+        'bindings/js/waiting_callback.cc',
+        'bindings/js/waiting_callback.h',
+      ],
+    },
+    {
+      # GN version: //mojo/tools:message_generator
+      'target_name': 'mojo_message_generator',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_common_lib',
+        'mojo_cpp_bindings',
+        'mojo_environment_chromium',
+        'mojo_system_impl',
+      ],
+      'sources': [
+        'tools/message_generator.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="android"', {
+      'targets': [
+        {
+          'target_name': 'mojo_jni_headers',
+          'type': 'none',
+          'dependencies': [
+            'mojo_java_set_jni_headers',
+          ],
+          'sources': [
+            'android/javatests/src/org/chromium/mojo/MojoTestCase.java',
+            'android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java',
+            'android/system/src/org/chromium/mojo/system/impl/CoreImpl.java',
+            'services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java',
+            'shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java',
+          ],
+          'variables': {
+            'jni_gen_package': 'mojo',
+         },
+          'includes': [ '../build/jni_generator.gypi' ],
+        },
+        {
+          'target_name': 'mojo_java_set_jni_headers',
+          'type': 'none',
+          'variables': {
+            'jni_gen_package': 'mojo',
+            'input_java_class': 'java/util/HashSet.class',
+          },
+          'includes': [ '../build/jar_file_jni_generator.gypi' ],
+        },
+        {
+          'target_name': 'mojo_system_java',
+          'type': 'none',
+          'dependencies': [
+            '../base/base.gyp:base_java',
+            'mojo_public_java',
+          ],
+          'variables': {
+            'java_in_dir': '<(DEPTH)/mojo/android/system',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+        {
+          'target_name': 'libmojo_system_java',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+            'mojo_common_lib',
+            'mojo_environment_chromium',
+            'mojo_jni_headers',
+	    'mojo_system_impl',
+          ],
+          'sources': [
+            'android/system/core_impl.cc',
+            'android/system/core_impl.h',
+          ],
+        },
+        {
+          'target_name': 'libmojo_java_unittest',
+          'type': 'shared_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:test_support_base',
+            'libmojo_system_java',
+            'mojo_jni_headers',
+            'mojo_public_bindings_test_utils',
+          ],
+          'defines': [
+            'UNIT_TEST'  # As exported from testing/gtest.gyp:gtest.
+          ],
+          'sources': [
+            'android/javatests/mojo_test_case.cc',
+            'android/javatests/mojo_test_case.h',
+            'android/javatests/init_library.cc',
+            'android/javatests/validation_test_util.cc',
+            'android/javatests/validation_test_util.h',
+          ],
+        },
+        {
+          'target_name': 'mojo_test_apk',
+          'type': 'none',
+          'dependencies': [
+            'mojo_bindings_java',
+            'mojo_public_test_interfaces',
+            'mojo_system_java',
+            '../base/base.gyp:base_java_test_support',
+          ],
+          'variables': {
+            'apk_name': 'MojoTest',
+            'java_in_dir': '<(DEPTH)/mojo/android/javatests',
+            'resource_dir': '<(DEPTH)/mojo/android/javatests/apk',
+            'native_lib_target': 'libmojo_java_unittest',
+            'is_test_apk': 1,
+            # Given that this apk tests itself, it needs to bring emma with it
+            # when instrumented.
+            'conditions': [
+              ['emma_coverage != 0', {
+                'emma_instrument': 1,
+              }],
+            ],
+          },
+          'includes': [ '../build/java_apk.gypi' ],
+        },
+      ]
+    }],
+  ]
+}
diff --git a/mojo/mojo_examples.gypi b/mojo/mojo_examples.gypi
new file mode 100644
index 0000000..9638743
--- /dev/null
+++ b/mojo/mojo_examples.gypi
@@ -0,0 +1,868 @@
+# 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.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo/examples/echo:client
+      'target_name': 'mojo_echo_client',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_echo_service_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/echo/echo_client.cc',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_echo_client',
+      'variables': {
+        'app_name': 'mojo_echo_client',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/echo:bindings
+      'target_name': 'mojo_echo_service_bindings',
+      'type': 'static_library',
+      'sources': [
+        'examples/echo/echo_service.mojom',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/echo:service
+      'target_name': 'mojo_echo_service',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_echo_service_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/echo/echo_service.cc',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_echo_service',
+      'variables': {
+        'app_name': 'mojo_echo_service',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/sample_app
+      'target_name': 'mojo_sample_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_geometry_bindings',
+        'mojo_gpu_bindings',
+        'mojo_native_viewport_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'sources': [
+        'examples/sample_app/gles2_client_impl.cc',
+        'examples/sample_app/gles2_client_impl.h',
+        'examples/sample_app/sample_app.cc',
+        'examples/sample_app/spinning_cube.cc',
+        'examples/sample_app/spinning_cube.h',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_sample_app',
+      'variables': {
+        'app_name': 'mojo_sample_app',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/apptest:bindings
+      'target_name': 'mojo_example_service_bindings',
+      'type': 'static_library',
+      'sources': [
+        'examples/apptest/example_service.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/apptest:service
+      'target_name': 'mojo_example_service',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_standalone', # For ApplicationDelegate.
+        'mojo_base.gyp:mojo_cpp_bindings',           # For *.mojom.h
+        'mojo_example_service_bindings',
+        'mojo_base.gyp:mojo_utility',                # For RunLoop.
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/apptest/example_service_application.cc',
+        'examples/apptest/example_service_application.h',
+        'examples/apptest/example_service_impl.cc',
+        'examples/apptest/example_service_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/apptest:apptests
+      'target_name': 'mojo_example_apptests',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_base.gyp:mojo_application_standalone', # For ApplicationDelegate.
+        'mojo_example_service',
+        'mojo_example_service_bindings',
+        'mojo_base.gyp:mojo_utility',                # For RunLoop.
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/apptest/example_apptest.cc',
+        'examples/apptest/example_client_application.cc',
+        'examples/apptest/example_client_application.h',
+        'examples/apptest/example_client_impl.cc',
+        'examples/apptest/example_client_impl.h',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_example_apptests',
+      'variables': {
+        'app_name': 'mojo_example_apptests',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/compositor_app
+      'target_name': 'mojo_compositor_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_cc_support',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_gpu_bindings',
+        'mojo_native_viewport_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'sources': [
+        'examples/compositor_app/compositor_app.cc',
+        'examples/compositor_app/compositor_host.cc',
+        'examples/compositor_app/compositor_host.h',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_compositor_app',
+      'variables': {
+        'app_name': 'mojo_compositor_app',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/wget
+      'target_name': 'mojo_wget',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_network_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/wget/wget.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/content_handler_demo
+      'target_name': 'mojo_content_handler_demo',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_content_handler_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/content_handler_demo/content_handler_demo.cc',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_wget',
+      'variables': {
+        'app_name': 'mojo_wget',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/media_viewer:bindings
+      'target_name': 'mojo_media_viewer_bindings',
+      'type': 'static_library',
+      'sources': [
+        'examples/media_viewer/media_viewer.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/png_viewer
+      'target_name': 'mojo_png_viewer',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../skia/skia.gyp:skia',
+        '../ui/gfx/gfx.gyp:gfx',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_content_handler_bindings',
+        'mojo_media_viewer_bindings',
+        'mojo_network_bindings',
+        'mojo_view_manager_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/png_viewer/png_viewer.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/pepper_container_app
+      'target_name': 'mojo_pepper_container_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        '../gpu/gpu.gyp:command_buffer_common',
+        '../ppapi/ppapi.gyp:ppapi_c',
+        '../ppapi/ppapi_internal.gyp:ppapi_example_gles2_spinning_cube',
+        '../ui/events/events.gyp:events_base',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_geometry_bindings',
+        'mojo_gpu_bindings',
+        'mojo_native_viewport_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'defines': [
+        # We don't really want to export. We could change how
+        # ppapi_{shared,thunk}_export.h are defined to avoid this.
+        'PPAPI_SHARED_IMPLEMENTATION',
+        'PPAPI_THUNK_IMPLEMENTATION',
+      ],
+      'sources': [
+        # Source files from ppapi/.
+        # An alternative is to depend on
+        # '../ppapi/ppapi_internal.gyp:ppapi_shared', but that target includes
+        # a lot of things that we don't need.
+        # TODO(yzshen): Consider extracting these files into a separate target
+        # which mojo_pepper_container_app and ppapi_shared both depend on.
+        '../ppapi/shared_impl/api_id.h',
+        '../ppapi/shared_impl/callback_tracker.cc',
+        '../ppapi/shared_impl/callback_tracker.h',
+        '../ppapi/shared_impl/host_resource.cc',
+        '../ppapi/shared_impl/host_resource.h',
+        '../ppapi/shared_impl/id_assignment.cc',
+        '../ppapi/shared_impl/id_assignment.h',
+        '../ppapi/shared_impl/ppapi_globals.cc',
+        '../ppapi/shared_impl/ppapi_globals.h',
+        '../ppapi/shared_impl/ppapi_shared_export.h',
+        '../ppapi/shared_impl/ppb_message_loop_shared.cc',
+        '../ppapi/shared_impl/ppb_message_loop_shared.h',
+        '../ppapi/shared_impl/ppb_view_shared.cc',
+        '../ppapi/shared_impl/ppb_view_shared.h',
+        '../ppapi/shared_impl/proxy_lock.cc',
+        '../ppapi/shared_impl/proxy_lock.h',
+        '../ppapi/shared_impl/resource.cc',
+        '../ppapi/shared_impl/resource.h',
+        '../ppapi/shared_impl/resource_tracker.cc',
+        '../ppapi/shared_impl/resource_tracker.h',
+        '../ppapi/shared_impl/scoped_pp_resource.cc',
+        '../ppapi/shared_impl/scoped_pp_resource.h',
+        '../ppapi/shared_impl/singleton_resource_id.h',
+        '../ppapi/shared_impl/tracked_callback.cc',
+        '../ppapi/shared_impl/tracked_callback.h',
+        '../ppapi/thunk/enter.cc',
+        '../ppapi/thunk/enter.h',
+        '../ppapi/thunk/interfaces_ppb_private.h',
+        '../ppapi/thunk/interfaces_ppb_private_flash.h',
+        '../ppapi/thunk/interfaces_ppb_private_no_permissions.h',
+        '../ppapi/thunk/interfaces_ppb_public_dev.h',
+        '../ppapi/thunk/interfaces_ppb_public_dev_channel.h',
+        '../ppapi/thunk/interfaces_ppb_public_stable.h',
+        '../ppapi/thunk/interfaces_preamble.h',
+        '../ppapi/thunk/ppapi_thunk_export.h',
+        '../ppapi/thunk/ppb_graphics_3d_api.h',
+        '../ppapi/thunk/ppb_graphics_3d_thunk.cc',
+        '../ppapi/thunk/ppb_instance_api.h',
+        '../ppapi/thunk/ppb_instance_thunk.cc',
+        '../ppapi/thunk/ppb_message_loop_api.h',
+        '../ppapi/thunk/ppb_view_api.h',
+        '../ppapi/thunk/ppb_view_thunk.cc',
+        '../ppapi/thunk/resource_creation_api.h',
+        '../ppapi/thunk/thunk.h',
+
+        'examples/pepper_container_app/graphics_3d_resource.cc',
+        'examples/pepper_container_app/graphics_3d_resource.h',
+        'examples/pepper_container_app/interface_list.cc',
+        'examples/pepper_container_app/interface_list.h',
+        'examples/pepper_container_app/mojo_ppapi_globals.cc',
+        'examples/pepper_container_app/mojo_ppapi_globals.h',
+        'examples/pepper_container_app/pepper_container_app.cc',
+        'examples/pepper_container_app/plugin_instance.cc',
+        'examples/pepper_container_app/plugin_instance.h',
+        'examples/pepper_container_app/plugin_module.cc',
+        'examples/pepper_container_app/plugin_module.h',
+        'examples/pepper_container_app/ppb_core_thunk.cc',
+        'examples/pepper_container_app/ppb_opengles2_thunk.cc',
+        'examples/pepper_container_app/resource_creation_impl.cc',
+        'examples/pepper_container_app/resource_creation_impl.h',
+        'examples/pepper_container_app/thunk.h',
+        'examples/pepper_container_app/type_converters.h',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/surfaces_app:parent_app
+      'target_name': 'mojo_surfaces_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_gpu_bindings',
+        'mojo_native_viewport_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_app_bindings',
+        'mojo_surfaces_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/surfaces_app/embedder.cc',
+        'examples/surfaces_app/embedder.h',
+        'examples/surfaces_app/surfaces_app.cc',
+        'examples/surfaces_app/surfaces_util.cc',
+        'examples/surfaces_app/surfaces_util.h',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/surfaces_app:bindings
+      'target_name': 'mojo_surfaces_app_bindings',
+      'type': 'static_library',
+      'sources': [
+        'examples/surfaces_app/child.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_geometry_bindings',
+        'mojo_surfaces_bindings',
+      ],
+    },
+    {
+      'target_name': 'package_mojo_surfaces_app',
+      'variables': {
+        'app_name': 'mojo_surfaces_app',
+      },
+      'includes': [ 'build/package_app.gypi' ],
+    },
+    {
+      # GN version: //mojo/examples/surfaces_app:child_app
+      'target_name': 'mojo_surfaces_child_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_surfaces_app_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'examples/surfaces_app/child_app.cc',
+        'examples/surfaces_app/child_impl.cc',
+        'examples/surfaces_app/child_impl.h',
+        'examples/surfaces_app/surfaces_util.cc',
+        'examples/surfaces_app/surfaces_util.h',
+      ],
+    },
+    {
+      # GN version: //mojo/examples/surfaces_app:child_gl_app
+      'target_name': 'mojo_surfaces_child_gl_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_gpu_bindings',
+        'mojo_surfaces_app_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'sources': [
+        'examples/surfaces_app/child_gl_app.cc',
+        'examples/surfaces_app/child_gl_impl.cc',
+        'examples/surfaces_app/child_gl_impl.h',
+        'examples/surfaces_app/surfaces_util.cc',
+        'examples/surfaces_app/surfaces_util.h',
+        'examples/sample_app/spinning_cube.cc',
+        'examples/sample_app/spinning_cube.h',
+      ],
+    }
+  ],
+  'conditions': [
+    ['use_aura==1', {
+      'targets': [
+        {
+          # GN version: //mojo/examples/aura_demo:mojo_aura_demo
+          'target_name': 'mojo_aura_demo',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../cc/cc.gyp:cc',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/compositor/compositor.gyp:compositor',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_aura_support',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/aura_demo/aura_demo.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/aura_demo:init
+          'target_name': 'mojo_aura_demo_init',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/aura_demo/view_manager_init.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/browser
+          'target_name': 'mojo_browser',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../cc/cc.gyp:cc',
+            '../third_party/icu/icu.gyp:icui18n',
+            '../third_party/icu/icu.gyp:icuuc',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/compositor/compositor.gyp:compositor',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/resources/ui_resources.gyp:ui_resources',
+            '../ui/resources/ui_resources.gyp:ui_test_pak',
+            '../ui/views/views.gyp:views',
+            '../url/url.gyp:url_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_aura_support',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_input_events_lib',
+            'mojo_navigation_bindings',
+            'mojo_views_support',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            'mojo_window_manager_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/browser/browser.cc',
+          ],
+        },
+        {
+          'target_name': 'package_mojo_aura_demo',
+          'variables': {
+            'app_name': 'mojo_aura_demo',
+          },
+          'includes': [ 'build/package_app.gypi' ],
+        },
+        {
+          # GYP version: //mojo/examples/demo_launcher
+          'target_name': 'mojo_demo_launcher',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../skia/skia.gyp:skia',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_cpp_bindings',
+            'mojo_base.gyp:mojo_utility',
+            'mojo_geometry_bindings',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'sources': [
+            'examples/demo_launcher/demo_launcher.cc',
+          ],
+        },
+        {
+          # GYP version: //mojo/examples/keyboard
+          'target_name': 'mojo_keyboard',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../cc/cc.gyp:cc',
+            '../third_party/icu/icu.gyp:icui18n',
+            '../third_party/icu/icu.gyp:icuuc',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/compositor/compositor.gyp:compositor',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/resources/ui_resources.gyp:ui_resources',
+            '../ui/resources/ui_resources.gyp:ui_test_pak',
+            '../ui/views/views.gyp:views',
+            '../url/url.gyp:url_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_aura_support',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_input_events_lib',
+            'mojo_keyboard_bindings',
+            'mojo_navigation_bindings',
+            'mojo_views_support',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/keyboard/keyboard_delegate.h',
+            'examples/keyboard/keyboard_view.cc',
+            'examples/keyboard/keyboard_view.h',
+            'examples/keyboard/keyboard.cc',
+            'examples/keyboard/keys.cc',
+            'examples/keyboard/keys.h',
+          ],
+        },
+        {
+          # GYP version: //mojo/examples/keyboard:bindings
+          'target_name': 'mojo_keyboard_bindings',
+          'type': 'static_library',
+          'sources': [
+            'examples/keyboard/keyboard.mojom',
+          ],
+          'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+          'export_dependent_settings': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/window_manager:bindings
+          'target_name': 'mojo_window_manager_bindings',
+          'type': 'static_library',
+          'sources': [
+            'examples/window_manager/window_manager.mojom',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+            'mojo_geometry_bindings',
+          ],
+          'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+          'export_dependent_settings': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/window_manager
+          'target_name': 'mojo_window_manager',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/resources/ui_resources.gyp:ui_resources',
+            '../ui/resources/ui_resources.gyp:ui_test_pak',
+            '../ui/views/views.gyp:views',
+            '../ui/wm/wm.gyp:wm',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_cpp_bindings',
+            'mojo_base.gyp:mojo_utility',
+            'mojo_aura_support',
+            'mojo_core_window_manager_lib',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_input_events_lib',
+            'mojo_keyboard_bindings',
+            'mojo_navigation_bindings',
+            'mojo_view_manager_lib',
+            'mojo_views_support',
+            'mojo_window_manager_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'sources': [
+            'examples/window_manager/debug_panel.h',
+            'examples/window_manager/debug_panel.cc',
+            'examples/window_manager/window_manager.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/embedded_app
+          'target_name': 'mojo_embedded_app',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../url/url.gyp:url_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_cpp_bindings',
+            'mojo_base.gyp:mojo_utility',
+            'mojo_geometry_bindings',
+            'mojo_navigation_bindings',
+            'mojo_view_manager_lib',
+            'mojo_window_manager_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'sources': [
+            'examples/embedded_app/embedded_app.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/nesting_app
+          'target_name': 'mojo_nesting_app',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../url/url.gyp:url_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_cpp_bindings',
+            'mojo_base.gyp:mojo_utility',
+            'mojo_geometry_bindings',
+            'mojo_navigation_bindings',
+            'mojo_view_manager_lib',
+            'mojo_window_manager_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'sources': [
+            'examples/nesting_app/nesting_app.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/media_viewer
+          'target_name': 'mojo_media_viewer',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../skia/skia.gyp:skia',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/views/views.gyp:views',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_input_events_lib',
+            'mojo_media_viewer_bindings',
+            'mojo_navigation_bindings',
+            'mojo_views_support',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/media_viewer/media_viewer.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow
+          'target_name': 'mojo_wm_flow_wm',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../skia/skia.gyp:skia',
+            '../ui/views/views.gyp:views',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_core_window_manager_lib',
+            'mojo_view_manager_lib',
+            'mojo_views_support',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/wm_flow/wm/frame_controller.cc',
+            'examples/wm_flow/wm/frame_controller.h',
+            'examples/wm_flow/wm/wm.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow:init
+          'target_name': 'mojo_wm_flow_init',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/wm_flow/init/init.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow:embedder_bindings
+          'target_name': 'mojo_wm_flow_embedder_bindings',
+          'type': 'static_library',
+          'sources': [
+            'examples/wm_flow/app/embedder.mojom',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+          'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+          'export_dependent_settings': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow:embeddee_bindings
+          'target_name': 'mojo_wm_flow_embeddee_bindings',
+          'type': 'static_library',
+          'sources': [
+            'examples/wm_flow/embedded/embeddee.mojom',
+          ],
+          'dependencies': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+          'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+          'export_dependent_settings': [
+            'mojo_base.gyp:mojo_cpp_bindings',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow:app
+          'target_name': 'mojo_wm_flow_app',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_core_window_manager_bindings',
+            'mojo_view_manager_lib',
+            'mojo_wm_flow_embeddee_bindings',
+            'mojo_wm_flow_embedder_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/wm_flow/app/app.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/examples/wm_flow:embedded
+          'target_name': 'mojo_wm_flow_embedded',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_core_window_manager_bindings',
+            'mojo_view_manager_lib',
+            'mojo_wm_flow_embeddee_bindings',
+            'mojo_wm_flow_embedder_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'examples/wm_flow/embedded/embedded.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_js_unittests.isolate b/mojo/mojo_js_unittests.isolate
new file mode 100644
index 0000000..2b3131b
--- /dev/null
+++ b/mojo/mojo_js_unittests.isolate
@@ -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.
+{
+  'includes': [
+    '../third_party/icu/icu.isolate',
+  ],
+  'conditions': [
+    ['OS=="win" or OS=="mac" or OS=="linux"', {
+      'variables': {
+        'command': [
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/mojo_js_unittests<(EXECUTABLE_SUFFIX)',
+          '--brave-new-test-launcher',
+          '--test-launcher-bot-mode',
+        ],
+        'files': [
+          '../gin/test/expect.js',
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/',
+          '<(PRODUCT_DIR)/mojo_js_unittests<(EXECUTABLE_SUFFIX)',
+          'bindings/js/',
+          'public/js/bindings/',
+        ],
+      },
+    }],
+    ['OS=="win"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/mojo_test_support.dll',
+        ],
+      },
+    }],
+    ['OS=="linux"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/lib/libmojo_test_support.so',
+        ],
+      },
+    }],
+    ['OS=="mac"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/libmojo_test_support.dylib',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_nacl.gyp b/mojo/mojo_nacl.gyp
new file mode 100644
index 0000000..a813046
--- /dev/null
+++ b/mojo/mojo_nacl.gyp
@@ -0,0 +1,121 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'conditions': [
+    ['disable_nacl==0 and disable_nacl_untrusted==0', {
+      'variables': {
+        'monacl_codegen_dir': '<(SHARED_INTERMEDIATE_DIR)/<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))/monacl',
+      },
+      'includes': [
+        'mojo_variables.gypi',
+        '../build/common_untrusted.gypi',
+        '../components/nacl/nacl_defines.gypi',
+      ],
+      'targets': [
+        {
+          'target_name': 'monacl_codegen',
+          'type': 'none',
+          'actions': [
+            {
+              'action_name': 'generate_nacl_bindings',
+              'inputs': [
+                'nacl/generator/generate_nacl_bindings.py',
+                'nacl/generator/interface.py',
+                'nacl/generator/interface_dsl.py',
+                'nacl/generator/mojo_syscall.cc.tmpl',
+                'nacl/generator/libmojo.cc.tmpl',
+              ],
+              'outputs': [
+                '<(monacl_codegen_dir)/mojo_syscall.cc',
+                '<(monacl_codegen_dir)/libmojo.cc',
+              ],
+              'action': [
+                'python',
+                'nacl/generator/generate_nacl_bindings.py',
+                '-d', '<(monacl_codegen_dir)',
+              ],
+            },
+          ],
+        },
+        {
+          'target_name': 'monacl_sel',
+          'type': 'static_library',
+          'defines': [
+            '<@(nacl_defines)',
+          ],
+          'include_dirs': [
+            '..',
+          ],
+          'sources': [
+            '<(monacl_codegen_dir)/mojo_syscall.cc',
+            'nacl/monacl_sel_main.cc',
+          ],
+          'dependencies': [
+            '<(DEPTH)/native_client/src/trusted/service_runtime/service_runtime.gyp:sel',
+          ],
+        },
+        {
+          'target_name': 'monacl_shell',
+          'type': 'executable',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_base.gyp:mojo_system_impl',
+            'monacl_sel',
+          ],
+          'sources': [
+            'nacl/monacl_shell.cc',
+          ],
+        },
+        {
+          'target_name': 'mojo_nacl',
+          'type': 'none',
+          'variables': {
+            'nlib_target': 'libmojo.a',
+            'build_glibc': 0,
+            'build_newlib': 0,
+            'build_pnacl_newlib': 1,
+          },
+          'defines': [
+            '<@(nacl_defines)',
+          ],
+          'sources': [
+            '<(monacl_codegen_dir)/libmojo.cc',
+          ],
+          'dependencies': [
+            'monacl_codegen',
+          ],
+        },
+        {
+          'target_name': 'monacl_test',
+          'type': 'none',
+          'variables': {
+            'nexe_target': 'monacl_test',
+            'build_newlib': 0,
+            'build_pnacl_newlib': 1,
+            'translate_pexe_with_build': 1,
+            'link_flags': [
+              '-pthread',
+              '-lmojo',
+              '-limc_syscalls',
+            ],
+            'sources': [
+              '<@(mojo_public_system_unittest_sources)',
+            ],
+          },
+          'dependencies': [
+            '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+            '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_lib',
+            '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:imc_syscalls_lib',
+            '<(DEPTH)/native_client/src/untrusted/pthread/pthread.gyp:pthread_lib',
+            '../testing/gtest_nacl.gyp:gtest_nacl',
+            '../testing/gtest_nacl.gyp:gtest_main_nacl',
+            'mojo_nacl',
+            'monacl_codegen',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_public.gypi b/mojo/mojo_public.gypi
new file mode 100644
index 0000000..cc7fc15
--- /dev/null
+++ b/mojo/mojo_public.gypi
@@ -0,0 +1,257 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo/public/c/system
+      'target_name': 'mojo_system',
+      'type': 'static_library',
+      'defines': [
+        'MOJO_SYSTEM_IMPLEMENTATION',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '..',
+        ],
+      },
+      'all_dependent_settings': {
+        'conditions': [
+          # We need to be able to call the MojoSetSystemThunks() function in
+          # system_thunks.cc
+          ['OS=="android"', {
+            'ldflags!': [
+              '-Wl,--exclude-libs=ALL',
+            ],
+          }],
+        ],
+      },
+      'sources': [
+        'public/c/system/buffer.h',
+        'public/c/system/core.h',
+        'public/c/system/data_pipe.h',
+        'public/c/system/functions.h',
+        'public/c/system/macros.h',
+        'public/c/system/message_pipe.h',
+        'public/c/system/system_export.h',
+        'public/c/system/types.h',
+        'public/platform/native/system_thunks.cc',
+        'public/platform/native/system_thunks.h',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/bindings
+      'target_name': 'mojo_cpp_bindings',
+      'type': 'static_library',
+      'include_dirs': [
+        '..'
+      ],
+      'sources': [
+        'public/cpp/bindings/array.h',
+        'public/cpp/bindings/callback.h',
+        'public/cpp/bindings/error_handler.h',
+        'public/cpp/bindings/interface_impl.h',
+        'public/cpp/bindings/interface_ptr.h',
+        'public/cpp/bindings/interface_request.h',
+        'public/cpp/bindings/message.h',
+        'public/cpp/bindings/message_filter.h',
+        'public/cpp/bindings/no_interface.h',
+        'public/cpp/bindings/string.h',
+        'public/cpp/bindings/type_converter.h',
+        'public/cpp/bindings/lib/array_internal.h',
+        'public/cpp/bindings/lib/array_internal.cc',
+        'public/cpp/bindings/lib/array_serialization.h',
+        'public/cpp/bindings/lib/bindings_internal.h',
+        'public/cpp/bindings/lib/bindings_serialization.cc',
+        'public/cpp/bindings/lib/bindings_serialization.h',
+        'public/cpp/bindings/lib/bounds_checker.cc',
+        'public/cpp/bindings/lib/bounds_checker.h',
+        'public/cpp/bindings/lib/buffer.h',
+        'public/cpp/bindings/lib/callback_internal.h',
+        'public/cpp/bindings/lib/connector.cc',
+        'public/cpp/bindings/lib/connector.h',
+        'public/cpp/bindings/lib/filter_chain.cc',
+        'public/cpp/bindings/lib/filter_chain.h',
+        'public/cpp/bindings/lib/fixed_buffer.cc',
+        'public/cpp/bindings/lib/fixed_buffer.h',
+        'public/cpp/bindings/lib/interface_impl_internal.h',
+        'public/cpp/bindings/lib/interface_ptr_internal.h',
+        'public/cpp/bindings/lib/message.cc',
+        'public/cpp/bindings/lib/message_builder.cc',
+        'public/cpp/bindings/lib/message_builder.h',
+        'public/cpp/bindings/lib/message_filter.cc',
+        'public/cpp/bindings/lib/message_header_validator.cc',
+        'public/cpp/bindings/lib/message_header_validator.h',
+        'public/cpp/bindings/lib/message_internal.h',
+        'public/cpp/bindings/lib/message_queue.cc',
+        'public/cpp/bindings/lib/message_queue.h',
+        'public/cpp/bindings/lib/no_interface.cc',
+        'public/cpp/bindings/lib/router.cc',
+        'public/cpp/bindings/lib/router.h',
+        'public/cpp/bindings/lib/shared_data.h',
+        'public/cpp/bindings/lib/shared_ptr.h',
+        'public/cpp/bindings/lib/string_serialization.h',
+        'public/cpp/bindings/lib/string_serialization.cc',
+        'public/cpp/bindings/lib/validation_errors.cc',
+        'public/cpp/bindings/lib/validation_errors.h',
+      ],
+    },
+    {
+      # GN version: //mojo/public/js/bindings
+      'target_name': 'mojo_js_bindings',
+      'type': 'static_library',
+      'include_dirs': [
+        '..'
+      ],
+      'sources': [
+        'public/js/bindings/constants.cc',
+        'public/js/bindings/constants.h',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/environment:standalone
+      'target_name': 'mojo_environment_standalone',
+      'type': 'static_library',
+      'sources': [
+        'public/c/environment/async_waiter.h',
+        'public/c/environment/logger.h',
+        'public/cpp/environment/environment.h',
+        'public/cpp/environment/lib/default_async_waiter.cc',
+        'public/cpp/environment/lib/default_async_waiter.h',
+        'public/cpp/environment/lib/default_logger.cc',
+        'public/cpp/environment/lib/default_logger.h',
+        'public/cpp/environment/lib/environment.cc',
+        'public/cpp/environment/lib/logging.cc',
+        'public/cpp/environment/logging.h',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/utility
+      'target_name': 'mojo_utility',
+      'type': 'static_library',
+      'sources': [
+        'public/cpp/utility/mutex.h',
+        'public/cpp/utility/run_loop.h',
+        'public/cpp/utility/run_loop_handler.h',
+        'public/cpp/utility/thread.h',
+        'public/cpp/utility/lib/mutex.cc',
+        'public/cpp/utility/lib/run_loop.cc',
+        'public/cpp/utility/lib/thread.cc',
+        'public/cpp/utility/lib/thread_local.h',
+        'public/cpp/utility/lib/thread_local_posix.cc',
+        'public/cpp/utility/lib/thread_local_win.cc',
+      ],
+      'conditions': [
+        # See crbug.com/342893:
+        ['OS=="win"', {
+          'sources!': [
+            'public/cpp/utility/mutex.h',
+            'public/cpp/utility/thread.h',
+            'public/cpp/utility/lib/mutex.cc',
+            'public/cpp/utility/lib/thread.cc',
+          ],
+        }],
+      ],
+      'include_dirs': [
+        '..',
+      ],
+    },
+    {
+      # GN version: //mojo/public/interfaces/application:application
+      'target_name': 'mojo_application_bindings',
+      'type': 'static_library',
+      'sources': [
+        'public/interfaces/application/application.mojom',
+        'public/interfaces/application/service_provider.mojom',
+        'public/interfaces/application/shell.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_cpp_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/application
+      'target_name': 'mojo_application_base',
+      'type': 'static_library',
+      'sources': [
+        'public/cpp/application/application_connection.h',
+        'public/cpp/application/application_delegate.h',
+        'public/cpp/application/application_impl.h',
+        'public/cpp/application/connect.h',
+        'public/cpp/application/service_provider_impl.h',
+        'public/cpp/application/interface_factory.h',
+        'public/cpp/application/interface_factory_impl.h',
+        'public/cpp/application/lib/application_connection.cc',
+        'public/cpp/application/lib/application_delegate.cc',
+        'public/cpp/application/lib/application_impl.cc',
+        'public/cpp/application/lib/service_provider_impl.cc',
+        'public/cpp/application/lib/service_connector.cc',
+        'public/cpp/application/lib/service_connector.h',
+        'public/cpp/application/lib/service_registry.cc',
+        'public/cpp/application/lib/service_registry.h',
+        'public/cpp/application/lib/weak_service_provider.cc',
+        'public/cpp/application/lib/weak_service_provider.h',
+      ],
+      'dependencies': [
+        'mojo_application_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_application_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/application:standalone"
+      'target_name': 'mojo_application_standalone',
+      'type': 'static_library',
+      'sources': [
+        'public/cpp/application/lib/application_runner.cc',
+        'public/cpp/application/application_runner.h',
+      ],
+      'dependencies': [
+        'mojo_application_base',
+        'mojo_environment_standalone',
+      ],
+      'export_dependent_settings': [
+        'mojo_application_base',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS == "android"', {
+      'targets': [
+        {
+          # GN version: //mojo/public/java_system
+          'target_name': 'mojo_public_java',
+          'type': 'none',
+          'variables': {
+            'java_in_dir': 'public/java/system',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+        {
+          # GN version: //mojo/public/java_bindings
+          'target_name': 'mojo_bindings_java',
+          'type': 'none',
+          'variables': {
+            'java_in_dir': 'public/java/bindings',
+          },
+          'dependencies': [
+            'mojo_public_java',
+          ],
+          'includes': [ '../build/java.gypi' ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_public_gles2_for_loadable_module.gypi b/mojo/mojo_public_gles2_for_loadable_module.gypi
new file mode 100644
index 0000000..42421dc
--- /dev/null
+++ b/mojo/mojo_public_gles2_for_loadable_module.gypi
@@ -0,0 +1,59 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# In the non component build, the thunks need to be linked directly into the
+# loadable module since they define symbols that should be exported from that
+# library. So, this variable expands out to either list the sources directly (in
+# the component build where no symbols need to be exported) a dependency.
+{
+  'conditions': [
+    ['component=="shared_library"', {
+      'dependencies': [
+        'mojo_base.gyp:mojo_gles2_impl',
+      ],
+    }, {  # component!="shared_library"
+      'defines': [
+        'MOJO_GLES2_IMPLEMENTATION',
+        'GLES2_USE_MOJO',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'dependencies': [
+        '../third_party/khronos/khronos.gyp:khronos_headers'
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '..',
+        ],
+        'defines': [
+          'GLES2_USE_MOJO',
+        ],
+      },
+      'all_dependent_settings': {
+        'conditions': [
+          # We need to be able to call the MojoSetGLES2Thunks() function in
+          # gles2_thunks.cc
+          ['OS=="android"', {
+            'ldflags!': [
+              '-Wl,--exclude-libs=ALL',
+            ],
+          }],
+        ],
+      },
+      'sources': [
+        'public/c/gles2/gles2.h',
+        'public/c/gles2/gles2_export.h',
+        'public/platform/native/gles2_thunks.cc',
+        'public/platform/native/gles2_thunks.h',
+        'public/platform/native/gles2_impl_thunks.cc',
+        'public/platform/native/gles2_impl_thunks.h',
+        'public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc',
+        'public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h',
+        'public/platform/native/gles2_impl_chromium_sync_point_thunks.cc',
+        'public/platform/native/gles2_impl_chromium_sync_point_thunks.h',
+      ],
+    }]
+  ]
+}
diff --git a/mojo/mojo_public_tests.gypi b/mojo/mojo_public_tests.gypi
new file mode 100644
index 0000000..6cf4730
--- /dev/null
+++ b/mojo/mojo_public_tests.gypi
@@ -0,0 +1,217 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo/public/c/test_support
+      'target_name': 'mojo_test_support',
+      'defines': [
+        'MOJO_TEST_SUPPORT_IMPLEMENTATION',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '..',
+        ],
+      },
+      'sources': [
+        'public/c/test_support/test_support.h',
+        'public/c/test_support/test_support_export.h',
+        # TODO(vtl): Convert this to thunks http://crbug.com/386799
+        'public/tests/test_support_private.cc',
+        'public/tests/test_support_private.h',
+      ],
+      'conditions': [
+        ['OS=="ios"', {
+          'type': 'static_library',
+        }, {
+          'type': 'shared_library',
+        }],
+        ['OS=="mac"', {
+          'xcode_settings': {
+            # Make it a run-path dependent library.
+            'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+          },
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/test_support:test_utils
+      'target_name': 'mojo_public_test_utils',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_test_support',
+      ],
+      'sources': [
+        'public/cpp/test_support/lib/test_support.cc',
+        'public/cpp/test_support/lib/test_utils.cc',
+        'public/cpp/test_support/test_utils.h',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils
+      'target_name': 'mojo_public_bindings_test_utils',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+      ],
+      'sources': [
+        'public/cpp/bindings/tests/validation_test_input_parser.cc',
+        'public/cpp/bindings/tests/validation_test_input_parser.h',
+      ],
+    },
+    # TODO(vtl): Reorganize the mojo_public_*_unittests.
+    {
+      # GN version: //mojo/public/cpp/bindings/tests:mojo_public_bindings_unittests
+      'target_name': 'mojo_public_bindings_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_cpp_bindings',
+        'mojo_environment_standalone',
+        'mojo_public_test_utils',
+        'mojo_run_all_unittests',
+        'mojo_public_bindings_test_utils',
+        'mojo_public_test_interfaces',
+        'mojo_utility',
+      ],
+      'sources': [
+        'public/cpp/bindings/tests/array_unittest.cc',
+        'public/cpp/bindings/tests/bounds_checker_unittest.cc',
+        'public/cpp/bindings/tests/buffer_unittest.cc',
+        'public/cpp/bindings/tests/connector_unittest.cc',
+        'public/cpp/bindings/tests/handle_passing_unittest.cc',
+        'public/cpp/bindings/tests/interface_ptr_unittest.cc',
+        'public/cpp/bindings/tests/request_response_unittest.cc',
+        'public/cpp/bindings/tests/router_unittest.cc',
+        'public/cpp/bindings/tests/sample_service_unittest.cc',
+        'public/cpp/bindings/tests/serialization_warning_unittest.cc',
+        'public/cpp/bindings/tests/string_unittest.cc',
+        'public/cpp/bindings/tests/struct_unittest.cc',
+        'public/cpp/bindings/tests/type_conversion_unittest.cc',
+        'public/cpp/bindings/tests/validation_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/environment/tests:mojo_public_environment_unittests
+      'target_name': 'mojo_public_environment_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_environment_standalone',
+        'mojo_public_test_utils',
+        'mojo_run_all_unittests',
+        'mojo_utility',
+      ],
+      'include_dirs': [ '..' ],
+      'sources': [
+        'public/cpp/environment/tests/async_waiter_unittest.cc',
+        'public/cpp/environment/tests/logger_unittest.cc',
+        'public/cpp/environment/tests/logging_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/application/tests:mojo_public_application_unittests
+      'target_name': 'mojo_public_application_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_application_standalone',
+        'mojo_utility',
+        'mojo_environment_standalone',
+        'mojo_run_all_unittests',
+      ],
+      'sources': [
+        'public/cpp/application/tests/service_registry_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/application/tests:mojo_public_system_unittests
+      'target_name': 'mojo_public_system_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_public_test_utils',
+        'mojo_run_all_unittests',
+      ],
+      'include_dirs': [ '..' ],
+      'sources': [
+        '<@(mojo_public_system_unittest_sources)',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/application/tests:mojo_public_utility_unittests
+      'target_name': 'mojo_public_utility_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_public_test_utils',
+        'mojo_run_all_unittests',
+        'mojo_utility',
+      ],
+      'include_dirs' : [ '..' ],
+      'sources': [
+        'public/cpp/utility/tests/mutex_unittest.cc',
+        'public/cpp/utility/tests/run_loop_unittest.cc',
+        'public/cpp/utility/tests/thread_unittest.cc',
+      ],
+      'conditions': [
+        # See crbug.com/342893:
+        ['OS=="win"', {
+          'sources!': [
+            'public/cpp/utility/tests/mutex_unittest.cc',
+            'public/cpp/utility/tests/thread_unittest.cc',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/public/c/system/tests:perftests
+      'target_name': 'mojo_public_system_perftests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_public_test_utils',
+        'mojo_run_all_perftests',
+        'mojo_utility',
+      ],
+      'sources': [
+        'public/c/system/tests/core_perftest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/public/interfaces/bindings/tests:test_interfaces
+      'target_name': 'mojo_public_test_interfaces',
+      'type': 'static_library',
+      'sources': [
+        'public/interfaces/bindings/tests/math_calculator.mojom',
+        'public/interfaces/bindings/tests/no_module.mojom',
+        'public/interfaces/bindings/tests/rect.mojom',
+        'public/interfaces/bindings/tests/regression_tests.mojom',
+        'public/interfaces/bindings/tests/sample_factory.mojom',
+        'public/interfaces/bindings/tests/sample_import.mojom',
+        'public/interfaces/bindings/tests/sample_import2.mojom',
+        'public/interfaces/bindings/tests/sample_interfaces.mojom',
+        'public/interfaces/bindings/tests/sample_service.mojom',
+        'public/interfaces/bindings/tests/serialization_test_structs.mojom',
+        'public/interfaces/bindings/tests/test_structs.mojom',
+        'public/interfaces/bindings/tests/validation_test_interfaces.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_cpp_bindings',
+      ],
+    },
+  ],
+}
diff --git a/mojo/mojo_python_unittests.isolate b/mojo/mojo_python_unittests.isolate
new file mode 100644
index 0000000..4fea84a
--- /dev/null
+++ b/mojo/mojo_python_unittests.isolate
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'conditions': [
+    ['OS=="linux"', {
+      'variables': {
+        'command': [
+          'tools/run_mojo_python_bindings_tests.py',
+          '--build-dir=<(PRODUCT_DIR)',
+        ],
+        'files': [
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/',
+          '<(PRODUCT_DIR)/python/',
+          'python/tests/',
+          'tools/pylib/mojo_python_tests_runner.py',
+          'tools/run_mojo_python_bindings_tests.py',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi
new file mode 100644
index 0000000..96f69f7
--- /dev/null
+++ b/mojo/mojo_services.gypi
@@ -0,0 +1,991 @@
+# 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.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo/services/clipboard/
+      'target_name': 'mojo_clipboard',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_clipboard_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'services/clipboard/clipboard_standalone_impl.cc',
+        'services/clipboard/clipboard_standalone_impl.h',
+        'services/clipboard/main.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/clipboard
+      'target_name': 'mojo_clipboard_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/clipboard/clipboard.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/clipboard:mojo_clipboard_unittests
+      'target_name': 'mojo_clipboard_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_application_manager',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_run_all_unittests',
+        'mojo_base.gyp:mojo_system_impl',
+        'mojo_clipboard_bindings',
+        'mojo_shell_test_support',
+      ],
+      'sources': [
+        'services/clipboard/clipboard_standalone_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/services/html_viewer
+      'target_name': 'mojo_html_viewer',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../cc/blink/cc_blink.gyp:cc_blink',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../media/blink/media_blink.gyp:media_blink',
+        '../media/media.gyp:media',
+        '../net/net.gyp:net',
+        '../skia/skia.gyp:skia',
+        '../third_party/WebKit/public/blink.gyp:blink',
+        '../ui/native_theme/native_theme.gyp:native_theme',
+        '../url/url.gyp:url_lib',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_clipboard_bindings',
+        'mojo_cc_support',
+        'mojo_content_handler_bindings',
+        'mojo_gpu_bindings',
+        'mojo_navigation_bindings',
+        'mojo_network_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_view_manager_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'include_dirs': [
+        'third_party/WebKit'
+      ],
+      'sources': [
+        'services/html_viewer/blink_basic_type_converters.cc',
+        'services/html_viewer/blink_basic_type_converters.h',
+        'services/html_viewer/blink_input_events_type_converters.cc',
+        'services/html_viewer/blink_input_events_type_converters.h',
+        'services/html_viewer/blink_platform_impl.cc',
+        'services/html_viewer/blink_platform_impl.h',
+        'services/html_viewer/blink_url_request_type_converters.cc',
+        'services/html_viewer/blink_url_request_type_converters.h',
+        'services/html_viewer/html_viewer.cc',
+        'services/html_viewer/html_document_view.cc',
+        'services/html_viewer/html_document_view.h',
+        'services/html_viewer/webclipboard_impl.cc',
+        'services/html_viewer/webclipboard_impl.h',
+        'services/html_viewer/webcookiejar_impl.cc',
+        'services/html_viewer/webcookiejar_impl.h',
+        'services/html_viewer/webmediaplayer_factory.cc',
+        'services/html_viewer/webmediaplayer_factory.h',
+        'services/html_viewer/webmimeregistry_impl.cc',
+        'services/html_viewer/webmimeregistry_impl.h',
+        'services/html_viewer/websockethandle_impl.cc',
+        'services/html_viewer/websockethandle_impl.h',
+        'services/html_viewer/webstoragenamespace_impl.cc',
+        'services/html_viewer/webstoragenamespace_impl.h',
+        'services/html_viewer/webthemeengine_impl.cc',
+        'services/html_viewer/webthemeengine_impl.h',
+        'services/html_viewer/webthread_impl.cc',
+        'services/html_viewer/webthread_impl.h',
+        'services/html_viewer/weburlloader_impl.cc',
+        'services/html_viewer/weburlloader_impl.h',
+        'services/html_viewer/weblayertreeview_impl.cc',
+        'services/html_viewer/weblayertreeview_impl.h',
+        'services/public/cpp/network/web_socket_read_queue.cc',
+        'services/public/cpp/network/web_socket_read_queue.h',
+        'services/public/cpp/network/web_socket_write_queue.cc',
+        'services/public/cpp/network/web_socket_write_queue.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/input_events
+      'target_name': 'mojo_input_events_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_INPUT_EVENTS_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../ui/events/events.gyp:events',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_input_events_bindings',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        '<(mojo_system_for_component)',
+      ],
+      'sources': [
+        'services/public/cpp/input_events/lib/input_events_type_converters.cc',
+        'services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc',
+        'services/public/cpp/input_events/lib/mojo_extended_key_event_data.h',
+        'services/public/cpp/input_events/input_events_type_converters.h',
+        'services/public/cpp/input_events/mojo_input_events_export.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/input_events
+      'target_name': 'mojo_input_events_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/input_events/input_event_constants.mojom',
+        'services/public/interfaces/input_events/input_events.mojom',
+        'services/public/interfaces/input_events/input_key_codes.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_geometry_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_geometry_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/geometry
+      'target_name': 'mojo_geometry_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/geometry/geometry.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/geometry
+      'target_name': 'mojo_geometry_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_GEOMETRY_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../skia/skia.gyp:skia',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_geometry_bindings',
+        '<(mojo_system_for_component)',
+      ],
+      'export_dependent_settings': [
+        '../ui/gfx/gfx.gyp:gfx',
+      ],
+      'sources': [
+        'services/public/cpp/geometry/lib/geometry_type_converters.cc',
+        'services/public/cpp/geometry/geometry_type_converters.h',
+        'services/public/cpp/geometry/mojo_geometry_export.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/surfaces
+      'target_name': 'mojo_surfaces_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_SURFACES_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../gpu/gpu.gyp:gpu',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_geometry_lib',
+        'mojo_surfaces_bindings',
+        '<(mojo_system_for_component)',
+      ],
+      'export_dependent_settings': [
+        'mojo_geometry_lib',
+        'mojo_surfaces_bindings',
+      ],
+      'sources': [
+        'services/public/cpp/surfaces/lib/surfaces_type_converters.cc',
+        'services/public/cpp/surfaces/lib/surfaces_utils.cc',
+        'services/public/cpp/surfaces/surfaces_type_converters.h',
+        'services/public/cpp/surfaces/surfaces_utils.h',
+        'services/public/cpp/surfaces/mojo_surfaces_export.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/surfaces/tests
+      'target_name': 'mojo_surfaces_lib_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../gpu/gpu.gyp:gpu',
+        '../skia/skia.gyp:skia',
+        '../testing/gtest.gyp:gtest',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        '../ui/gfx/gfx.gyp:gfx_test_support',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_base.gyp:mojo_run_all_unittests',
+        'mojo_geometry_lib',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+      ],
+      'sources': [
+        'services/public/cpp/surfaces/tests/surface_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/services/gles2
+      'target_name': 'mojo_gles2_service',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../gpu/gpu.gyp:command_buffer_service',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        '../ui/gl/gl.gyp:gl',
+        'mojo_base.gyp:mojo_gles2_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_gles2_bindings',
+      ],
+      'sources': [
+        'services/gles2/command_buffer_impl.cc',
+        'services/gles2/command_buffer_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/gpu
+      'target_name': 'mojo_gpu_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/gpu/gpu.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/native_viewport
+      'target_name': 'mojo_native_viewport_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/native_viewport/native_viewport.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+        'mojo_input_events_bindings',
+        'mojo_surface_id_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+        'mojo_input_events_bindings',
+        'mojo_surface_id_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/native_viewport
+      'target_name': 'mojo_native_viewport_service_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc_surfaces',
+        '../skia/skia.gyp:skia',
+        '../ui/events/events.gyp:events',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_gles2_service',
+        'mojo_gpu_bindings',
+        'mojo_input_events_lib',
+        'mojo_native_viewport_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+      ],
+      'export_dependent_settings': [
+        'mojo_geometry_bindings',
+        'mojo_gpu_bindings',
+        'mojo_native_viewport_bindings',
+        'mojo_surfaces_bindings',
+      ],
+      'sources': [
+        'services/native_viewport/gpu_impl.cc',
+        'services/native_viewport/gpu_impl.h',
+        'services/native_viewport/native_viewport_impl.cc',
+        'services/native_viewport/native_viewport_impl.h',
+        'services/native_viewport/platform_viewport.h',
+        'services/native_viewport/platform_viewport_android.cc',
+        'services/native_viewport/platform_viewport_headless.cc',
+        'services/native_viewport/platform_viewport_headless.h',
+        'services/native_viewport/platform_viewport_mac.mm',
+        'services/native_viewport/platform_viewport_ozone.cc',
+        'services/native_viewport/platform_viewport_stub.cc',
+        'services/native_viewport/platform_viewport_win.cc',
+        'services/native_viewport/platform_viewport_x11.cc',
+        'services/native_viewport/viewport_surface.cc',
+        'services/native_viewport/viewport_surface.h',
+      ],
+      'conditions': [
+        ['OS=="win" or OS=="android" or OS=="linux" or OS=="mac"', {
+          'sources!': [
+            'services/native_viewport/platform_viewport_stub.cc',
+          ],
+        }],
+        ['OS=="android"', {
+          'dependencies': [
+            'mojo_base.gyp:mojo_jni_headers',
+          ],
+        }],
+        ['OS=="win"', {
+          'dependencies': [
+            '../ui/platform_window/win/win_window.gyp:win_window',
+          ],
+        }],
+        ['use_x11==1', {
+          'dependencies': [
+            '../ui/platform_window/x11/x11_window.gyp:x11_window',
+            '../ui/events/platform/x11/x11_events_platform.gyp:x11_events_platform',
+          ],
+        }],
+        ['use_ozone==1', {
+          'dependencies': [
+            '../ui/ozone/ozone.gyp:ozone',
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'mojo_native_viewport_service',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_native_viewport_bindings',
+        'mojo_native_viewport_service_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'export_dependent_settings': [
+        'mojo_native_viewport_bindings',
+      ],
+      'sources': [
+        'services/native_viewport/main.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/navigation
+      'target_name': 'mojo_navigation_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/navigation/navigation.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_network_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/content_handler
+      'target_name': 'mojo_content_handler_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/content_handler/content_handler.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_network_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/network
+      'target_name': 'mojo_network_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/network/cookie_store.mojom',
+        'services/public/interfaces/network/net_address.mojom',
+        'services/public/interfaces/network/network_error.mojom',
+        'services/public/interfaces/network/network_service.mojom',
+        'services/public/interfaces/network/tcp_bound_socket.mojom',
+        'services/public/interfaces/network/tcp_client_socket.mojom',
+        'services/public/interfaces/network/tcp_server_socket.mojom',
+        'services/public/interfaces/network/udp_socket.mojom',
+        'services/public/interfaces/network/url_loader.mojom',
+        'services/public/interfaces/network/web_socket.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/network:lib
+      'target_name': 'mojo_network_service_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../net/net.gyp:net',
+        '../url/url.gyp:url_lib',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_network_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_network_bindings',
+      ],
+      'sources': [
+        'services/network/cookie_store_impl.cc',
+        'services/network/cookie_store_impl.h',
+        'services/network/network_context.cc',
+        'services/network/network_context.h',
+        'services/network/network_service_impl.cc',
+        'services/network/network_service_impl.h',
+        'services/network/url_loader_impl.cc',
+        'services/network/url_loader_impl.h',
+        'services/network/web_socket_impl.cc',
+        'services/network/web_socket_impl.h',
+        'services/public/cpp/network/web_socket_read_queue.cc',
+        'services/public/cpp/network/web_socket_read_queue.h',
+        'services/public/cpp/network/web_socket_write_queue.cc',
+        'services/public/cpp/network/web_socket_write_queue.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/network
+      'target_name': 'mojo_network_service',
+      'type': 'loadable_module',
+      'dependencies': [
+        'mojo_network_bindings',
+        'mojo_network_service_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'export_dependent_settings': [
+        'mojo_network_bindings',
+      ],
+      'sources': [
+        'services/network/main.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/services/surfaces
+      'target_name': 'mojo_surfaces_service',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc',
+        '../cc/cc.gyp:cc_surfaces',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_cc_support',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_gpu_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'sources': [
+        'services/surfaces/surfaces_impl.cc',
+        'services/surfaces/surfaces_impl.h',
+        'services/surfaces/surfaces_service_application.cc',
+        'services/surfaces/surfaces_service_application.h',
+        'services/surfaces/surfaces_service_impl.cc',
+        'services/surfaces/surfaces_service_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/view_manager:common
+      'target_name': 'mojo_view_manager_common',
+      'type': 'static_library',
+      'sources': [
+        'services/public/cpp/view_manager/types.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/view_manager
+      'target_name': 'mojo_view_manager_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/view_manager/view_manager.mojom',
+        'services/public/interfaces/view_manager/view_manager_constants.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_geometry_bindings',
+        'mojo_input_events_bindings',
+        'mojo_surface_id_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_geometry_bindings',
+        'mojo_input_events_bindings',
+        'mojo_surface_id_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/view_manager
+      'target_name': 'mojo_view_manager_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../cc/cc.gyp:cc_surfaces',
+        '../gpu/gpu.gyp:gpu',
+        '../skia/skia.gyp:skia',
+        '../ui/events/events.gyp:events',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
+        '../third_party/khronos/khronos.gyp:khronos_headers',
+        'mojo_base.gyp:mojo_application_chromium',
+        'mojo_base.gyp:mojo_application_bindings',
+        'mojo_core_window_manager_bindings',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_surfaces_bindings',
+        'mojo_surfaces_lib',
+        'mojo_view_manager_bindings',
+        'mojo_view_manager_common',
+        'mojo_gpu_bindings',
+      ],
+      'includes': [
+        'mojo_public_gles2_for_loadable_module.gypi',
+      ],
+      'sources': [
+        'services/public/cpp/view_manager/lib/bitmap_uploader.cc',
+        'services/public/cpp/view_manager/lib/bitmap_uploader.h',
+        'services/public/cpp/view_manager/lib/view.cc',
+        'services/public/cpp/view_manager/lib/view_manager_client_factory.cc',
+        'services/public/cpp/view_manager/lib/view_manager_client_impl.cc',
+        'services/public/cpp/view_manager/lib/view_manager_client_impl.h',
+        'services/public/cpp/view_manager/lib/view_manager_context.cc',
+        'services/public/cpp/view_manager/lib/view_observer.cc',
+        'services/public/cpp/view_manager/lib/view_private.cc',
+        'services/public/cpp/view_manager/lib/view_private.h',
+        'services/public/cpp/view_manager/view.h',
+        'services/public/cpp/view_manager/view_manager.h',
+        'services/public/cpp/view_manager/view_manager_client_factory.h',
+        'services/public/cpp/view_manager/view_manager_context.h',
+        'services/public/cpp/view_manager/view_manager_delegate.h',
+        'services/public/cpp/view_manager/view_observer.h',
+        'services/public/cpp/view_manager/window_manager_delegate.h',
+      ],
+      'export_dependent_settings': [
+        'mojo_gpu_bindings',
+        'mojo_surfaces_bindings',
+        'mojo_view_manager_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/cpp/view_manager/tests:mojo_view_manager_lib_unittests
+      'target_name': 'mojo_view_manager_lib_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_test_support',
+        'mojo_base.gyp:mojo_environment_chromium',
+        'mojo_geometry_bindings',
+        'mojo_geometry_lib',
+        'mojo_shell_test_support',
+        'mojo_view_manager_bindings',
+        'mojo_view_manager_lib',
+      ],
+      'sources': [
+        'services/public/cpp/view_manager/tests/view_unittest.cc',
+        'services/public/cpp/view_manager/tests/view_manager_unittest.cc',
+      ],
+      'conditions': [
+        ['use_aura==1', {
+          'dependencies': [
+            'mojo_view_manager_run_unittests'
+          ],
+        }, {  # use_aura==0
+          'dependencies': [
+            'mojo_base.gyp:mojo_run_all_unittests',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/surfaces
+      'target_name': 'mojo_surfaces_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/surfaces/surfaces.mojom',
+        'services/public/interfaces/surfaces/surfaces_service.mojom',
+        'services/public/interfaces/surfaces/quads.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+        'mojo_surface_id_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+        'mojo_base.gyp:mojo_gles2_bindings',
+        'mojo_geometry_bindings',
+        'mojo_surface_id_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/surfaces:surface_id
+      'target_name': 'mojo_surface_id_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/surfaces/surface_id.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/test_service:bindings
+      'target_name': 'mojo_test_service_bindings',
+      'type': 'static_library',
+      'sources': [
+         # TODO(tim): Move to services/public/interfaces?
+        'services/test_service/test_request_tracker.mojom',
+        'services/test_service/test_service.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+    {
+      # GN version: //mojo/services/test_service
+      'target_name': 'mojo_test_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_test_service_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'services/test_service/test_request_tracker_client_impl.cc',
+        'services/test_service/test_request_tracker_client_impl.h',
+        'services/test_service/test_service_application.cc',
+        'services/test_service/test_service_application.h',
+        'services/test_service/test_service_impl.cc',
+        'services/test_service/test_service_impl.h',
+        'services/test_service/test_time_service_impl.cc',
+        'services/test_service/test_time_service_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/test_service:request_tracker
+      'target_name': 'mojo_test_request_tracker_app',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_base.gyp:mojo_utility',
+        'mojo_base.gyp:mojo_application_standalone',
+        'mojo_test_service_bindings',
+        '<(mojo_system_for_loadable_module)',
+      ],
+      'sources': [
+        'services/test_service/test_request_tracker_client_impl.cc',
+        'services/test_service/test_request_tracker_client_impl.h',
+        'services/test_service/test_request_tracker_application.cc',
+        'services/test_service/test_request_tracker_application.h',
+        'services/test_service/test_time_service_impl.cc',
+        'services/test_service/test_time_service_impl.h',
+        'services/test_service/test_request_tracker_impl.cc',
+        'services/test_service/test_request_tracker_impl.h',
+      ],
+    },
+    {
+      # GN version: //mojo/services/public/interfaces/window_manager
+      'target_name': 'mojo_core_window_manager_bindings',
+      'type': 'static_library',
+      'sources': [
+        'services/public/interfaces/window_manager/window_manager.mojom',
+      ],
+      'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+      'export_dependent_settings': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+      'dependencies': [
+        'mojo_base.gyp:mojo_cpp_bindings',
+      ],
+    },
+  ],
+  'conditions': [
+    ['use_aura==1', {
+      'targets': [
+        {
+          # GN version: //mojo/services/view_manager
+          'target_name': 'mojo_view_manager',
+          'type': 'loadable_module',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../cc/cc.gyp:cc_surfaces',
+            '../skia/skia.gyp:skia',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/events/events.gyp:events',
+            '../ui/events/events.gyp:events_base',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_gpu_bindings',
+            'mojo_input_events_bindings',
+            'mojo_input_events_lib',
+            'mojo_native_viewport_bindings',
+            'mojo_surfaces_bindings',
+            'mojo_surfaces_lib',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_common',
+            'mojo_gpu_bindings',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'services/view_manager/access_policy.h',
+            'services/view_manager/access_policy_delegate.h',
+            'services/view_manager/connection_manager.cc',
+            'services/view_manager/connection_manager.h',
+            'services/view_manager/default_access_policy.cc',
+            'services/view_manager/default_access_policy.h',
+            'services/view_manager/display_manager.cc',
+            'services/view_manager/display_manager.h',
+            'services/view_manager/ids.h',
+            'services/view_manager/main.cc',
+            'services/view_manager/server_view.cc',
+            'services/view_manager/server_view.h',
+            'services/view_manager/server_view_delegate.h',
+            'services/view_manager/view_manager_export.h',
+            'services/view_manager/view_manager_init_service_context.cc',
+            'services/view_manager/view_manager_init_service_context.h',
+            'services/view_manager/view_manager_init_service_impl.cc',
+            'services/view_manager/view_manager_init_service_impl.h',
+            'services/view_manager/view_manager_service_impl.cc',
+            'services/view_manager/view_manager_service_impl.h',
+            'services/view_manager/window_manager_access_policy.cc',
+            'services/view_manager/window_manager_access_policy.h',
+          ],
+          'includes': [
+            'mojo_public_gles2_for_loadable_module.gypi',
+          ],
+          'defines': [
+            'MOJO_VIEW_MANAGER_IMPLEMENTATION',
+          ],
+        },
+        {
+          # GN version: //mojo/services/public/cpp/view_manager/lib:run_unittests
+          'target_name': 'mojo_view_manager_run_unittests',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:test_support_base',
+          ],
+          'sources': [
+            'services/public/cpp/view_manager/lib/view_manager_test_suite.cc',
+            'services/public/cpp/view_manager/lib/view_manager_test_suite.h',
+            'services/public/cpp/view_manager/lib/view_manager_unittests.cc',
+          ],
+          'conditions': [
+            ['use_x11==1', {
+              'dependencies': [
+                '../ui/gfx/x/gfx_x11.gyp:gfx_x11',
+              ],
+            }],
+          ],
+        },
+        {
+          # GN version: //mojo/services/view_manager:mojo_view_manager_unittests
+          'target_name': 'mojo_view_manager_unittests',
+          'type': 'executable',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:test_support_base',
+            '../skia/skia.gyp:skia',
+            '../testing/gtest.gyp:gtest',
+            '../ui/aura/aura.gyp:aura',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            'mojo_application_manager',
+            'mojo_base.gyp:mojo_system_impl',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_geometry_bindings',
+            'mojo_geometry_lib',
+            'mojo_input_events_bindings',
+            'mojo_input_events_lib',
+            'mojo_shell_test_support',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_common',
+            'mojo_view_manager_run_unittests',
+            # Included only to force deps for bots.
+            'mojo_native_viewport_service',
+            'mojo_surfaces_service',
+            'mojo_view_manager',
+          ],
+          'sources': [
+            'services/view_manager/test_change_tracker.cc',
+            'services/view_manager/test_change_tracker.h',
+            'services/view_manager/view_manager_unittest.cc',
+          ],
+          'conditions': [
+             ['OS=="win"', {
+               'dependencies': [
+                 '../ui/gfx/gfx.gyp:gfx',
+               ],
+             }],
+           ],
+        },
+        {
+          'target_name': 'package_mojo_view_manager',
+          'variables': {
+            'app_name': 'mojo_view_manager',
+          },
+          'includes': [ 'build/package_app.gypi' ],
+        },
+        {
+          # GN version: //mojo/services/window_manager:lib
+          'target_name': 'mojo_core_window_manager_lib',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../ui/base/ui_base.gyp:ui_base',
+            '../ui/gfx/gfx.gyp:gfx',
+            '../ui/gfx/gfx.gyp:gfx_geometry',
+            '../ui/wm/wm.gyp:wm',
+            'mojo_base.gyp:mojo_common_lib',
+            'mojo_base.gyp:mojo_application_chromium',
+            'mojo_aura_support',
+            'mojo_core_window_manager_bindings',
+            'mojo_input_events_lib',
+            'mojo_view_manager_lib',
+          ],
+          'sources': [
+            'services/window_manager/window_manager_app.cc',
+            'services/window_manager/window_manager_app.h',
+            'services/window_manager/window_manager_service_impl.cc',
+            'services/window_manager/window_manager_service_impl.h',
+          ],
+        },
+        {
+          # GN version: //mojo/services/window_manager
+          'target_name': 'mojo_core_window_manager',
+          'type': 'loadable_module',
+          'dependencies': [
+            'mojo_core_window_manager_lib',
+            '<(mojo_system_for_loadable_module)',
+          ],
+          'sources': [
+            'services/window_manager/main.cc',
+          ],
+        },
+        {
+          # GN version: //mojo/services/window_manager:mojo_core_window_manager_unittests
+          'target_name': 'mojo_core_window_manager_unittests',
+          'type': 'executable',
+          'dependencies': [
+            '../base/base.gyp:test_support_base',
+            '../testing/gtest.gyp:gtest',
+            'mojo_application_manager',
+            'mojo_base.gyp:mojo_system_impl',
+            'mojo_base.gyp:mojo_environment_chromium',
+            'mojo_core_window_manager_bindings',
+            'mojo_shell_test_support',
+            'mojo_view_manager_bindings',
+            'mojo_view_manager_lib',
+          ],
+          'sources': [
+            'services/window_manager/window_manager_api_unittest.cc',
+            'services/window_manager/window_manager_unittests.cc',
+          ],
+          'conditions': [
+            ['OS=="linux"', {
+              'dependencies': [
+                '../third_party/mesa/mesa.gyp:osmesa',
+                'mojo_native_viewport_service_lib',
+              ],
+            }],
+            ['use_x11==1', {
+              'dependencies': [
+                '../ui/gfx/x/gfx_x11.gyp:gfx_x11',
+              ],
+            }],
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_variables.gypi b/mojo/mojo_variables.gypi
new file mode 100644
index 0000000..39146d7
--- /dev/null
+++ b/mojo/mojo_variables.gypi
@@ -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.
+
+#
+# A set of GYP variables that are shared between various mojo .gyp files.
+#
+{
+  'variables': {
+    'chromium_code': 1,
+    'mojo_shell_debug_url%': "",
+    'conditions': [
+      #
+      # The following mojo_system-prefixed variables are used to express a
+      # dependency on the mojo system APIs.
+      #
+      # In a component == "shared_library" build, everything can link against
+      # mojo_system_impl because it is built as a shared library. However, in a
+      # component != "shared_library" build, mojo_system_impl is linked into an
+      # executable (e.g., mojo_shell), and must be injected into other shared
+      # libraries (i.e., Mojo Apps) that need the mojo system API.
+      #
+      # For component targets, add <(mojo_system_for_component) to your
+      # dependencies section.  For loadable module targets (e.g., a Mojo App),
+      # add <(mojo_system_for_loadable_module) to your dependencies section.
+      #
+      # NOTE: component != "shared_library" implies that we are generating a
+      # static library, and in that case, it is expected that the target
+      # listing the component as a dependency will specify either mojo_system
+      # or mojo_system_impl to link against. This enables multiple targets to
+      # link against the same component library without having to agree on
+      # which Mojo system library they are using.
+      #
+      ['component=="shared_library"', {
+        'mojo_system_for_component': "<(DEPTH)/mojo/mojo_base.gyp:mojo_system_impl",
+        'mojo_system_for_loadable_module': "<(DEPTH)/mojo/mojo_base.gyp:mojo_system_impl",
+        'mojo_gles2_for_component': "<(DEPTH)/mojo/mojo_base.gyp:mojo_gles2_impl",
+      }, {
+        'mojo_system_for_component': "<(DEPTH)/mojo/mojo_base.gyp:mojo_none",
+        'mojo_system_for_loadable_module': "<(DEPTH)/mojo/mojo_base.gyp:mojo_system",
+        'mojo_gles2_for_component': "<(DEPTH)/mojo/mojo_base.gyp:mojo_none",
+      }],
+    ],
+    'mojo_public_system_unittest_sources': [
+      'public/c/system/tests/core_unittest.cc',
+      'public/c/system/tests/core_unittest_pure_c.c',
+      'public/c/system/tests/macros_unittest.cc',
+      'public/cpp/system/tests/core_unittest.cc',
+      'public/cpp/system/tests/macros_unittest.cc',
+    ],
+  },
+}
diff --git a/mojo/nacl/DEPS b/mojo/nacl/DEPS
new file mode 100644
index 0000000..69e7ada
--- /dev/null
+++ b/mojo/nacl/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+native_client/src/public",
+  "+native_client/src/trusted/desc",
+  "+native_client/src/trusted/service_runtime",
+]
diff --git a/mojo/nacl/README b/mojo/nacl/README
new file mode 100644
index 0000000..36913ac
--- /dev/null
+++ b/mojo/nacl/README
@@ -0,0 +1,24 @@
+This is a prototype for plumbing Mojo into the NaCl sandbox.  It is
+currently insecure (see below), does not provide a stable ABI (IRT
+support must be added), and does not support Mojo functions that
+return pointers (for example, MojoMapBuffer).
+
+generator/interface.py contains a programmatic description of the
+stable Mojo interface.  This will need to be updated as the interface
+changes.  Run generator/generate_nacl_bindings.py to generate the
+bindings that plumb this interface into the NaCl sandbox.
+
+To test: Build "monacl_shell" and "monacl_test".  Run monacl_shell
+with the IRT as the first argument and the monacl_test as the second
+argument.  For example, to run a Debug 32-bit Intel build:
+
+  cd out/Debug
+  ./monacl_shell irt_core_newlib_x32.nexe monacl_test_newlib_x32.nexe
+
+Security TODO list:
+  Separate trusted and untrusted Mojo handles.
+  Validate and copy option structures.
+  Protect untrusted buffers passed into Mojo:
+    NaClVmIoWillStart/HasEnded.
+    volatile accesses to untrusted memory (untrusted code could race).
+  Overflow checking in array bounds validation.
\ No newline at end of file
diff --git a/mojo/nacl/generator/generate_nacl_bindings.py b/mojo/nacl/generator/generate_nacl_bindings.py
new file mode 100755
index 0000000..2c8ae9d
--- /dev/null
+++ b/mojo/nacl/generator/generate_nacl_bindings.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=W0104,W0106,F0401,R0201
+
+import optparse
+import os.path
+import sys
+
+import interface
+
+
+def _ScriptDir():
+  return os.path.dirname(os.path.abspath(__file__))
+
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = _ScriptDir()
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+
+def _AddThirdPartyImportPath():
+  sys.path.append(os.path.join(_GetDirAbove('mojo'), 'third_party'))
+
+
+_AddThirdPartyImportPath()
+import jinja2
+
+loader = jinja2.FileSystemLoader(_ScriptDir())
+jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True)
+
+
+# Accumulate lines of code with varying levels of indentation.
+class CodeWriter(object):
+  def __init__(self):
+    self._lines = []
+    self._margin = ''
+    self._margin_stack = []
+
+  def __lshift__(self, line):
+    self._lines.append((self._margin + line).rstrip())
+
+  def PushMargin(self):
+    self._margin_stack.append(self._margin)
+    self._margin += '  '
+
+  def PopMargin(self):
+    self._margin = self._margin_stack.pop()
+
+  def GetValue(self):
+    return '\n'.join(self._lines).rstrip() + '\n'
+
+  def Indent(self):
+    return Indent(self)
+
+
+# Context handler that automatically indents and dedents a CodeWriter
+class Indent(object):
+  def __init__(self, writer):
+    self._writer = writer
+
+  def __enter__(self):
+    self._writer.PushMargin()
+
+  def __exit__(self, type_, value, traceback):
+    self._writer.PopMargin()
+
+
+def TemplateFile(name):
+  return os.path.join(os.path.dirname(__file__), name)
+
+
+# Wraps comma separated lists as needed.
+def Wrap(pre, items, post):
+  complete = pre + ', '.join(items) + post
+  if len(complete) <= 80:
+    return [complete]
+  lines = [pre]
+  indent = '    '
+  for i, item in enumerate(items):
+    if i < len(items) - 1:
+      lines.append(indent + item + ',')
+    else:
+      lines.append(indent + item + post)
+  return lines
+
+
+def GeneratorWarning():
+  return ('// WARNING this file was generated by %s\n// Do not edit by hand.' %
+          os.path.basename(__file__))
+
+
+# Untrusted library implementing the public Mojo API.
+def GenerateLibMojo(functions, out):
+  template = jinja_env.get_template('libmojo.cc.tmpl')
+
+  code = CodeWriter()
+
+  for f in functions:
+    for line in Wrap('%s %s(' % (f.return_type, f.name), f.ParamList(), ') {'):
+      code << line
+
+    # 2 extra parameters: message ID and return value.
+    num_params = len(f.params) + 2
+
+    with code.Indent():
+      code << 'uint32_t params[%d];' % num_params
+      return_type = f.result_param.base_type
+      if return_type == 'MojoResult':
+        default = 'MOJO_RESULT_INVALID_ARGUMENT'
+      elif return_type == 'MojoTimeTicks':
+        default = '0'
+      else:
+        raise Exception('Unhandled return type: ' + return_type)
+      code << '%s %s = %s;' % (return_type, f.result_param.name, default)
+
+      # Message ID
+      code << 'params[0] = %d;' % f.uid
+      # Parameter pointers
+      cast_template = 'params[%d] = reinterpret_cast<uint32_t>(%s);'
+      for p in f.params:
+        ptr = p.name
+        if p.IsPassedByValue():
+          ptr = '&' + ptr
+        code << cast_template % (p.uid + 1, ptr)
+      # Return value pointer
+      code << cast_template % (num_params - 1, '&' + f.result_param.name)
+
+      code << 'DoMojoCall(params, sizeof(params));'
+      code << 'return %s;' % f.result_param.name
+
+    code << '}'
+    code << ''
+
+  body = code.GetValue()
+  text = template.render(
+    generator_warning=GeneratorWarning(),
+    body=body)
+  out.write(text)
+
+
+# Parameters passed into trusted code are handled differently depending on
+# details of the parameter.  ParamImpl instances encapsulate these differences
+# and are used to generate the code that transfers parameters across the
+# untrusted/trusted boundary.
+class ParamImpl(object):
+  def __init__(self, param):
+    self.param = param
+
+  # Declare whatever variables are needed to handle this particular parameter.
+  def DeclareVars(self, code):
+    raise NotImplementedError()
+
+  # Convert the untrusted representation of the parameter into a trusted
+  # representation, such as a scalar value or a trusted pointer into the
+  # untrusted address space.
+  def ConvertParam(self):
+    raise NotImplementedError()
+
+  # For this particular parameter, what expression should be passed when
+  # invoking the trusted Mojo API function?
+  def CallParam(self):
+    raise NotImplementedError()
+
+  # After invoking the trusted Mojo API function, transfer data back into
+  # untrusted memory.  Overriden for Out and InOut parameters.
+  def CopyOut(self, code):
+    pass
+
+  # Converting array parameters needs to be defered until after the scalar
+  # parameter containing the size of the array has itself been converted.
+  def IsArray(self):
+    return False
+
+
+class ScalarInputImpl(ParamImpl):
+  def DeclareVars(self, code):
+    code << '%s %s_value;' % (self.param.base_type, self.param.name)
+
+  def ConvertParam(self):
+    p = self.param
+    return ('ConvertScalarInput(nap, params[%d], &%s_value)' %
+            (p.uid + 1, p.name))
+
+  def CallParam(self):
+    return '%s_value' % self.param.name
+
+
+class ScalarOutputImpl(ParamImpl):
+  def DeclareVars(self, code):
+    code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
+    code << '%s %s_value;' % (self.param.base_type, self.param.name)
+
+  def ConvertParam(self):
+    p = self.param
+    return 'ConvertScalarOutput(nap, params[%d], &%s_ptr)' % (p.uid + 1, p.name)
+
+  def CallParam(self):
+    return '&%s_value' % self.param.name
+
+  def CopyOut(self, code):
+    name = self.param.name
+    code << '*%s_ptr = %s_value;' % (name, name)
+
+
+class ScalarInOutImpl(ParamImpl):
+  def DeclareVars(self, code):
+    code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
+    code << '%s %s_value;' % (self.param.base_type, self.param.name)
+
+  def ConvertParam(self):
+    p = self.param
+    return ('ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
+            (p.uid + 1, CBool(p.is_optional), p.name, p.name))
+
+  def CallParam(self):
+    name = self.param.name
+    expr = '&%s_value' % name
+    if self.param.is_optional:
+      expr = '%s_ptr ? %s : NULL' % (name, expr)
+    return expr
+
+  def CopyOut(self, code):
+    name = self.param.name
+    if self.param.is_optional:
+      code << 'if (%s_ptr != NULL) {' % (name)
+      with code.Indent():
+        code << '*%s_ptr = %s_value;' % (name, name)
+      code << '}'
+    else:
+      code << '*%s_ptr = %s_value;' % (name, name)
+
+
+class ArrayImpl(ParamImpl):
+  def DeclareVars(self, code):
+    code << '%s %s;' % (self.param.param_type, self.param.name)
+
+  def ConvertParam(self):
+    p = self.param
+    if p.base_type == 'void':
+      element_size = '1'
+    else:
+      element_size = 'sizeof(*%s)' % p.name
+
+    return ('ConvertArray(nap, params[%d], %s, %s, %s, &%s)' %
+            (p.uid + 1, p.size + '_value', element_size, CBool(p.is_optional),
+             p.name))
+
+  def CallParam(self):
+    return self.param.name
+
+  def IsArray(self):
+    return True
+
+
+class StructInputImpl(ParamImpl):
+  def DeclareVars(self, code):
+    code << '%s %s;' % (self.param.param_type, self.param.name)
+
+  def ConvertParam(self):
+    p = self.param
+    return ('ConvertStruct(nap, params[%d], %s, &%s)' %
+            (p.uid + 1, CBool(p.is_optional), p.name))
+
+  def CallParam(self):
+    return self.param.name
+
+
+def ImplForParam(p):
+  if p.IsScalar():
+    if p.is_output:
+      if p.is_input:
+        return ScalarInOutImpl(p)
+      else:
+        return ScalarOutputImpl(p)
+    else:
+      return ScalarInputImpl(p)
+  elif p.is_array:
+    return ArrayImpl(p)
+  elif p.is_struct:
+    return StructInputImpl(p)
+  else:
+    assert False, p
+
+
+def CBool(value):
+  return 'true' if value else 'false'
+
+
+# A trusted wrapper that validates the arguments passed from untrusted code
+# before passing them to the underlying public Mojo API.
+def GenerateMojoSyscall(functions, out):
+  template = jinja_env.get_template('mojo_syscall.cc.tmpl')
+
+  code = CodeWriter()
+  code.PushMargin()
+
+  for f in functions:
+    impls = [ImplForParam(p) for p in f.params]
+    impls.append(ImplForParam(f.result_param))
+
+    code << 'case %d:' % f.uid
+
+    code.PushMargin()
+
+    code << '{'
+
+    with code.Indent():
+      num_params = len(f.params) + 2
+      code << 'if (num_params != %d) {' % num_params
+      with code.Indent():
+        code << 'return -1;'
+      code << '}'
+
+      # Declare temporaries.
+      for impl in impls:
+        impl.DeclareVars(code)
+
+      def ConvertParam(code, impl):
+        code << 'if (!%s) {' % impl.ConvertParam()
+        with code.Indent():
+          code << 'return -1;'
+        code << '}'
+
+      code << '{'
+      with code.Indent():
+        code << 'ScopedCopyLock copy_lock(nap);'
+        # Convert and validate pointers in two passes.
+        # Arrays cannot be validated until the size parameter has been
+        # converted.
+        for impl in impls:
+          if not impl.IsArray():
+            ConvertParam(code, impl)
+        for impl in impls:
+          if impl.IsArray():
+            ConvertParam(code, impl)
+      code << '}'
+      code << ''
+
+      # Call
+      getParams = [impl.CallParam() for impl in impls[:-1]]
+      code << 'result_value = %s(%s);' % (f.name, ', '.join(getParams))
+      code << ''
+
+      # Write outputs
+      code << '{'
+      with code.Indent():
+        code << 'ScopedCopyLock copy_lock(nap);'
+        for impl in impls:
+          impl.CopyOut(code)
+      code << '}'
+      code << ''
+
+      code << 'return 0;'
+    code << '}'
+
+    code.PopMargin()
+
+  body = code.GetValue()
+  text = template.render(
+    generator_warning=GeneratorWarning(),
+    body=body)
+  out.write(text)
+
+
+def OutFile(dir_path, name):
+  if not os.path.exists(dir_path):
+    os.makedirs(dir_path)
+  return open(os.path.join(dir_path, name), 'w')
+
+
+def main(args):
+  usage = 'usage: %prog [options]'
+  parser = optparse.OptionParser(usage=usage)
+  parser.add_option(
+      '-d',
+      dest='out_dir',
+      metavar='DIR',
+      help='output generated code into directory DIR')
+  options, args = parser.parse_args(args=args)
+  if not options.out_dir:
+    parser.error('-d is required')
+  if args:
+    parser.error('unexpected positional arguments: %s' % ' '.join(args))
+
+  mojo = interface.MakeInterface()
+
+  out = OutFile(options.out_dir, 'libmojo.cc')
+  GenerateLibMojo(mojo.functions, out)
+
+  out = OutFile(options.out_dir, 'mojo_syscall.cc')
+  GenerateMojoSyscall(mojo.functions, out)
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/mojo/nacl/generator/interface.py b/mojo/nacl/generator/interface.py
new file mode 100644
index 0000000..2a23fd7
--- /dev/null
+++ b/mojo/nacl/generator/interface.py
@@ -0,0 +1,108 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=F0401
+
+import interface_dsl
+
+def MakeInterface():
+  mojo = interface_dsl.Interface()
+
+  f = mojo.Func('MojoCreateSharedBuffer', 'MojoResult')
+  f.Param('options').InStruct('MojoCreateSharedBufferOptions').Optional()
+  f.Param('num_bytes').In('uint64_t')
+  f.Param('shared_buffer_handle').Out('MojoHandle')
+
+  f = mojo.Func('MojoDuplicateBufferHandle', 'MojoResult')
+  f.Param('buffer_handle').In('MojoHandle')
+  f.Param('options').InStruct('MojoDuplicateBufferHandleOptions').Optional()
+  f.Param('new_buffer_handle').Out('MojoHandle')
+
+  f = mojo.Func('MojoMapBuffer', 'MojoResult')
+  f.Param('buffer_handle').In('MojoHandle')
+  f.Param('offset').In('uint64_t')
+  f.Param('num_bytes').In('uint64_t')
+  f.Param('buffer').Out('void*')
+  f.Param('flags').In('MojoMapBufferFlags')
+
+  f = mojo.Func('MojoUnmapBuffer', 'MojoResult')
+  f.Param('buffer').In('void*')
+
+  f = mojo.Func('MojoCreateDataPipe', 'MojoResult')
+  f.Param('options').InStruct('MojoCreateDataPipeOptions').Optional()
+  f.Param('data_pipe_producer_handle').Out('MojoHandle')
+  f.Param('data_pipe_consumer_handle').Out('MojoHandle')
+
+  f = mojo.Func('MojoWriteData', 'MojoResult')
+  f.Param('data_pipe_producer_handle').In('MojoHandle')
+  f.Param('elements').InArray('void', 'num_bytes')
+  f.Param('num_bytes').InOut('uint32_t')
+  f.Param('flags').In('MojoWriteDataFlags')
+
+  f = mojo.Func('MojoBeginWriteData', 'MojoResult')
+  f.Param('data_pipe_producer_handle').In('MojoHandle')
+  f.Param('buffer').Out('void*')
+  f.Param('buffer_num_bytes').InOut('uint32_t')
+  f.Param('flags').In('MojoWriteDataFlags')
+
+  f = mojo.Func('MojoEndWriteData', 'MojoResult')
+  f.Param('data_pipe_producer_handle').In('MojoHandle')
+  f.Param('num_bytes_written').In('uint32_t')
+
+  f = mojo.Func('MojoReadData', 'MojoResult')
+  f.Param('data_pipe_consumer_handle').In('MojoHandle')
+  f.Param('elements').OutArray('void', 'num_bytes')
+  f.Param('num_bytes').InOut('uint32_t')
+  f.Param('flags').In('MojoReadDataFlags')
+
+  f = mojo.Func('MojoBeginReadData', 'MojoResult')
+  f.Param('data_pipe_consumer_handle').In('MojoHandle')
+  f.Param('buffer').Out('const void*')
+  f.Param('buffer_num_bytes').InOut('uint32_t')
+  f.Param('flags').In('MojoReadDataFlags')
+
+  f = mojo.Func('MojoEndReadData', 'MojoResult')
+  f.Param('data_pipe_consumer_handle').In('MojoHandle')
+  f.Param('num_bytes_read').In('uint32_t')
+
+  f = mojo.Func('MojoGetTimeTicksNow', 'MojoTimeTicks')
+
+  f = mojo.Func('MojoClose', 'MojoResult')
+  f.Param('handle').In('MojoHandle')
+
+  f = mojo.Func('MojoWait', 'MojoResult')
+  f.Param('handle').In('MojoHandle')
+  f.Param('signals').In('MojoHandleSignals')
+  f.Param('deadline').In('MojoDeadline')
+
+  f = mojo.Func('MojoWaitMany', 'MojoResult')
+  f.Param('handles').InArray('MojoHandle', 'num_handles')
+  f.Param('signals').InArray('MojoHandleSignals', 'num_handles')
+  f.Param('num_handles').In('uint32_t')
+  f.Param('deadline').In('MojoDeadline')
+
+  f = mojo.Func('MojoCreateMessagePipe', 'MojoResult')
+  f.Param('options').InStruct('MojoCreateMessagePipeOptions').Optional()
+  f.Param('message_pipe_handle0').Out('MojoHandle')
+  f.Param('message_pipe_handle1').Out('MojoHandle')
+
+  f = mojo.Func('MojoWriteMessage', 'MojoResult')
+  f.Param('message_pipe_handle').In('MojoHandle')
+  f.Param('bytes').InArray('void', 'num_bytes').Optional()
+  f.Param('num_bytes').In('uint32_t')
+  f.Param('handles').InArray('MojoHandle', 'num_handles').Optional()
+  f.Param('num_handles').In('uint32_t')
+  f.Param('flags').In('MojoWriteMessageFlags')
+
+  f = mojo.Func('MojoReadMessage', 'MojoResult')
+  f.Param('message_pipe_handle').In('MojoHandle')
+  f.Param('bytes').OutArray('void', 'num_bytes').Optional()
+  f.Param('num_bytes').InOut('uint32_t').Optional()
+  f.Param('handles').OutArray('MojoHandle', 'num_handles').Optional()
+  f.Param('num_handles').InOut('uint32_t').Optional()
+  f.Param('flags').In('MojoReadMessageFlags')
+
+  mojo.Finalize()
+
+  return mojo
diff --git a/mojo/nacl/generator/interface_dsl.py b/mojo/nacl/generator/interface_dsl.py
new file mode 100644
index 0000000..dfc85aa
--- /dev/null
+++ b/mojo/nacl/generator/interface_dsl.py
@@ -0,0 +1,116 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class Interface(object):
+  def __init__(self):
+    self.functions = []
+
+  def Func(self, name, return_type):
+    f = Function(self, len(self.functions), name, return_type)
+    self.functions.append(f)
+    return f
+
+  def Finalize(self):
+    for f in self.functions:
+      f.Finalize()
+
+class Function(object):
+  def __init__(self, parent, uid, name, return_type):
+    self.parent = parent
+    self.uid = uid
+    self.name = name
+    self.return_type = return_type
+    self.params = []
+    self.param_by_name = {}
+    self.result_param = None
+
+  def Param(self, name, param_type=None):
+    p = Param(self, len(self.params), name, param_type)
+    self.params.append(p)
+    self.param_by_name[name] = p
+    return p
+
+  def ParamList(self):
+    return [param.param_type + ' ' + param.name for param in self.params]
+
+  def ParamDecl(self):
+    if self.params:
+      return ', '.join(self.ParamList())
+    else:
+      return 'void'
+
+  def Finalize(self):
+    self.result_param = Param(self, len(self.params), 'result')
+    self.result_param.Out(self.return_type)
+
+class Param(object):
+  def __init__(self, parent, uid, name, param_type=None):
+    self.parent = parent
+    self.uid = uid
+    self.name = name
+    self.base_type = param_type
+    self.param_type = param_type
+    self.size = None
+    self.is_input = False
+    self.is_output = False
+    self.is_array = False
+    self.is_struct = False
+    self.is_optional = False
+
+  def GetSizeParam(self):
+    assert self.size
+    return self.parent.param_by_name[self.size]
+
+  def In(self, ty):
+    self.base_type = ty
+    self.param_type = ty
+    self.is_input = True
+    return self
+
+  def InArray(self, ty, size):
+    self.base_type = ty
+    self.param_type = 'const ' + ty + '*'
+    self.size = size
+    self.is_input = True
+    self.is_array = True
+    return self
+
+  def InStruct(self, ty):
+    self.base_type = ty
+    self.param_type = 'const struct ' + ty + '*'
+    self.is_input = True
+    self.is_struct = True
+    return self
+
+  def InOut(self, ty):
+    self.base_type = ty
+    self.param_type = ty + '*'
+    self.is_input = True
+    self.is_output = True
+    return self
+
+  def Out(self, ty):
+    self.base_type = ty
+    self.param_type = ty + '*'
+    self.is_output = True
+    return self
+
+  def OutArray(self, ty, size):
+    self.base_type = ty
+    self.param_type = ty + '*'
+    self.size = size
+    self.is_array = True
+    self.is_output = True
+    return self
+
+  def Optional(self):
+    assert not self.IsPassedByValue()
+    self.is_optional = True
+    return self
+
+  def IsScalar(self):
+    return not self.is_array and not self.is_struct
+
+  def IsPassedByValue(self):
+    return not self.is_output and self.IsScalar()
diff --git a/mojo/nacl/generator/libmojo.cc.tmpl b/mojo/nacl/generator/libmojo.cc.tmpl
new file mode 100644
index 0000000..7690d82
--- /dev/null
+++ b/mojo/nacl/generator/libmojo.cc.tmpl
@@ -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.
+
+{{generator_warning}}
+
+#include "mojo/public/c/system/core.h"
+#include "native_client/src/public/chrome_main.h"
+#include "native_client/src/public/imc_syscalls.h"
+#include "native_client/src/public/imc_types.h"
+
+#define NACL_MOJO_DESC (NACL_CHROME_DESC_BASE + 2)
+
+static void DoMojoCall(uint32_t params[], nacl_abi_size_t num_params) {
+  NaClAbiNaClImcMsgIoVec iov[1] = {
+    {params, num_params}
+  };
+  NaClAbiNaClImcMsgHdr msgh = {iov, 1, NULL, 0};
+  // Note: return value unchecked.  We're relying on the result parameter being
+  // unmodified - if the syscall fails, the Mojo function will return whatever
+  // the result parameter was initialized to before this function was called.
+  imc_sendmsg(NACL_MOJO_DESC, &msgh, 0);
+}
+
+{{body}}
diff --git a/mojo/nacl/generator/mojo_syscall.cc.tmpl b/mojo/nacl/generator/mojo_syscall.cc.tmpl
new file mode 100644
index 0000000..136e672
--- /dev/null
+++ b/mojo/nacl/generator/mojo_syscall.cc.tmpl
@@ -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.
+
+{{generator_warning}}
+
+#include "mojo/nacl/mojo_syscall.h"
+
+#include <stdio.h>
+
+#include "mojo/nacl/mojo_syscall_internal.h"
+#include "mojo/public/c/system/core.h"
+#include "native_client/src/public/chrome_main.h"
+#include "native_client/src/public/nacl_app.h"
+#include "native_client/src/trusted/desc/nacl_desc_custom.h"
+
+namespace {
+
+void MojoDescDestroy(void* handle) {
+}
+
+ssize_t MojoDescSendMsg(void* handle,
+                        const struct NaClImcTypedMsgHdr* msg,
+                        int flags) {
+  struct NaClApp* nap = static_cast<struct NaClApp*>(handle);
+
+  if (msg->iov_length != 1 || msg->ndesc_length != 0) {
+    return -1;
+  }
+
+  uint32_t volatile* params = static_cast<uint32_t volatile*>(msg->iov[0].base);
+  size_t num_params = msg->iov[0].length / sizeof(*params);
+
+  if (num_params < 1) {
+    return -1;
+  }
+
+  uint32_t msg_type = params[0];
+  switch (msg_type) {
+{{body}}
+  }
+
+  return -1;
+}
+
+ssize_t MojoDescRecvMsg(void* handle,
+                        struct NaClImcTypedMsgHdr* msg,
+                        int flags) {
+  return -1;
+}
+
+struct NaClDesc* MakeMojoDesc(struct NaClApp* nap) {
+  struct NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
+  funcs.Destroy = MojoDescDestroy;
+  funcs.SendMsg = MojoDescSendMsg;
+  funcs.RecvMsg = MojoDescRecvMsg;
+  return NaClDescMakeCustomDesc(nap, &funcs);
+}
+
+} // namespace
+
+#define NACL_MOJO_DESC (NACL_CHROME_DESC_BASE + 2)
+
+void InjectMojo(struct NaClApp* nap) {
+  NaClAppSetDesc(nap, NACL_MOJO_DESC, MakeMojoDesc(nap));
+}
diff --git a/mojo/nacl/mojo_syscall.h b/mojo/nacl/mojo_syscall.h
new file mode 100644
index 0000000..f195769
--- /dev/null
+++ b/mojo/nacl/mojo_syscall.h
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium 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_NACL_MOJO_SYSCALL_H_
+#define MOJO_NACL_MOJO_SYSCALL_H_
+
+void InjectMojo(struct NaClApp* nap);
+
+#endif // MOJO_NACL_MOJO_SYSCALL_H_
diff --git a/mojo/nacl/mojo_syscall_internal.h b/mojo/nacl/mojo_syscall_internal.h
new file mode 100644
index 0000000..f0795e6
--- /dev/null
+++ b/mojo/nacl/mojo_syscall_internal.h
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium 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_NACL_MOJO_SYSCALL_INTERNAL_H_
+#define MOJO_NACL_MOJO_SYSCALL_INTERNAL_H_
+
+#include "native_client/src/trusted/service_runtime/nacl_copy.h"
+#include "native_client/src/trusted/service_runtime/sel_ldr.h"
+
+namespace {
+
+class ScopedCopyLock {
+ public:
+  explicit ScopedCopyLock(struct NaClApp* nap) : nap_(nap) {
+    NaClCopyTakeLock(nap_);
+  }
+  ~ScopedCopyLock() {
+    NaClCopyDropLock(nap_);
+  }
+ private:
+  struct NaClApp* nap_;
+};
+
+static inline uintptr_t NaClUserToSysAddrArray(
+    struct NaClApp* nap,
+    uint32_t uaddr,
+    size_t count,
+    size_t size) {
+  // TODO(ncbray): overflow checking
+  size_t range = count * size;
+  return NaClUserToSysAddrRange(nap, uaddr, range);
+}
+
+template <typename T> bool ConvertScalarInput(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    T* value) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T));
+    if (temp != kNaClBadAddress) {
+      *value = *reinterpret_cast<T volatile*>(temp);
+      return true;
+    }
+  }
+  return false;
+}
+
+template <typename T> bool ConvertScalarOutput(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    T volatile** sys_ptr) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T));
+    if (temp != kNaClBadAddress) {
+      *sys_ptr = reinterpret_cast<T volatile*>(temp);
+      return true;
+    }
+  }
+  *sys_ptr = 0; // Paranoia.
+  return false;
+}
+
+template <typename T> bool ConvertScalarInOut(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    bool optional,
+    T* value,
+    T volatile** sys_ptr) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T));
+    if (temp != kNaClBadAddress) {
+      T volatile* converted = reinterpret_cast<T volatile*>(temp);
+      *sys_ptr = converted;
+      *value = *converted;
+      return true;
+    }
+  } else if (optional) {
+    *sys_ptr = 0;
+    *value = static_cast<T>(0); // Paranoia.
+    return true;
+  }
+  *sys_ptr = 0; // Paranoia.
+  *value = static_cast<T>(0); // Paranoia.
+  return false;
+}
+
+template <typename T> bool ConvertArray(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    uint32_t length,
+    size_t element_size,
+    bool optional,
+    T** sys_ptr) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrArray(nap, user_ptr, length,
+                                            element_size);
+    if (temp != kNaClBadAddress) {
+      *sys_ptr = reinterpret_cast<T*>(temp);
+      return true;
+    }
+  } else if (optional) {
+    *sys_ptr = 0;
+    return true;
+  }
+  return false;
+}
+
+template <typename T> bool ConvertBytes(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    uint32_t length,
+    bool optional,
+    T** sys_ptr) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, length);
+    if (temp != kNaClBadAddress) {
+      *sys_ptr = reinterpret_cast<T*>(temp);
+      return true;
+    }
+  } else if (optional) {
+    *sys_ptr = 0;
+    return true;
+  }
+  return false;
+}
+
+// TODO(ncbray): size validation and complete copy.
+// TODO(ncbray): ensure non-null / missized structs are covered by a test case.
+template <typename T> bool ConvertStruct(
+    struct NaClApp* nap,
+    uint32_t user_ptr,
+    bool optional,
+    T** sys_ptr) {
+  if (user_ptr) {
+    uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T));
+    if (temp != kNaClBadAddress) {
+      *sys_ptr = reinterpret_cast<T*>(temp);
+      return true;
+    }
+  } else if (optional) {
+    *sys_ptr = 0;
+    return true;
+  }
+  return false;
+}
+
+} // namespace
+
+#endif // MOJO_NACL_MOJO_SYSCALL_INTERNAL_H_
diff --git a/mojo/nacl/monacl_sel_main.cc b/mojo/nacl/monacl_sel_main.cc
new file mode 100644
index 0000000..4fbc575
--- /dev/null
+++ b/mojo/nacl/monacl_sel_main.cc
@@ -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.
+
+#include "mojo/nacl/monacl_sel_main.h"
+
+#include <stdio.h>
+
+#include "mojo/nacl/mojo_syscall.h"
+#include "native_client/src/public/chrome_main.h"
+#include "native_client/src/public/nacl_app.h"
+#include "native_client/src/trusted/desc/nacl_desc_io.h"
+#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
+
+namespace mojo {
+
+void LaunchNaCl(const char* nexe_file, const char* irt_file,
+                int app_argc, char* app_argv[]) {
+  NaClChromeMainInit();
+
+  // Open the IRT.
+  struct NaClDesc* irt_desc = (struct NaClDesc*) NaClDescIoDescOpen(
+      irt_file, NACL_ABI_O_RDONLY, 0);
+  if (NULL == irt_desc) {
+    perror(irt_file);
+    exit(1);
+  }
+
+  // Open the main executable.
+  struct NaClDesc* nexe_desc = (struct NaClDesc*) NaClDescIoDescOpen(
+      nexe_file, NACL_ABI_O_RDONLY, 0);
+  if (NULL == nexe_desc) {
+    perror(nexe_file);
+    exit(1);
+  }
+
+  struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
+  args->nexe_desc = nexe_desc;
+  args->irt_desc = irt_desc;
+
+  args->argc = app_argc;
+  args->argv = app_argv;
+
+  struct NaClApp* nap = NaClAppCreate();
+  InjectMojo(nap);
+
+  int exit_status = 1;
+  NaClChromeMainStart(nap, args, &exit_status);
+  NaClExit(exit_status);
+}
+
+} // namespace mojo
diff --git a/mojo/nacl/monacl_sel_main.h b/mojo/nacl/monacl_sel_main.h
new file mode 100644
index 0000000..f543d16
--- /dev/null
+++ b/mojo/nacl/monacl_sel_main.h
@@ -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.
+
+#ifndef MOJO_NACL_MONACL_SEL_MAIN_H_
+#define MOJO_NACL_MONACL_SEL_MAIN_H_
+
+namespace mojo {
+
+void LaunchNaCl(const char* nexe_file, const char* irt_file,
+                int app_argc, char* app_argv[]);
+
+} // namespace mojo
+
+#endif // MOJO_NACL_MONACL_SEL_MAIN_H_
diff --git a/mojo/nacl/monacl_shell.cc b/mojo/nacl/monacl_shell.cc
new file mode 100644
index 0000000..7922d9a
--- /dev/null
+++ b/mojo/nacl/monacl_shell.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 <iostream>
+
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/nacl/monacl_sel_main.h"
+
+
+int main(int argc, char* argv[]) {
+  if (argc < 3) {
+    std::cout << "Usage: " << argv[0] << " irt.nexe app.nexe [args for app]" <<
+        std::endl;
+    return 1;
+  }
+
+  const char* irt_file = argv[1];
+  const char* nexe_file = argv[2];
+
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+
+  // Does not return.
+  mojo::LaunchNaCl(nexe_file, irt_file, argc - 2, argv + 2);
+  return 1;
+}
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
new file mode 100644
index 0000000..1874a6c
--- /dev/null
+++ b/mojo/public/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.
+
+group("public") {
+  # Meta-target, don't link into production code.
+  testonly = true
+  deps = [
+    ":libmojo_sdk",
+    ":sdk",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+  ]
+
+  if (is_linux) {
+    deps += [
+      "//mojo/public/python",
+    ]
+  }
+
+  if (is_android) {
+    deps += [
+      "//mojo/public/java:system",
+      "//mojo/public/java:bindings",
+    ]
+  }
+}
+
+group("sdk") {
+  deps = [
+    "//mojo/public/c/system",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/application",
+    "//mojo/public/js/bindings",
+  ]
+}
+
+static_library("libmojo_sdk") {
+  complete_static_lib = true
+  deps = [ ":sdk" ]
+}
diff --git a/mojo/public/DEPS b/mojo/public/DEPS
new file mode 100644
index 0000000..0c679b9
--- /dev/null
+++ b/mojo/public/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "-base",
+  "-build",
+  "-mojo",
+  "+mojo/public",
+]
diff --git a/mojo/public/README.md b/mojo/public/README.md
new file mode 100644
index 0000000..a31a8a8
--- /dev/null
+++ b/mojo/public/README.md
@@ -0,0 +1,43 @@
+Mojo Public API
+===============
+
+The Mojo Public API is a binary stable API to the Mojo system.
+
+It consists of support for a number of programming languages (with a directory
+for each support language), some "build" tools and build-time requirements, and
+interface definitions for Mojo services (specified using an IDL).
+
+Note that there are various subdirectories named tests/. These contain tests of
+the code in the enclosing directory, and are not meant for use by Mojo
+applications.
+
+C/CPP/JS
+--------
+
+The c/, cpp/, js/ subdirectories define the API for C, C++, and JavaScript,
+respectively.
+
+The basic principle for these directories is that they consist of the source
+files that one needs at build/deployment/run time (as appropriate for the
+language), organized in a natural way for the particular language.
+
+Interfaces
+----------
+
+The interfaces/ subdirectory contains Mojo IDL (a.k.a. .mojom) descriptions of
+standard Mojo services.
+
+Platform
+--------
+
+The platform/ subdirectory contains any build-time requirements (e.g., static
+libraries) that may be needed to produce a Mojo application for certain
+platforms, such as a native shared library or as a NaCl binary.
+
+Tools
+-----
+
+The tools/ subdirectory contains tools that are useful/necessary at
+build/deployment time. These tools may be needed (as a practical necessity) to
+use the API in any given language, e.g., to generate bindings from Mojo IDL
+files.
diff --git a/mojo/public/c/DEPS b/mojo/public/c/DEPS
new file mode 100644
index 0000000..5272770
--- /dev/null
+++ b/mojo/public/c/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+  # Require explicit dependencies in each directory.
+  "-mojo/public",
+  # But everyone can depend on the C system headers.
+  "+mojo/public/c/system",
+]
+
+specific_include_rules = {
+  r".*_(unit|perf)test\.cc": [
+    "+testing",
+    # Our test harness is C++, so allow the use of C++:
+    "+mojo/public/cpp/system",
+    "+mojo/public/cpp/test_support",
+    "+mojo/public/cpp/utility",
+  ],
+}
diff --git a/mojo/public/c/PRESUBMIT.py b/mojo/public/c/PRESUBMIT.py
new file mode 100644
index 0000000..4cba433
--- /dev/null
+++ b/mojo/public/c/PRESUBMIT.py
@@ -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.
+
+"""Presubmit script for mojo/public/c.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
+                                                              output_api)
+  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
+  return results
diff --git a/mojo/public/c/README.md b/mojo/public/c/README.md
new file mode 100644
index 0000000..8e11545
--- /dev/null
+++ b/mojo/public/c/README.md
@@ -0,0 +1,45 @@
+Mojo Public C API
+=================
+
+This directory contains C language bindings for the Mojo Public API.
+
+Environment
+-----------
+
+The environment/ subdirectory defines some common things that, while not part of
+the system API, may be required for GLES2 (for example). These are things that a
+Mojo application may be required to provide to the GLES2 (for example) library
+in order to use it. (However, the Mojo application may implement these things as
+it sees fit.)
+
+GLES2
+-----
+
+The gles2/ subdirectory defines the GLES2 C API that's available to Mojo
+applications. To use GLES2, Mojo applications must link against a dynamic
+library (the exact mechanism being platform-dependent) and use the header files
+in this directory as well as the standard Khronos GLES2 header files.
+
+The reason for this, rather than providing GLES2 using the standard Mojo IPC
+mechanism, is performance: The protocol (and transport mechanisms) used to
+communicate with the Mojo GLES2 service is not stable nor "public" (mainly for
+performance reasons), and using the dynamic library shields the application from
+changes to the underlying system.
+
+System
+------
+
+The system/ subdirectory provides definitions of the basic low-level API used by
+all Mojo applications (whether directly or indirectly). These consist primarily
+of the IPC primitives used to communicate with Mojo services.
+
+Though the message protocol is stable, the implementation of the transport is
+not, and access to the IPC mechanisms must be via the primitives defined in this
+directory.
+
+Test Support
+------------
+
+This directory contains a C API for running tests. This API is only available
+under special, specific test conditions. It is not meant for general use by Mojo
+applications.
diff --git a/mojo/public/c/environment/BUILD.gn b/mojo/public/c/environment/BUILD.gn
new file mode 100644
index 0000000..669da7d
--- /dev/null
+++ b/mojo/public/c/environment/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.
+
+source_set("environment") {
+  sources = [
+    "async_waiter.h",
+    "logger.h",
+  ]
+
+  deps = [ "//mojo/public/c/system" ]
+}
diff --git a/mojo/public/c/environment/async_waiter.h b/mojo/public/c/environment/async_waiter.h
new file mode 100644
index 0000000..b5a1f75
--- /dev/null
+++ b/mojo/public/c/environment/async_waiter.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_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
+
+#include "mojo/public/c/system/types.h"
+
+typedef uintptr_t MojoAsyncWaitID;
+
+typedef void (*MojoAsyncWaitCallback)(void* closure, MojoResult result);
+
+// Functions for asynchronously waiting (and cancelling asynchronous waits) on a
+// handle.
+//
+// Thread-safety:
+//   - |CancelWait(wait_id)| may only be called on the same thread as the
+//     |AsyncWait()| that provided |wait_id| was called on.
+//   - A given |MojoAsyncWaiter|'s functions may only be called on the thread(s)
+//     that it is defined to be valid on (typically including the thread on
+//     which the |MojoAsyncWaiter| was provided). E.g., a library may require
+//     initialization with a single |MojoAsyncWaiter| and stipulate that it only
+//     be used on threads on which that |MojoAsyncWaiter| is valid.
+//   - If a |MojoAsyncWaiter| is valid on multiple threads, then its functions
+//     must be thread-safe (subject to the first restriction above).
+struct MojoAsyncWaiter {
+  // Arranges for |callback| to be called on the current thread at some future
+  // when |handle| satisfies |signals| or it is known that it will never satisfy
+  // |signals| (with the same behavior as |MojoWait()|).
+  //
+  // |callback| will not be called in the nested context of |AsyncWait()|, but
+  // only, e.g., from some run loop. |callback| is provided with the |closure|
+  // argument as well as the result of the wait. For each call to |AsyncWait()|,
+  // |callback| will be called at most once.
+  //
+  // |handle| must not be closed or transferred (via |MojoWriteMessage()|; this
+  // is equivalent to closing the handle) until either the callback has been
+  // executed or the async wait has been cancelled using the returned (nonzero)
+  // |MojoAsyncWaitID| (see |CancelWait()|). Otherwise, an invalid (or, worse,
+  // re-used) handle may be waited on by the implementation of this
+  // |MojoAsyncWaiter|.
+  //
+  // Note that once the callback has been called, the returned |MojoAsyncWaitID|
+  // becomes invalid.
+  MojoAsyncWaitID (*AsyncWait)(MojoHandle handle,
+                               MojoHandleSignals signals,
+                               MojoDeadline deadline,
+                               MojoAsyncWaitCallback callback,
+                               void* closure);
+
+  // Cancels an outstanding async wait (specified by |wait_id|) initiated by
+  // |AsyncWait()|. This may only be called from the same thread on which the
+  // corresponding |AsyncWait()| was called, and may only be called if the
+  // callback to |AsyncWait()| has not been called.
+  //
+  // Once this has been called, the callback provided to |AsyncWait()| will not
+  // be called. Moreover, it is then immediately safe to close or transfer the
+  // handle provided to |AsyncWait()|. (I.e., the implementation of this
+  // |MojoAsyncWaiter| will no longer wait on, or do anything else with, the
+  // handle.)
+  void (*CancelWait)(MojoAsyncWaitID wait_id);
+};
+
+#endif  // MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
diff --git a/mojo/public/c/environment/logger.h b/mojo/public/c/environment/logger.h
new file mode 100644
index 0000000..c492a66
--- /dev/null
+++ b/mojo/public/c/environment/logger.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_PUBLIC_C_ENVIRONMENT_LOGGER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
+
+#include <stdint.h>
+
+// |MojoLogLevel|: Used to specify the type of log message. Values are ordered
+// by severity (i.e., higher numerical values are more severe).
+
+typedef int32_t MojoLogLevel;
+
+#ifdef __cplusplus
+const MojoLogLevel MOJO_LOG_LEVEL_VERBOSE = -1;
+const MojoLogLevel MOJO_LOG_LEVEL_INFO = 0;
+const MojoLogLevel MOJO_LOG_LEVEL_WARNING = 1;
+const MojoLogLevel MOJO_LOG_LEVEL_ERROR = 2;
+const MojoLogLevel MOJO_LOG_LEVEL_FATAL = 3;
+#else
+#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) - 1)
+#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel)0)
+#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel)1)
+#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel)2)
+#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel)3)
+#endif
+
+// Structure with basic logging functions (on top of which more friendly logging
+// macros may be built). The functions are thread-safe, except for
+// |SetMinimumLogLevel()| (see below).
+struct MojoLogger {
+  // Logs |message| at level |log_level| if |log_level| is at least the current
+  // minimum log level. If |log_level| is |MOJO_LOG_LEVEL_FATAL| (or greater),
+  // aborts the application/process.
+  void (*LogMessage)(MojoLogLevel log_level, const char* message);
+
+  // Gets the minimum log level (see above), which will always be at most
+  // |MOJO_LOG_LEVEL_FATAL|. (Though |LogMessage()| will automatically avoid
+  // logging messages below the minimum log level, this may be used to avoid
+  // extra work.)
+  MojoLogLevel (*GetMinimumLogLevel)(void);
+
+  // Sets the minimum log level (see above) to the lesser of |minimum_log_level|
+  // and |MOJO_LOG_LEVEL_FATAL|.
+  //
+  // Warning: This function may not be thread-safe, and should not be called
+  // concurrently with other |MojoLogger| functions. (In some environments --
+  // such as Chromium -- that share a logger across applications, this may mean
+  // that it is almost never safe to call this.)
+  void (*SetMinimumLogLevel)(MojoLogLevel minimum_log_level);
+};
+
+#endif  // MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
diff --git a/mojo/public/c/gles2/BUILD.gn b/mojo/public/c/gles2/BUILD.gn
new file mode 100644
index 0000000..df0007b
--- /dev/null
+++ b/mojo/public/c/gles2/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.
+
+config("gles2_config") {
+  defines = [ "GLES2_USE_MOJO" ]
+}
+
+source_set("gles2") {
+  sources = [
+    "gles2.h",
+    "gles2_export.h",
+  ]
+
+  public_configs = [ ":gles2_config" ]
+
+  public_deps = [
+    "//mojo/public/c/environment",
+    "//mojo/public/c/system",
+  ]
+}
diff --git a/mojo/public/c/gles2/DEPS b/mojo/public/c/gles2/DEPS
new file mode 100644
index 0000000..3887457
--- /dev/null
+++ b/mojo/public/c/gles2/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/c/environment",
+]
diff --git a/mojo/public/c/gles2/chromium_sync_point.h b/mojo/public/c/gles2/chromium_sync_point.h
new file mode 100644
index 0000000..6369712
--- /dev/null
+++ b/mojo/public/c/gles2/chromium_sync_point.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_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_
diff --git a/mojo/public/c/gles2/chromium_texture_mailbox.h b/mojo/public/c/gles2/chromium_texture_mailbox.h
new file mode 100644
index 0000000..177ebbb
--- /dev/null
+++ b/mojo/public/c/gles2/chromium_texture_mailbox.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_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
diff --git a/mojo/public/c/gles2/gles2.h b/mojo/public/c/gles2/gles2.h
new file mode 100644
index 0000000..36c6f7f
--- /dev/null
+++ b/mojo/public/c/gles2/gles2.h
@@ -0,0 +1,45 @@
+// 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_PUBLIC_C_GLES2_GLES2_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_GLES2_EXPORT MojoGLES2Context
+    MojoGLES2CreateContext(MojoHandle handle,
+                           MojoGLES2ContextLost lost_callback,
+                           void* closure,
+                           const MojoAsyncWaiter* async_waiter);
+MOJO_GLES2_EXPORT void MojoGLES2DestroyContext(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2MakeCurrent(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2SwapBuffers(void);
+
+// TODO(piman): We shouldn't have to leak those 2 interfaces, especially in a
+// type-unsafe way.
+MOJO_GLES2_EXPORT void* MojoGLES2GetGLES2Interface(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void* MojoGLES2GetContextSupport(MojoGLES2Context context);
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_GLES2_H_
diff --git a/mojo/public/c/gles2/gles2_call_visitor_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_autogen.h
new file mode 100644
index 0000000..72494c5
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_autogen.h
@@ -0,0 +1,544 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(ActiveTexture, void, (GLenum texture), (texture))
+VISIT_GL_CALL(AttachShader,
+              void,
+              (GLuint program, GLuint shader),
+              (program, shader))
+VISIT_GL_CALL(BindAttribLocation,
+              void,
+              (GLuint program, GLuint index, const char* name),
+              (program, index, name))
+VISIT_GL_CALL(BindBuffer,
+              void,
+              (GLenum target, GLuint buffer),
+              (target, buffer))
+VISIT_GL_CALL(BindFramebuffer,
+              void,
+              (GLenum target, GLuint framebuffer),
+              (target, framebuffer))
+VISIT_GL_CALL(BindRenderbuffer,
+              void,
+              (GLenum target, GLuint renderbuffer),
+              (target, renderbuffer))
+VISIT_GL_CALL(BindTexture,
+              void,
+              (GLenum target, GLuint texture),
+              (target, texture))
+VISIT_GL_CALL(BlendColor,
+              void,
+              (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+              (red, green, blue, alpha))
+VISIT_GL_CALL(BlendEquation, void, (GLenum mode), (mode))
+VISIT_GL_CALL(BlendEquationSeparate,
+              void,
+              (GLenum modeRGB, GLenum modeAlpha),
+              (modeRGB, modeAlpha))
+VISIT_GL_CALL(BlendFunc,
+              void,
+              (GLenum sfactor, GLenum dfactor),
+              (sfactor, dfactor))
+VISIT_GL_CALL(BlendFuncSeparate,
+              void,
+              (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha),
+              (srcRGB, dstRGB, srcAlpha, dstAlpha))
+VISIT_GL_CALL(BufferData,
+              void,
+              (GLenum target, GLsizeiptr size, const void* data, GLenum usage),
+              (target, size, data, usage))
+VISIT_GL_CALL(
+    BufferSubData,
+    void,
+    (GLenum target, GLintptr offset, GLsizeiptr size, const void* data),
+    (target, offset, size, data))
+VISIT_GL_CALL(CheckFramebufferStatus, GLenum, (GLenum target), (target))
+VISIT_GL_CALL(Clear, void, (GLbitfield mask), (mask))
+VISIT_GL_CALL(ClearColor,
+              void,
+              (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+              (red, green, blue, alpha))
+VISIT_GL_CALL(ClearDepthf, void, (GLclampf depth), (depth))
+VISIT_GL_CALL(ClearStencil, void, (GLint s), (s))
+VISIT_GL_CALL(ColorMask,
+              void,
+              (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha),
+              (red, green, blue, alpha))
+VISIT_GL_CALL(CompileShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(
+    CompressedTexImage2D,
+    void,
+    (GLenum target,
+     GLint level,
+     GLenum internalformat,
+     GLsizei width,
+     GLsizei height,
+     GLint border,
+     GLsizei imageSize,
+     const void* data),
+    (target, level, internalformat, width, height, border, imageSize, data))
+VISIT_GL_CALL(
+    CompressedTexSubImage2D,
+    void,
+    (GLenum target,
+     GLint level,
+     GLint xoffset,
+     GLint yoffset,
+     GLsizei width,
+     GLsizei height,
+     GLenum format,
+     GLsizei imageSize,
+     const void* data),
+    (target, level, xoffset, yoffset, width, height, format, imageSize, data))
+VISIT_GL_CALL(CopyTexImage2D,
+              void,
+              (GLenum target,
+               GLint level,
+               GLenum internalformat,
+               GLint x,
+               GLint y,
+               GLsizei width,
+               GLsizei height,
+               GLint border),
+              (target, level, internalformat, x, y, width, height, border))
+VISIT_GL_CALL(CopyTexSubImage2D,
+              void,
+              (GLenum target,
+               GLint level,
+               GLint xoffset,
+               GLint yoffset,
+               GLint x,
+               GLint y,
+               GLsizei width,
+               GLsizei height),
+              (target, level, xoffset, yoffset, x, y, width, height))
+VISIT_GL_CALL(CreateProgram, GLuint, (), ())
+VISIT_GL_CALL(CreateShader, GLuint, (GLenum type), (type))
+VISIT_GL_CALL(CullFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(DeleteBuffers,
+              void,
+              (GLsizei n, const GLuint* buffers),
+              (n, buffers))
+VISIT_GL_CALL(DeleteFramebuffers,
+              void,
+              (GLsizei n, const GLuint* framebuffers),
+              (n, framebuffers))
+VISIT_GL_CALL(DeleteProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(DeleteRenderbuffers,
+              void,
+              (GLsizei n, const GLuint* renderbuffers),
+              (n, renderbuffers))
+VISIT_GL_CALL(DeleteShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(DeleteTextures,
+              void,
+              (GLsizei n, const GLuint* textures),
+              (n, textures))
+VISIT_GL_CALL(DepthFunc, void, (GLenum func), (func))
+VISIT_GL_CALL(DepthMask, void, (GLboolean flag), (flag))
+VISIT_GL_CALL(DepthRangef, void, (GLclampf zNear, GLclampf zFar), (zNear, zFar))
+VISIT_GL_CALL(DetachShader,
+              void,
+              (GLuint program, GLuint shader),
+              (program, shader))
+VISIT_GL_CALL(Disable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(DisableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(DrawArrays,
+              void,
+              (GLenum mode, GLint first, GLsizei count),
+              (mode, first, count))
+VISIT_GL_CALL(DrawElements,
+              void,
+              (GLenum mode, GLsizei count, GLenum type, const void* indices),
+              (mode, count, type, indices))
+VISIT_GL_CALL(Enable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(EnableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(Finish, void, (), ())
+VISIT_GL_CALL(Flush, void, (), ())
+VISIT_GL_CALL(FramebufferRenderbuffer,
+              void,
+              (GLenum target,
+               GLenum attachment,
+               GLenum renderbuffertarget,
+               GLuint renderbuffer),
+              (target, attachment, renderbuffertarget, renderbuffer))
+VISIT_GL_CALL(FramebufferTexture2D,
+              void,
+              (GLenum target,
+               GLenum attachment,
+               GLenum textarget,
+               GLuint texture,
+               GLint level),
+              (target, attachment, textarget, texture, level))
+VISIT_GL_CALL(FrontFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(GenBuffers, void, (GLsizei n, GLuint * buffers), (n, buffers))
+VISIT_GL_CALL(GenerateMipmap, void, (GLenum target), (target))
+VISIT_GL_CALL(GenFramebuffers,
+              void,
+              (GLsizei n, GLuint * framebuffers),
+              (n, framebuffers))
+VISIT_GL_CALL(GenRenderbuffers,
+              void,
+              (GLsizei n, GLuint * renderbuffers),
+              (n, renderbuffers))
+VISIT_GL_CALL(GenTextures, void, (GLsizei n, GLuint * textures), (n, textures))
+VISIT_GL_CALL(GetActiveAttrib,
+              void,
+              (GLuint program,
+               GLuint index,
+               GLsizei bufsize,
+               GLsizei * length,
+               GLint * size,
+               GLenum * type,
+               char* name),
+              (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(GetActiveUniform,
+              void,
+              (GLuint program,
+               GLuint index,
+               GLsizei bufsize,
+               GLsizei * length,
+               GLint * size,
+               GLenum * type,
+               char* name),
+              (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(
+    GetAttachedShaders,
+    void,
+    (GLuint program, GLsizei maxcount, GLsizei * count, GLuint * shaders),
+    (program, maxcount, count, shaders))
+VISIT_GL_CALL(GetAttribLocation,
+              GLint,
+              (GLuint program, const char* name),
+              (program, name))
+VISIT_GL_CALL(GetBooleanv,
+              void,
+              (GLenum pname, GLboolean * params),
+              (pname, params))
+VISIT_GL_CALL(GetBufferParameteriv,
+              void,
+              (GLenum target, GLenum pname, GLint * params),
+              (target, pname, params))
+VISIT_GL_CALL(GetError, GLenum, (), ())
+VISIT_GL_CALL(GetFloatv,
+              void,
+              (GLenum pname, GLfloat * params),
+              (pname, params))
+VISIT_GL_CALL(GetFramebufferAttachmentParameteriv,
+              void,
+              (GLenum target, GLenum attachment, GLenum pname, GLint * params),
+              (target, attachment, pname, params))
+VISIT_GL_CALL(GetIntegerv,
+              void,
+              (GLenum pname, GLint * params),
+              (pname, params))
+VISIT_GL_CALL(GetProgramiv,
+              void,
+              (GLuint program, GLenum pname, GLint * params),
+              (program, pname, params))
+VISIT_GL_CALL(
+    GetProgramInfoLog,
+    void,
+    (GLuint program, GLsizei bufsize, GLsizei * length, char* infolog),
+    (program, bufsize, length, infolog))
+VISIT_GL_CALL(GetRenderbufferParameteriv,
+              void,
+              (GLenum target, GLenum pname, GLint * params),
+              (target, pname, params))
+VISIT_GL_CALL(GetShaderiv,
+              void,
+              (GLuint shader, GLenum pname, GLint * params),
+              (shader, pname, params))
+VISIT_GL_CALL(GetShaderInfoLog,
+              void,
+              (GLuint shader, GLsizei bufsize, GLsizei * length, char* infolog),
+              (shader, bufsize, length, infolog))
+VISIT_GL_CALL(
+    GetShaderPrecisionFormat,
+    void,
+    (GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision),
+    (shadertype, precisiontype, range, precision))
+VISIT_GL_CALL(GetShaderSource,
+              void,
+              (GLuint shader, GLsizei bufsize, GLsizei * length, char* source),
+              (shader, bufsize, length, source))
+VISIT_GL_CALL(GetString, const GLubyte*, (GLenum name), (name))
+VISIT_GL_CALL(GetTexParameterfv,
+              void,
+              (GLenum target, GLenum pname, GLfloat * params),
+              (target, pname, params))
+VISIT_GL_CALL(GetTexParameteriv,
+              void,
+              (GLenum target, GLenum pname, GLint * params),
+              (target, pname, params))
+VISIT_GL_CALL(GetUniformfv,
+              void,
+              (GLuint program, GLint location, GLfloat * params),
+              (program, location, params))
+VISIT_GL_CALL(GetUniformiv,
+              void,
+              (GLuint program, GLint location, GLint * params),
+              (program, location, params))
+VISIT_GL_CALL(GetUniformLocation,
+              GLint,
+              (GLuint program, const char* name),
+              (program, name))
+VISIT_GL_CALL(GetVertexAttribfv,
+              void,
+              (GLuint index, GLenum pname, GLfloat * params),
+              (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribiv,
+              void,
+              (GLuint index, GLenum pname, GLint * params),
+              (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribPointerv,
+              void,
+              (GLuint index, GLenum pname, void** pointer),
+              (index, pname, pointer))
+VISIT_GL_CALL(Hint, void, (GLenum target, GLenum mode), (target, mode))
+VISIT_GL_CALL(IsBuffer, GLboolean, (GLuint buffer), (buffer))
+VISIT_GL_CALL(IsEnabled, GLboolean, (GLenum cap), (cap))
+VISIT_GL_CALL(IsFramebuffer, GLboolean, (GLuint framebuffer), (framebuffer))
+VISIT_GL_CALL(IsProgram, GLboolean, (GLuint program), (program))
+VISIT_GL_CALL(IsRenderbuffer, GLboolean, (GLuint renderbuffer), (renderbuffer))
+VISIT_GL_CALL(IsShader, GLboolean, (GLuint shader), (shader))
+VISIT_GL_CALL(IsTexture, GLboolean, (GLuint texture), (texture))
+VISIT_GL_CALL(LineWidth, void, (GLfloat width), (width))
+VISIT_GL_CALL(LinkProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(PixelStorei, void, (GLenum pname, GLint param), (pname, param))
+VISIT_GL_CALL(PolygonOffset,
+              void,
+              (GLfloat factor, GLfloat units),
+              (factor, units))
+VISIT_GL_CALL(ReadPixels,
+              void,
+              (GLint x,
+               GLint y,
+               GLsizei width,
+               GLsizei height,
+               GLenum format,
+               GLenum type,
+               void* pixels),
+              (x, y, width, height, format, type, pixels))
+VISIT_GL_CALL(ReleaseShaderCompiler, void, (), ())
+VISIT_GL_CALL(
+    RenderbufferStorage,
+    void,
+    (GLenum target, GLenum internalformat, GLsizei width, GLsizei height),
+    (target, internalformat, width, height))
+VISIT_GL_CALL(SampleCoverage,
+              void,
+              (GLclampf value, GLboolean invert),
+              (value, invert))
+VISIT_GL_CALL(Scissor,
+              void,
+              (GLint x, GLint y, GLsizei width, GLsizei height),
+              (x, y, width, height))
+VISIT_GL_CALL(ShaderBinary,
+              void,
+              (GLsizei n,
+               const GLuint* shaders,
+               GLenum binaryformat,
+               const void* binary,
+               GLsizei length),
+              (n, shaders, binaryformat, binary, length))
+VISIT_GL_CALL(ShaderSource,
+              void,
+              (GLuint shader,
+               GLsizei count,
+               const GLchar* const* str,
+               const GLint* length),
+              (shader, count, str, length))
+VISIT_GL_CALL(StencilFunc,
+              void,
+              (GLenum func, GLint ref, GLuint mask),
+              (func, ref, mask))
+VISIT_GL_CALL(StencilFuncSeparate,
+              void,
+              (GLenum face, GLenum func, GLint ref, GLuint mask),
+              (face, func, ref, mask))
+VISIT_GL_CALL(StencilMask, void, (GLuint mask), (mask))
+VISIT_GL_CALL(StencilMaskSeparate,
+              void,
+              (GLenum face, GLuint mask),
+              (face, mask))
+VISIT_GL_CALL(StencilOp,
+              void,
+              (GLenum fail, GLenum zfail, GLenum zpass),
+              (fail, zfail, zpass))
+VISIT_GL_CALL(StencilOpSeparate,
+              void,
+              (GLenum face, GLenum fail, GLenum zfail, GLenum zpass),
+              (face, fail, zfail, zpass))
+VISIT_GL_CALL(TexImage2D,
+              void,
+              (GLenum target,
+               GLint level,
+               GLint internalformat,
+               GLsizei width,
+               GLsizei height,
+               GLint border,
+               GLenum format,
+               GLenum type,
+               const void* pixels),
+              (target,
+               level,
+               internalformat,
+               width,
+               height,
+               border,
+               format,
+               type,
+               pixels))
+VISIT_GL_CALL(TexParameterf,
+              void,
+              (GLenum target, GLenum pname, GLfloat param),
+              (target, pname, param))
+VISIT_GL_CALL(TexParameterfv,
+              void,
+              (GLenum target, GLenum pname, const GLfloat* params),
+              (target, pname, params))
+VISIT_GL_CALL(TexParameteri,
+              void,
+              (GLenum target, GLenum pname, GLint param),
+              (target, pname, param))
+VISIT_GL_CALL(TexParameteriv,
+              void,
+              (GLenum target, GLenum pname, const GLint* params),
+              (target, pname, params))
+VISIT_GL_CALL(
+    TexSubImage2D,
+    void,
+    (GLenum target,
+     GLint level,
+     GLint xoffset,
+     GLint yoffset,
+     GLsizei width,
+     GLsizei height,
+     GLenum format,
+     GLenum type,
+     const void* pixels),
+    (target, level, xoffset, yoffset, width, height, format, type, pixels))
+VISIT_GL_CALL(Uniform1f, void, (GLint location, GLfloat x), (location, x))
+VISIT_GL_CALL(Uniform1fv,
+              void,
+              (GLint location, GLsizei count, const GLfloat* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform1i, void, (GLint location, GLint x), (location, x))
+VISIT_GL_CALL(Uniform1iv,
+              void,
+              (GLint location, GLsizei count, const GLint* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform2f,
+              void,
+              (GLint location, GLfloat x, GLfloat y),
+              (location, x, y))
+VISIT_GL_CALL(Uniform2fv,
+              void,
+              (GLint location, GLsizei count, const GLfloat* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform2i,
+              void,
+              (GLint location, GLint x, GLint y),
+              (location, x, y))
+VISIT_GL_CALL(Uniform2iv,
+              void,
+              (GLint location, GLsizei count, const GLint* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform3f,
+              void,
+              (GLint location, GLfloat x, GLfloat y, GLfloat z),
+              (location, x, y, z))
+VISIT_GL_CALL(Uniform3fv,
+              void,
+              (GLint location, GLsizei count, const GLfloat* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform3i,
+              void,
+              (GLint location, GLint x, GLint y, GLint z),
+              (location, x, y, z))
+VISIT_GL_CALL(Uniform3iv,
+              void,
+              (GLint location, GLsizei count, const GLint* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform4f,
+              void,
+              (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+              (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4fv,
+              void,
+              (GLint location, GLsizei count, const GLfloat* v),
+              (location, count, v))
+VISIT_GL_CALL(Uniform4i,
+              void,
+              (GLint location, GLint x, GLint y, GLint z, GLint w),
+              (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4iv,
+              void,
+              (GLint location, GLsizei count, const GLint* v),
+              (location, count, v))
+VISIT_GL_CALL(
+    UniformMatrix2fv,
+    void,
+    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+    (location, count, transpose, value))
+VISIT_GL_CALL(
+    UniformMatrix3fv,
+    void,
+    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+    (location, count, transpose, value))
+VISIT_GL_CALL(
+    UniformMatrix4fv,
+    void,
+    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+    (location, count, transpose, value))
+VISIT_GL_CALL(UseProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(ValidateProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(VertexAttrib1f, void, (GLuint indx, GLfloat x), (indx, x))
+VISIT_GL_CALL(VertexAttrib1fv,
+              void,
+              (GLuint indx, const GLfloat* values),
+              (indx, values))
+VISIT_GL_CALL(VertexAttrib2f,
+              void,
+              (GLuint indx, GLfloat x, GLfloat y),
+              (indx, x, y))
+VISIT_GL_CALL(VertexAttrib2fv,
+              void,
+              (GLuint indx, const GLfloat* values),
+              (indx, values))
+VISIT_GL_CALL(VertexAttrib3f,
+              void,
+              (GLuint indx, GLfloat x, GLfloat y, GLfloat z),
+              (indx, x, y, z))
+VISIT_GL_CALL(VertexAttrib3fv,
+              void,
+              (GLuint indx, const GLfloat* values),
+              (indx, values))
+VISIT_GL_CALL(VertexAttrib4f,
+              void,
+              (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+              (indx, x, y, z, w))
+VISIT_GL_CALL(VertexAttrib4fv,
+              void,
+              (GLuint indx, const GLfloat* values),
+              (indx, values))
+VISIT_GL_CALL(VertexAttribPointer,
+              void,
+              (GLuint indx,
+               GLint size,
+               GLenum type,
+               GLboolean normalized,
+               GLsizei stride,
+               const void* ptr),
+              (indx, size, type, normalized, stride, ptr))
+VISIT_GL_CALL(Viewport,
+              void,
+              (GLint x, GLint y, GLsizei width, GLsizei height),
+              (x, y, width, height))
diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h
new file mode 100644
index 0000000..3c3c4b9
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h
@@ -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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(InsertSyncPointCHROMIUM, GLuint, (), ())
+VISIT_GL_CALL(WaitSyncPointCHROMIUM, void, (GLuint sync_point), (sync_point))
diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h
new file mode 100644
index 0000000..184c2f2
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(GenMailboxCHROMIUM, void, (GLbyte * mailbox), (mailbox))
+VISIT_GL_CALL(ProduceTextureCHROMIUM,
+              void,
+              (GLenum target, const GLbyte* mailbox),
+              (target, mailbox))
+VISIT_GL_CALL(ProduceTextureDirectCHROMIUM,
+              void,
+              (GLuint texture, GLenum target, const GLbyte* mailbox),
+              (texture, target, mailbox))
+VISIT_GL_CALL(ConsumeTextureCHROMIUM,
+              void,
+              (GLenum target, const GLbyte* mailbox),
+              (target, mailbox))
+VISIT_GL_CALL(CreateAndConsumeTextureCHROMIUM,
+              GLuint,
+              (GLenum target, const GLbyte* mailbox),
+              (target, mailbox))
diff --git a/mojo/public/c/gles2/gles2_export.h b/mojo/public/c/gles2/gles2_export.h
new file mode 100644
index 0000000..60667b1
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_export.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_PUBLIC_C_GLES2_GLES2_EXPORT_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
+
+#if defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL)
+#if defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GLES2_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GLES2_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_GLES2_IMPL)
+
+#define MOJO_GLES2_EXPORT
+
+#endif  // defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL)
+
+#endif  // MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
diff --git a/mojo/public/c/gles2/gles2_types.h b/mojo/public/c/gles2/gles2_types.h
new file mode 100644
index 0000000..3ecf4db
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_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_PUBLIC_C_GLES2_GLES2_TYPES_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MojoGLES2ContextPrivate* MojoGLES2Context;
+typedef void (*MojoGLES2ContextLost)(void* closure);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
new file mode 100644
index 0000000..b47b3da
--- /dev/null
+++ b/mojo/public/c/system/BUILD.gn
@@ -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.
+
+# Depend on this target to use the types etc defined in the system without
+# linking against a specific implementation of the system. To link against a
+# particular implementation, use the :for_component or
+# :for_shared_library targets, depending on the type of target you are.
+source_set("system") {
+  sources = [
+    "buffer.h",
+    "core.h",
+    "data_pipe.h",
+    "functions.h",
+    "macros.h",
+    "message_pipe.h",
+    "system_export.h",
+    "types.h",
+  ]
+}
+
+# In an is_component_build build, everything can link against //mojo/edk/system
+# because it is built as a shared library. However, in a static build,
+# //mojo/edk/system is linked into an executable (e.g., mojo_shell), and must be
+# injected into other shared libraries (i.e., Mojo Apps) that need the mojo
+# system API.
+#
+# For component targets, add //mojo/public/c/system:for_component to your deps
+# section.
+#
+# For shared_library targets (e.g., a Mojo App), add
+# //mojo/public/c/system:for_shared_library to your deps
+
+group("for_shared_library") {
+  public_deps = [ ":system" ]
+  if (is_component_build) {
+    deps = [ "//mojo/edk/system" ]
+  } else {
+    deps = [ "//mojo/public/platform/native:system_thunks" ]
+  }
+}
+
+group("for_component") {
+  public_deps = [ ":system" ]
+  if (is_component_build) {
+    deps = [ "//mojo/edk/system" ]
+  }
+}
diff --git a/mojo/public/c/system/buffer.h b/mojo/public/c/system/buffer.h
new file mode 100644
index 0000000..19e3c52
--- /dev/null
+++ b/mojo/public/c/system/buffer.h
@@ -0,0 +1,186 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains types/constants and functions specific to buffers (and in
+// particular shared buffers).
+// TODO(vtl): Reorganize this file (etc.) to separate general buffer functions
+// from (shared) buffer creation.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateSharedBufferOptions|: Used to specify creation parameters for a
+// shared buffer to |MojoCreateSharedBuffer()|.
+//   |uint32_t struct_size|: Set to the size of the
+//       |MojoCreateSharedBufferOptions| struct. (Used to allow for future
+//       extensions.)
+//   |MojoCreateSharedBufferOptionsFlags flags|: Reserved for future use.
+//       |MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE|: No flags; default mode.
+//
+// TODO(vtl): Maybe add a flag to indicate whether the memory should be
+// executable or not?
+// TODO(vtl): Also a flag for discardable (ashmem-style) buffers.
+
+typedef uint32_t MojoCreateSharedBufferOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateSharedBufferOptionsFlags
+    MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE \
+  ((MojoCreateSharedBufferOptionsFlags)0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateSharedBufferOptions {
+  uint32_t struct_size;
+  MojoCreateSharedBufferOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateSharedBufferOptions) == 8,
+                    MojoCreateSharedBufferOptions_has_wrong_size);
+
+// |MojoDuplicateBufferHandleOptions|: Used to specify parameters in duplicating
+// access to a shared buffer to |MojoDuplicateBufferHandle()|.
+//   |uint32_t struct_size|: Set to the size of the
+//       |MojoDuplicateBufferHandleOptions| struct. (Used to allow for future
+//       extensions.)
+//   |MojoDuplicateBufferHandleOptionsFlags flags|: Reserved for future use.
+//       |MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE|: No flags; default
+//       mode.
+//
+// TODO(vtl): Add flags to remove writability (and executability)? Also, COW?
+
+typedef uint32_t MojoDuplicateBufferHandleOptionsFlags;
+
+#ifdef __cplusplus
+const MojoDuplicateBufferHandleOptionsFlags
+    MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \
+  ((MojoDuplicateBufferHandleOptionsFlags)0)
+#endif
+
+struct MojoDuplicateBufferHandleOptions {
+  uint32_t struct_size;
+  MojoDuplicateBufferHandleOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoDuplicateBufferHandleOptions) == 8,
+                    MojoDuplicateBufferHandleOptions_has_wrong_size);
+
+// |MojoMapBufferFlags|: Used to specify different modes to |MojoMapBuffer()|.
+//   |MOJO_MAP_BUFFER_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoMapBufferFlags;
+
+#ifdef __cplusplus
+const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE = 0;
+#else
+#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags)0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a buffer of size |num_bytes| bytes that can be shared between
+// applications (by duplicating the handle -- see |MojoDuplicateBufferHandle()|
+// -- and passing it over a message pipe). To access the buffer, one must call
+// |MojoMapBuffer()|.
+//
+// |options| may be set to null for a shared buffer with the default options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the shared
+// buffer. (On failure, it is not modified.)
+//
+// Note: While more than |num_bytes| bytes may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |*options| is invalid).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+//       been reached (e.g., if the requested size was too large, or if the
+//       maximum number of handles was exceeded).
+//   |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer(
+    const struct MojoCreateSharedBufferOptions* options,  // Optional.
+    uint64_t num_bytes,                                   // In.
+    MojoHandle* shared_buffer_handle);                    // Out.
+
+// Duplicates the handle |buffer_handle| to a buffer. This creates another
+// handle (returned in |*new_buffer_handle| on success), which can then be sent
+// to another application over a message pipe, while retaining access to the
+// |buffer_handle| (and any mappings that it may have).
+//
+// |options| may be set to null to duplicate the buffer handle with the default
+// options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the new
+// buffer handle. (On failure, it is not modified.)
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |buffer_handle| is not a valid buffer handle or |*options| is invalid).
+//   |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoDuplicateBufferHandle(
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,  // Optional.
+    MojoHandle* new_buffer_handle);                          // Out.
+
+// Maps the part (at offset |offset| of length |num_bytes|) of the buffer given
+// by |buffer_handle| into memory, with options specified by |flags|. |offset +
+// num_bytes| must be less than or equal to the size of the buffer. On success,
+// |*buffer| points to memory with the requested part of the buffer. (On
+// failure, it is not modified.)
+//
+// A single buffer handle may have multiple active mappings (possibly depending
+// on the buffer type). The permissions (e.g., writable or executable) of the
+// returned memory may depend on the properties of the buffer and properties
+// attached to the buffer handle as well as |flags|.
+//
+// Note: Though data outside the specified range may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |buffer_handle| is not a valid buffer handle or the range specified by
+//       |offset| and |num_bytes| is not valid).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if the mapping operation itself failed
+//       (e.g., due to not having appropriate address space available).
+MOJO_SYSTEM_EXPORT MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+                                            uint64_t offset,
+                                            uint64_t num_bytes,
+                                            void** buffer,  // Out.
+                                            MojoMapBufferFlags flags);
+
+// Unmaps a buffer pointer that was mapped by |MojoMapBuffer()|. |buffer| must
+// have been the result of |MojoMapBuffer()| (not some pointer strictly inside
+// the mapped memory), and the entire mapping will be removed (partial unmapping
+// is not supported). A mapping may only be unmapped exactly once.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |buffer| is invalid (e.g., is not the
+//       result of |MojoMapBuffer()| or has already been unmapped).
+MOJO_SYSTEM_EXPORT MojoResult MojoUnmapBuffer(void* buffer);  // In.
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h
new file mode 100644
index 0000000..0e78786
--- /dev/null
+++ b/mojo/public/c/system/core.h
@@ -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.
+
+// This is a catch-all header that includes everything.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_C_SYSTEM_CORE_H_
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_CORE_H_
diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h
new file mode 100644
index 0000000..c8087ea
--- /dev/null
+++ b/mojo/public/c/system/data_pipe.h
@@ -0,0 +1,364 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains types/constants and functions specific to data pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateDataPipeOptions|: Used to specify creation parameters for a data
+// pipe to |MojoCreateDataPipe()|.
+//   |uint32_t struct_size|: Set to the size of the |MojoCreateDataPipeOptions|
+//       struct. (Used to allow for future extensions.)
+//   |MojoCreateDataPipeOptionsFlags flags|: Used to specify different modes of
+//       operation.
+//     |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+//     |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD|: May discard data for
+//         whatever reason; best-effort delivery. In particular, if the capacity
+//         is reached, old data may be discard to make room for new data.
+//   |uint32_t element_num_bytes|: The size of an element, in bytes. All
+//       transactions and buffers will consist of an integral number of
+//       elements. Must be nonzero.
+//   |uint32_t capacity_num_bytes|: The capacity of the data pipe, in number of
+//       bytes; must be a multiple of |element_num_bytes|. The data pipe will
+//       always be able to queue AT LEAST this much data. Set to zero to opt for
+//       a system-dependent automatically-calculated capacity (which will always
+//       be at least one element).
+
+typedef uint32_t MojoCreateDataPipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE =
+    0;
+const MojoCreateDataPipeOptionsFlags
+    MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \
+  ((MojoCreateDataPipeOptionsFlags)0)
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD \
+  ((MojoCreateDataPipeOptionsFlags)1 << 0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateDataPipeOptions {
+  uint32_t struct_size;
+  MojoCreateDataPipeOptionsFlags flags;
+  uint32_t element_num_bytes;
+  uint32_t capacity_num_bytes;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateDataPipeOptions) == 16,
+                    MojoCreateDataPipeOptions_has_wrong_size);
+
+// |MojoWriteDataFlags|: Used to specify different modes to |MojoWriteData()|
+// and |MojoBeginWriteData()|.
+//   |MOJO_WRITE_DATA_FLAG_NONE| - No flags; default mode.
+//   |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| - Write either all the elements
+//       requested or none of them.
+
+typedef uint32_t MojoWriteDataFlags;
+
+#ifdef __cplusplus
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE = 0;
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+#else
+#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags)0)
+#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags)1 << 0)
+#endif
+
+// |MojoReadDataFlags|: Used to specify different modes to |MojoReadData()| and
+// |MojoBeginReadData()|.
+//   |MOJO_READ_DATA_FLAG_NONE| - No flags; default mode.
+//   |MOJO_READ_DATA_FLAG_ALL_OR_NONE| - Read (or discard) either the requested
+//        number of elements or none.
+//   |MOJO_READ_DATA_FLAG_DISCARD| - Discard (up to) the requested number of
+//        elements.
+//   |MOJO_READ_DATA_FLAG_QUERY| - Query the number of elements available to
+//       read. For use with |MojoReadData()| only. Mutually exclusive with
+//       |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is
+//       ignored if this flag is set.
+
+typedef uint32_t MojoReadDataFlags;
+
+#ifdef __cplusplus
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE = 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2;
+#else
+#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags)0)
+#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags)1 << 0)
+#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags)1 << 1)
+#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags)1 << 2)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a data pipe, which is a unidirectional communication channel for
+// unframed data, with the given options. Data is unframed, but must come as
+// (multiples of) discrete elements, of the size given in |options|. See
+// |MojoCreateDataPipeOptions| for a description of the different options
+// available for data pipes.
+//
+// |options| may be set to null for a data pipe with the default options (which
+// will have an element size of one byte and have some system-dependent
+// capacity).
+//
+// On success, |*data_pipe_producer_handle| will be set to the handle for the
+// producer and |*data_pipe_consumer_handle| will be set to the handle for the
+// consumer. (On failure, they are not modified.)
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |*options| is invalid).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+//       been reached (e.g., if the requested capacity was too large, or if the
+//       maximum number of handles was exceeded).
+//   |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe(
+    const struct MojoCreateDataPipeOptions* options,  // Optional.
+    MojoHandle* data_pipe_producer_handle,            // Out.
+    MojoHandle* data_pipe_consumer_handle);           // Out.
+
+// Writes the given data to the data pipe producer given by
+// |data_pipe_producer_handle|. |elements| points to data of size |*num_bytes|;
+// |*num_bytes| should be a multiple of the data pipe's element size. If
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is set in |flags|, either all the data
+// will be written or none is.
+//
+// On success, |*num_bytes| is set to the amount of data that was actually
+// written.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation), this will discard as much data as required to write the given
+// data, starting with the earliest written data that has not been consumed.
+// However, even with "may discard", if |*num_bytes| is greater than the data
+// pipe's capacity (and |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is not set), this
+// will write the maximum amount possible (namely, the data pipe's capacity) and
+// set |*num_bytes| to that amount. It will *not* discard data from |elements|.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_producer_dispatcher| is not a handle to a data pipe
+//       producer or |*num_bytes| is not a multiple of the data pipe's element
+//       size).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+//       closed.
+//   |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+//       |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+//       (specified by |*num_bytes|) could not be written.
+//   |MOJO_RESULT_BUSY| if there is a two-phase write ongoing with
+//       |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+//       called, but not yet the matching |MojoEndWriteData()|).
+//   |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+//       consumer is still open) and |flags| does *not* have
+//       |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set.
+//
+// TODO(vtl): Should there be a way of querying how much data can be written?
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoWriteData(MojoHandle data_pipe_producer_handle,
+                  const void* elements,
+                  uint32_t* num_bytes,  // In/out.
+                  MojoWriteDataFlags flags);
+
+// Begins a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle|. On success, |*buffer| will be a pointer to which
+// the caller can write |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored).
+//
+// During a two-phase write, |data_pipe_producer_handle| is *not* writable.
+// E.g., if another thread tries to write to it, it will get |MOJO_RESULT_BUSY|;
+// that thread can then wait for |data_pipe_producer_handle| to become writable
+// again.
+//
+// Once the caller has finished writing data to |*buffer|, it should call
+// |MojoEndWriteData()| to specify the amount written and to complete the
+// two-phase write.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation) and |flags| has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, this may
+// discard some data.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_producer_handle| is not a handle to a data pipe producer or
+//       flags has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and
+//       |*buffer_num_bytes| is not a multiple of the element size).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+//       closed.
+//   |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+//       |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+//       (specified by |*buffer_num_bytes|) cannot be written contiguously at
+//       this time. (Note that there may be space available for the required
+//       amount of data, but the "next" write position may not be large enough.)
+//   |MOJO_RESULT_BUSY| if there is already a two-phase write ongoing with
+//       |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+//       called, but not yet the matching |MojoEndWriteData()|).
+//   |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+//       consumer is still open).
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+                       void** buffer,               // Out.
+                       uint32_t* buffer_num_bytes,  // In/out.
+                       MojoWriteDataFlags flags);
+
+// Ends a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle| that was begun by a call to
+// |MojoBeginWriteData()| on the same handle. |num_bytes_written| should
+// indicate the amount of data actually written; it must be less than or equal
+// to the value of |*buffer_num_bytes| output by |MojoBeginWriteData()| and must
+// be a multiple of the element size. The buffer given by |*buffer| from
+// |MojoBeginWriteData()| must have been filled with exactly |num_bytes_written|
+// bytes of data.
+//
+// On failure, the two-phase write (if any) is ended (so the handle may become
+// writable again, if there's space available) but no data written to |*buffer|
+// is "put into" the data pipe.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_producer_handle| is not a handle to a data pipe producer or
+//       |num_bytes_written| is invalid (greater than the maximum value provided
+//       by |MojoBeginWriteData()| or not a multiple of the element size).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer is not in a
+//       two-phase write (e.g., |MojoBeginWriteData()| was not called or
+//       |MojoEndWriteData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+                     uint32_t num_bytes_written);
+
+// Reads data from the data pipe consumer given by |data_pipe_consumer_handle|.
+// May also be used to discard data or query the amount of data available.
+//
+// If |flags| has neither |MOJO_READ_DATA_FLAG_DISCARD| nor
+// |MOJO_READ_DATA_FLAG_QUERY| set, this tries to read up to |*num_bytes| (which
+// must be a multiple of the data pipe's element size) bytes of data to
+// |elements| and set |*num_bytes| to the amount actually read. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, it will either read exactly
+// |*num_bytes| bytes of data or none.
+//
+// If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to
+// |*num_bytes| (which again be a multiple of the element size) bytes of data,
+// setting |*num_bytes| to the amount actually discarded. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE|, it will either discard exactly
+// |*num_bytes| bytes of data or none. In this case, |MOJO_READ_DATA_FLAG_QUERY|
+// must not be set, and |elements| is ignored (and should typically be set to
+// null).
+//
+// If flags has |MOJO_READ_DATA_FLAG_QUERY| set, it queries the amount of data
+// available, setting |*num_bytes| to the number of bytes available. In this
+// case, |MOJO_READ_DATA_FLAG_DISCARD| must not be set, and
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is ignored, as are |elements| and the input
+// value of |*num_bytes|.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success (see above for a description of the different
+//       operations).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_consumer_handle| is invalid, the combination of flags in
+//       |flags| is invalid, etc.).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+//       closed and data (or the required amount of data) was not available to
+//       be read or discarded.
+//   |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+//       set and the required amount of data is not available to be read or
+//       discarded (and the producer is still open).
+//   |MOJO_RESULT_BUSY| if there is a two-phase read ongoing with
+//       |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+//       called, but not yet the matching |MojoEndReadData()|).
+//   |MOJO_RESULT_SHOULD_WAIT| if there is no data to be read or discarded (and
+//       the producer is still open) and |flags| does *not* have
+//       |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set.
+MOJO_SYSTEM_EXPORT MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+                                           void* elements,       // Out.
+                                           uint32_t* num_bytes,  // In/out.
+                                           MojoReadDataFlags flags);
+
+// Begins a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle|. On success, |*buffer| will be a pointer from
+// which the caller can read |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored). |flags| must
+// not have |MOJO_READ_DATA_FLAG_DISCARD| or |MOJO_READ_DATA_FLAG_QUERY| set.
+//
+// During a two-phase read, |data_pipe_consumer_handle| is *not* readable.
+// E.g., if another thread tries to read from it, it will get
+// |MOJO_RESULT_BUSY|; that thread can then wait for |data_pipe_consumer_handle|
+// to become readable again.
+//
+// Once the caller has finished reading data from |*buffer|, it should call
+// |MojoEndReadData()| to specify the amount read and to complete the two-phase
+// read.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_consumer_handle| is not a handle to a data pipe consumer,
+//       |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set and
+//       |*buffer_num_bytes| is not a multiple of the element size, or |flags|
+//       has invalid flags set).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+//       closed.
+//   |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+//       set and the required amount of data (specified by |*buffer_num_bytes|)
+//       cannot be read from a contiguous buffer at this time. (Note that there
+//       may be the required amount of data, but it may not be contiguous.)
+//   |MOJO_RESULT_BUSY| if there is already a two-phase read ongoing with
+//       |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+//       called, but not yet the matching |MojoEndReadData()|).
+//   |MOJO_RESULT_SHOULD_WAIT| if no data can currently be read (and the
+//       producer is still open).
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+                      const void** buffer,         // Out.
+                      uint32_t* buffer_num_bytes,  // In/out.
+                      MojoReadDataFlags flags);
+
+// Ends a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle| that was begun by a call to |MojoBeginReadData()|
+// on the same handle. |num_bytes_read| should indicate the amount of data
+// actually read; it must be less than or equal to the value of
+// |*buffer_num_bytes| output by |MojoBeginReadData()| and must be a multiple of
+// the element size.
+//
+// On failure, the two-phase read (if any) is ended (so the handle may become
+// readable again) but no data is "removed" from the data pipe.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |data_pipe_consumer_handle| is not a handle to a data pipe consumer or
+//       |num_bytes_written| is greater than the maximum value provided by
+//       |MojoBeginReadData()| or not a multiple of the element size).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer is not in a
+//       two-phase read (e.g., |MojoBeginReadData()| was not called or
+//       |MojoEndReadData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+                    uint32_t num_bytes_read);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h
new file mode 100644
index 0000000..6045f2f
--- /dev/null
+++ b/mojo/public/c/system/functions.h
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains basic functions common to different Mojo system APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+
+// Note: This header should be compilable as C.
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: Pointer parameters that are labelled "optional" may be null (at least
+// under some circumstances). Non-const pointer parameters are also labeled
+// "in", "out", or "in/out", to indicate how they are used. (Note that how/if
+// such a parameter is used may depend on other parameters or the requested
+// operation's success/failure. E.g., a separate |flags| parameter may control
+// whether a given "in/out" parameter is used for input, output, or both.)
+
+// Platform-dependent monotonically increasing tick count representing "right
+// now." The resolution of this clock is ~1-15ms.  Resolution varies depending
+// on hardware/operating system configuration.
+MOJO_SYSTEM_EXPORT MojoTimeTicks MojoGetTimeTicksNow(void);
+
+// Closes the given |handle|.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle.
+//
+// Concurrent operations on |handle| may succeed (or fail as usual) if they
+// happen before the close, be cancelled with result |MOJO_RESULT_CANCELLED| if
+// they properly overlap (this is likely the case with |MojoWait()|, etc.), or
+// fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after.
+MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle);
+
+// Waits on the given handle until a signal indicated by |signals| is satisfied,
+// it becomes known that no signal indicated by |signals| will ever be satisfied
+// (see the description of the |MOJO_RESULT_CANCELLED| and
+// |MOJO_RESULT_FAILED_PRECONDITION| return values below), or until |deadline|
+// has passed.
+//
+// If |deadline| is |MOJO_DEADLINE_INDEFINITE|, this will wait "forever" (until
+// one of the other wait termination conditions is satisfied). If |deadline| is
+// 0, this will return |MOJO_RESULT_DEADLINE_EXCEEDED| only if one of the other
+// termination conditions (e.g., a signal is satisfied, or all signals are
+// unsatisfiable) is not already satisfied.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if some signal in |signals| was satisfied (or is already
+//       satisfied).
+//   |MOJO_RESULT_CANCELLED| if |handle| was closed (necessarily from another
+//       thread) during the wait.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle (e.g., if
+//       it has already been closed).
+//   |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+//       the signals being satisfied.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if it becomes known that none of the
+//       signals in |signals| can ever be satisfied (e.g., when waiting on one
+//       end of a message pipe and the other end is closed).
+//
+// If there are multiple waiters (on different threads, obviously) waiting on
+// the same handle and signal, and that signal becomes is satisfied, all waiters
+// will be awoken.
+MOJO_SYSTEM_EXPORT MojoResult MojoWait(MojoHandle handle,
+                                       MojoHandleSignals signals,
+                                       MojoDeadline deadline);
+
+// Waits on |handles[0]|, ..., |handles[num_handles-1]| until (at least) one
+// satisfies a signal indicated in its respective |signals[0]|, ...,
+// |signals[num_handles-1]|, it becomes known that no signal in some
+// |signals[i]| will ever be satisfied, or until |deadline| has passed.
+//
+// This means that |MojoWaitMany()| behaves as if |MojoWait()| were called on
+// each handle/signals pair simultaneously, completing when the first
+// |MojoWait()| would complete.
+//
+// See |MojoWait()| for more details about |deadline|.
+//
+// Returns:
+//   The index |i| (from 0 to |num_handles-1|) if |handle[i]| satisfies a signal
+//       from |signals[i]|.
+//   |MOJO_RESULT_CANCELLED| if some |handle[i]| was closed (necessarily from
+//       another thread) during the wait.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some |handle[i]| is not a valid handle
+//       (e.g., if it has already been closed).
+//   |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+//       handles satisfying any of its signals.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that SOME
+//       |handle[i]| will ever satisfy any of the signals in |signals[i]|.
+MOJO_SYSTEM_EXPORT MojoResult MojoWaitMany(const MojoHandle* handles,
+                                           const MojoHandleSignals* signals,
+                                           uint32_t num_handles,
+                                           MojoDeadline deadline);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
diff --git a/mojo/public/c/system/macros.h b/mojo/public/c/system/macros.h
new file mode 100644
index 0000000..564ee60
--- /dev/null
+++ b/mojo/public/c/system/macros.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium 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_PUBLIC_C_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+
+#include <stddef.h>
+
+// Annotate a variable indicating it's okay if it's unused.
+// Use like:
+//   int x MOJO_ALLOW_UNUSED = ...;
+#if defined(__GNUC__)
+#define MOJO_ALLOW_UNUSED __attribute__((unused))
+#else
+#define MOJO_ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating that the caller must examine the return value.
+// Use like:
+//   int foo() MOJO_WARN_UNUSED_RESULT;
+// Note that it can only be used on the prototype, and not the definition.
+#if defined(__GNUC__)
+#define MOJO_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define MOJO_WARN_UNUSED_RESULT
+#endif
+
+#ifdef __cplusplus
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+//   scoped_ptr<MyType> my_var = ...;
+//   if (TakeOwnership(my_var.get()) == SUCCESS)
+//     mojo_ignore_result(my_var.release());
+//
+template <typename T>
+inline void mojo_ignore_result(const T&) {
+}
+#endif
+
+// Assert things at compile time. (|msg| should be a valid identifier name.)
+// This macro is currently C++-only, but we want to use it in the C core.h.
+// Use like:
+//   MOJO_COMPILE_ASSERT(sizeof(Foo) == 12, Foo_has_invalid_size);
+#if __cplusplus >= 201103L
+#define MOJO_COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+#elif defined(__cplusplus)
+namespace mojo {
+template <bool>
+struct CompileAssert {};
+}
+#define MOJO_COMPILE_ASSERT(expr, msg) \
+  typedef ::mojo::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#else
+#define MOJO_COMPILE_ASSERT(expr, msg)
+#endif
+
+// Like the C++11 |alignof| operator.
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNOF(type) alignof(type)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNOF(type) __alignof__(type)
+#elif defined(_MSC_VER)
+// The use of |sizeof| is to work around a bug in MSVC 2010 (see
+// http://goo.gl/isH0C; supposedly fixed since then).
+#define MOJO_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#else
+#error "Please define MOJO_ALIGNOF() for your compiler."
+#endif
+
+// Specify the alignment of a |struct|, etc.
+// Use like:
+//   struct MOJO_ALIGNAS(8) Foo { ... };
+// Unlike the C++11 |alignas()|, |alignment| must be an integer. It may not be a
+// type, nor can it be an expression like |MOJO_ALIGNOF(type)| (due to the
+// non-C++11 MSVS version).
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNAS(alignment) alignas(alignment)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNAS(alignment) __attribute__((aligned(alignment)))
+#elif defined(_MSC_VER)
+#define MOJO_ALIGNAS(alignment) __declspec(align(alignment))
+#else
+#error "Please define MOJO_ALIGNAS() for your compiler."
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_MACROS_H_
diff --git a/mojo/public/c/system/main.h b/mojo/public/c/system/main.h
new file mode 100644
index 0000000..31e2012
--- /dev/null
+++ b/mojo/public/c/system/main.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_PUBLIC_C_SYSTEM_MAIN_H_
+#define MOJO_PUBLIC_C_SYSTEM_MAIN_H_
+
+#include "mojo/public/c/system/types.h"
+
+// Implement MojoMain directly as the entry point for an application.
+//
+// MojoResult MojoMain(MojoHandle service_provider_handle) {
+//   ...
+// }
+//
+// TODO(davemoore): Establish this as part of our SDK for third party mojo
+// application writers.
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(WIN32)
+__declspec(dllexport) MojoResult
+    __cdecl MojoMain(MojoHandle service_provider_handle);
+#else  // !defined(WIN32)
+__attribute__((visibility("default"))) MojoResult
+    MojoMain(MojoHandle service_provider_handle);
+#endif  // defined(WIN32)
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_MAIN_H_
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
new file mode 100644
index 0000000..b08ba75
--- /dev/null
+++ b/mojo/public/c/system/message_pipe.h
@@ -0,0 +1,179 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains types/constants and functions specific to message pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateMessagePipeOptions|: Used to specify creation parameters for a
+// message pipe to |MojoCreateMessagePipe()|.
+//   |uint32_t struct_size|: Set to the size of the
+//       |MojoCreateMessagePipeOptions| struct. (Used to allow for future
+//       extensions.)
+//   |MojoCreateMessagePipeOptionsFlags flags|: Reserved for future use.
+//       |MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+
+typedef uint32_t MojoCreateMessagePipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateMessagePipeOptionsFlags
+    MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE \
+  ((MojoCreateMessagePipeOptionsFlags)0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateMessagePipeOptions {
+  uint32_t struct_size;
+  MojoCreateMessagePipeOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateMessagePipeOptions) == 8,
+                    MojoCreateMessagePipeOptions_has_wrong_size);
+
+// |MojoWriteMessageFlags|: Used to specify different modes to
+// |MojoWriteMessage()|.
+//   |MOJO_WRITE_MESSAGE_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoWriteMessageFlags;
+
+#ifdef __cplusplus
+const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE = 0;
+#else
+#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags)0)
+#endif
+
+// |MojoReadMessageFlags|: Used to specify different modes to
+// |MojoReadMessage()|.
+//   |MOJO_READ_MESSAGE_FLAG_NONE| - No flags; default mode.
+//   |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| - If the message is unable to be read
+//       for whatever reason (e.g., the caller-supplied buffer is too small),
+//       discard the message (i.e., simply dequeue it).
+
+typedef uint32_t MojoReadMessageFlags;
+
+#ifdef __cplusplus
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0;
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags)0)
+#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags)1 << 0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a message pipe, which is a bidirectional communication channel for
+// framed data (i.e., messages). Messages can contain plain data and/or Mojo
+// handles.
+//
+// |options| may be set to null for a message pipe with the default options.
+//
+// On success, |*message_pipe_handle0| and |*message_pipe_handle1| are set to
+// handles for the two endpoints (ports) for the message pipe.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+//       |*options| is invalid).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+//       been reached.
+//
+// TODO(vtl): Add an options struct pointer argument.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe(
+    const struct MojoCreateMessagePipeOptions* options,  // Optional.
+    MojoHandle* message_pipe_handle0,                    // Out.
+    MojoHandle* message_pipe_handle1);                   // Out.
+
+// Writes a message to the message pipe endpoint given by |message_pipe_handle|,
+// with message data specified by |bytes| of size |num_bytes| and attached
+// handles specified by |handles| of count |num_handles|, and options specified
+// by |flags|. If there is no message data, |bytes| may be null, in which case
+// |num_bytes| must be zero. If there are no attached handles, |handles| may be
+// null, in which case |num_handles| must be zero.
+//
+// If handles are attached, on success the handles will no longer be valid (the
+// receiver will receive equivalent, but logically different, handles). Handles
+// to be sent should not be in simultaneous use (e.g., on another thread).
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success (i.e., the message was enqueued).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+//       |message_pipe_handle| is not a valid handle, or some of the
+//       requirements above are not satisfied).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if some system limit has been reached, or
+//       the number of handles to send is too large (TODO(vtl): reconsider the
+//       latter case).
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+//       Note that closing an endpoint is not necessarily synchronous (e.g.,
+//       across processes), so this function may be succeed even if the other
+//       endpoint has been closed (in which case the message would be dropped).
+//   |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+//   |MOJO_RESULT_BUSY| if some handle to be sent is currently in use.
+//
+// TODO(vtl): Add a notion of capacity for message pipes, and return
+// |MOJO_RESULT_SHOULD_WAIT| if the message pipe is full.
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoWriteMessage(MojoHandle message_pipe_handle,
+                     const void* bytes,  // Optional.
+                     uint32_t num_bytes,
+                     const MojoHandle* handles,  // Optional.
+                     uint32_t num_handles,
+                     MojoWriteMessageFlags flags);
+
+// Reads a message from the message pipe endpoint given by
+// |message_pipe_handle|; also usable to query the size of the next message or
+// discard the next message. |bytes|/|*num_bytes| indicate the buffer/buffer
+// size to receive the message data (if any) and |handles|/|*num_handles|
+// indicate the buffer/maximum handle count to receive the attached handles (if
+// any).
+//
+// |num_bytes| and |num_handles| are optional "in-out" parameters. If non-null,
+// on return |*num_bytes| and |*num_handles| will usually indicate the number
+// of bytes and number of attached handles in the "next" message, respectively,
+// whether that message was read or not. (If null, the number of bytes/handles
+// is treated as zero.)
+//
+// If |bytes| is null, then |*num_bytes| must be zero, and similarly for
+// |handles| and |*num_handles|.
+//
+// Partial reads are NEVER done. Either a full read is done and |MOJO_RESULT_OK|
+// returned, or the read is NOT done and |MOJO_RESULT_RESOURCE_EXHAUSTED| is
+// returned (if |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| was set, the message is
+// also discarded in this case).
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success (i.e., a message was actually read).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if one of the buffers to receive the
+//       message/attached handles (|bytes|/|*num_bytes| or
+//       |handles|/|*num_handles|) was too small. (TODO(vtl): Reconsider this
+//       error code; should distinguish this from the hitting-system-limits
+//       case.)
+//   |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoReadMessage(MojoHandle message_pipe_handle,
+                    void* bytes,            // Optional out.
+                    uint32_t* num_bytes,    // Optional in/out.
+                    MojoHandle* handles,    // Optional out.
+                    uint32_t* num_handles,  // Optional in/out.
+                    MojoReadMessageFlags flags);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/c/system/system_export.h b/mojo/public/c/system/system_export.h
new file mode 100644
index 0000000..bc3b459
--- /dev/null
+++ b/mojo/public/c/system/system_export.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_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
+#define MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
+
+#if defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_SYSTEM_IMPL)
+
+#define MOJO_SYSTEM_EXPORT
+
+#endif  // defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn
new file mode 100644
index 0000000..e9aa06f
--- /dev/null
+++ b/mojo/public/c/system/tests/BUILD.gn
@@ -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.
+
+source_set("tests") {
+  testonly = true
+  visibility = [ "//mojo/public/cpp/system/tests:mojo_public_system_unittests" ]
+
+  deps = [
+    "//mojo/public/c/environment",
+    "//mojo/public/c/system",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "core_unittest.cc",
+    "core_unittest_pure_c.c",
+    "macros_unittest.cc",
+  ]
+}
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_system_perftests
+executable("perftests") {
+  testonly = true
+  output_name = "mojo_public_system_perftests"
+
+  sources = [
+    "core_perftest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/common/test:run_all_perftests",
+    "//mojo/public/c/environment",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/cpp/utility",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc
new file mode 100644
index 0000000..b9a8114
--- /dev/null
+++ b/mojo/public/c/system/tests/core_perftest.cc
@@ -0,0 +1,339 @@
+// 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.
+
+// This tests the performance of the C API.
+
+#include "mojo/public/c/system/core.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(vtl): (here and below) crbug.com/342893
+#if !defined(WIN32)
+#include <time.h>
+#include "mojo/public/cpp/utility/thread.h"
+#endif  // !defined(WIN32)
+
+namespace {
+
+#if !defined(WIN32)
+class MessagePipeWriterThread : public mojo::Thread {
+ public:
+  MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes)
+      : handle_(handle), num_bytes_(num_bytes), num_writes_(0) {}
+  virtual ~MessagePipeWriterThread() {}
+
+  virtual void Run() override {
+    char buffer[10000];
+    assert(num_bytes_ <= sizeof(buffer));
+
+    // TODO(vtl): Should I throttle somehow?
+    for (;;) {
+      MojoResult result = MojoWriteMessage(
+          handle_, buffer, num_bytes_, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+      if (result == MOJO_RESULT_OK) {
+        num_writes_++;
+        continue;
+      }
+
+      // We failed to write.
+      // Either |handle_| or its peer was closed.
+      assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
+             result == MOJO_RESULT_FAILED_PRECONDITION);
+      break;
+    }
+  }
+
+  // Use only after joining the thread.
+  int64_t num_writes() const { return num_writes_; }
+
+ private:
+  const MojoHandle handle_;
+  const uint32_t num_bytes_;
+  int64_t num_writes_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread);
+};
+
+class MessagePipeReaderThread : public mojo::Thread {
+ public:
+  explicit MessagePipeReaderThread(MojoHandle handle)
+      : handle_(handle), num_reads_(0) {}
+  virtual ~MessagePipeReaderThread() {}
+
+  virtual void Run() override {
+    char buffer[10000];
+
+    for (;;) {
+      uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+      MojoResult result = MojoReadMessage(
+          handle_, buffer, &num_bytes, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE);
+      if (result == MOJO_RESULT_OK) {
+        num_reads_++;
+        continue;
+      }
+
+      if (result == MOJO_RESULT_SHOULD_WAIT) {
+        result = MojoWait(
+            handle_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+        if (result == MOJO_RESULT_OK) {
+          // Go to the top of the loop to read again.
+          continue;
+        }
+      }
+
+      // We failed to read and possibly failed to wait.
+      // Either |handle_| or its peer was closed.
+      assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
+             result == MOJO_RESULT_FAILED_PRECONDITION);
+      break;
+    }
+  }
+
+  // Use only after joining the thread.
+  int64_t num_reads() const { return num_reads_; }
+
+ private:
+  const MojoHandle handle_;
+  int64_t num_reads_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeReaderThread);
+};
+#endif  // !defined(WIN32)
+
+class CorePerftest : public testing::Test {
+ public:
+  CorePerftest() : buffer_(NULL), num_bytes_(0) {}
+  virtual ~CorePerftest() {}
+
+  static void NoOp(void* /*closure*/) {}
+
+  static void MessagePipe_CreateAndClose(void* closure) {
+    CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoResult result MOJO_ALLOW_UNUSED;
+    result = MojoCreateMessagePipe(NULL, &self->h0_, &self->h1_);
+    assert(result == MOJO_RESULT_OK);
+    result = MojoClose(self->h0_);
+    assert(result == MOJO_RESULT_OK);
+    result = MojoClose(self->h1_);
+    assert(result == MOJO_RESULT_OK);
+  }
+
+  static void MessagePipe_WriteAndRead(void* closure) {
+    CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoResult result MOJO_ALLOW_UNUSED;
+    result = MojoWriteMessage(self->h0_,
+                              self->buffer_,
+                              self->num_bytes_,
+                              NULL,
+                              0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE);
+    assert(result == MOJO_RESULT_OK);
+    uint32_t read_bytes = self->num_bytes_;
+    result = MojoReadMessage(self->h1_,
+                             self->buffer_,
+                             &read_bytes,
+                             NULL,
+                             NULL,
+                             MOJO_READ_MESSAGE_FLAG_NONE);
+    assert(result == MOJO_RESULT_OK);
+  }
+
+  static void MessagePipe_EmptyRead(void* closure) {
+    CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoResult result MOJO_ALLOW_UNUSED;
+    result = MojoReadMessage(
+        self->h0_, NULL, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+    assert(result == MOJO_RESULT_SHOULD_WAIT);
+  }
+
+ protected:
+#if !defined(WIN32)
+  void DoMessagePipeThreadedTest(unsigned num_writers,
+                                 unsigned num_readers,
+                                 uint32_t num_bytes) {
+    static const int64_t kPerftestTimeMicroseconds = 3 * 1000000;
+
+    assert(num_writers > 0);
+    assert(num_readers > 0);
+
+    MojoResult result MOJO_ALLOW_UNUSED;
+    result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+    assert(result == MOJO_RESULT_OK);
+
+    std::vector<MessagePipeWriterThread*> writers;
+    for (unsigned i = 0; i < num_writers; i++)
+      writers.push_back(new MessagePipeWriterThread(h0_, num_bytes));
+
+    std::vector<MessagePipeReaderThread*> readers;
+    for (unsigned i = 0; i < num_readers; i++)
+      readers.push_back(new MessagePipeReaderThread(h1_));
+
+    // Start time here, just before we fire off the threads.
+    const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+
+    // Interleave the starts.
+    for (unsigned i = 0; i < num_writers || i < num_readers; i++) {
+      if (i < num_writers)
+        writers[i]->Start();
+      if (i < num_readers)
+        readers[i]->Start();
+    }
+
+    Sleep(kPerftestTimeMicroseconds);
+
+    // Close both handles to make writers and readers stop immediately.
+    result = MojoClose(h0_);
+    assert(result == MOJO_RESULT_OK);
+    result = MojoClose(h1_);
+    assert(result == MOJO_RESULT_OK);
+
+    // Join everything.
+    for (unsigned i = 0; i < num_writers; i++)
+      writers[i]->Join();
+    for (unsigned i = 0; i < num_readers; i++)
+      readers[i]->Join();
+
+    // Stop time here.
+    MojoTimeTicks end_time = MojoGetTimeTicksNow();
+
+    // Add up write and read counts, and destroy the threads.
+    int64_t num_writes = 0;
+    for (unsigned i = 0; i < num_writers; i++) {
+      num_writes += writers[i]->num_writes();
+      delete writers[i];
+    }
+    writers.clear();
+    int64_t num_reads = 0;
+    for (unsigned i = 0; i < num_readers; i++) {
+      num_reads += readers[i]->num_reads();
+      delete readers[i];
+    }
+    readers.clear();
+
+    char test_name[200];
+    sprintf(test_name,
+            "MessagePipe_Threaded_Writes_%uw_%ur_%ubytes",
+            num_writers,
+            num_readers,
+            static_cast<unsigned>(num_bytes));
+    mojo::test::LogPerfResult(
+        test_name,
+        1000000.0 * static_cast<double>(num_writes) / (end_time - start_time),
+        "writes/second");
+    sprintf(test_name,
+            "MessagePipe_Threaded_Reads_%uw_%ur_%ubytes",
+            num_writers,
+            num_readers,
+            static_cast<unsigned>(num_bytes));
+    mojo::test::LogPerfResult(
+        test_name,
+        1000000.0 * static_cast<double>(num_reads) / (end_time - start_time),
+        "reads/second");
+  }
+#endif  // !defined(WIN32)
+
+  MojoHandle h0_;
+  MojoHandle h1_;
+
+  void* buffer_;
+  uint32_t num_bytes_;
+
+ private:
+#if !defined(WIN32)
+  void Sleep(int64_t microseconds) {
+    struct timespec req = {
+        static_cast<time_t>(microseconds / 1000000),       // Seconds.
+        static_cast<long>(microseconds % 1000000) * 1000L  // Nanoseconds.
+    };
+    int rv MOJO_ALLOW_UNUSED;
+    rv = nanosleep(&req, NULL);
+    assert(rv == 0);
+  }
+#endif  // !defined(WIN32)
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(CorePerftest);
+};
+
+// A no-op test so we can compare performance.
+TEST_F(CorePerftest, NoOp) {
+  mojo::test::IterateAndReportPerf("NoOp", &CorePerftest::NoOp, this);
+}
+
+TEST_F(CorePerftest, MessagePipe_CreateAndClose) {
+  mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose",
+                                   &CorePerftest::MessagePipe_CreateAndClose,
+                                   this);
+}
+
+TEST_F(CorePerftest, MessagePipe_WriteAndRead) {
+  MojoResult result MOJO_ALLOW_UNUSED;
+  result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+  assert(result == MOJO_RESULT_OK);
+  char buffer[10000] = {0};
+  buffer_ = buffer;
+  num_bytes_ = 10u;
+  mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10bytes",
+                                   &CorePerftest::MessagePipe_WriteAndRead,
+                                   this);
+  num_bytes_ = 100u;
+  mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_100bytes",
+                                   &CorePerftest::MessagePipe_WriteAndRead,
+                                   this);
+  num_bytes_ = 1000u;
+  mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_1000bytes",
+                                   &CorePerftest::MessagePipe_WriteAndRead,
+                                   this);
+  num_bytes_ = 10000u;
+  mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10000bytes",
+                                   &CorePerftest::MessagePipe_WriteAndRead,
+                                   this);
+  result = MojoClose(h0_);
+  assert(result == MOJO_RESULT_OK);
+  result = MojoClose(h1_);
+  assert(result == MOJO_RESULT_OK);
+}
+
+TEST_F(CorePerftest, MessagePipe_EmptyRead) {
+  MojoResult result MOJO_ALLOW_UNUSED;
+  result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+  assert(result == MOJO_RESULT_OK);
+  mojo::test::IterateAndReportPerf(
+      "MessagePipe_EmptyRead", &CorePerftest::MessagePipe_EmptyRead, this);
+  result = MojoClose(h0_);
+  assert(result == MOJO_RESULT_OK);
+  result = MojoClose(h1_);
+  assert(result == MOJO_RESULT_OK);
+}
+
+#if !defined(WIN32)
+TEST_F(CorePerftest, MessagePipe_Threaded) {
+  DoMessagePipeThreadedTest(1u, 1u, 100u);
+  DoMessagePipeThreadedTest(2u, 2u, 100u);
+  DoMessagePipeThreadedTest(3u, 3u, 100u);
+  DoMessagePipeThreadedTest(10u, 10u, 100u);
+  DoMessagePipeThreadedTest(10u, 1u, 100u);
+  DoMessagePipeThreadedTest(1u, 10u, 100u);
+
+  // For comparison of overhead:
+  DoMessagePipeThreadedTest(1u, 1u, 10u);
+  // 100 was done above.
+  DoMessagePipeThreadedTest(1u, 1u, 1000u);
+  DoMessagePipeThreadedTest(1u, 1u, 10000u);
+
+  DoMessagePipeThreadedTest(3u, 3u, 10u);
+  // 100 was done above.
+  DoMessagePipeThreadedTest(3u, 3u, 1000u);
+  DoMessagePipeThreadedTest(3u, 3u, 10000u);
+}
+#endif  // !defined(WIN32)
+
+}  // namespace
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
new file mode 100644
index 0000000..d071f48
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -0,0 +1,298 @@
+// 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.
+
+// This file tests the C API.
+
+#include "mojo/public/c/system/core.h"
+
+#include <string.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(CoreTest, GetTimeTicksNow) {
+  const MojoTimeTicks start = MojoGetTimeTicksNow();
+  EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+      << "MojoGetTimeTicksNow should return nonzero value";
+}
+
+// The only handle that's guaranteed to be invalid is |MOJO_HANDLE_INVALID|.
+// Tests that everything that takes a handle properly recognizes it.
+TEST(CoreTest, InvalidHandle) {
+  MojoHandle h0, h1;
+  MojoHandleSignals sig;
+  char buffer[10] = {0};
+  uint32_t buffer_size;
+  void* write_pointer;
+  const void* read_pointer;
+
+  // Close:
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID));
+
+  // Wait:
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000));
+  h0 = MOJO_HANDLE_INVALID;
+  sig = ~MOJO_HANDLE_SIGNAL_NONE;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+  // Message pipe:
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoWriteMessage(h0, buffer, 3, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoReadMessage(
+          h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Data pipe:
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWriteData(h0, buffer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+  write_pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoBeginWriteData(
+                h0, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndWriteData(h0, 1));
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoReadData(h0, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+  read_pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoBeginReadData(
+                h0, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndReadData(h0, 1));
+
+  // Shared buffer:
+  h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoDuplicateBufferHandle(h0, NULL, &h1));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoMapBuffer(h0, 0, 1, &write_pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+}
+
+TEST(CoreTest, BasicMessagePipe) {
+  MojoHandle h0, h1;
+  MojoHandleSignals sig;
+  char buffer[10] = {0};
+  uint32_t buffer_size;
+
+  h0 = MOJO_HANDLE_INVALID;
+  h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &h0, &h1));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Shouldn't be readable.
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // Should be writable.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+  // Try to read.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(
+      MOJO_RESULT_SHOULD_WAIT,
+      MojoReadMessage(
+          h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Write to |h1|.
+  static const char kHello[] = "hello";
+  buffer_size = static_cast<uint32_t>(sizeof(kHello));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoWriteMessage(
+          h1, kHello, buffer_size, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // |h0| should be readable.
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+  // Read from |h0|.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoReadMessage(
+          h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size);
+  EXPECT_STREQ(kHello, buffer);
+
+  // |h0| should no longer be readable.
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 10));
+
+  // Close |h0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+
+  // |h1| should no longer be readable or writable.
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      MojoWait(
+          h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, 1000));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+// TODO(ncbray): enable these tests once NaCl supports the corresponding APIs.
+#ifdef __native_client__
+#define MAYBE_BasicDataPipe DISABLED_BasicDataPipe
+#define MAYBE_BasicSharedBuffer DISABLED_BasicSharedBuffer
+#else
+#define MAYBE_BasicDataPipe BasicDataPipe
+#define MAYBE_BasicSharedBuffer BasicSharedBuffer
+#endif
+
+TEST(CoreTest, MAYBE_BasicDataPipe) {
+  MojoHandle hp, hc;
+  MojoHandleSignals sig;
+  char buffer[20] = {0};
+  uint32_t buffer_size;
+  void* write_pointer;
+  const void* read_pointer;
+
+  hp = MOJO_HANDLE_INVALID;
+  hc = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(NULL, &hp, &hc));
+  EXPECT_NE(hp, MOJO_HANDLE_INVALID);
+  EXPECT_NE(hc, MOJO_HANDLE_INVALID);
+
+  // The consumer |hc| shouldn't be readable.
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // The producer |hp| should be writable.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+  // Try to read from |hc|.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+  // Try to begin a two-phase read from |hc|.
+  read_pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoBeginReadData(
+                hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+  // Write to |hp|.
+  static const char kHello[] = "hello ";
+  // Don't include terminating null.
+  buffer_size = static_cast<uint32_t>(strlen(kHello));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoWriteData(hp, kHello, &buffer_size, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // |hc| should be(come) readable.
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitMany(&hc, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+  // Do a two-phase write to |hp|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoBeginWriteData(
+                hp, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+  static const char kWorld[] = "world";
+  ASSERT_GE(buffer_size, sizeof(kWorld));
+  // Include the terminating null.
+  memcpy(write_pointer, kWorld, sizeof(kWorld));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoEndWriteData(hp, static_cast<uint32_t>(sizeof(kWorld))));
+
+  // Read one character from |hc|.
+  memset(buffer, 0, sizeof(buffer));
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+  // Close |hp|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hp));
+
+  // |hc| should still be readable.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // Do a two-phase read from |hc|.
+  read_pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoBeginReadData(
+                hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+  ASSERT_LE(buffer_size, sizeof(buffer) - 1);
+  memcpy(&buffer[1], read_pointer, buffer_size);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size));
+  EXPECT_STREQ("hello world", buffer);
+
+  // |hc| should no longer be readable.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 1000));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hc));
+
+  // TODO(vtl): Test the other way around -- closing the consumer should make
+  // the producer never-writable?
+}
+
+TEST(CoreTest, MAYBE_BasicSharedBuffer) {
+  MojoHandle h0, h1;
+  void* pointer;
+
+  // Create a shared buffer (|h0|).
+  h0 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(NULL, 100, &h0));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+
+  // Map everything.
+  pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoMapBuffer(h0, 0, 100, &pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+  ASSERT_TRUE(pointer);
+  static_cast<char*>(pointer)[50] = 'x';
+
+  // Duplicate |h0| to |h1|.
+  h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, NULL, &h1));
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Close |h0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+
+  // The mapping should still be good.
+  static_cast<char*>(pointer)[51] = 'y';
+
+  // Unmap it.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
+
+  // Map half of |h1|.
+  pointer = NULL;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoMapBuffer(h1, 50, 50, &pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+  ASSERT_TRUE(pointer);
+
+  // It should have what we wrote.
+  EXPECT_EQ('x', static_cast<char*>(pointer)[0]);
+  EXPECT_EQ('y', static_cast<char*>(pointer)[1]);
+
+  // Unmap it.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+// Defined in core_unittest_pure_c.c.
+extern "C" const char* MinimalCTest(void);
+
+// This checks that things actually work in C (not C++).
+TEST(CoreTest, MinimalCTest) {
+  const char* failure = MinimalCTest();
+  EXPECT_TRUE(failure == NULL) << failure;
+}
+
+// TODO(vtl): Add multi-threaded tests.
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
new file mode 100644
index 0000000..33de688
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -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.
+
+#ifdef __cplusplus
+#error "This file should be compiled as C, not C++."
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+// Include all the header files that are meant to be compilable as C. Start with
+// core.h, since it's the most important one.
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/c/system/macros.h"
+
+// The joys of the C preprocessor....
+#define STRINGIFY(x) #x
+#define STRINGIFY2(x) STRINGIFY(x)
+#define FAILURE(message) \
+  __FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message
+
+// Poor man's gtest.
+#define EXPECT_EQ(a, b)                                                  \
+  do {                                                                   \
+    if ((a) != (b))                                                      \
+      return FAILURE(STRINGIFY(a) " != " STRINGIFY(b) " (expected ==)"); \
+  } while (0)
+#define EXPECT_NE(a, b)                                                  \
+  do {                                                                   \
+    if ((a) == (b))                                                      \
+      return FAILURE(STRINGIFY(a) " == " STRINGIFY(b) " (expected !=)"); \
+  } while (0)
+
+// This function exists mainly to be compiled and linked. We do some cursory
+// checks and call it from a unit test, to make sure that link problems aren't
+// missed due to deadstripping. Returns null on success and a string on failure
+// (describing the failure).
+const char* MinimalCTest(void) {
+  // MSVS before 2013 *really* only supports C90: All variables must be declared
+  // at the top. (MSVS 2013 is more reasonable.)
+  MojoTimeTicks ticks;
+  MojoHandle handle0, handle1;
+  MojoHandleSignals signals;
+  const char kHello[] = "hello";
+  char buffer[200] = {0};
+  uint32_t num_bytes;
+
+  ticks = MojoGetTimeTicksNow();
+  EXPECT_NE(ticks, 0);
+
+  handle0 = MOJO_HANDLE_INVALID;
+  EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0));
+
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE));
+
+  handle1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1));
+
+  signals = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWaitMany(&handle0, &signals, 1, 1));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessage(handle0,
+                             kHello,
+                             (uint32_t)sizeof(kHello),
+                             NULL,
+                             0u,
+                             MOJO_WRITE_DATA_FLAG_NONE));
+
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+  num_bytes = (uint32_t)sizeof(buffer);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(handle1,
+                            buffer,
+                            &num_bytes,
+                            NULL,
+                            NULL,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes);
+  EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello)));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle0));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle1));
+
+  // TODO(vtl): data pipe
+
+  return NULL;
+}
diff --git a/mojo/public/c/system/tests/macros_unittest.cc b/mojo/public/c/system/tests/macros_unittest.cc
new file mode 100644
index 0000000..6a694b8
--- /dev/null
+++ b/mojo/public/c/system/tests/macros_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file tests the C Mojo system macros and consists of "positive" tests,
+// i.e., those verifying that things work (without compile errors, or even
+// warnings if warnings are treated as errors).
+// TODO(vtl): Fix no-compile tests (which are all disabled; crbug.com/105388)
+// and write some "negative" tests.
+
+#include "mojo/public/c/system/macros.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(MacrosTest, AllowUnused) {
+  // Test that no warning/error is issued even though |x| is unused.
+  int x MOJO_ALLOW_UNUSED = 123;
+}
+
+int MustUseReturnedResult() MOJO_WARN_UNUSED_RESULT;
+int MustUseReturnedResult() {
+  return 456;
+}
+
+TEST(MacrosTest, WarnUnusedResult) {
+  if (!MustUseReturnedResult())
+    abort();
+}
+
+// First test |MOJO_COMPILE_ASSERT()| in a global scope.
+MOJO_COMPILE_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t),
+                    bad_compile_assert_failure_in_global_scope);
+
+TEST(MacrosTest, CompileAssert) {
+  // Then in a local scope.
+  MOJO_COMPILE_ASSERT(sizeof(int32_t) == 2 * sizeof(int16_t),
+                      bad_compile_assert_failure);
+}
+
+TEST(MacrosTest, Alignof) {
+  // Strictly speaking, this isn't a portable test, but I think it'll pass on
+  // all the platforms we currently support.
+  EXPECT_EQ(1u, MOJO_ALIGNOF(char));
+  EXPECT_EQ(4u, MOJO_ALIGNOF(int32_t));
+  EXPECT_EQ(8u, MOJO_ALIGNOF(int64_t));
+  EXPECT_EQ(8u, MOJO_ALIGNOF(double));
+}
+
+// These structs are used in the Alignas test. Define them globally to avoid
+// MSVS warnings/errors.
+#if defined(_MSC_VER)
+#pragma warning(push)
+// Disable the warning "structure was padded due to __declspec(align())".
+#pragma warning(disable : 4324)
+#endif
+struct MOJO_ALIGNAS(1) StructAlignas1 {
+  char x;
+};
+struct MOJO_ALIGNAS(4) StructAlignas4 {
+  char x;
+};
+struct MOJO_ALIGNAS(8) StructAlignas8 {
+  char x;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+TEST(MacrosTest, Alignas) {
+  EXPECT_EQ(1u, MOJO_ALIGNOF(StructAlignas1));
+  EXPECT_EQ(4u, MOJO_ALIGNOF(StructAlignas4));
+  EXPECT_EQ(8u, MOJO_ALIGNOF(StructAlignas8));
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
new file mode 100644
index 0000000..5a72d2f
--- /dev/null
+++ b/mojo/public/c/system/types.h
@@ -0,0 +1,176 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains types and constants/macros common to different Mojo system
+// APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+#define MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+
+// TODO(vtl): Notes: Use of undefined flags will lead to undefined behavior
+// (typically they'll be ignored), not necessarily an error.
+
+// |MojoTimeTicks|: Used to specify time ticks. Value is in microseconds.
+
+typedef int64_t MojoTimeTicks;
+
+// |MojoHandle|: Handles to Mojo objects.
+//   |MOJO_HANDLE_INVALID| - A value that is never a valid handle.
+
+typedef uint32_t MojoHandle;
+
+#ifdef __cplusplus
+const MojoHandle MOJO_HANDLE_INVALID = 0;
+#else
+#define MOJO_HANDLE_INVALID ((MojoHandle)0)
+#endif
+
+// |MojoResult|: Result codes for Mojo operations. Non-negative values are
+// success codes; negative values are error/failure codes.
+//   |MOJO_RESULT_OK| - Not an error; returned on success. Note that positive
+//       |MojoResult|s may also be used to indicate success.
+//   |MOJO_RESULT_CANCELLED| - Operation was cancelled, typically by the caller.
+//   |MOJO_RESULT_UNKNOWN| - Unknown error (e.g., if not enough information is
+//       available for a more specific error).
+//   |MOJO_RESULT_INVALID_ARGUMENT| - Caller specified an invalid argument. This
+//       differs from |MOJO_RESULT_FAILED_PRECONDITION| in that the former
+//       indicates arguments that are invalid regardless of the state of the
+//       system.
+//   |MOJO_RESULT_DEADLINE_EXCEEDED| - Deadline expired before the operation
+//       could complete.
+//   |MOJO_RESULT_NOT_FOUND| - Some requested entity was not found (i.e., does
+//       not exist).
+//   |MOJO_RESULT_ALREADY_EXISTS| - Some entity or condition that we attempted
+//       to create already exists.
+//   |MOJO_RESULT_PERMISSION_DENIED| - The caller does not have permission to
+//       for the operation (use |MOJO_RESULT_RESOURCE_EXHAUSTED| for rejections
+//       caused by exhausting some resource instead).
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| - Some resource required for the call
+//       (possibly some quota) has been exhausted.
+//   |MOJO_RESULT_FAILED_PRECONDITION| - The system is not in a state required
+//       for the operation (use this if the caller must do something to rectify
+//       the state before retrying).
+//   |MOJO_RESULT_ABORTED| - The operation was aborted by the system, possibly
+//       due to a concurrency issue (use this if the caller may retry at a
+//       higher level).
+//   |MOJO_RESULT_OUT_OF_RANGE| - The operation was attempted past the valid
+//       range. Unlike |MOJO_RESULT_INVALID_ARGUMENT|, this indicates that the
+//       operation may be/become valid depending on the system state. (This
+//       error is similar to |MOJO_RESULT_FAILED_PRECONDITION|, but is more
+//       specific.)
+//   |MOJO_RESULT_UNIMPLEMENTED| - The operation is not implemented, supported,
+//       or enabled.
+//   |MOJO_RESULT_INTERNAL| - Internal error: this should never happen and
+//       indicates that some invariant expected by the system has been broken.
+//   |MOJO_RESULT_UNAVAILABLE| - The operation is (temporarily) currently
+//       unavailable. The caller may simply retry the operation (possibly with a
+//       backoff).
+//   |MOJO_RESULT_DATA_LOSS| - Unrecoverable data loss or corruption.
+//   |MOJO_RESULT_BUSY| - One of the resources involved is currently being used
+//       (possibly on another thread) in a way that prevents the current
+//       operation from proceeding, e.g., if the other operation may result in
+//       the resource being invalidated.
+//   |MOJO_RESULT_SHOULD_WAIT| - The request cannot currently be completed
+//       (e.g., if the data requested is not yet available). The caller should
+//       wait for it to be feasible using |MojoWait()| or |MojoWaitMany()|.
+//
+// Note that positive values are also available as success codes.
+//
+// The codes from |MOJO_RESULT_OK| to |MOJO_RESULT_DATA_LOSS| come from
+// Google3's canonical error codes.
+//
+// TODO(vtl): Add a |MOJO_RESULT_UNSATISFIABLE|?
+
+typedef int32_t MojoResult;
+
+#ifdef __cplusplus
+const MojoResult MOJO_RESULT_OK = 0;
+const MojoResult MOJO_RESULT_CANCELLED = -1;
+const MojoResult MOJO_RESULT_UNKNOWN = -2;
+const MojoResult MOJO_RESULT_INVALID_ARGUMENT = -3;
+const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED = -4;
+const MojoResult MOJO_RESULT_NOT_FOUND = -5;
+const MojoResult MOJO_RESULT_ALREADY_EXISTS = -6;
+const MojoResult MOJO_RESULT_PERMISSION_DENIED = -7;
+const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED = -8;
+const MojoResult MOJO_RESULT_FAILED_PRECONDITION = -9;
+const MojoResult MOJO_RESULT_ABORTED = -10;
+const MojoResult MOJO_RESULT_OUT_OF_RANGE = -11;
+const MojoResult MOJO_RESULT_UNIMPLEMENTED = -12;
+const MojoResult MOJO_RESULT_INTERNAL = -13;
+const MojoResult MOJO_RESULT_UNAVAILABLE = -14;
+const MojoResult MOJO_RESULT_DATA_LOSS = -15;
+const MojoResult MOJO_RESULT_BUSY = -16;
+const MojoResult MOJO_RESULT_SHOULD_WAIT = -17;
+#else
+#define MOJO_RESULT_OK ((MojoResult)0)
+#define MOJO_RESULT_CANCELLED ((MojoResult) - 1)
+#define MOJO_RESULT_UNKNOWN ((MojoResult) - 2)
+#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult) - 3)
+#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult) - 4)
+#define MOJO_RESULT_NOT_FOUND ((MojoResult) - 5)
+#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult) - 6)
+#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult) - 7)
+#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult) - 8)
+#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult) - 9)
+#define MOJO_RESULT_ABORTED ((MojoResult) - 10)
+#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult) - 11)
+#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult) - 12)
+#define MOJO_RESULT_INTERNAL ((MojoResult) - 13)
+#define MOJO_RESULT_UNAVAILABLE ((MojoResult) - 14)
+#define MOJO_RESULT_DATA_LOSS ((MojoResult) - 15)
+#define MOJO_RESULT_BUSY ((MojoResult) - 16)
+#define MOJO_RESULT_SHOULD_WAIT ((MojoResult) - 17)
+#endif
+
+// |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except
+// for |MOJO_DEADLINE_INDEFINITE|).
+//   |MOJO_DEADLINE_INDEFINITE| - Used to indicate "forever".
+
+typedef uint64_t MojoDeadline;
+
+#ifdef __cplusplus
+const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1);
+#else
+#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) - 1)
+#endif
+
+// |MojoHandleSignals|: Used to specify signals that can be waited on for a
+// handle (and which can be triggered), e.g., the ability to read or write to
+// the handle.
+//   |MOJO_HANDLE_SIGNAL_NONE| - No flags. |MojoWait()|, etc. will return
+//       |MOJO_RESULT_FAILED_PRECONDITION| if you attempt to wait on this.
+//   |MOJO_HANDLE_SIGNAL_READABLE| - Can read (e.g., a message) from the handle.
+//   |MOJO_HANDLE_SIGNAL_WRITABLE| - Can write (e.g., a message) to the handle.
+
+typedef uint32_t MojoHandleSignals;
+
+#ifdef __cplusplus
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1;
+#else
+#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0)
+#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0)
+#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1)
+#endif
+
+// TODO(vtl): Add out parameters with this to MojoWait/MojoWaitMany.
+// Note: This struct is not extensible (and only has 32-bit quantities), so it's
+// 32-bit-aligned.
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int32_t) == 4, int32_t_has_weird_alignment);
+struct MOJO_ALIGNAS(4) MojoHandleSignalsState {
+  MojoHandleSignals satisfied_signals;
+  MojoHandleSignals satisfiable_signals;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoHandleSignalsState) == 8,
+                    MojoHandleSignalsState_has_wrong_size);
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_TYPES_H_
diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn
new file mode 100644
index 0000000..42db434
--- /dev/null
+++ b/mojo/public/c/test_support/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_test_support
+shared_library("test_support") {
+  output_name = "mojo_test_support"
+
+  defines = [ "MOJO_TEST_SUPPORT_IMPLEMENTATION" ]
+
+  sources = [
+    "test_support.h",
+    "test_support_export.h",
+    # TODO(vtl): Convert this to thunks http://crbug.com/386799
+    "../../tests/test_support_private.cc",
+    "../../tests/test_support_private.h",
+  ]
+
+  if (is_mac) {
+# TODO(GYP)
+#   # Make it a run-path dependent library.
+#   'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+  }
+}
diff --git a/mojo/public/c/test_support/test_support.h b/mojo/public/c/test_support/test_support.h
new file mode 100644
index 0000000..2b686b2
--- /dev/null
+++ b/mojo/public/c/test_support/test_support.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_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdio.h>
+
+#include "mojo/public/c/test_support/test_support_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_TEST_SUPPORT_EXPORT void MojoTestSupportLogPerfResult(
+    const char* test_name,
+    double value,
+    const char* units);
+
+// Opens a "/"-delimited file path relative to the source root.
+MOJO_TEST_SUPPORT_EXPORT FILE* MojoTestSupportOpenSourceRootRelativeFile(
+    const char* source_root_relative_path);
+
+// Enumerates a "/"-delimited directory path relative to the source root.
+// Returns only regular files. The return value is a heap-allocated array of
+// heap-allocated strings. Each must be free'd separately.
+//
+// The return value is built like so:
+//
+//   char** rv = (char**) calloc(N + 1, sizeof(char*));
+//   rv[0] = strdup("a");
+//   rv[1] = strdup("b");
+//   rv[2] = strdup("c");
+//   ...
+//   rv[N] = NULL;
+//
+MOJO_TEST_SUPPORT_EXPORT
+char** MojoTestSupportEnumerateSourceRootRelativeDirectory(
+    const char* source_root_relative_path);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/mojo/public/c/test_support/test_support_export.h b/mojo/public/c/test_support/test_support_export.h
new file mode 100644
index 0000000..e22a9e3
--- /dev/null
+++ b/mojo/public/c/test_support/test_support_export.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_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+
+#if defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllexport)
+#else
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_TEST_SUPPORT_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#endif  // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
diff --git a/mojo/public/cpp/DEPS b/mojo/public/cpp/DEPS
new file mode 100644
index 0000000..74acd7c
--- /dev/null
+++ b/mojo/public/cpp/DEPS
@@ -0,0 +1,18 @@
+include_rules = [
+  # Require explicit dependencies in each directory.
+  "-mojo/public",
+  # But everyone can depend on the C and C++ system headers.
+  "+mojo/public/c/system",
+  "+mojo/public/cpp/system",
+  # Ditto for the C environment headers (but not the C++ environment, since it
+  # has dependencies of its own).
+  "+mojo/public/c/environment",
+]
+
+specific_include_rules = {
+  r".*_(unit|perf)test\.cc": [
+    "+testing",
+    "+mojo/public/cpp/test_support",
+    "+mojo/public/cpp/utility",
+  ],
+}
diff --git a/mojo/public/cpp/README.md b/mojo/public/cpp/README.md
new file mode 100644
index 0000000..8f03d98
--- /dev/null
+++ b/mojo/public/cpp/README.md
@@ -0,0 +1,71 @@
+Mojo Public C++ API
+===================
+
+This directory contains C++ language bindings for the Mojo Public API.
+
+A number of subdirectories provide wrappers for the lower-level C APIs (in
+subdirectories of the same name, under mojo/public/c/). Typically, these
+wrappers provide increased convenience and/or type-safety.
+
+Other subdirectories provide support (static) libraries of various sorts. In
+this case, the organization is to have the public interface for the library in
+defined in header files in the subdirectory itself and the implementation of the
+library at a lower level, under a lib (sub)subdirectory. A developer should be
+able to substitute their own implementation of any such support library, and
+expect other support libraries, which may depend on that library, to work
+properly.
+
+Bindings
+--------
+
+The bindings/ subdirectory contains a support (static) library needed by the
+code generated by the bindings generator tool (in mojo/public/tools/bindings/),
+which translates Mojo IDL (.mojom) files into idiomatic C++ (among other
+languages).
+
+This library depends on the Environment library.
+
+Environment
+-----------
+
+The environment/ subdirectory contains a support (static) library that
+represents shared state needed to support the Bindings and GLES2 libraries.
+
+This library depends on the Utility library.
+
+
+GLES2
+-----
+
+The gles2/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/gles2/ (which provides access to GLES2).
+
+These wrappers depend on the Environment library.
+
+Shell
+-----
+
+The shell/ subdirectory contains a support (static) library that aids in writing
+Mojo applications and interacting with the Shell service.
+
+System
+------
+
+The system/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/system/, which defines the basic, "core" API,
+especially used to communicate with Mojo services.
+
+Test Support
+------------
+
+The test_support/ subdirectory contains C++ wrappers of the test-only API
+defined in mojo/public/c/test_support/. It is not meant for general use by Mojo
+applications.
+
+Utility
+-------
+
+The utility/ subdirectory contains a support (static) library that provides
+various basic functionality. Most notably, it provides an implementation of a
+RunLoop based on MojoWaitMany() that applications may use as the basis for
+asynchronous message processing.
diff --git a/mojo/public/cpp/application/BUILD.gn b/mojo/public/cpp/application/BUILD.gn
new file mode 100644
index 0000000..900edef
--- /dev/null
+++ b/mojo/public/cpp/application/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_public.gypi:mojo_application_base
+source_set("application") {
+  sources = [
+    "application_connection.h",
+    "application_delegate.h",
+    "application_impl.h",
+    "connect.h",
+    "service_provider_impl.h",
+    "interface_factory.h",
+    "interface_factory_impl.h",
+    "lib/application_connection.cc",
+    "lib/application_delegate.cc",
+    "lib/application_impl.cc",
+    "lib/service_provider_impl.cc",
+    "lib/service_connector.cc",
+    "lib/service_connector.h",
+    "lib/service_registry.cc",
+    "lib/service_registry.h",
+    "lib/weak_service_provider.cc",
+    "lib/weak_service_provider.h",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment",
+    "//mojo/public/cpp/system",
+    "//mojo/public/interfaces/application",
+  ]
+}
+
+# GYP version: mojo/mojo_public.gypi:mojo_application_standalone
+source_set("standalone") {
+  sources = [
+    "lib/application_runner.cc"
+  ]
+
+  public_deps = [ ":application" ]
+
+  deps = [
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/utility",
+  ]
+}
diff --git a/mojo/public/cpp/application/DEPS b/mojo/public/cpp/application/DEPS
new file mode 100644
index 0000000..dcd8e6f
--- /dev/null
+++ b/mojo/public/cpp/application/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+  "+mojo/public/cpp/bindings",
+  "+mojo/public/cpp/environment",
+  "+mojo/public/interfaces/application",
+  "+mojo/public/interfaces/service_provider",
+]
+
+specific_include_rules = {
+  r"application_runner_chromium.*": [
+    "+base",
+    "+mojo/common",
+    "+mojo/public/cpp"
+  ],
+  r"application_runner.*": [
+    "+mojo/public/cpp"
+  ],
+}
diff --git a/mojo/public/cpp/application/application_connection.h b/mojo/public/cpp/application/application_connection.h
new file mode 100644
index 0000000..d5d6dde
--- /dev/null
+++ b/mojo/public/cpp/application/application_connection.h
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium 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_PUBLIC_APPLICATION_APPLICATION_CONNECTION_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_CONNECTION_H_
+
+#include <string>
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+// An instance of this class is passed to
+// ApplicationDelegate's ConfigureIncomingConnection() method each time a
+// connection is made to this app, and to ApplicationDelegate's
+// ConfigureOutgoingConnection() method when the app connects to
+// another.
+//
+// To use define a class that implements your specific service api, e.g. FooImpl
+// to implement a service named Foo.
+// That class must subclass an InterfaceImpl specialization.
+//
+// Then implement an InterfaceFactory<Foo> that binds instances of FooImpl to
+// InterfaceRequest<Foo>s and register that on the connection.
+//
+// connection->AddService(&factory);
+//
+// Or if you have multiple factories implemented by the same type, explicitly
+// specify the interface to register the factory for:
+//
+// connection->AddService<Foo>(&my_foo_and_bar_factory_);
+// connection->AddService<Bar>(&my_foo_and_bar_factory_);
+//
+// The InterfaceFactory must outlive the ApplicationConnection.
+class ApplicationConnection {
+ public:
+  virtual ~ApplicationConnection();
+
+  template <typename Interface>
+  void AddService(InterfaceFactory<Interface>* factory) {
+    AddServiceConnector(
+        new internal::InterfaceFactoryConnector<Interface>(factory));
+  }
+
+  // Connect to the service implementing |Interface|.
+  template <typename Interface>
+  void ConnectToService(InterfacePtr<Interface>* ptr) {
+    MessagePipe pipe;
+    ptr->Bind(pipe.handle0.Pass());
+    GetServiceProvider()->ConnectToService(Interface::Name_,
+                                           pipe.handle1.Pass());
+  }
+
+  // The url identifying the application on the other end of this connection.
+  virtual const std::string& GetRemoteApplicationURL() = 0;
+
+  // Establishes a new connection to an application.
+  // TODO(davemoore): Would it be better to expose the ApplicationImpl?
+  virtual ApplicationConnection* ConnectToApplication(
+      const std::string& url) = 0;
+
+  // Connect to application identified by |application_url| and connect to
+  // the service implementation of the interface identified by |Interface|.
+  template <typename Interface>
+  void ConnectToService(const std::string& application_url,
+                        InterfacePtr<Interface>* ptr) {
+    ConnectToApplication(application_url)->ConnectToService(ptr);
+  }
+
+  // Raw ServiceProvider interface to remote application.
+  virtual ServiceProvider* GetServiceProvider() = 0;
+
+ private:
+  virtual void AddServiceConnector(
+      internal::ServiceConnectorBase* service_connector) = 0;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_APPLICATION_CONNECTION_H_
diff --git a/mojo/public/cpp/application/application_delegate.h b/mojo/public/cpp/application/application_delegate.h
new file mode 100644
index 0000000..cb67308
--- /dev/null
+++ b/mojo/public/cpp/application/application_delegate.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_PUBLIC_APPLICATION_APPLICATION_DELEGATE_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_DELEGATE_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+class ApplicationImpl;
+
+class ApplicationDelegate {
+ public:
+  ApplicationDelegate();
+  virtual ~ApplicationDelegate();
+
+  // Implement this method to create the specific subclass of
+  // ApplicationDelegate. Ownership is taken by the caller. It will be deleted.
+  static ApplicationDelegate* Create();
+
+  virtual void Initialize(ApplicationImpl* app);
+
+  // Override this method to configure what services a connection supports when
+  // being connected to from an app.
+  // return false to reject the connection entirely.
+  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection);
+
+  // Override this method to configure what services a connection supports when
+  // connecting to another app.
+  // return false to reject the connection entirely.
+  virtual bool ConfigureOutgoingConnection(ApplicationConnection* connection);
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationDelegate);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_APPLICATION_DELEGATE_H_
diff --git a/mojo/public/cpp/application/application_impl.h b/mojo/public/cpp/application/application_impl.h
new file mode 100644
index 0000000..4b4cb93
--- /dev/null
+++ b/mojo/public/cpp/application/application_impl.h
@@ -0,0 +1,110 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_APPLICATION_APPLICATION_IMPL_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_IMPL_H_
+#include <vector>
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/cpp/application/lib/service_registry.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+
+namespace mojo {
+
+class ApplicationDelegate;
+
+// Utility class for communicating with the Shell, and providing Services
+// to clients.
+//
+// To use define a class that implements your specific server api, e.g. FooImpl
+// to implement a service named Foo.
+// That class must subclass an InterfaceImpl specialization.
+//
+// If there is context that is to be shared amongst all instances, define a
+// constructor with that class as its only argument, otherwise define an empty
+// constructor.
+//
+// class FooImpl : public InterfaceImpl<Foo> {
+//  public:
+//   FooImpl(ApplicationContext* app_context) {}
+// };
+//
+// or
+//
+// class BarImpl : public InterfaceImpl<Bar> {
+//  public:
+//   // contexts will remain valid for the lifetime of BarImpl.
+//   BarImpl(ApplicationContext* app_context, BarContext* service_context)
+//          : app_context_(app_context), servicecontext_(context) {}
+//
+// Create an ApplicationImpl instance that collects any service implementations.
+//
+// ApplicationImpl app(service_provider_handle);
+// app.AddService<FooImpl>();
+//
+// BarContext context;
+// app.AddService<BarImpl>(&context);
+//
+//
+class ApplicationImpl : public InterfaceImpl<Application> {
+ public:
+  ApplicationImpl(ApplicationDelegate* delegate,
+                  ScopedMessagePipeHandle shell_handle);
+  ApplicationImpl(ApplicationDelegate* delegate,
+                  MojoHandle shell_handle);
+  virtual ~ApplicationImpl();
+
+  Shell* shell() const { return shell_.get(); }
+
+  // Returns any initial configuration arguments, passed by the Shell.
+  const Array<String>& args() { return args_; }
+
+  // Establishes a new connection to an application. Caller does not own.
+  ApplicationConnection* ConnectToApplication(const String& application_url);
+
+  // Connect to application identified by |application_url| and connect to the
+  // service implementation of the interface identified by |Interface|.
+  template <typename Interface>
+  void ConnectToService(const std::string& application_url,
+                        InterfacePtr<Interface>* ptr) {
+    ConnectToApplication(application_url)->ConnectToService(ptr);
+  }
+
+ private:
+  class ShellPtrWatcher;
+
+  void BindShell(ScopedMessagePipeHandle shell_handle);
+  void ClearConnections();
+  void OnShellError() {
+    ClearConnections();
+    Terminate();
+  };
+
+  // Quits the main run loop for this application.
+  static void Terminate();
+
+  // Application implementation.
+  virtual void Initialize(Array<String> args) override;
+  virtual void AcceptConnection(const String& requestor_url,
+                                ServiceProviderPtr provider) override;
+
+  typedef std::vector<internal::ServiceRegistry*> ServiceRegistryList;
+
+  bool initialized_;
+  ServiceRegistryList incoming_service_registries_;
+  ServiceRegistryList outgoing_service_registries_;
+  ApplicationDelegate* delegate_;
+  ShellPtr shell_;
+  ShellPtrWatcher* shell_watch_;
+  Array<String> args_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_APPLICATION_IMPL_H_
diff --git a/mojo/public/cpp/application/application_runner.h b/mojo/public/cpp/application/application_runner.h
new file mode 100644
index 0000000..b88ec8e
--- /dev/null
+++ b/mojo/public/cpp/application/application_runner.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_PUBLIC_APPLICATION_APPLICATION_RUNNER_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_RUNNER_H_
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class ApplicationDelegate;
+
+// A utility for running an Application. The typical use case is to use
+// when writing your MojoMain:
+//
+//  MojoResult MojoMain(MojoHandle shell_handle) {
+//    mojo::ApplicationRunner runner(new MyApplicationDelegate());
+//    return runner.Run(shell_handle);
+//  }
+//
+// ApplicationRunner takes care of mojo environment initialization and
+// shutdown, and starting a RunLoop from which your application can run and
+// ultimately Quit().
+class ApplicationRunner {
+ public:
+  // Takes ownership of |delegate|.
+  explicit ApplicationRunner(ApplicationDelegate* delegate);
+  ~ApplicationRunner();
+
+  // Once the various parameters have been set above, use Run to initialize an
+  // ApplicationImpl wired to the provided delegate, and run a RunLoop until
+  // the application exits.
+  MojoResult Run(MojoHandle shell_handle);
+
+ private:
+  ApplicationDelegate* delegate_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationRunner);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_APPLICATION_RUNNER_H_
diff --git a/mojo/public/cpp/application/connect.h b/mojo/public/cpp/application/connect.h
new file mode 100644
index 0000000..a41c028
--- /dev/null
+++ b/mojo/public/cpp/application/connect.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+template <typename Interface>
+inline void ConnectToService(ServiceProvider* service_provider,
+                             InterfacePtr<Interface>* ptr) {
+  MessagePipe pipe;
+  ptr->Bind(pipe.handle0.Pass());
+  service_provider->ConnectToService(Interface::Name_, pipe.handle1.Pass());
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
diff --git a/mojo/public/cpp/application/interface_factory.h b/mojo/public/cpp/application/interface_factory.h
new file mode 100644
index 0000000..90abd13
--- /dev/null
+++ b/mojo/public/cpp/application/interface_factory.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_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_H_
+
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+template <typename Interface> class InterfaceRequest;
+
+// Implement this class to provide implementations of a given interface and
+// bind them to incoming requests. The implementation of this class is
+// responsible for managing the lifetime of the implementations of the
+// interface.
+template <typename Interface>
+class InterfaceFactory {
+ public:
+  virtual ~InterfaceFactory() {}
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Interface> request) = 0;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_H_
diff --git a/mojo/public/cpp/application/interface_factory_impl.h b/mojo/public/cpp/application/interface_factory_impl.h
new file mode 100644
index 0000000..72d3254
--- /dev/null
+++ b/mojo/public/cpp/application/interface_factory_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.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_IMPL_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_IMPL_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+
+namespace mojo {
+
+// Use this class to allocate and bind instances of Impl to interface requests.
+// The lifetime of the constructed Impl is bound to the pipe.
+template <typename Impl,
+          typename Interface = typename Impl::ImplementedInterface>
+class InterfaceFactoryImpl : public InterfaceFactory<Interface> {
+ public:
+  virtual ~InterfaceFactoryImpl() {}
+
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Interface> request) override {
+    BindToRequest(new Impl(), &request);
+  }
+};
+
+// Use this class to allocate and bind instances of Impl constructed with a
+// context parameter to interface requests. The lifetime of the constructed
+// Impl is bound to the pipe.
+template <typename Impl,
+          typename Context,
+          typename Interface = typename Impl::ImplementedInterface>
+class InterfaceFactoryImplWithContext : public InterfaceFactory<Interface> {
+ public:
+  explicit InterfaceFactoryImplWithContext(Context* context)
+      : context_(context) {}
+  virtual ~InterfaceFactoryImplWithContext() {}
+
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Interface> request) override {
+    BindToRequest(new Impl(context_), &request);
+  }
+
+ private:
+  Context* context_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_IMPL_H_
diff --git a/mojo/public/cpp/application/lazy_interface_ptr.h b/mojo/public/cpp/application/lazy_interface_ptr.h
new file mode 100644
index 0000000..1f4b973
--- /dev/null
+++ b/mojo/public/cpp/application/lazy_interface_ptr.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_LAZY_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LAZY_INTERFACE_PTR_H_
+
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+template<typename Interface>
+class LazyInterfacePtr : public InterfacePtr<Interface> {
+ public:
+  LazyInterfacePtr() : service_provider_(nullptr) {}
+
+  LazyInterfacePtr(ServiceProvider* service_provider)
+      : service_provider_(service_provider) {
+  }
+
+  void set_service_provider(ServiceProvider* service_provider) {
+    if (service_provider != service_provider_) {
+      InterfacePtr<Interface>::reset();
+    }
+    service_provider_ = service_provider;
+  }
+
+  Interface* get() const {
+    if (!InterfacePtr<Interface>::get()) {
+      mojo::ConnectToService<Interface>(
+          service_provider_,
+          const_cast<LazyInterfacePtr<Interface>*>(this));
+    }
+    return InterfacePtr<Interface>::get();
+  }
+  Interface* operator->() const { return get(); }
+  Interface& operator*() const { return *get(); }
+
+ private:
+  ServiceProvider* service_provider_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_LAZY_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/application/lib/application_connection.cc b/mojo/public/cpp/application/lib/application_connection.cc
new file mode 100644
index 0000000..4978a35
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_connection.cc
@@ -0,0 +1,12 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/application/application_connection.h"
+
+namespace mojo {
+
+ApplicationConnection::~ApplicationConnection() {}
+
+}  // namespace mojo
+
diff --git a/mojo/public/cpp/application/lib/application_delegate.cc b/mojo/public/cpp/application/lib/application_delegate.cc
new file mode 100644
index 0000000..715daa0
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_delegate.cc
@@ -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.
+
+#include "mojo/public/cpp/application/application_delegate.h"
+
+namespace mojo {
+
+ApplicationDelegate::ApplicationDelegate() {}
+ApplicationDelegate::~ApplicationDelegate() {}
+
+void ApplicationDelegate::Initialize(ApplicationImpl* app) {}
+
+bool ApplicationDelegate::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  return true;
+}
+
+bool ApplicationDelegate::ConfigureOutgoingConnection(
+    ApplicationConnection* connection) {
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/application_impl.cc b/mojo/public/cpp/application/lib/application_impl.cc
new file mode 100644
index 0000000..0c86356
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_impl.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium 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/public/cpp/application/application_impl.h"
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/lib/service_registry.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+class ApplicationImpl::ShellPtrWatcher : public ErrorHandler {
+ public:
+  ShellPtrWatcher(ApplicationImpl* impl)
+    : impl_(impl) {}
+
+  virtual ~ShellPtrWatcher() {}
+
+  virtual void OnConnectionError() override { impl_->OnShellError(); }
+
+ private:
+  ApplicationImpl* impl_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ShellPtrWatcher);
+};
+
+ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate,
+                                 ScopedMessagePipeHandle shell_handle)
+    : initialized_(false), delegate_(delegate), shell_watch_(nullptr) {
+  BindShell(shell_handle.Pass());
+}
+
+ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate,
+                                 MojoHandle shell_handle)
+    : initialized_(false), delegate_(delegate), shell_watch_(nullptr) {
+  BindShell(MakeScopedHandle(MessagePipeHandle(shell_handle)));
+}
+
+void ApplicationImpl::ClearConnections() {
+  for (ServiceRegistryList::iterator i(incoming_service_registries_.begin());
+      i != incoming_service_registries_.end(); ++i)
+    delete *i;
+  for (ServiceRegistryList::iterator i(outgoing_service_registries_.begin());
+      i != outgoing_service_registries_.end(); ++i)
+    delete *i;
+  incoming_service_registries_.clear();
+  outgoing_service_registries_.clear();
+}
+
+ApplicationImpl::~ApplicationImpl() {
+  ClearConnections();
+  delete shell_watch_;
+}
+
+void ApplicationImpl::Initialize(Array<String> args) {
+  MOJO_CHECK(!initialized_);
+  initialized_ = true;
+  args_ = args.Pass();
+  delegate_->Initialize(this);
+}
+
+ApplicationConnection* ApplicationImpl::ConnectToApplication(
+    const String& application_url) {
+  MOJO_CHECK(initialized_);
+  ServiceProviderPtr out_service_provider;
+  shell_->ConnectToApplication(application_url, Get(&out_service_provider));
+  internal::ServiceRegistry* registry = new internal::ServiceRegistry(
+      this,
+      application_url,
+      out_service_provider.Pass());
+  if (!delegate_->ConfigureOutgoingConnection(registry)) {
+    delete registry;
+    return nullptr;
+  }
+  outgoing_service_registries_.push_back(registry);
+  return registry;
+}
+
+void ApplicationImpl::BindShell(ScopedMessagePipeHandle shell_handle) {
+  shell_watch_ = new ShellPtrWatcher(this);
+  shell_.Bind(shell_handle.Pass());
+  shell_.set_client(this);
+  shell_.set_error_handler(shell_watch_);
+}
+
+void ApplicationImpl::AcceptConnection(const String& requestor_url,
+                                       ServiceProviderPtr service_provider) {
+  internal::ServiceRegistry* registry = new internal::ServiceRegistry(
+      this, requestor_url, service_provider.Pass());
+  if (!delegate_->ConfigureIncomingConnection(registry)) {
+    delete registry;
+    return;
+  }
+  incoming_service_registries_.push_back(registry);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/application_runner.cc b/mojo/public/cpp/application/lib/application_runner.cc
new file mode 100644
index 0000000..b451a90
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_runner.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/public/cpp/application/application_runner.h"
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+// static
+void ApplicationImpl::Terminate() {
+  RunLoop::current()->Quit();
+}
+
+ApplicationRunner::ApplicationRunner(ApplicationDelegate* delegate)
+    : delegate_(delegate) {}
+ApplicationRunner::~ApplicationRunner() { assert(!delegate_); }
+
+MojoResult ApplicationRunner::Run(MojoHandle shell_handle) {
+  Environment env;
+  {
+    RunLoop loop;
+    ApplicationImpl app(delegate_, shell_handle);
+    loop.Run();
+  }
+
+  delete delegate_;
+  delegate_ = nullptr;
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_connector.cc b/mojo/public/cpp/application/lib/service_connector.cc
new file mode 100644
index 0000000..51e7d84
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_connector.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceConnectorBase::ServiceConnectorBase(const std::string& name)
+    : name_(name),
+      application_connection_(nullptr) {
+}
+
+ServiceConnectorBase::~ServiceConnectorBase() {}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_connector.h b/mojo/public/cpp/application/lib/service_connector.h
new file mode 100644
index 0000000..9ddaeb7
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_connector.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_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace internal {
+
+class ServiceConnectorBase {
+ public:
+  ServiceConnectorBase(const std::string& name);
+  virtual ~ServiceConnectorBase();
+  virtual void ConnectToService(const std::string& name,
+                                ScopedMessagePipeHandle client_handle) = 0;
+  std::string name() const { return name_; }
+  void set_application_connection(ApplicationConnection* connection) {
+      application_connection_ = connection; }
+
+ protected:
+  std::string name_;
+  ApplicationConnection* application_connection_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnectorBase);
+};
+
+template <typename Interface>
+class InterfaceFactoryConnector : public ServiceConnectorBase {
+ public:
+  explicit InterfaceFactoryConnector(InterfaceFactory<Interface>* factory)
+      : ServiceConnectorBase(Interface::Name_), factory_(factory) {}
+  virtual ~InterfaceFactoryConnector() {}
+
+  virtual void ConnectToService(const std::string& name,
+                                ScopedMessagePipeHandle client_handle) {
+    factory_->Create(application_connection_,
+                     MakeRequest<Interface>(client_handle.Pass()));
+  }
+
+ private:
+  InterfaceFactory<Interface>* factory_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceFactoryConnector);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
diff --git a/mojo/public/cpp/application/lib/service_provider_impl.cc b/mojo/public/cpp/application/lib/service_provider_impl.cc
new file mode 100644
index 0000000..08a0648
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_provider_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/public/cpp/application/service_provider_impl.h"
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/cpp/application/lib/weak_service_provider.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+ServiceProviderImpl::ServiceProviderImpl() : remote_(nullptr) {
+}
+
+ServiceProviderImpl::~ServiceProviderImpl() {
+}
+
+ServiceProvider* ServiceProviderImpl::CreateRemoteServiceProvider() {
+  // TODO(beng): it sure would be nice if this method could return a scoped_ptr.
+  MOJO_DCHECK(!remote_);
+  remote_ = new internal::WeakServiceProvider(this, client());
+  return remote_;
+}
+
+void ServiceProviderImpl::ConnectToService(
+    const String& service_name,
+    ScopedMessagePipeHandle client_handle) {
+  if (service_connectors_.find(service_name) == service_connectors_.end()) {
+    client_handle.reset();
+    return;
+  }
+
+  internal::ServiceConnectorBase* service_connector =
+      service_connectors_[service_name];
+  return service_connector->ConnectToService(service_name,
+                                             client_handle.Pass());
+}
+
+void ServiceProviderImpl::OnConnectionError() {
+  ClearRemote();
+}
+
+void ServiceProviderImpl::AddServiceConnector(
+    internal::ServiceConnectorBase* service_connector) {
+  RemoveServiceConnector(service_connector);
+  service_connectors_[service_connector->name()] = service_connector;
+  // TODO(beng): perhaps take app connection thru ctor??
+  service_connector->set_application_connection(nullptr);
+}
+
+void ServiceProviderImpl::RemoveServiceConnector(
+    internal::ServiceConnectorBase* service_connector) {
+  NameToServiceConnectorMap::iterator it =
+      service_connectors_.find(service_connector->name());
+  if (it == service_connectors_.end())
+    return;
+  delete it->second;
+  service_connectors_.erase(it);
+}
+
+void ServiceProviderImpl::ClearRemote() {
+  if (remote_) {
+    remote_->Clear();
+    remote_ = nullptr;
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_registry.cc b/mojo/public/cpp/application/lib/service_registry.cc
new file mode 100644
index 0000000..c83e84f
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_registry.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 The Chromium 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/public/cpp/application/lib/service_registry.h"
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceRegistry::ServiceRegistry(ApplicationImpl* application_impl,
+                                 const std::string& url,
+                                 ServiceProviderPtr service_provider)
+    : application_impl_(application_impl),
+      url_(url),
+      remote_service_provider_(service_provider.Pass()) {
+  remote_service_provider_.set_client(this);
+}
+
+ServiceRegistry::ServiceRegistry() : application_impl_(nullptr) {}
+
+ServiceRegistry::~ServiceRegistry() {
+  for (NameToServiceConnectorMap::iterator i =
+           name_to_service_connector_.begin();
+       i != name_to_service_connector_.end(); ++i) {
+    delete i->second;
+  }
+  name_to_service_connector_.clear();
+}
+
+void ServiceRegistry::AddServiceConnector(
+    ServiceConnectorBase* service_connector) {
+  RemoveServiceConnectorInternal(service_connector);
+  name_to_service_connector_[service_connector->name()] = service_connector;
+  service_connector->set_application_connection(this);
+}
+
+void ServiceRegistry::RemoveServiceConnector(
+    ServiceConnectorBase* service_connector) {
+  RemoveServiceConnectorInternal(service_connector);
+  if (name_to_service_connector_.empty())
+    remote_service_provider_.reset();
+}
+
+bool ServiceRegistry::RemoveServiceConnectorInternal(
+    ServiceConnectorBase* service_connector) {
+  NameToServiceConnectorMap::iterator it =
+      name_to_service_connector_.find(service_connector->name());
+  if (it == name_to_service_connector_.end())
+    return false;
+  delete it->second;
+  name_to_service_connector_.erase(it);
+  return true;
+}
+
+const std::string& ServiceRegistry::GetRemoteApplicationURL() {
+  return url_;
+}
+
+ServiceProvider* ServiceRegistry::GetServiceProvider() {
+  return remote_service_provider_.get();
+}
+
+ApplicationConnection* ServiceRegistry::ConnectToApplication(
+    const std::string& url) {
+  return application_impl_->ConnectToApplication(url);
+}
+
+void ServiceRegistry::ConnectToService(const mojo::String& service_name,
+                                       ScopedMessagePipeHandle client_handle) {
+  if (name_to_service_connector_.find(service_name) ==
+      name_to_service_connector_.end()) {
+    client_handle.reset();
+    return;
+  }
+  internal::ServiceConnectorBase* service_connector =
+      name_to_service_connector_[service_name];
+  return service_connector->ConnectToService(service_name,
+                                             client_handle.Pass());
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_registry.h b/mojo/public/cpp/application/lib/service_registry.h
new file mode 100644
index 0000000..32d4b2b
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_registry.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_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+class Application;
+class ApplicationImpl;
+
+namespace internal {
+
+class ServiceConnectorBase;
+
+// A ServiceRegistry represents each half of a connection between two
+// applications, allowing customization of which services are published to the
+// other.
+class ServiceRegistry : public ServiceProvider, public ApplicationConnection {
+ public:
+  ServiceRegistry();
+  ServiceRegistry(ApplicationImpl* application_impl,
+                  const std::string& url,
+                  ServiceProviderPtr service_provider);
+  virtual ~ServiceRegistry();
+
+  // ApplicationConnection overrides.
+  virtual void AddServiceConnector(
+      ServiceConnectorBase* service_connector) override;
+  virtual const std::string& GetRemoteApplicationURL() override;
+  virtual ApplicationConnection* ConnectToApplication(
+      const std::string& url) override;
+  virtual ServiceProvider* GetServiceProvider() override;
+
+  virtual void RemoveServiceConnector(ServiceConnectorBase* service_connector);
+
+ private:
+  // ServiceProvider method.
+  virtual void ConnectToService(const mojo::String& service_name,
+                                ScopedMessagePipeHandle client_handle) override;
+
+  ApplicationImpl* application_impl_;
+  const std::string url_;
+
+ private:
+  bool RemoveServiceConnectorInternal(
+      ServiceConnectorBase* service_connector);
+
+  Application* application_;
+  typedef std::map<std::string, ServiceConnectorBase*>
+      NameToServiceConnectorMap;
+  NameToServiceConnectorMap name_to_service_connector_;
+  ServiceProviderPtr remote_service_provider_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceRegistry);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
diff --git a/mojo/public/cpp/application/lib/weak_service_provider.cc b/mojo/public/cpp/application/lib/weak_service_provider.cc
new file mode 100644
index 0000000..b0d2511
--- /dev/null
+++ b/mojo/public/cpp/application/lib/weak_service_provider.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/public/cpp/application/lib/weak_service_provider.h"
+
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+WeakServiceProvider::WeakServiceProvider(ServiceProviderImpl* creator,
+                                         ServiceProvider* service_provider)
+      : creator_(creator),
+        service_provider_(service_provider) {}
+
+WeakServiceProvider::~WeakServiceProvider() {
+  if (creator_)
+    creator_->ClearRemote();
+}
+
+void WeakServiceProvider::Clear() {
+  creator_ = nullptr;
+  service_provider_ = nullptr;
+}
+
+void WeakServiceProvider::ConnectToService(
+    const String& service_name,
+    ScopedMessagePipeHandle client_handle) {
+  if (service_provider_)
+    service_provider_->ConnectToService(service_name, client_handle.Pass());
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/application/lib/weak_service_provider.h b/mojo/public/cpp/application/lib/weak_service_provider.h
new file mode 100644
index 0000000..05ce939
--- /dev/null
+++ b/mojo/public/cpp/application/lib/weak_service_provider.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_PUBLIC_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
+#define MOJO_PUBLIC_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+class ServiceProviderImpl;
+namespace internal {
+class ServiceConnectorBase;
+
+// Implements a weak pointer to a ServiceProvider. Necessary as the lifetime of
+// the ServiceProviderImpl is bound to that of its pipe, but code may continue
+// to reference a remote service provider beyond the lifetime of said pipe.
+// Calls to ConnectToService() are silently dropped when the pipe is closed.
+class WeakServiceProvider : public ServiceProvider {
+ public:
+  WeakServiceProvider(ServiceProviderImpl* creator,
+                      ServiceProvider* service_provider);
+  virtual ~WeakServiceProvider();
+
+  void Clear();
+
+ private:
+  // Overridden from ServiceProvider:
+  virtual void ConnectToService(const String& service_name,
+                                ScopedMessagePipeHandle client_handle) override;
+
+  ServiceProviderImpl* creator_;
+  ServiceProvider* service_provider_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(WeakServiceProvider);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
diff --git a/mojo/public/cpp/application/service_provider_impl.h b/mojo/public/cpp/application/service_provider_impl.h
new file mode 100644
index 0000000..f388511
--- /dev/null
+++ b/mojo/public/cpp/application/service_provider_impl.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
+#define MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+namespace internal {
+class WeakServiceProvider;
+class ServiceConnectorBase;
+}
+
+// Implements a registry that can be used to expose services to another app.
+class ServiceProviderImpl : public InterfaceImpl<ServiceProvider> {
+ public:
+  ServiceProviderImpl();
+  virtual ~ServiceProviderImpl();
+
+  template <typename Interface>
+  void AddService(InterfaceFactory<Interface>* factory) {
+    AddServiceConnector(
+        new internal::InterfaceFactoryConnector<Interface>(factory));
+  }
+
+  // Returns an instance of a ServiceProvider that weakly wraps this impl's
+  // connection to some other app. Whereas the lifetime of an instance of
+  // ServiceProviderImpl is bound to the lifetime of the pipe it
+  // encapsulates, the lifetime of the ServiceProvider instance returned by this
+  // method is assumed by the caller. After the pipe is closed
+  // ConnectToService() calls on this object will be silently dropped.
+  // This method must only be called once per ServiceProviderImpl.
+  ServiceProvider* CreateRemoteServiceProvider();
+
+ private:
+  typedef std::map<std::string, internal::ServiceConnectorBase*>
+      NameToServiceConnectorMap;
+
+  friend class internal::WeakServiceProvider;
+
+  // Overridden from ServiceProvider:
+  virtual void ConnectToService(const String& service_name,
+                                ScopedMessagePipeHandle client_handle) override;
+
+  // Overridden from InterfaceImpl:
+  virtual void OnConnectionError() override;
+
+  void AddServiceConnector(
+      internal::ServiceConnectorBase* service_connector);
+  void RemoveServiceConnector(
+      internal::ServiceConnectorBase* service_connector);
+
+  void ClearRemote();
+
+  NameToServiceConnectorMap service_connectors_;
+
+  internal::WeakServiceProvider* remote_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceProviderImpl);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
diff --git a/mojo/public/cpp/application/tests/BUILD.gn b/mojo/public/cpp/application/tests/BUILD.gn
new file mode 100644
index 0000000..4c22c19
--- /dev/null
+++ b/mojo/public/cpp/application/tests/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_application_unittests
+test("mojo_public_application_unittests") {
+  deps = [
+    "//base",
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/public/cpp/application:standalone",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/utility",
+    "//testing/gtest",
+  ]
+
+  sources = [ "service_registry_unittest.cc" ]
+}
diff --git a/mojo/public/cpp/application/tests/service_registry_unittest.cc b/mojo/public/cpp/application/tests/service_registry_unittest.cc
new file mode 100644
index 0000000..a0511ff
--- /dev/null
+++ b/mojo/public/cpp/application/tests/service_registry_unittest.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/public/cpp/application/lib/service_registry.h"
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+class TestConnector : public ServiceConnectorBase {
+ public:
+  TestConnector(const std::string& name, int* delete_count)
+      : ServiceConnectorBase(name), delete_count_(delete_count) {}
+  virtual ~TestConnector() { (*delete_count_)++; }
+  virtual void ConnectToService(
+      const std::string& name,
+      ScopedMessagePipeHandle client_handle) override {}
+
+ private:
+  int* delete_count_;
+};
+
+TEST(ServiceRegistryTest, Ownership) {
+  int delete_count = 0;
+
+  // Destruction.
+  {
+    ServiceRegistry registry;
+    registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+  }
+  EXPECT_EQ(1, delete_count);
+
+  // Removal.
+  {
+    ServiceRegistry registry;
+    ServiceConnectorBase* c = new TestConnector("TC1", &delete_count);
+    registry.AddServiceConnector(c);
+    registry.RemoveServiceConnector(c);
+    EXPECT_EQ(2, delete_count);
+  }
+
+  // Multiple.
+  {
+    ServiceRegistry registry;
+    registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+    registry.AddServiceConnector(new TestConnector("TC2", &delete_count));
+  }
+  EXPECT_EQ(4, delete_count);
+
+  // Re-addition.
+  {
+    ServiceRegistry registry;
+    registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+    registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+    EXPECT_EQ(5, delete_count);
+  }
+  EXPECT_EQ(6, delete_count);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
new file mode 100644
index 0000000..dad10d8
--- /dev/null
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -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.
+
+source_set("bindings") {
+  sources = [
+    "array.h",
+    "error_handler.h",
+    "interface_ptr.h",
+    "message.h",
+    "message_filter.h",
+    "no_interface.h",
+    "string.h",
+    "struct_ptr.h",
+    "type_converter.h",
+    "lib/array_internal.cc",
+    "lib/array_internal.h",
+    "lib/array_serialization.h",
+    "lib/bindings_internal.h",
+    "lib/bindings_serialization.cc",
+    "lib/bindings_serialization.h",
+    "lib/bounds_checker.cc",
+    "lib/bounds_checker.h",
+    "lib/buffer.h",
+    "lib/connector.cc",
+    "lib/connector.h",
+    "lib/filter_chain.cc",
+    "lib/filter_chain.h",
+    "lib/fixed_buffer.cc",
+    "lib/fixed_buffer.h",
+    "lib/message.cc",
+    "lib/message_builder.cc",
+    "lib/message_builder.h",
+    "lib/message_filter.cc",
+    "lib/message_header_validator.cc",
+    "lib/message_header_validator.h",
+    "lib/message_internal.h",
+    "lib/message_queue.cc",
+    "lib/message_queue.h",
+    "lib/no_interface.cc",
+    "lib/router.cc",
+    "lib/router.h",
+    "lib/string_serialization.cc",
+    "lib/string_serialization.h",
+    "lib/validation_errors.cc",
+    "lib/validation_errors.h",
+  ]
+
+  deps = [
+    ":callback",
+    "//mojo/public/cpp/environment",
+    "//mojo/public/cpp/system",
+  ]
+}
+
+source_set("callback") {
+  sources = [
+    "callback.h",
+    "lib/callback_internal.h",
+    "lib/template_util.h",
+    "lib/shared_data.h",
+    "lib/shared_ptr.h",
+  ]
+
+  deps = [ "//mojo/public/cpp/system" ]
+}
diff --git a/mojo/public/cpp/bindings/DEPS b/mojo/public/cpp/bindings/DEPS
new file mode 100644
index 0000000..2a0496e
--- /dev/null
+++ b/mojo/public/cpp/bindings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/cpp/environment",
+]
diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h
new file mode 100644
index 0000000..53c8c18
--- /dev/null
+++ b/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,159 @@
+// 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_PUBLIC_CPP_BINDINGS_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// Provides read-only access to array data.
+template <typename T>
+class Array {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(Array, RValue)
+ public:
+  typedef internal::ArrayTraits<T, internal::IsMoveOnlyType<T>::value>
+      Traits;
+  typedef typename Traits::ConstRefType ConstRefType;
+  typedef typename Traits::RefType RefType;
+  typedef typename Traits::StorageType StorageType;
+  typedef typename Traits::ForwardType ForwardType;
+
+  typedef internal::Array_Data<typename internal::WrapperTraits<T>::DataType>
+      Data_;
+
+  Array() : is_null_(true) {}
+  explicit Array(size_t size) : vec_(size), is_null_(false) {
+    Traits::Initialize(&vec_);
+  }
+  ~Array() { Traits::Finalize(&vec_); }
+
+  Array(RValue other) : is_null_(true) { Take(other.object); }
+  Array& operator=(RValue other) {
+    Take(other.object);
+    return *this;
+  }
+
+  static Array New(size_t size) {
+    return Array(size).Pass();
+  }
+
+  template <typename U>
+  static Array From(const U& other) {
+    return TypeConverter<Array, U>::Convert(other);
+  }
+
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, Array>::Convert(*this);
+  }
+
+  void reset() {
+    if (!vec_.empty()) {
+      Traits::Finalize(&vec_);
+      vec_.clear();
+    }
+    is_null_ = true;
+  }
+
+  bool is_null() const { return is_null_; }
+
+  ConstRefType front() const { return vec_.front(); }
+  RefType front() { return vec_.front(); }
+
+  size_t size() const { return vec_.size(); }
+
+  ConstRefType at(size_t offset) const { return Traits::at(&vec_, offset); }
+  ConstRefType operator[](size_t offset) const { return at(offset); }
+
+  RefType at(size_t offset) { return Traits::at(&vec_, offset); }
+  RefType operator[](size_t offset) { return at(offset); }
+
+  void push_back(ForwardType value) {
+    is_null_ = false;
+    Traits::PushBack(&vec_, value);
+  }
+
+  void resize(size_t size) {
+    is_null_ = false;
+    Traits::Resize(&vec_, size);
+  }
+
+  const std::vector<StorageType>& storage() const {
+    return vec_;
+  }
+  operator const std::vector<StorageType>&() const {
+    return vec_;
+  }
+
+  void Swap(Array* other) {
+    std::swap(is_null_, other->is_null_);
+    vec_.swap(other->vec_);
+  }
+  void Swap(std::vector<StorageType>* other) {
+    is_null_ = false;
+    vec_.swap(*other);
+  }
+
+  // Please note that calling this method will fail compilation if the element
+  // type cannot be cloned (which usually means that it is a Mojo handle type or
+  // a type contains Mojo handles).
+  Array Clone() const {
+    Array result;
+    result.is_null_ = is_null_;
+    Traits::Clone(vec_, &result.vec_);
+    return result.Pass();
+  }
+
+ private:
+  typedef std::vector<StorageType> Array::*Testable;
+
+ public:
+  operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+  void Take(Array* other) {
+    reset();
+    Swap(other);
+  }
+
+  std::vector<StorageType> vec_;
+  bool is_null_;
+};
+
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::vector<E> > {
+  static Array<T> Convert(const std::vector<E>& input) {
+    Array<T> result(input.size());
+    for (size_t i = 0; i < input.size(); ++i)
+      result[i] = TypeConverter<T, E>::Convert(input[i]);
+    return result.Pass();
+  }
+};
+
+template <typename E, typename T>
+struct TypeConverter<std::vector<E>, Array<T> > {
+  static std::vector<E> Convert(const Array<T>& input) {
+    std::vector<E> result;
+    if (!input.is_null()) {
+      result.resize(input.size());
+      for (size_t i = 0; i < input.size(); ++i)
+        result[i] = TypeConverter<E, T>::Convert(input[i]);
+    }
+    return result;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/callback.h b/mojo/public/cpp/bindings/callback.h
new file mode 100644
index 0000000..0cd4924
--- /dev/null
+++ b/mojo/public/cpp/bindings/callback.h
@@ -0,0 +1,522 @@
+// This file was GENERATED by command:
+//     pump.py callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+
+#include "mojo/public/cpp/bindings/lib/callback_internal.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename Sig>
+class Callback;
+
+template <>
+class Callback<void()> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run() const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run() const {
+    if (sink_.get())
+      sink_->Run();
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run() const override { sink.Run(); }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1>
+class Callback<void(A1)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(typename internal::Callback_ParamTraits<A1>::ForwardType a1) const {
+    if (sink_.get())
+      sink_->Run(internal::Forward(a1));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(typename internal::Callback_ParamTraits<A1>::ForwardType
+                         a1) const override {
+      sink.Run(internal::Forward(a1));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2>
+class Callback<void(A1, A2)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3>
+class Callback<void(A1, A2, A3)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+      typename internal::Callback_ParamTraits<A3>::ForwardType a3) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+class Callback<void(A1, A2, A3, A4)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+      typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+      typename internal::Callback_ParamTraits<A4>::ForwardType a4) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+class Callback<void(A1, A2, A3, A4, A5)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+      typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+      typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+      typename internal::Callback_ParamTraits<A5>::ForwardType a5) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6>
+class Callback<void(A1, A2, A3, A4, A5, A6)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+        typename internal::Callback_ParamTraits<A6>::ForwardType a6) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+      typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+      typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+      typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+      typename internal::Callback_ParamTraits<A6>::ForwardType a6) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5),
+          internal::Forward(a6));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+        typename internal::Callback_ParamTraits<A6>::ForwardType a6)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5),
+          internal::Forward(a6));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6, typename A7>
+class Callback<void(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+        typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+        typename internal::Callback_ParamTraits<A7>::ForwardType a7) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run(
+      typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+      typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+      typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+      typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+      typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+      typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+      typename internal::Callback_ParamTraits<A7>::ForwardType a7) const {
+    if (sink_.get())
+      sink_->Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5),
+          internal::Forward(a6),
+          internal::Forward(a7));
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run(
+        typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+        typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+        typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+        typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+        typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+        typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+        typename internal::Callback_ParamTraits<A7>::ForwardType a7)
+        const override {
+      sink.Run(
+          internal::Forward(a1),
+          internal::Forward(a2),
+          internal::Forward(a3),
+          internal::Forward(a4),
+          internal::Forward(a5),
+          internal::Forward(a6),
+          internal::Forward(a7));
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+typedef Callback<void()> Closure;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/callback.h.pump b/mojo/public/cpp/bindings/callback.h.pump
new file mode 100644
index 0000000..856e85b
--- /dev/null
+++ b/mojo/public/cpp/bindings/callback.h.pump
@@ -0,0 +1,95 @@
+$$ This is a pump file for generating file templates.  Pump is a python
+$$ script that is part of the Google Test suite of utilities.  Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+
+#include "mojo/public/cpp/bindings/lib/callback_internal.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename Sig>
+class Callback;
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <$for ARG , [[typename A$(ARG)]]>
+class Callback<void($for ARG , [[A$(ARG)]])> {
+ public:
+  struct Runnable {
+    virtual ~Runnable() {}
+    virtual void Run($if ARITY > 0 [[
+
+        ]]$for ARG ,
+        [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const = 0;
+  };
+
+  Callback() {}
+
+  // The Callback assumes ownership of |runnable|.
+  explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+  // Any class that is copy-constructable and has a compatible Run method may
+  // be adapted to a Callback using this constructor.
+  template <typename Sink>
+  Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+  void Run($if ARITY > 1 [[
+
+      ]]$for ARG ,
+      [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const {
+    if (sink_.get())
+      sink_->Run($if ARITY > 1 [[
+
+          ]]$for ARG ,
+          [[internal::Forward(a$(ARG))]]);
+  }
+
+  bool is_null() const {
+    return !sink_.get();
+  }
+
+  void reset() {
+    sink_.reset();
+  }
+
+ private:
+  template <typename Sink>
+  struct Adapter : public Runnable {
+    explicit Adapter(const Sink& sink) : sink(sink) {}
+    virtual void Run($if ARITY > 0 [[
+
+        ]]$for ARG ,
+        [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const override {
+      sink.Run($if ARITY > 1 [[
+
+          ]]$for ARG ,
+          [[internal::Forward(a$(ARG))]]);
+    }
+    Sink sink;
+  };
+
+  internal::SharedPtr<Runnable> sink_;
+};
+
+]]  $$ for ARITY
+
+typedef Callback<void()> Closure;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/error_handler.h b/mojo/public/cpp/bindings/error_handler.h
new file mode 100644
index 0000000..8ce1af2
--- /dev/null
+++ b/mojo/public/cpp/bindings/error_handler.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+
+namespace mojo {
+
+// This interface is used to report connection errors.
+class ErrorHandler {
+ public:
+  virtual ~ErrorHandler() {}
+  virtual void OnConnectionError() = 0;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
diff --git a/mojo/public/cpp/bindings/interface_impl.h b/mojo/public/cpp/bindings/interface_impl.h
new file mode 100644
index 0000000..da1055d
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_impl.h
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/interface_impl_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// InterfaceImpl<..> is designed to be the base class of an interface
+// implementation. It may be bound to a pipe or a proxy, see BindToPipe and
+// BindToProxy.
+template <typename Interface>
+class InterfaceImpl : public internal::InterfaceImplBase<Interface> {
+ public:
+  typedef typename Interface::Client Client;
+  typedef Interface ImplementedInterface;
+
+  InterfaceImpl() : internal_state_(this) {}
+  virtual ~InterfaceImpl() {}
+
+  // Returns a proxy to the client interface. This is null upon construction,
+  // and becomes non-null after OnClientConnected. NOTE: It remains non-null
+  // until this instance is deleted.
+  Client* client() { return internal_state_.client(); }
+
+  // Blocks the current thread for the first incoming method call, i.e., either
+  // a call to a method or a client callback method. Returns |true| if a method
+  // has been called, |false| in case of error. It must only be called on a
+  // bound object.
+  bool WaitForIncomingMethodCall() {
+    return internal_state_.WaitForIncomingMethodCall();
+  }
+
+  // Called when the client has connected to this instance.
+  virtual void OnConnectionEstablished() {}
+
+  // Called when the client is no longer connected to this instance. NOTE: The
+  // client() method continues to return a non-null pointer after this method
+  // is called. After this method is called, any method calls made on client()
+  // will be silently ignored.
+  virtual void OnConnectionError() {}
+
+  // DO NOT USE. Exposed only for internal use and for testing.
+  internal::InterfaceImplState<Interface>* internal_state() {
+    return &internal_state_;
+  }
+
+ private:
+  internal::InterfaceImplState<Interface> internal_state_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImpl);
+};
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// MessagePipe. The instance is returned for convenience in member initializer
+// lists, etc.
+//
+// If the pipe is closed, the instance's OnConnectionError method will be called
+// and then the instance will be deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then the end
+// point of the pipe will be closed and the error handler's OnConnectionError
+// method will be called.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl>
+Impl* BindToPipe(
+    Impl* instance,
+    ScopedMessagePipeHandle handle,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  instance->internal_state()->Bind(handle.Pass(), true, waiter);
+  return instance;
+}
+
+// Like BindToPipe but does not delete the instance after a channel error.
+template <typename Impl>
+Impl* WeakBindToPipe(
+    Impl* instance,
+    ScopedMessagePipeHandle handle,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  instance->internal_state()->Bind(handle.Pass(), false, waiter);
+  return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfacePtr<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called and then the instance will be
+// deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl, typename Interface>
+Impl* BindToProxy(
+    Impl* instance,
+    InterfacePtr<Interface>* ptr,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  instance->internal_state()->BindProxy(ptr, true, waiter);
+  return instance;
+}
+
+// Like BindToProxy but does not delete the instance after a channel error.
+template <typename Impl, typename Interface>
+Impl* WeakBindToProxy(
+    Impl* instance,
+    InterfacePtr<Interface>* ptr,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  instance->internal_state()->BindProxy(ptr, false, waiter);
+  return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfaceRequest<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called and then the instance will be
+// deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance will receive a SetClient call, providing it
+// with a proxy to the client on the other end of the pipe.
+template <typename Impl, typename Interface>
+Impl* BindToRequest(
+    Impl* instance,
+    InterfaceRequest<Interface>* request,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  return BindToPipe(instance, request->PassMessagePipe(), waiter);
+}
+
+// Like BindToRequest but does not delete the instance after a channel error.
+template <typename Impl, typename Interface>
+Impl* WeakBindToRequest(
+    Impl* instance,
+    InterfaceRequest<Interface>* request,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  return WeakBindToPipe(instance, request->PassMessagePipe(), waiter);
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
new file mode 100644
index 0000000..8dfd465
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/lib/interface_ptr_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class ErrorHandler;
+
+// InterfacePtr represents a proxy to a remote instance of an interface.
+template <typename Interface>
+class InterfacePtr {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfacePtr, RValue)
+ public:
+  InterfacePtr() {}
+
+  InterfacePtr(RValue other) {
+    internal_state_.Swap(&other.object->internal_state_);
+  }
+  InterfacePtr& operator=(RValue other) {
+    reset();
+    internal_state_.Swap(&other.object->internal_state_);
+    return *this;
+  }
+
+  ~InterfacePtr() {}
+
+  Interface* get() const {
+    return internal_state_.instance();
+  }
+  Interface* operator->() const { return get(); }
+  Interface& operator*() const { return *get(); }
+
+  void reset() {
+    State doomed;
+    internal_state_.Swap(&doomed);
+  }
+
+  // Blocks the current thread for the first incoming method call, i.e., either
+  // a call to a client method or a callback method. Returns |true| if a method
+  // has been called, |false| in case of error. It must only be called on a
+  // bound object.
+  bool WaitForIncomingMethodCall() {
+    return internal_state_.WaitForIncomingMethodCall();
+  }
+
+  // This method configures the InterfacePtr<..> to be a proxy to a remote
+  // object on the other end of the given pipe.
+  //
+  // The proxy is bound to the current thread, which means its methods may
+  // only be called on the current thread.
+  //
+  // To move a bound InterfacePtr<..> to another thread, call PassMessagePipe().
+  // Then create a new InterfacePtr<..> on another thread, and bind the new
+  // InterfacePtr<..> to the message pipe on that thread.
+  void Bind(
+      ScopedMessagePipeHandle handle,
+      const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+    reset();
+    internal_state_.Bind(handle.Pass(), waiter);
+  }
+
+  // The client interface may only be set after this InterfacePtr<..> is bound.
+  void set_client(typename Interface::Client* client) {
+    internal_state_.set_client(client);
+  }
+
+  // This method may be called to query if the underlying pipe has encountered
+  // an error. If true, this means method calls made on this interface will be
+  // dropped (and may have already been dropped) on the floor.
+  bool encountered_error() const {
+    return internal_state_.encountered_error();
+  }
+
+  // This method may be called to register an ErrorHandler to observe a
+  // connection error on the underlying pipe. It must only be called on a bound
+  // object.
+  // The callback runs asynchronously from the current message loop.
+  void set_error_handler(ErrorHandler* error_handler) {
+    internal_state_.set_error_handler(error_handler);
+  }
+
+  // Returns the underlying message pipe handle (if any) and resets the
+  // InterfacePtr<..> to its uninitialized state. This method is helpful if you
+  // need to move a proxy to another thread. See related notes for Bind.
+  ScopedMessagePipeHandle PassMessagePipe() {
+    State state;
+    internal_state_.Swap(&state);
+    return state.PassMessagePipe();
+  }
+
+  // DO NOT USE. Exposed only for internal use and for testing.
+  internal::InterfacePtrState<Interface>* internal_state() {
+    return &internal_state_;
+  }
+
+  // Allow InterfacePtr<> to be used in boolean expressions, but not
+  // implicitly convertible to a real bool (which is dangerous).
+ private:
+  typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable;
+
+ public:
+  operator Testable() const {
+    return internal_state_.is_bound() ? &InterfacePtr::internal_state_
+                                      : nullptr;
+  }
+
+ private:
+  typedef internal::InterfacePtrState<Interface> State;
+  mutable State internal_state_;
+};
+
+// Takes a handle to the proxy end-point of a pipe. On the other end is
+// presumed to be an interface implementation of type |Interface|. Returns a
+// generated proxy to that interface, which may be used on the current thread.
+// It is valid to call set_client on the returned InterfacePtr<..> to set an
+// instance of Interface::Client.
+template <typename Interface>
+InterfacePtr<Interface> MakeProxy(
+    ScopedMessagePipeHandle handle,
+    const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+  InterfacePtr<Interface> ptr;
+  if (handle.is_valid())
+    ptr.Bind(handle.Pass(), waiter);
+  return ptr.Pass();
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h
new file mode 100644
index 0000000..6b7d303
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_request.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_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+namespace mojo {
+
+// Used in methods that return instances of remote objects.
+template <typename Interface>
+class InterfaceRequest {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfaceRequest, RValue)
+ public:
+  InterfaceRequest() {}
+
+  InterfaceRequest(RValue other) {
+    handle_ = other.object->handle_.Pass();
+  }
+  InterfaceRequest& operator=(RValue other) {
+    handle_ = other.object->handle_.Pass();
+    return *this;
+  }
+
+  // Returns true if the request has yet to be completed.
+  bool is_pending() const { return handle_.is_valid(); }
+
+  void Bind(ScopedMessagePipeHandle handle) {
+    handle_ = handle.Pass();
+  }
+
+  ScopedMessagePipeHandle PassMessagePipe() {
+    return handle_.Pass();
+  }
+
+ private:
+  ScopedMessagePipeHandle handle_;
+};
+
+template <typename Interface>
+InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) {
+  InterfaceRequest<Interface> request;
+  request.Bind(handle.Pass());
+  return request.Pass();
+}
+
+// Used to construct a request that synchronously binds an InterfacePtr<..>,
+// making it immediately usable upon return. The resulting request object may
+// then be later bound to an InterfaceImpl<..> via BindToRequest.
+//
+// Given the following interface:
+//
+//   interface Foo {
+//     CreateBar(Bar& bar);
+//   }
+//
+// The caller of CreateBar would have code similar to the following:
+//
+//   InterfacePtr<Foo> foo = ...;
+//   InterfacePtr<Bar> bar;
+//   foo->CreateBar(Get(&bar));
+//
+// Upon return from CreateBar, |bar| is ready to have methods called on it.
+//
+template <typename Interface>
+InterfaceRequest<Interface> Get(InterfacePtr<Interface>* ptr) {
+  MessagePipe pipe;
+  ptr->Bind(pipe.handle0.Pass());
+  return MakeRequest<Interface>(pipe.handle1.Pass());
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
diff --git a/mojo/public/cpp/bindings/lib/DEPS b/mojo/public/cpp/bindings/lib/DEPS
new file mode 100644
index 0000000..b809b58
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+mojo/public/cpp/bindings",
+  "+mojo/public/cpp/environment",
+  "+mojo/public/cpp/system",
+]
diff --git a/mojo/public/cpp/bindings/lib/TODO b/mojo/public/cpp/bindings/lib/TODO
new file mode 100644
index 0000000..21bcb6f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/TODO
@@ -0,0 +1,6 @@
+TODOs:
+ - Ensure validation checks are solid
+ - Add tests of validation logic
+ - Optimize Buffer classes?
+ - Add compile-time asserts to verify object packing and padding.
+ - Investigate making arrays of objects not be arrays of pointers.
diff --git a/mojo/public/cpp/bindings/lib/array_internal.cc b/mojo/public/cpp/bindings/lib/array_internal.cc
new file mode 100644
index 0000000..9f62aa2
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.cc
@@ -0,0 +1,75 @@
+// 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/public/cpp/bindings/lib/array_internal.h"
+
+#include <sstream>
+
+namespace mojo {
+namespace internal {
+
+std::string MakeMessageWithArrayIndex(const char* message,
+                                      size_t size,
+                                      size_t index) {
+  std::ostringstream stream;
+  stream << message << ": array size - " << size << "; index - " << index;
+  return stream.str();
+}
+
+std::string MakeMessageWithExpectedArraySize(const char* message,
+                                             size_t size,
+                                             size_t expected_size) {
+  std::ostringstream stream;
+  stream << message << ": array size - " << size << "; expected size - "
+         << expected_size;
+  return stream.str();
+}
+
+ArrayDataTraits<bool>::BitRef::~BitRef() {
+}
+
+ArrayDataTraits<bool>::BitRef::BitRef(uint8_t* storage, uint8_t mask)
+    : storage_(storage),
+      mask_(mask) {
+}
+
+ArrayDataTraits<bool>::BitRef&
+ArrayDataTraits<bool>::BitRef::operator=(bool value) {
+  if (value) {
+    *storage_ |= mask_;
+  } else {
+    *storage_ &= ~mask_;
+  }
+  return *this;
+}
+
+ArrayDataTraits<bool>::BitRef&
+ArrayDataTraits<bool>::BitRef::operator=(const BitRef& value) {
+  return (*this) = static_cast<bool>(value);
+}
+
+ArrayDataTraits<bool>::BitRef::operator bool() const {
+  return (*storage_ & mask_) != 0;
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+    const ArrayHeader* header,
+    ElementType* elements,
+    std::vector<Handle>* handles) {
+  for (uint32_t i = 0; i < header->num_elements; ++i)
+    EncodeHandle(&elements[i], handles);
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+    const ArrayHeader* header,
+    ElementType* elements,
+    std::vector<Handle>* handles) {
+  for (uint32_t i = 0; i < header->num_elements; ++i)
+    DecodeHandle(&elements[i], handles);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h
new file mode 100644
index 0000000..3681d8e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -0,0 +1,521 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
+
+#include <new>
+#include <vector>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+template <typename T> class Array;
+class String;
+
+namespace internal {
+
+// std::numeric_limits<uint32_t>::max() is not a compile-time constant (until
+// C++11).
+const uint32_t kMaxUint32 = 0xFFFFFFFF;
+
+std::string MakeMessageWithArrayIndex(const char* message,
+                                      size_t size,
+                                      size_t index);
+
+std::string MakeMessageWithExpectedArraySize(const char* message,
+                                             size_t size,
+                                             size_t expected_size);
+
+template <typename T>
+struct ArrayDataTraits {
+  typedef T StorageType;
+  typedef T& Ref;
+  typedef T const& ConstRef;
+
+  static const uint32_t kMaxNumElements =
+      (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+  static uint32_t GetStorageSize(uint32_t num_elements) {
+    MOJO_DCHECK(num_elements <= kMaxNumElements);
+    return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+  }
+  static Ref ToRef(StorageType* storage, size_t offset) {
+    return storage[offset];
+  }
+  static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+    return storage[offset];
+  }
+};
+
+template <typename P>
+struct ArrayDataTraits<P*> {
+  typedef StructPointer<P> StorageType;
+  typedef P*& Ref;
+  typedef P* const& ConstRef;
+
+  static const uint32_t kMaxNumElements =
+      (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+  static uint32_t GetStorageSize(uint32_t num_elements) {
+    MOJO_DCHECK(num_elements <= kMaxNumElements);
+    return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+  }
+  static Ref ToRef(StorageType* storage, size_t offset) {
+    return storage[offset].ptr;
+  }
+  static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+    return storage[offset].ptr;
+  }
+};
+
+template <typename T>
+struct ArrayDataTraits<Array_Data<T>*> {
+  typedef ArrayPointer<T> StorageType;
+  typedef Array_Data<T>*& Ref;
+  typedef Array_Data<T>* const& ConstRef;
+
+  static const uint32_t kMaxNumElements =
+      (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+  static uint32_t GetStorageSize(uint32_t num_elements) {
+    MOJO_DCHECK(num_elements <= kMaxNumElements);
+    return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+  }
+  static Ref ToRef(StorageType* storage, size_t offset) {
+    return storage[offset].ptr;
+  }
+  static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+    return storage[offset].ptr;
+  }
+};
+
+// Specialization of Arrays for bools, optimized for space. It has the
+// following differences from a generalized Array:
+// * Each element takes up a single bit of memory.
+// * Accessing a non-const single element uses a helper class |BitRef|, which
+// emulates a reference to a bool.
+template <>
+struct ArrayDataTraits<bool> {
+  // Helper class to emulate a reference to a bool, used for direct element
+  // access.
+  class BitRef {
+   public:
+    ~BitRef();
+    BitRef& operator=(bool value);
+    BitRef& operator=(const BitRef& value);
+    operator bool() const;
+   private:
+    friend struct ArrayDataTraits<bool>;
+    BitRef(uint8_t* storage, uint8_t mask);
+    BitRef();
+    uint8_t* storage_;
+    uint8_t mask_;
+  };
+
+  // Because each element consumes only 1/8 byte.
+  static const uint32_t kMaxNumElements = kMaxUint32;
+
+  typedef uint8_t StorageType;
+  typedef BitRef Ref;
+  typedef bool ConstRef;
+
+  static uint32_t GetStorageSize(uint32_t num_elements) {
+    return sizeof(ArrayHeader) + ((num_elements + 7) / 8);
+  }
+  static BitRef ToRef(StorageType* storage, size_t offset) {
+    return BitRef(&storage[offset / 8], 1 << (offset % 8));
+  }
+  static bool ToConstRef(const StorageType* storage, size_t offset) {
+    return (storage[offset / 8] & (1 << (offset % 8))) != 0;
+  }
+};
+
+// Array type information needed for valdiation.
+template <uint32_t in_expected_num_elements,
+          bool in_element_is_nullable,
+          typename InElementValidateParams>
+class ArrayValidateParams {
+ public:
+  // Validation information for elements. It is either another specialization of
+  // ArrayValidateParams (if elements are arrays) or NoValidateParams.
+  typedef InElementValidateParams ElementValidateParams;
+
+  // If |expected_num_elements| is not 0, the array is expected to have exactly
+  // that number of elements.
+  static const uint32_t expected_num_elements = in_expected_num_elements;
+  // Whether the elements are nullable.
+  static const bool element_is_nullable = in_element_is_nullable;
+};
+
+// NoValidateParams is used to indicate the end of an ArrayValidateParams chain.
+class NoValidateParams {
+};
+
+// What follows is code to support the serialization of Array_Data<T>. There
+// are two interesting cases: arrays of primitives and arrays of objects.
+// Arrays of objects are represented as arrays of pointers to objects.
+
+template <typename T, bool is_handle> struct ArraySerializationHelper;
+
+template <typename T>
+struct ArraySerializationHelper<T, false> {
+  typedef typename ArrayDataTraits<T>::StorageType ElementType;
+
+  static void EncodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+  }
+
+  static void DecodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+  }
+
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               BoundsChecker* bounds_checker) {
+    MOJO_COMPILE_ASSERT(!element_is_nullable,
+                        Primitive_type_should_be_non_nullable);
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams, NoValidateParams>::value),
+        Primitive_type_should_not_have_array_validate_params);
+    return true;
+  }
+};
+
+template <>
+struct ArraySerializationHelper<Handle, true> {
+  typedef ArrayDataTraits<Handle>::StorageType ElementType;
+
+  static void EncodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles);
+
+  static void DecodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles);
+
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               BoundsChecker* bounds_checker) {
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams, NoValidateParams>::value),
+        Handle_type_should_not_have_array_validate_params);
+
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!element_is_nullable &&
+          elements[i].value() == kEncodedInvalidHandleValue) {
+        ReportValidationError(
+            VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+            MakeMessageWithArrayIndex(
+                "invalid handle in array expecting valid handles",
+                header->num_elements, i).c_str());
+        return false;
+      }
+      if (!bounds_checker->ClaimHandle(elements[i])) {
+        ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE);
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+template <typename H>
+struct ArraySerializationHelper<H, true> {
+  typedef typename ArrayDataTraits<H>::StorageType ElementType;
+
+  static void EncodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+    ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+        header, elements, handles);
+  }
+
+  static void DecodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+    ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+        header, elements, handles);
+  }
+
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               BoundsChecker* bounds_checker) {
+    return ArraySerializationHelper<Handle, true>::
+        ValidateElements<element_is_nullable, ElementValidateParams>(
+            header, elements, bounds_checker);
+  }
+};
+
+template <typename P>
+struct ArraySerializationHelper<P*, false> {
+  typedef typename ArrayDataTraits<P*>::StorageType ElementType;
+
+  static void EncodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+    for (uint32_t i = 0; i < header->num_elements; ++i)
+      Encode(&elements[i], handles);
+  }
+
+  static void DecodePointersAndHandles(const ArrayHeader* header,
+                                       ElementType* elements,
+                                       std::vector<Handle>* handles) {
+    for (uint32_t i = 0; i < header->num_elements; ++i)
+      Decode(&elements[i], handles);
+  }
+
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               BoundsChecker* bounds_checker) {
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!element_is_nullable && !elements[i].offset) {
+        ReportValidationError(
+            VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+            MakeMessageWithArrayIndex(
+                "null in array expecting valid pointers",
+                header->num_elements, i).c_str());
+        return false;
+      }
+      if (!ValidateEncodedPointer(&elements[i].offset)) {
+        ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER);
+        return false;
+      }
+      if (!ValidateCaller<P, ElementValidateParams>::Run(
+              DecodePointerRaw(&elements[i].offset), bounds_checker)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  template <typename T, typename Params>
+  struct ValidateCaller {
+    static bool Run(const void* data, BoundsChecker* bounds_checker) {
+      MOJO_COMPILE_ASSERT(
+          (IsSame<Params, NoValidateParams>::value),
+          Struct_type_should_not_have_array_validate_params);
+
+      return T::Validate(data, bounds_checker);
+    }
+  };
+
+  template <typename T, typename Params>
+  struct ValidateCaller<Array_Data<T>, Params> {
+    static bool Run(const void* data, BoundsChecker* bounds_checker) {
+      return Array_Data<T>::template Validate<Params>(data, bounds_checker);
+    }
+  };
+};
+
+template <typename T>
+class Array_Data {
+ public:
+  typedef ArrayDataTraits<T> Traits;
+  typedef typename Traits::StorageType StorageType;
+  typedef typename Traits::Ref Ref;
+  typedef typename Traits::ConstRef ConstRef;
+  typedef ArraySerializationHelper<T, IsHandle<T>::value> Helper;
+
+  // Returns null if |num_elements| or the corresponding storage size cannot be
+  // stored in uint32_t.
+  static Array_Data<T>* New(size_t num_elements, Buffer* buf) {
+    if (num_elements > Traits::kMaxNumElements)
+      return nullptr;
+
+    uint32_t num_bytes =
+        Traits::GetStorageSize(static_cast<uint32_t>(num_elements));
+    return new (buf->Allocate(num_bytes)) Array_Data<T>(
+        num_bytes, static_cast<uint32_t>(num_elements));
+  }
+
+  template <typename Params>
+  static bool Validate(const void* data, BoundsChecker* bounds_checker) {
+    if (!data)
+      return true;
+    if (!IsAligned(data)) {
+      ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+      return false;
+    }
+    if (!bounds_checker->IsValidRange(data, sizeof(ArrayHeader))) {
+      ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+      return false;
+    }
+    const ArrayHeader* header = static_cast<const ArrayHeader*>(data);
+    if (header->num_elements > Traits::kMaxNumElements ||
+        header->num_bytes < Traits::GetStorageSize(header->num_elements)) {
+      ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+      return false;
+    }
+    if (Params::expected_num_elements != 0 &&
+        header->num_elements != Params::expected_num_elements) {
+      ReportValidationError(
+          VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+          MakeMessageWithExpectedArraySize(
+              "fixed-size array has wrong number of elements",
+              header->num_elements, Params::expected_num_elements).c_str());
+      return false;
+    }
+    if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+      ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+      return false;
+    }
+
+    const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data);
+    return Helper::template ValidateElements<
+        Params::element_is_nullable, typename Params::ElementValidateParams>(
+            &object->header_, object->storage(), bounds_checker);
+  }
+
+  size_t size() const { return header_.num_elements; }
+
+  Ref at(size_t offset) {
+    MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements));
+    return Traits::ToRef(storage(), offset);
+  }
+
+  ConstRef at(size_t offset) const {
+    MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements));
+    return Traits::ToConstRef(storage(), offset);
+  }
+
+  StorageType* storage() {
+    return reinterpret_cast<StorageType*>(
+        reinterpret_cast<char*>(this) + sizeof(*this));
+  }
+
+  const StorageType* storage() const {
+    return reinterpret_cast<const StorageType*>(
+        reinterpret_cast<const char*>(this) + sizeof(*this));
+  }
+
+  void EncodePointersAndHandles(std::vector<Handle>* handles) {
+    Helper::EncodePointersAndHandles(&header_, storage(), handles);
+  }
+
+  void DecodePointersAndHandles(std::vector<Handle>* handles) {
+    Helper::DecodePointersAndHandles(&header_, storage(), handles);
+  }
+
+ private:
+  Array_Data(uint32_t num_bytes, uint32_t num_elements) {
+    header_.num_bytes = num_bytes;
+    header_.num_elements = num_elements;
+  }
+  ~Array_Data() {}
+
+  internal::ArrayHeader header_;
+
+  // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
+};
+MOJO_COMPILE_ASSERT(sizeof(Array_Data<char>) == 8, bad_sizeof_Array_Data);
+
+// UTF-8 encoded
+typedef Array_Data<char> String_Data;
+
+template <typename T, bool kIsMoveOnlyType> struct ArrayTraits {};
+
+template <typename T> struct ArrayTraits<T, false> {
+  typedef T StorageType;
+  typedef typename std::vector<T>::reference RefType;
+  typedef typename std::vector<T>::const_reference ConstRefType;
+  typedef ConstRefType ForwardType;
+  static inline void Initialize(std::vector<T>* vec) {
+  }
+  static inline void Finalize(std::vector<T>* vec) {
+  }
+  static inline ConstRefType at(const std::vector<T>* vec, size_t offset) {
+    return vec->at(offset);
+  }
+  static inline RefType at(std::vector<T>* vec, size_t offset) {
+    return vec->at(offset);
+  }
+  static inline void Resize(std::vector<T>* vec, size_t size) {
+    vec->resize(size);
+  }
+  static inline void PushBack(std::vector<T>* vec, ForwardType value) {
+    vec->push_back(value);
+  }
+  static inline void Clone(const std::vector<T>& src_vec,
+                           std::vector<T>* dest_vec) {
+    dest_vec->assign(src_vec.begin(), src_vec.end());
+  }
+};
+
+template <typename T> struct ArrayTraits<T, true> {
+  struct StorageType {
+    char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8];  // Make 8-byte aligned.
+  };
+  typedef T& RefType;
+  typedef const T& ConstRefType;
+  typedef T ForwardType;
+  static inline void Initialize(std::vector<StorageType>* vec) {
+    for (size_t i = 0; i < vec->size(); ++i)
+      new (vec->at(i).buf) T();
+  }
+  static inline void Finalize(std::vector<StorageType>* vec) {
+    for (size_t i = 0; i < vec->size(); ++i)
+      reinterpret_cast<T*>(vec->at(i).buf)->~T();
+  }
+  static inline ConstRefType at(const std::vector<StorageType>* vec,
+                                size_t offset) {
+    return *reinterpret_cast<const T*>(vec->at(offset).buf);
+  }
+  static inline RefType at(std::vector<StorageType>* vec, size_t offset) {
+    return *reinterpret_cast<T*>(vec->at(offset).buf);
+  }
+  static inline void Resize(std::vector<StorageType>* vec, size_t size) {
+    size_t old_size = vec->size();
+    for (size_t i = size; i < old_size; i++)
+      reinterpret_cast<T*>(vec->at(i).buf)->~T();
+    ResizeStorage(vec, size);
+    for (size_t i = old_size; i < vec->size(); i++)
+      new (vec->at(i).buf) T();
+  }
+  static inline void PushBack(std::vector<StorageType>* vec, RefType value) {
+    size_t old_size = vec->size();
+    ResizeStorage(vec, old_size + 1);
+    new (vec->at(old_size).buf) T(value.Pass());
+  }
+  static inline void ResizeStorage(std::vector<StorageType>* vec, size_t size) {
+    if (size <= vec->capacity()) {
+      vec->resize(size);
+      return;
+    }
+    std::vector<StorageType> new_storage(size);
+    for (size_t i = 0; i < vec->size(); i++)
+      new (new_storage.at(i).buf) T(at(vec, i).Pass());
+    vec->swap(new_storage);
+    Finalize(&new_storage);
+  }
+  static inline void Clone(const std::vector<StorageType>& src_vec,
+                           std::vector<StorageType>* dest_vec) {
+    Resize(dest_vec, src_vec.size());
+    for (size_t i = 0; i < src_vec.size(); ++i)
+      at(dest_vec, i) = at(&src_vec, i).Clone();
+  }
+};
+
+template <> struct WrapperTraits<String, false> {
+  typedef String_Data* DataType;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
new file mode 100644
index 0000000..4407de8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
+
+#include <string.h>  // For |memcpy()|.
+
+#include <vector>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input);
+
+// Because ValidateParams requires explicit argument specification, the
+// argument-dependent loopup technique used to omit namespace when calling
+// Serialize_() doesn't seem to work. Therefore, this function is named
+// differently from those Serialize_() overloads.
+template <typename ValidateParams, typename E, typename F>
+inline void SerializeArray_(Array<E> input, internal::Buffer* buf,
+                            internal::Array_Data<F>** output);
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output);
+
+namespace internal {
+
+template <typename E, typename F, bool move_only = IsMoveOnlyType<E>::value>
+struct ArraySerializer;
+
+template <typename E, typename F> struct ArraySerializer<E, F, false> {
+  MOJO_COMPILE_ASSERT(sizeof(E) == sizeof(F), wrong_array_serializer);
+  static size_t GetSerializedSize(const Array<E>& input) {
+    return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E));
+  }
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static void SerializeElements(
+      Array<E> input, Buffer* buf, Array_Data<F>* output) {
+    MOJO_COMPILE_ASSERT(!element_is_nullable,
+                        Primitive_type_should_be_non_nullable);
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams, NoValidateParams>::value),
+        Primitive_type_should_not_have_array_validate_params);
+
+    memcpy(output->storage(), &input.storage()[0], input.size() * sizeof(E));
+  }
+  static void DeserializeElements(
+      Array_Data<F>* input, Array<E>* output) {
+    std::vector<E> result(input->size());
+    memcpy(&result[0], input->storage(), input->size() * sizeof(E));
+    output->Swap(&result);
+  }
+};
+
+template <> struct ArraySerializer<bool, bool, false> {
+  static size_t GetSerializedSize(const Array<bool>& input) {
+    return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8);
+  }
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static void SerializeElements(
+      Array<bool> input, Buffer* buf, Array_Data<bool>* output) {
+    MOJO_COMPILE_ASSERT(!element_is_nullable,
+                        Primitive_type_should_be_non_nullable);
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams, NoValidateParams>::value),
+        Primitive_type_should_not_have_array_validate_params);
+
+    // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+    for (size_t i = 0; i < input.size(); ++i)
+      output->at(i) = input[i];
+  }
+  static void DeserializeElements(
+      Array_Data<bool>* input, Array<bool>* output) {
+    Array<bool> result(input->size());
+    // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+    for (size_t i = 0; i < input->size(); ++i)
+      result.at(i) = input->at(i);
+    output->Swap(&result);
+  }
+};
+
+template <typename H> struct ArraySerializer<ScopedHandleBase<H>, H, true> {
+  static size_t GetSerializedSize(const Array<ScopedHandleBase<H> >& input) {
+    return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H));
+  }
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static void SerializeElements(Array<ScopedHandleBase<H> > input,
+                                Buffer* buf,
+                                Array_Data<H>* output) {
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams, NoValidateParams>::value),
+        Handle_type_should_not_have_array_validate_params);
+
+    for (size_t i = 0; i < input.size(); ++i) {
+      output->at(i) = input[i].release();  // Transfer ownership of the handle.
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !element_is_nullable && !output->at(i).is_valid(),
+          VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+          MakeMessageWithArrayIndex(
+              "invalid handle in array expecting valid handles",
+              input.size(), i));
+    }
+  }
+  static void DeserializeElements(
+      Array_Data<H>* input, Array<ScopedHandleBase<H> >* output) {
+    Array<ScopedHandleBase<H> > result(input->size());
+    for (size_t i = 0; i < input->size(); ++i)
+      result.at(i) = MakeScopedHandle(FetchAndReset(&input->at(i)));
+    output->Swap(&result);
+  }
+};
+
+template <typename S> struct ArraySerializer<S, typename S::Data_*, true> {
+  static size_t GetSerializedSize(const Array<S>& input) {
+    size_t size = sizeof(Array_Data<typename S::Data_*>) +
+        input.size() * sizeof(internal::StructPointer<typename S::Data_>);
+    for (size_t i = 0; i < input.size(); ++i)
+      size += GetSerializedSize_(input[i]);
+    return size;
+  }
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static void SerializeElements(Array<S> input,
+                                Buffer* buf,
+                                Array_Data<typename S::Data_*>* output) {
+    for (size_t i = 0; i < input.size(); ++i) {
+      typename S::Data_* element;
+      SerializeCaller<S, ElementValidateParams>::Run(
+          input[i].Pass(), buf, &element);
+      output->at(i) = element;
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !element_is_nullable && !element,
+          VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+          MakeMessageWithArrayIndex(
+              "null in array expecting valid pointers", input.size(), i));
+    }
+  }
+  static void DeserializeElements(
+      Array_Data<typename S::Data_*>* input, Array<S>* output) {
+    Array<S> result(input->size());
+    for (size_t i = 0; i < input->size(); ++i) {
+      S element;
+      Deserialize_(input->at(i), &element);
+      result[i] = element.Pass();
+    }
+    output->Swap(&result);
+  }
+
+ private:
+  template <typename T, typename Params>
+  struct SerializeCaller {
+    static void Run(T input, Buffer* buf, typename T::Data_** output) {
+      MOJO_COMPILE_ASSERT((IsSame<Params, NoValidateParams>::value),
+                          Struct_type_should_not_have_array_validate_params);
+
+      Serialize_(input.Pass(), buf, output);
+    }
+  };
+
+  template <typename T, typename Params>
+  struct SerializeCaller<Array<T>, Params> {
+    static void Run(Array<T> input,
+                    Buffer* buf,
+                    typename Array<T>::Data_** output) {
+      SerializeArray_<Params>(input.Pass(), buf, output);
+    }
+  };
+};
+
+template <> struct ArraySerializer<String, String_Data*, false> {
+  static size_t GetSerializedSize(const Array<String>& input) {
+    size_t size = sizeof(Array_Data<String_Data*>) +
+        input.size() * sizeof(internal::StringPointer);
+    for (size_t i = 0; i < input.size(); ++i)
+      size += GetSerializedSize_(input[i]);
+    return size;
+  }
+  template <bool element_is_nullable, typename ElementValidateParams>
+  static void SerializeElements(
+      Array<String> input,
+      Buffer* buf,
+      Array_Data<String_Data*>* output) {
+    MOJO_COMPILE_ASSERT(
+        (IsSame<ElementValidateParams,
+                ArrayValidateParams<0, false, NoValidateParams> >::value),
+        String_type_has_unexpected_array_validate_params);
+
+    for (size_t i = 0; i < input.size(); ++i) {
+      String_Data* element;
+      Serialize_(input[i], buf, &element);
+      output->at(i) = element;
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !element_is_nullable && !element,
+          VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+          MakeMessageWithArrayIndex(
+              "null in array expecting valid strings", input.size(), i));
+    }
+  }
+  static void DeserializeElements(
+      Array_Data<String_Data*>* input, Array<String>* output) {
+    Array<String> result(input->size());
+    for (size_t i = 0; i < input->size(); ++i)
+      Deserialize_(input->at(i), &result[i]);
+    output->Swap(&result);
+  }
+};
+
+}  // namespace internal
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input) {
+  if (!input)
+    return 0;
+  typedef typename internal::WrapperTraits<E>::DataType F;
+  return internal::ArraySerializer<E, F>::GetSerializedSize(input);
+}
+
+template <typename ValidateParams, typename E, typename F>
+inline void SerializeArray_(Array<E> input, internal::Buffer* buf,
+                            internal::Array_Data<F>** output) {
+  if (input) {
+    MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+        ValidateParams::expected_num_elements != 0 &&
+            input.size() != ValidateParams::expected_num_elements,
+        internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+        internal::MakeMessageWithExpectedArraySize(
+            "fixed-size array has wrong number of elements",
+            input.size(), ValidateParams::expected_num_elements));
+
+    internal::Array_Data<F>* result =
+        internal::Array_Data<F>::New(input.size(), buf);
+    if (result) {
+      internal::ArraySerializer<E, F>::template SerializeElements<
+          ValidateParams::element_is_nullable,
+          typename ValidateParams::ElementValidateParams>(
+              internal::Forward(input), buf, result);
+    }
+    *output = result;
+  } else {
+    *output = nullptr;
+  }
+}
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* input, Array<E>* output) {
+  if (input) {
+    internal::ArraySerializer<E, F>::DeserializeElements(input, output);
+  } else {
+    output->reset();
+  }
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h
new file mode 100644
index 0000000..f12be10
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -0,0 +1,86 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class String;
+
+namespace internal {
+template <typename T> class Array_Data;
+
+#pragma pack(push, 1)
+
+struct StructHeader {
+  uint32_t num_bytes;
+  uint32_t num_fields;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructHeader) == 8, bad_sizeof_StructHeader);
+
+struct ArrayHeader {
+  uint32_t num_bytes;
+  uint32_t num_elements;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayHeader) == 8, bad_sizeof_ArrayHeader);
+
+template <typename T>
+union StructPointer {
+  uint64_t offset;
+  T* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructPointer<char>) == 8, bad_sizeof_StructPointer);
+
+template <typename T>
+union ArrayPointer {
+  uint64_t offset;
+  Array_Data<T>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayPointer<char>) == 8, bad_sizeof_ArrayPointer);
+
+union StringPointer {
+  uint64_t offset;
+  Array_Data<char>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StringPointer) == 8, bad_sizeof_StringPointer);
+
+#pragma pack(pop)
+
+template <typename T>
+void ResetIfNonNull(T* ptr) {
+  if (ptr)
+    *ptr = T();
+}
+
+template <typename T>
+T FetchAndReset(T* ptr) {
+  T temp = *ptr;
+  *ptr = T();
+  return temp;
+}
+
+template <typename H> struct IsHandle {
+  enum { value = IsBaseOf<Handle, H>::value };
+};
+
+template <typename T, bool move_only = IsMoveOnlyType<T>::value>
+struct WrapperTraits;
+
+template <typename T> struct WrapperTraits<T, false> {
+  typedef T DataType;
+};
+template <typename H> struct WrapperTraits<ScopedHandleBase<H>, true> {
+  typedef H DataType;
+};
+template <typename S> struct WrapperTraits<S, true> {
+  typedef typename S::Data_* DataType;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.cc b/mojo/public/cpp/bindings/lib/bindings_serialization.cc
new file mode 100644
index 0000000..7161efe
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.cc
@@ -0,0 +1,117 @@
+// 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/public/cpp/bindings/lib/bindings_serialization.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+const size_t kAlignment = 8;
+
+template<typename T>
+T AlignImpl(T t) {
+  return t + (kAlignment - (t % kAlignment)) % kAlignment;
+}
+
+}  // namespace
+
+size_t Align(size_t size) {
+  return AlignImpl(size);
+}
+
+char* AlignPointer(char* ptr) {
+  return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+bool IsAligned(const void* ptr) {
+  return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment);
+}
+
+void EncodePointer(const void* ptr, uint64_t* offset) {
+  if (!ptr) {
+    *offset = 0;
+    return;
+  }
+
+  const char* p_obj = reinterpret_cast<const char*>(ptr);
+  const char* p_slot = reinterpret_cast<const char*>(offset);
+  MOJO_DCHECK(p_obj > p_slot);
+
+  *offset = static_cast<uint64_t>(p_obj - p_slot);
+}
+
+const void* DecodePointerRaw(const uint64_t* offset) {
+  if (!*offset)
+    return nullptr;
+  return reinterpret_cast<const char*>(offset) + *offset;
+}
+
+bool ValidateEncodedPointer(const uint64_t* offset) {
+  // Cast to uintptr_t so overflow behavior is well defined.
+  return reinterpret_cast<uintptr_t>(offset) + *offset >=
+      reinterpret_cast<uintptr_t>(offset);
+}
+
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles) {
+  if (handle->is_valid()) {
+    handles->push_back(*handle);
+    handle->set_value(static_cast<MojoHandle>(handles->size() - 1));
+  } else {
+    handle->set_value(kEncodedInvalidHandleValue);
+  }
+}
+
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles) {
+  if (handle->value() == kEncodedInvalidHandleValue) {
+    *handle = Handle();
+    return;
+  }
+  MOJO_DCHECK(handle->value() < handles->size());
+  // Just leave holes in the vector so we don't screw up other indices.
+  *handle = FetchAndReset(&handles->at(handle->value()));
+}
+
+bool ValidateStructHeader(const void* data,
+                          uint32_t min_num_bytes,
+                          uint32_t min_num_fields,
+                          BoundsChecker* bounds_checker) {
+  MOJO_DCHECK(min_num_bytes >= sizeof(StructHeader));
+
+  if (!IsAligned(data)) {
+    ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+    return false;
+  }
+  if (!bounds_checker->IsValidRange(data, sizeof(StructHeader))) {
+    ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+    return false;
+  }
+
+  const StructHeader* header = static_cast<const StructHeader*>(data);
+
+  // TODO(yzshen): Currently our binding code cannot handle structs of smaller
+  // size or with fewer fields than the version that it sees. That needs to be
+  // changed in order to provide backward compatibility.
+  if (header->num_bytes < min_num_bytes ||
+      header->num_fields < min_num_fields) {
+    ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+    return false;
+  }
+
+  if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+    ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.h b/mojo/public/cpp/bindings/lib/bindings_serialization.h
new file mode 100644
index 0000000..6bebf90
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.h
@@ -0,0 +1,83 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+class BoundsChecker;
+
+// Please note that this is a different value than |mojo::kInvalidHandleValue|,
+// which is the "decoded" invalid handle.
+const MojoHandle kEncodedInvalidHandleValue = static_cast<MojoHandle>(-1);
+
+size_t Align(size_t size);
+char* AlignPointer(char* ptr);
+
+bool IsAligned(const void* ptr);
+
+// Pointers are encoded as relative offsets. The offsets are relative to the
+// address of where the offset value is stored, such that the pointer may be
+// recovered with the expression:
+//
+//   ptr = reinterpret_cast<char*>(offset) + *offset
+//
+// A null pointer is encoded as an offset value of 0.
+//
+void EncodePointer(const void* ptr, uint64_t* offset);
+// Note: This function doesn't validate the encoded pointer value.
+const void* DecodePointerRaw(const uint64_t* offset);
+
+// Note: This function doesn't validate the encoded pointer value.
+template <typename T>
+inline void DecodePointer(const uint64_t* offset, T** ptr) {
+  *ptr = reinterpret_cast<T*>(const_cast<void*>(DecodePointerRaw(offset)));
+}
+
+// Checks whether decoding the pointer will overflow and produce a pointer
+// smaller than |offset|.
+bool ValidateEncodedPointer(const uint64_t* offset);
+
+// Handles are encoded as indices into a vector of handles. These functions
+// manipulate the value of |handle|, mapping it to and from an index.
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles);
+// Note: This function doesn't validate the encoded handle value.
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles);
+
+// The following 2 functions are used to encode/decode all objects (structs and
+// arrays) in a consistent manner.
+
+template <typename T>
+inline void Encode(T* obj, std::vector<Handle>* handles) {
+  if (obj->ptr)
+    obj->ptr->EncodePointersAndHandles(handles);
+  EncodePointer(obj->ptr, &obj->offset);
+}
+
+// Note: This function doesn't validate the encoded pointer and handle values.
+template <typename T>
+inline void Decode(T* obj, std::vector<Handle>* handles) {
+  DecodePointer(&obj->offset, &obj->ptr);
+  if (obj->ptr)
+    obj->ptr->DecodePointersAndHandles(handles);
+}
+
+// If returns true, this function also claims the memory range of the size
+// specified in the struct header, starting from |data|.
+// Note: |min_num_bytes| must be no less than sizeof(StructHeader).
+bool ValidateStructHeader(const void* data,
+                          uint32_t min_num_bytes,
+                          uint32_t min_num_fields,
+                          BoundsChecker* bounds_checker);
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.cc b/mojo/public/cpp/bindings/lib/bounds_checker.cc
new file mode 100644
index 0000000..87f5b7c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+BoundsChecker::BoundsChecker(const void* data, uint32_t data_num_bytes,
+                             size_t num_handles)
+    : data_begin_(reinterpret_cast<uintptr_t>(data)),
+      data_end_(data_begin_ + data_num_bytes),
+      handle_begin_(0),
+      handle_end_(static_cast<uint32_t>(num_handles)) {
+  if (data_end_ < data_begin_) {
+    // The calculation of |data_end_| overflowed.
+    // It shouldn't happen but if it does, set the range to empty so
+    // IsValidRange() and ClaimMemory() always fail.
+    MOJO_DCHECK(false) << "Not reached";
+    data_end_ = data_begin_;
+  }
+  if (handle_end_ < num_handles) {
+    // Assigning |num_handles| to |handle_end_| overflowed.
+    // It shouldn't happen but if it does, set the handle index range to empty.
+    MOJO_DCHECK(false) << "Not reached";
+    handle_end_ = 0;
+  }
+}
+
+BoundsChecker::~BoundsChecker() {
+}
+
+bool BoundsChecker::ClaimMemory(const void* position, uint32_t num_bytes) {
+  uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+  uintptr_t end = begin + num_bytes;
+
+  if (!InternalIsValidRange(begin, end))
+    return false;
+
+  data_begin_ = end;
+  return true;
+}
+
+bool BoundsChecker::ClaimHandle(const Handle& encoded_handle) {
+  uint32_t index = encoded_handle.value();
+  if (index == kEncodedInvalidHandleValue)
+    return true;
+
+  if (index < handle_begin_ || index >= handle_end_)
+    return false;
+
+  // |index| + 1 shouldn't overflow, because |index| is not the max value of
+  // uint32_t (it is less than |handle_end_|).
+  handle_begin_ = index + 1;
+  return true;
+}
+
+bool BoundsChecker::IsValidRange(const void* position,
+                                 uint32_t num_bytes) const {
+  uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+  uintptr_t end = begin + num_bytes;
+
+  return InternalIsValidRange(begin, end);
+}
+
+bool BoundsChecker::InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
+  return end > begin && begin >= data_begin_ && end <= data_end_;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.h b/mojo/public/cpp/bindings/lib/bounds_checker.h
new file mode 100644
index 0000000..6c47230
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.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_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class Handle;
+
+namespace internal {
+
+// BoundsChecker is used to validate object sizes, pointers and handle indices
+// for payload of incoming messages.
+class BoundsChecker {
+ public:
+  // [data, data + data_num_bytes) specifies the initial valid memory range.
+  // [0, num_handles) specifies the initial valid range of handle indices.
+  BoundsChecker(const void* data, uint32_t data_num_bytes,
+                size_t num_handles);
+
+  ~BoundsChecker();
+
+  // Claims the specified memory range.
+  // The method succeeds if the range is valid to claim. (Please see
+  // the comments for IsValidRange().)
+  // On success, the valid memory range is shrinked to begin right after the end
+  // of the claimed range.
+  bool ClaimMemory(const void* position, uint32_t num_bytes);
+
+  // Claims the specified encoded handle (which is basically a handle index).
+  // The method succeeds if:
+  // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
+  // - the handle is contained inside the valid range of handle indices. In this
+  // case, the valid range is shinked to begin right after the claimed handle.
+  bool ClaimHandle(const Handle& encoded_handle);
+
+  // Returns true if the specified range is not empty, and the range is
+  // contained inside the valid memory range.
+  bool IsValidRange(const void* position, uint32_t num_bytes) const;
+
+ private:
+  bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
+
+  // [data_begin_, data_end_) is the valid memory range.
+  uintptr_t data_begin_;
+  uintptr_t data_end_;
+
+  // [handle_begin_, handle_end_) is the valid handle index range.
+  uint32_t handle_begin_;
+  uint32_t handle_end_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChecker);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
new file mode 100644
index 0000000..c3b570e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -0,0 +1,24 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
+
+#include <stddef.h>
+
+namespace mojo {
+namespace internal {
+
+// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
+// zero-initialized. Allocations remain valid for the lifetime of the Buffer.
+class Buffer {
+ public:
+  virtual ~Buffer() {}
+  virtual void* Allocate(size_t num_bytes) = 0;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/callback_internal.h b/mojo/public/cpp/bindings/lib/callback_internal.h
new file mode 100644
index 0000000..f76ebef
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/callback_internal.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_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+
+namespace mojo {
+class String;
+
+namespace internal {
+
+template <typename T>
+struct Callback_ParamTraits {
+  typedef T ForwardType;
+};
+
+template <>
+struct Callback_ParamTraits<String> {
+  typedef const String& ForwardType;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
new file mode 100644
index 0000000..c0b70b8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -0,0 +1,205 @@
+// 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/public/cpp/bindings/lib/connector.h"
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+Connector::Connector(ScopedMessagePipeHandle message_pipe,
+                     const MojoAsyncWaiter* waiter)
+    : error_handler_(nullptr),
+      waiter_(waiter),
+      message_pipe_(message_pipe.Pass()),
+      incoming_receiver_(nullptr),
+      async_wait_id_(0),
+      error_(false),
+      drop_writes_(false),
+      enforce_errors_from_incoming_receiver_(true),
+      destroyed_flag_(nullptr) {
+  // Even though we don't have an incoming receiver, we still want to monitor
+  // the message pipe to know if is closed or encounters an error.
+  WaitToReadMore();
+}
+
+Connector::~Connector() {
+  if (destroyed_flag_)
+    *destroyed_flag_ = true;
+
+  CancelWait();
+}
+
+void Connector::CloseMessagePipe() {
+  CancelWait();
+  Close(message_pipe_.Pass());
+}
+
+ScopedMessagePipeHandle Connector::PassMessagePipe() {
+  CancelWait();
+  return message_pipe_.Pass();
+}
+
+bool Connector::WaitForIncomingMessage() {
+  if (error_)
+    return false;
+
+  MojoResult rv = Wait(message_pipe_.get(),
+                       MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE);
+  if (rv != MOJO_RESULT_OK) {
+    NotifyError();
+    return false;
+  }
+  mojo_ignore_result(ReadSingleMessage(&rv));
+  return (rv == MOJO_RESULT_OK);
+}
+
+bool Connector::Accept(Message* message) {
+  MOJO_CHECK(message_pipe_.is_valid());
+
+  if (error_)
+    return false;
+
+  if (drop_writes_)
+    return true;
+
+  MojoResult rv = WriteMessageRaw(
+      message_pipe_.get(),
+      message->data(),
+      message->data_num_bytes(),
+      message->mutable_handles()->empty() ? nullptr :
+          reinterpret_cast<const MojoHandle*>(
+              &message->mutable_handles()->front()),
+      static_cast<uint32_t>(message->mutable_handles()->size()),
+      MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+  switch (rv) {
+    case MOJO_RESULT_OK:
+      // The handles were successfully transferred, so we don't need the message
+      // to track their lifetime any longer.
+      message->mutable_handles()->clear();
+      break;
+    case MOJO_RESULT_FAILED_PRECONDITION:
+      // There's no point in continuing to write to this pipe since the other
+      // end is gone. Avoid writing any future messages. Hide write failures
+      // from the caller since we'd like them to continue consuming any backlog
+      // of incoming messages before regarding the message pipe as closed.
+      drop_writes_ = true;
+      break;
+    case MOJO_RESULT_BUSY:
+      // We'd get a "busy" result if one of the message's handles is:
+      //   - |message_pipe_|'s own handle;
+      //   - simultaneously being used on another thread; or
+      //   - in a "busy" state that prohibits it from being transferred (e.g.,
+      //     a data pipe handle in the middle of a two-phase read/write,
+      //     regardless of which thread that two-phase read/write is happening
+      //     on).
+      // TODO(vtl): I wonder if this should be a |MOJO_DCHECK()|. (But, until
+      // crbug.com/389666, etc. are resolved, this will make tests fail quickly
+      // rather than hanging.)
+      MOJO_CHECK(false) << "Race condition or other bug detected";
+      return false;
+    default:
+      // This particular write was rejected, presumably because of bad input.
+      // The pipe is not necessarily in a bad state.
+      return false;
+  }
+  return true;
+}
+
+// static
+void Connector::CallOnHandleReady(void* closure, MojoResult result) {
+  Connector* self = static_cast<Connector*>(closure);
+  self->OnHandleReady(result);
+}
+
+void Connector::OnHandleReady(MojoResult result) {
+  MOJO_CHECK(async_wait_id_ != 0);
+  async_wait_id_ = 0;
+  if (result != MOJO_RESULT_OK) {
+    NotifyError();
+    return;
+  }
+  ReadAllAvailableMessages();
+  // At this point, this object might have been deleted. Return.
+}
+
+void Connector::WaitToReadMore() {
+  MOJO_CHECK(!async_wait_id_);
+  async_wait_id_ = waiter_->AsyncWait(message_pipe_.get().value(),
+                                      MOJO_HANDLE_SIGNAL_READABLE,
+                                      MOJO_DEADLINE_INDEFINITE,
+                                      &Connector::CallOnHandleReady,
+                                      this);
+}
+
+bool Connector::ReadSingleMessage(MojoResult* read_result) {
+  bool receiver_result = false;
+
+  // Detect if |this| was destroyed during message dispatch. Allow for the
+  // possibility of re-entering ReadMore() through message dispatch.
+  bool was_destroyed_during_dispatch = false;
+  bool* previous_destroyed_flag = destroyed_flag_;
+  destroyed_flag_ = &was_destroyed_during_dispatch;
+
+  MojoResult rv = ReadAndDispatchMessage(
+      message_pipe_.get(), incoming_receiver_, &receiver_result);
+  if (read_result)
+    *read_result = rv;
+
+  if (was_destroyed_during_dispatch) {
+    if (previous_destroyed_flag)
+      *previous_destroyed_flag = true;  // Propagate flag.
+    return false;
+  }
+  destroyed_flag_ = previous_destroyed_flag;
+
+  if (rv == MOJO_RESULT_SHOULD_WAIT)
+    return true;
+
+  if (rv != MOJO_RESULT_OK ||
+      (enforce_errors_from_incoming_receiver_ && !receiver_result)) {
+    NotifyError();
+    return false;
+  }
+  return true;
+}
+
+void Connector::ReadAllAvailableMessages() {
+  while (!error_) {
+    MojoResult rv;
+
+    // Return immediately if |this| was destroyed. Do not touch any members!
+    if (!ReadSingleMessage(&rv))
+      return;
+
+    if (rv == MOJO_RESULT_SHOULD_WAIT) {
+      WaitToReadMore();
+      break;
+    }
+  }
+}
+
+void Connector::CancelWait() {
+  if (!async_wait_id_)
+    return;
+
+  waiter_->CancelWait(async_wait_id_);
+  async_wait_id_ = 0;
+}
+
+void Connector::NotifyError() {
+  error_ = true;
+  CancelWait();
+  if (error_handler_)
+    error_handler_->OnConnectionError();
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/connector.h b/mojo/public/cpp/bindings/lib/connector.h
new file mode 100644
index 0000000..dc598a6
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.h
@@ -0,0 +1,113 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class ErrorHandler;
+
+namespace internal {
+
+// The Connector class is responsible for performing read/write operations on a
+// MessagePipe. It writes messages it receives through the MessageReceiver
+// interface that it subclasses, and it forwards messages it reads through the
+// MessageReceiver interface assigned as its incoming receiver.
+//
+// NOTE: MessagePipe I/O is non-blocking.
+//
+class Connector : public MessageReceiver {
+ public:
+  // The Connector takes ownership of |message_pipe|.
+  explicit Connector(
+      ScopedMessagePipeHandle message_pipe,
+      const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+  virtual ~Connector();
+
+  // Sets the receiver to handle messages read from the message pipe.  The
+  // Connector will read messages from the pipe regardless of whether or not an
+  // incoming receiver has been set.
+  void set_incoming_receiver(MessageReceiver* receiver) {
+    incoming_receiver_ = receiver;
+  }
+
+  // Errors from incoming receivers will force the connector into an error
+  // state, where no more messages will be processed. This method is used
+  // during testing to prevent that from happening.
+  void set_enforce_errors_from_incoming_receiver(bool enforce) {
+    enforce_errors_from_incoming_receiver_ = enforce;
+  }
+
+  // Sets the error handler to receive notifications when an error is
+  // encountered while reading from the pipe or waiting to read from the pipe.
+  void set_error_handler(ErrorHandler* error_handler) {
+    error_handler_ = error_handler;
+  }
+
+  // Returns true if an error was encountered while reading from the pipe or
+  // waiting to read from the pipe.
+  bool encountered_error() const { return error_; }
+
+  // Closes the pipe, triggering the error state. Connector is put into a
+  // quiescent state.
+  void CloseMessagePipe();
+
+  // Releases the pipe, not triggering the error state. Connector is put into
+  // a quiescent state.
+  ScopedMessagePipeHandle PassMessagePipe();
+
+  // Waits for the next message on the pipe, blocking until one arrives or an
+  // error happens. Returns |true| if a message has been delivered, |false|
+  // otherwise.
+  bool WaitForIncomingMessage();
+
+  // MessageReceiver implementation:
+  virtual bool Accept(Message* message) override;
+
+ private:
+  static void CallOnHandleReady(void* closure, MojoResult result);
+  void OnHandleReady(MojoResult result);
+
+  void WaitToReadMore();
+
+  // Returns false if |this| was destroyed during message dispatch.
+  MOJO_WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
+
+  // |this| can be destroyed during message dispatch.
+  void ReadAllAvailableMessages();
+
+  void NotifyError();
+
+  // Cancels any calls made to |waiter_|.
+  void CancelWait();
+
+  ErrorHandler* error_handler_;
+  const MojoAsyncWaiter* waiter_;
+
+  ScopedMessagePipeHandle message_pipe_;
+  MessageReceiver* incoming_receiver_;
+
+  MojoAsyncWaitID async_wait_id_;
+  bool error_;
+  bool drop_writes_;
+  bool enforce_errors_from_incoming_receiver_;
+
+  // If non-null, this will be set to true when the Connector is destroyed.  We
+  // use this flag to allow for the Connector to be destroyed as a side-effect
+  // of dispatching an incoming message.
+  bool* destroyed_flag_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Connector);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc
new file mode 100644
index 0000000..6634562
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.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/public/cpp/bindings/lib/filter_chain.h"
+
+#include <algorithm>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) {
+}
+
+FilterChain::FilterChain(RValue other) : sink_(other.object->sink_) {
+  other.object->sink_ = nullptr;
+  filters_.swap(other.object->filters_);
+}
+
+FilterChain& FilterChain::operator=(RValue other) {
+  std::swap(sink_, other.object->sink_);
+  filters_.swap(other.object->filters_);
+  return *this;
+}
+
+FilterChain::~FilterChain() {
+  for (std::vector<MessageFilter*>::iterator iter = filters_.begin();
+       iter != filters_.end();
+       ++iter) {
+    delete *iter;
+  }
+}
+
+void FilterChain::SetSink(MessageReceiver* sink) {
+  MOJO_DCHECK(!sink_);
+  sink_ = sink;
+  if (!filters_.empty())
+    filters_.back()->set_sink(sink);
+}
+
+MessageReceiver* FilterChain::GetHead() {
+  MOJO_DCHECK(sink_);
+  return filters_.empty() ? sink_ : filters_.front();
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.h b/mojo/public/cpp/bindings/lib/filter_chain.h
new file mode 100644
index 0000000..fc66642
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+class FilterChain {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(FilterChain, RValue)
+
+ public:
+  // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+  // this object is alive.
+  explicit FilterChain(MessageReceiver* sink = nullptr);
+
+  // Move-only constructor and operator=.
+  FilterChain(RValue other);
+  FilterChain& operator=(RValue other);
+
+  ~FilterChain();
+
+  template <typename FilterType>
+  inline void Append();
+
+  // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+  // this object is alive.
+  void SetSink(MessageReceiver* sink);
+
+  // Returns a receiver to accept messages. Messages flow through all filters in
+  // the same order as they were appended to the chain. If all filters allow a
+  // message to pass, it will be forwarded to |sink_|.
+  // The returned value is invalidated when this object goes away.
+  MessageReceiver* GetHead();
+
+ private:
+  // Owned by this object.
+  std::vector<MessageFilter*> filters_;
+
+  MessageReceiver* sink_;
+};
+
+template <typename FilterType>
+inline void FilterChain::Append() {
+  FilterType* filter = new FilterType(sink_);
+  if (!filters_.empty())
+    filters_.back()->set_sink(filter);
+  filters_.push_back(filter);
+}
+
+template <>
+inline void FilterChain::Append<PassThroughFilter>() {
+}
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
new file mode 100644
index 0000000..9542ef8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+FixedBuffer::FixedBuffer(size_t size)
+    : ptr_(nullptr),
+      cursor_(0),
+      size_(internal::Align(size)) {
+  // calloc() required to zero memory and thus avoid info leaks.
+  ptr_ = static_cast<char*>(calloc(size_, 1));
+}
+
+FixedBuffer::~FixedBuffer() {
+  free(ptr_);
+}
+
+void* FixedBuffer::Allocate(size_t delta) {
+  delta = internal::Align(delta);
+
+  if (delta == 0 || delta > size_ - cursor_) {
+    MOJO_DCHECK(false) << "Not reached";
+    return nullptr;
+  }
+
+  char* result = ptr_ + cursor_;
+  cursor_ += delta;
+
+  return result;
+}
+
+void* FixedBuffer::Leak() {
+  char* ptr = ptr_;
+  ptr_ = nullptr;
+  cursor_ = 0;
+  size_ = 0;
+  return ptr;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
new file mode 100644
index 0000000..d23f664
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// FixedBuffer provides a simple way to allocate objects within a fixed chunk
+// of memory. Objects are allocated by calling the |Allocate| method, which
+// extends the buffer accordingly. Objects allocated in this way are not freed
+// explicitly. Instead, they remain valid so long as the FixedBuffer remains
+// valid.  The Leak method may be used to steal the underlying memory from the
+// FixedBuffer.
+//
+// Typical usage:
+//
+//   {
+//     FixedBuffer buf(8 + 8);
+//
+//     int* a = static_cast<int*>(buf->Allocate(sizeof(int)));
+//     *a = 2;
+//
+//     double* b = static_cast<double*>(buf->Allocate(sizeof(double)));
+//     *b = 3.14f;
+//
+//     void* data = buf.Leak();
+//     Process(data);
+//
+//     free(data);
+//   }
+//
+class FixedBuffer : public Buffer {
+ public:
+  explicit FixedBuffer(size_t size);
+  virtual ~FixedBuffer();
+
+  // Grows the buffer by |num_bytes| and returns a pointer to the start of the
+  // addition. The resulting address is 8-byte aligned, and the content of the
+  // memory is zero-filled.
+  virtual void* Allocate(size_t num_bytes) override;
+
+  size_t size() const { return size_; }
+
+  // Returns the internal memory owned by the Buffer to the caller. The Buffer
+  // relinquishes its pointer, effectively resetting the state of the Buffer
+  // and leaving the caller responsible for freeing the returned memory address
+  // when no longer needed.
+  void* Leak();
+
+ private:
+  char* ptr_;
+  size_t cursor_;
+  size_t size_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_impl_internal.h b/mojo/public/cpp/bindings/lib/interface_impl_internal.h
new file mode 100644
index 0000000..492b182
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_impl_internal.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfaceImplBase : public Interface {
+ public:
+  virtual ~InterfaceImplBase() {}
+  virtual void OnConnectionEstablished() = 0;
+  virtual void OnConnectionError() = 0;
+};
+
+template <typename Interface>
+class InterfaceImplState : public ErrorHandler {
+ public:
+  typedef typename Interface::Client Client;
+
+  explicit InterfaceImplState(InterfaceImplBase<Interface>* instance)
+      : router_(nullptr),
+        proxy_(nullptr),
+        instance_bound_to_pipe_(false)
+#ifndef NDEBUG
+        ,
+        deleting_instance_due_to_error_(false)
+#endif
+  {
+    MOJO_DCHECK(instance);
+    stub_.set_sink(instance);
+  }
+
+  virtual ~InterfaceImplState() {
+#ifndef NDEBUG
+    MOJO_DCHECK(!instance_bound_to_pipe_ || deleting_instance_due_to_error_);
+#endif
+    delete proxy_;
+    if (router_) {
+      router_->set_error_handler(nullptr);
+      delete router_;
+    }
+  }
+
+  void BindProxy(
+      InterfacePtr<Interface>* ptr,
+      bool instance_bound_to_pipe,
+      const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+    MessagePipe pipe;
+    ptr->Bind(pipe.handle0.Pass(), waiter);
+    Bind(pipe.handle1.Pass(), instance_bound_to_pipe, waiter);
+  }
+
+  void Bind(ScopedMessagePipeHandle handle,
+            bool instance_bound_to_pipe,
+            const MojoAsyncWaiter* waiter) {
+    MOJO_CHECK(!router_);
+
+    FilterChain filters;
+    filters.Append<MessageHeaderValidator>();
+    filters.Append<typename Interface::RequestValidator_>();
+    filters.Append<typename Interface::Client::ResponseValidator_>();
+
+    router_ = new Router(handle.Pass(), filters.Pass(), waiter);
+    router_->set_incoming_receiver(&stub_);
+    router_->set_error_handler(this);
+
+    proxy_ = new typename Client::Proxy_(router_);
+
+    instance_bound_to_pipe_ = instance_bound_to_pipe;
+
+    instance()->OnConnectionEstablished();
+  }
+
+  bool WaitForIncomingMethodCall() {
+    MOJO_DCHECK(router_);
+    return router_->WaitForIncomingMessage();
+  }
+
+  Router* router() { return router_; }
+  Client* client() { return proxy_; }
+
+ private:
+  InterfaceImplBase<Interface>* instance() {
+    return static_cast<InterfaceImplBase<Interface>*>(stub_.sink());
+  }
+
+  virtual void OnConnectionError() override {
+    // If the the instance is not bound to the pipe, the instance might choose
+    // to delete itself in the OnConnectionError handler, which would in turn
+    // delete this.  Save the error behavior before invoking the error handler
+    // so we can correctly decide what to do.
+    bool bound = instance_bound_to_pipe_;
+    instance()->OnConnectionError();
+    if (!bound)
+      return;
+#ifndef NDEBUG
+    deleting_instance_due_to_error_ = true;
+#endif
+    delete instance();
+  }
+
+  Router* router_;
+  typename Client::Proxy_* proxy_;
+  typename Interface::Stub_ stub_;
+  bool instance_bound_to_pipe_;
+#ifndef NDEBUG
+  bool deleting_instance_due_to_error_;
+#endif
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImplState);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_internal.h b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
new file mode 100644
index 0000000..77386fa
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
+
+#include <algorithm>  // For |std::swap()|.
+
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+struct MojoAsyncWaiter;
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfacePtrState {
+ public:
+  InterfacePtrState() : proxy_(nullptr), router_(nullptr), waiter_(nullptr) {}
+
+  ~InterfacePtrState() {
+    // Destruction order matters here. We delete |proxy_| first, even though
+    // |router_| may have a reference to it, so that |~Interface| may have a
+    // shot at generating new outbound messages (ie, invoking client methods).
+    delete proxy_;
+    delete router_;
+  }
+
+  Interface* instance() {
+    ConfigureProxyIfNecessary();
+
+    // This will be null if the object is not bound.
+    return proxy_;
+  }
+
+  void Swap(InterfacePtrState* other) {
+    std::swap(other->proxy_, proxy_);
+    std::swap(other->router_, router_);
+    handle_.swap(other->handle_);
+    std::swap(other->waiter_, waiter_);
+  }
+
+  void Bind(ScopedMessagePipeHandle handle, const MojoAsyncWaiter* waiter) {
+    MOJO_DCHECK(!proxy_);
+    MOJO_DCHECK(!router_);
+    MOJO_DCHECK(!handle_.is_valid());
+    MOJO_DCHECK(!waiter_);
+
+    handle_ = handle.Pass();
+    waiter_ = waiter;
+  }
+
+  bool WaitForIncomingMethodCall() {
+    ConfigureProxyIfNecessary();
+
+    MOJO_DCHECK(router_);
+    return router_->WaitForIncomingMessage();
+  }
+
+  ScopedMessagePipeHandle PassMessagePipe() {
+    if (router_)
+      return router_->PassMessagePipe();
+
+    waiter_ = nullptr;
+    return handle_.Pass();
+  }
+
+  bool is_bound() const {
+    return handle_.is_valid() || router_;
+  }
+
+  void set_client(typename Interface::Client* client) {
+    ConfigureProxyIfNecessary();
+
+    MOJO_DCHECK(proxy_);
+    proxy_->stub.set_sink(client);
+  }
+
+  bool encountered_error() const {
+    return router_ ? router_->encountered_error() : false;
+  }
+
+  void set_error_handler(ErrorHandler* error_handler) {
+    ConfigureProxyIfNecessary();
+
+    MOJO_DCHECK(router_);
+    router_->set_error_handler(error_handler);
+  }
+
+  Router* router_for_testing() {
+    ConfigureProxyIfNecessary();
+    return router_;
+  }
+
+ private:
+  class ProxyWithStub : public Interface::Proxy_ {
+   public:
+    explicit ProxyWithStub(MessageReceiverWithResponder* receiver)
+        : Interface::Proxy_(receiver) {
+    }
+    typename Interface::Client::Stub_ stub;
+   private:
+    MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyWithStub);
+  };
+
+  void ConfigureProxyIfNecessary() {
+    // The proxy has been configured.
+    if (proxy_) {
+      MOJO_DCHECK(router_);
+      return;
+    }
+    // The object hasn't been bound.
+    if (!waiter_) {
+      MOJO_DCHECK(!handle_.is_valid());
+      return;
+    }
+
+    FilterChain filters;
+    filters.Append<MessageHeaderValidator>();
+    filters.Append<typename Interface::Client::RequestValidator_>();
+    filters.Append<typename Interface::ResponseValidator_>();
+
+    router_ = new Router(handle_.Pass(), filters.Pass(), waiter_);
+    waiter_ = nullptr;
+
+    ProxyWithStub* proxy = new ProxyWithStub(router_);
+    router_->set_incoming_receiver(&proxy->stub);
+
+    proxy_ = proxy;
+  }
+
+  ProxyWithStub* proxy_;
+  Router* router_;
+
+  // |proxy_| and |router_| are not initialized until read/write with the
+  // message pipe handle is needed. Before that, |handle_| and |waiter_| store
+  // the arguments of Bind().
+  ScopedMessagePipeHandle handle_;
+  const MojoAsyncWaiter* waiter_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
new file mode 100644
index 0000000..cec60c0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/message.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+Message::Message()
+    : data_num_bytes_(0),
+      data_(nullptr) {
+}
+
+Message::~Message() {
+  free(data_);
+
+  for (std::vector<Handle>::iterator it = handles_.begin();
+       it != handles_.end(); ++it) {
+    if (it->is_valid())
+      CloseRaw(*it);
+  }
+}
+
+void Message::AllocUninitializedData(uint32_t num_bytes) {
+  MOJO_DCHECK(!data_);
+  data_num_bytes_ = num_bytes;
+  data_ = static_cast<internal::MessageData*>(malloc(num_bytes));
+}
+
+void Message::AdoptData(uint32_t num_bytes, internal::MessageData* data) {
+  MOJO_DCHECK(!data_);
+  data_num_bytes_ = num_bytes;
+  data_ = data;
+}
+
+void Message::Swap(Message* other) {
+  std::swap(data_num_bytes_, other->data_num_bytes_);
+  std::swap(data_, other->data_);
+  std::swap(handles_, other->handles_);
+}
+
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+                                  MessageReceiver* receiver,
+                                  bool* receiver_result) {
+  MojoResult rv;
+
+  uint32_t num_bytes = 0, num_handles = 0;
+  rv = ReadMessageRaw(handle,
+                      nullptr,
+                      &num_bytes,
+                      nullptr,
+                      &num_handles,
+                      MOJO_READ_MESSAGE_FLAG_NONE);
+  if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED)
+    return rv;
+
+  Message message;
+  message.AllocUninitializedData(num_bytes);
+  message.mutable_handles()->resize(num_handles);
+
+  rv = ReadMessageRaw(handle,
+                      message.mutable_data(),
+                      &num_bytes,
+                      message.mutable_handles()->empty()
+                          ? nullptr
+                          : reinterpret_cast<MojoHandle*>(
+                                &message.mutable_handles()->front()),
+                      &num_handles,
+                      MOJO_READ_MESSAGE_FLAG_NONE);
+  if (receiver && rv == MOJO_RESULT_OK)
+    *receiver_result = receiver->Accept(&message);
+
+  return rv;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
new file mode 100644
index 0000000..c746644
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.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/public/cpp/bindings/lib/message_builder.h"
+
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Header>
+void Allocate(Buffer* buf, Header** header) {
+  *header = static_cast<Header*>(buf->Allocate(sizeof(Header)));
+  (*header)->num_bytes = sizeof(Header);
+}
+
+MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size)
+    : buf_(sizeof(MessageHeader) + payload_size) {
+  MessageHeader* header;
+  Allocate(&buf_, &header);
+  header->num_fields = 2;
+  header->name = name;
+}
+
+MessageBuilder::~MessageBuilder() {
+}
+
+void MessageBuilder::Finish(Message* message) {
+  uint32_t num_bytes = static_cast<uint32_t>(buf_.size());
+  message->AdoptData(num_bytes, static_cast<MessageData*>(buf_.Leak()));
+}
+
+MessageBuilder::MessageBuilder(size_t size)
+    : buf_(size) {
+}
+
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+                                                         size_t payload_size,
+                                                         uint32_t flags,
+                                                         uint64_t request_id)
+    : MessageBuilder(sizeof(MessageHeaderWithRequestID) + payload_size) {
+  MessageHeaderWithRequestID* header;
+  Allocate(&buf_, &header);
+  header->num_fields = 3;
+  header->name = name;
+  header->flags = flags;
+  header->request_id = request_id;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h
new file mode 100644
index 0000000..b4988ff
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -0,0 +1,63 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+class MessageBuilder {
+ public:
+  MessageBuilder(uint32_t name, size_t payload_size);
+  ~MessageBuilder();
+
+  Buffer* buffer() { return &buf_; }
+
+  // Call Finish when done making allocations in |buffer()|. Upon return,
+  // |message| will contain the message data, and |buffer()| will no longer be
+  // valid to reference.
+  void Finish(Message* message);
+
+ protected:
+  explicit MessageBuilder(size_t size);
+  FixedBuffer buf_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
+};
+
+class MessageWithRequestIDBuilder : public MessageBuilder {
+ public:
+  MessageWithRequestIDBuilder(uint32_t name, size_t payload_size,
+                              uint32_t flags, uint64_t request_id);
+};
+
+class RequestMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+  RequestMessageBuilder(uint32_t name, size_t payload_size)
+      : MessageWithRequestIDBuilder(name, payload_size, kMessageExpectsResponse,
+                                    0) {
+  }
+};
+
+class ResponseMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+  ResponseMessageBuilder(uint32_t name, size_t payload_size,
+                         uint64_t request_id)
+      : MessageWithRequestIDBuilder(name, payload_size, kMessageIsResponse,
+                                    request_id) {
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
diff --git a/mojo/public/cpp/bindings/lib/message_filter.cc b/mojo/public/cpp/bindings/lib/message_filter.cc
new file mode 100644
index 0000000..b09f40d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_filter.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+
+MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) {
+}
+
+MessageFilter::~MessageFilter() {
+}
+
+PassThroughFilter::PassThroughFilter(MessageReceiver* sink)
+    : MessageFilter(sink) {
+}
+
+bool PassThroughFilter::Accept(Message* message) {
+  return sink_->Accept(message);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc
new file mode 100644
index 0000000..a55917a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+bool IsValidMessageHeader(const MessageHeader* header) {
+  // NOTE: Our goal is to preserve support for future extension of the message
+  // header. If we encounter fields we do not understand, we must ignore them.
+
+  // Extra validation of the struct header:
+  if (header->num_fields == 2) {
+    if (header->num_bytes != sizeof(MessageHeader)) {
+      ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  } else if (header->num_fields == 3) {
+    if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) {
+      ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  } else if (header->num_fields > 3) {
+    if (header->num_bytes < sizeof(MessageHeaderWithRequestID)) {
+      ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  }
+
+  // Validate flags (allow unknown bits):
+
+  // These flags require a RequestID.
+  if (header->num_fields < 3 &&
+        ((header->flags & kMessageExpectsResponse) ||
+         (header->flags & kMessageIsResponse))) {
+    ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
+    return false;
+  }
+
+  // These flags are mutually exclusive.
+  if ((header->flags & kMessageExpectsResponse) &&
+      (header->flags & kMessageIsResponse)) {
+    ReportValidationError(
+        VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+    : MessageFilter(sink) {
+}
+
+bool MessageHeaderValidator::Accept(Message* message) {
+  // Pass 0 as number of handles because we don't expect any in the header, even
+  // if |message| contains handles.
+  BoundsChecker bounds_checker(message->data(), message->data_num_bytes(), 0);
+
+  if (!ValidateStructHeader(message->data(), sizeof(MessageHeader), 2,
+                            &bounds_checker)) {
+    return false;
+  }
+
+  if (!IsValidMessageHeader(message->header()))
+    return false;
+
+  return sink_->Accept(message);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.h b/mojo/public/cpp/bindings/lib/message_header_validator.h
new file mode 100644
index 0000000..790aa9b
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.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_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+namespace internal {
+
+class MessageHeaderValidator : public MessageFilter {
+ public:
+  explicit MessageHeaderValidator(MessageReceiver* sink = nullptr);
+
+  virtual bool Accept(Message* message) override;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h
new file mode 100644
index 0000000..3c67902
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_internal.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_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+#pragma pack(push, 1)
+
+enum {
+  kMessageExpectsResponse = 1 << 0,
+  kMessageIsResponse      = 1 << 1
+};
+
+struct MessageHeader : internal::StructHeader {
+  uint32_t name;
+  uint32_t flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeader) == 16, bad_sizeof_MessageHeader);
+
+struct MessageHeaderWithRequestID : MessageHeader {
+  uint64_t request_id;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeaderWithRequestID) == 24,
+                    bad_sizeof_MessageHeaderWithRequestID);
+
+struct MessageData {
+  MessageHeader header;
+};
+
+MOJO_COMPILE_ASSERT(sizeof(MessageData) == sizeof(MessageHeader),
+                    bad_sizeof_MessageData);
+
+#pragma pack(pop)
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/message_queue.cc b/mojo/public/cpp/bindings/lib/message_queue.cc
new file mode 100644
index 0000000..fd701e9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_queue.cc
@@ -0,0 +1,48 @@
+// 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/public/cpp/bindings/lib/message_queue.h"
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+  while (!queue_.empty())
+    Pop();
+}
+
+bool MessageQueue::IsEmpty() const {
+  return queue_.empty();
+}
+
+Message* MessageQueue::Peek() {
+  MOJO_DCHECK(!queue_.empty());
+  return queue_.front();
+}
+
+void MessageQueue::Push(Message* message) {
+  queue_.push(new Message());
+  queue_.back()->Swap(message);
+}
+
+void MessageQueue::Pop(Message* message) {
+  MOJO_DCHECK(!queue_.empty());
+  queue_.front()->Swap(message);
+  Pop();
+}
+
+void MessageQueue::Pop() {
+  MOJO_DCHECK(!queue_.empty());
+  delete queue_.front();
+  queue_.pop();
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_queue.h b/mojo/public/cpp/bindings/lib/message_queue.h
new file mode 100644
index 0000000..4e46b54
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_queue.h
@@ -0,0 +1,47 @@
+// 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_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
+
+#include <queue>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+// A queue for Message objects.
+class MessageQueue {
+ public:
+  MessageQueue();
+  ~MessageQueue();
+
+  bool IsEmpty() const;
+  Message* Peek();
+
+  // This method transfers ownership of |message->data| and |message->handles|
+  // to the message queue, resetting |message| in the process.
+  void Push(Message* message);
+
+  // Removes the next message from the queue, transferring ownership of its
+  // data and handles to the given |message|.
+  void Pop(Message* message);
+
+  // Removes the next message from the queue, discarding its data and handles.
+  // This is meant to be used in conjunction with |Peek|.
+  void Pop();
+
+ private:
+  std::queue<Message*> queue_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
diff --git a/mojo/public/cpp/bindings/lib/no_interface.cc b/mojo/public/cpp/bindings/lib/no_interface.cc
new file mode 100644
index 0000000..9e0945c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/no_interface.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/public/cpp/bindings/no_interface.h"
+
+namespace mojo {
+
+const char* NoInterface::Name_ = "mojo::NoInterface";
+
+bool NoInterfaceStub::Accept(Message* message) {
+  return false;
+}
+
+bool NoInterfaceStub::AcceptWithResponder(Message* message,
+                                          MessageReceiver* responder) {
+  return false;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc
new file mode 100644
index 0000000..3478840
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium 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/public/cpp/bindings/lib/router.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+class ResponderThunk : public MessageReceiver {
+ public:
+  explicit ResponderThunk(const SharedData<Router*>& router)
+      : router_(router) {
+  }
+  virtual ~ResponderThunk() {
+  }
+
+  // MessageReceiver implementation:
+  virtual bool Accept(Message* message) override {
+    MOJO_DCHECK(message->has_flag(kMessageIsResponse));
+
+    bool result = false;
+
+    Router* router = router_.value();
+    if (router)
+      result = router->Accept(message);
+
+    return result;
+  }
+
+ private:
+  SharedData<Router*> router_;
+};
+
+// ----------------------------------------------------------------------------
+
+Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router)
+    : router_(router) {
+}
+
+Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() {
+}
+
+bool Router::HandleIncomingMessageThunk::Accept(Message* message) {
+  return router_->HandleIncomingMessage(message);
+}
+
+// ----------------------------------------------------------------------------
+
+Router::Router(ScopedMessagePipeHandle message_pipe,
+               FilterChain filters,
+               const MojoAsyncWaiter* waiter)
+    : thunk_(this),
+      filters_(filters.Pass()),
+      connector_(message_pipe.Pass(), waiter),
+      weak_self_(this),
+      incoming_receiver_(nullptr),
+      next_request_id_(0),
+      testing_mode_(false) {
+  filters_.SetSink(&thunk_);
+  connector_.set_incoming_receiver(filters_.GetHead());
+}
+
+Router::~Router() {
+  weak_self_.set_value(nullptr);
+
+  for (ResponderMap::const_iterator i = responders_.begin();
+       i != responders_.end(); ++i) {
+    delete i->second;
+  }
+}
+
+bool Router::Accept(Message* message) {
+  MOJO_DCHECK(!message->has_flag(kMessageExpectsResponse));
+  return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message,
+                                 MessageReceiver* responder) {
+  MOJO_DCHECK(message->has_flag(kMessageExpectsResponse));
+
+  // Reserve 0 in case we want it to convey special meaning in the future.
+  uint64_t request_id = next_request_id_++;
+  if (request_id == 0)
+    request_id = next_request_id_++;
+
+  message->set_request_id(request_id);
+  if (!connector_.Accept(message))
+    return false;
+
+  // We assume ownership of |responder|.
+  responders_[request_id] = responder;
+  return true;
+}
+
+void Router::EnableTestingMode() {
+  testing_mode_ = true;
+  connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+  if (message->has_flag(kMessageExpectsResponse)) {
+    if (incoming_receiver_) {
+      MessageReceiver* responder = new ResponderThunk(weak_self_);
+      bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+      if (!ok)
+        delete responder;
+      return ok;
+    }
+
+    // If we receive a request expecting a response when the client is not
+    // listening, then we have no choice but to tear down the pipe.
+    connector_.CloseMessagePipe();
+  } else if (message->has_flag(kMessageIsResponse)) {
+    uint64_t request_id = message->request_id();
+    ResponderMap::iterator it = responders_.find(request_id);
+    if (it == responders_.end()) {
+      MOJO_DCHECK(testing_mode_);
+      return false;
+    }
+    MessageReceiver* responder = it->second;
+    responders_.erase(it);
+    bool ok = responder->Accept(message);
+    delete responder;
+    return ok;
+  } else {
+    if (incoming_receiver_)
+      return incoming_receiver_->Accept(message);
+    // OK to drop message on the floor.
+  }
+
+  return false;
+}
+
+// ----------------------------------------------------------------------------
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h
new file mode 100644
index 0000000..ebd4928
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+
+#include <map>
+
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+class Router : public MessageReceiverWithResponder {
+ public:
+  Router(ScopedMessagePipeHandle message_pipe,
+         FilterChain filters,
+         const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+  virtual ~Router();
+
+  // Sets the receiver to handle messages read from the message pipe that do
+  // not have the kMessageIsResponse flag set.
+  void set_incoming_receiver(MessageReceiverWithResponder* receiver) {
+    incoming_receiver_ = receiver;
+  }
+
+  // Sets the error handler to receive notifications when an error is
+  // encountered while reading from the pipe or waiting to read from the pipe.
+  void set_error_handler(ErrorHandler* error_handler) {
+    connector_.set_error_handler(error_handler);
+  }
+
+  // Returns true if an error was encountered while reading from the pipe or
+  // waiting to read from the pipe.
+  bool encountered_error() const { return connector_.encountered_error(); }
+
+  void CloseMessagePipe() {
+    connector_.CloseMessagePipe();
+  }
+
+  ScopedMessagePipeHandle PassMessagePipe() {
+    return connector_.PassMessagePipe();
+  }
+
+  // MessageReceiver implementation:
+  virtual bool Accept(Message* message) override;
+  virtual bool AcceptWithResponder(Message* message,
+                                   MessageReceiver* responder) override;
+
+  // Blocks the current thread for the first incoming method call, i.e., either
+  // a call to a client method or a callback method.
+  bool WaitForIncomingMessage() {
+    return connector_.WaitForIncomingMessage();
+  }
+
+  // Sets this object to testing mode.
+  // In testing mode:
+  // - the object is more tolerant of unrecognized response messages;
+  // - the connector continues working after seeing errors from its incoming
+  //   receiver.
+  void EnableTestingMode();
+
+ private:
+  typedef std::map<uint64_t, MessageReceiver*> ResponderMap;
+
+  class HandleIncomingMessageThunk : public MessageReceiver {
+   public:
+    HandleIncomingMessageThunk(Router* router);
+    virtual ~HandleIncomingMessageThunk();
+
+    // MessageReceiver implementation:
+    virtual bool Accept(Message* message) override;
+
+   private:
+    Router* router_;
+  };
+
+  bool HandleIncomingMessage(Message* message);
+
+  HandleIncomingMessageThunk thunk_;
+  FilterChain filters_;
+  Connector connector_;
+  SharedData<Router*> weak_self_;
+  MessageReceiverWithResponder* incoming_receiver_;
+  ResponderMap responders_;
+  uint64_t next_request_id_;
+  bool testing_mode_;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
diff --git a/mojo/public/cpp/bindings/lib/shared_data.h b/mojo/public/cpp/bindings/lib/shared_data.h
new file mode 100644
index 0000000..c7bd54f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_data.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to allocate an instance of T that can be shared via reference counting.
+template <typename T>
+class SharedData {
+ public:
+  ~SharedData() {
+    holder_->Release();
+  }
+
+  SharedData() : holder_(new Holder()) {
+  }
+
+  explicit SharedData(const T& value) : holder_(new Holder(value)) {
+  }
+
+  SharedData(const SharedData<T>& other) : holder_(other.holder_) {
+    holder_->Retain();
+  }
+
+  SharedData<T>& operator=(const SharedData<T>& other) {
+    if (other.holder_ == holder_)
+      return *this;
+    holder_->Release();
+    holder_ = other.holder_;
+    holder_->Retain();
+    return *this;
+  }
+
+  void reset() {
+    holder_->Release();
+    holder_ = new Holder();
+  }
+
+  void reset(const T& value) {
+    holder_->Release();
+    holder_ = new Holder(value);
+  }
+
+  void set_value(const T& value) {
+    holder_->value = value;
+  }
+  T* mutable_value() {
+    return &holder_->value;
+  }
+  const T& value() const {
+    return holder_->value;
+  }
+
+ private:
+  class Holder {
+   public:
+    Holder() : value(), ref_count_(1) {
+    }
+    Holder(const T& value) : value(value), ref_count_(1) {
+    }
+
+    void Retain() { ++ref_count_; }
+    void Release() { if (--ref_count_ == 0) delete this; }
+
+    T value;
+
+   private:
+    int ref_count_;
+    MOJO_DISALLOW_COPY_AND_ASSIGN(Holder);
+  };
+
+  Holder* holder_;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
diff --git a/mojo/public/cpp/bindings/lib/shared_ptr.h b/mojo/public/cpp/bindings/lib/shared_ptr.h
new file mode 100644
index 0000000..899e792
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_ptr.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
+
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to manage a heap-allocated instance of P that can be shared via
+// reference counting. When the last reference is dropped, the instance is
+// deleted.
+template <typename P>
+class SharedPtr {
+ public:
+  SharedPtr() {}
+
+  explicit SharedPtr(P* ptr) {
+    impl_.mutable_value()->ptr = ptr;
+  }
+
+  // Default copy-constructor and assignment operator are OK.
+
+  P* get() {
+    return impl_.value().ptr;
+  }
+  const P* get() const {
+    return impl_.value().ptr;
+  }
+
+  void reset() {
+    impl_.reset();
+  }
+
+  P* operator->() { return get(); }
+  const P* operator->() const { return get(); }
+
+ private:
+  class Impl {
+   public:
+    ~Impl() {
+      if (ptr)
+        delete ptr;
+    }
+
+    Impl() : ptr(nullptr) {
+    }
+
+    Impl(P* ptr) : ptr(ptr) {
+    }
+
+    P* ptr;
+
+   private:
+    MOJO_DISALLOW_COPY_AND_ASSIGN(Impl);
+  };
+
+  SharedData<Impl> impl_;
+};
+
+}  // namespace mojo
+}  // namespace internal
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.cc b/mojo/public/cpp/bindings/lib/string_serialization.cc
new file mode 100644
index 0000000..a0544c0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.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/public/cpp/bindings/lib/string_serialization.h"
+
+#include <string.h>
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input) {
+  if (!input)
+    return 0;
+  return internal::Align(sizeof(internal::String_Data) + input.size());
+}
+
+void Serialize_(const String& input, internal::Buffer* buf,
+                internal::String_Data** output) {
+  if (input) {
+    internal::String_Data* result =
+        internal::String_Data::New(input.size(), buf);
+    if (result)
+      memcpy(result->storage(), input.data(), input.size());
+    *output = result;
+  } else {
+    *output = nullptr;
+  }
+}
+
+void Deserialize_(internal::String_Data* input, String* output) {
+  if (input) {
+    String result(input->storage(), input->size());
+    result.Swap(output);
+  } else {
+    output->reset();
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
new file mode 100644
index 0000000..bad2a0c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input);
+void Serialize_(const String& input, internal::Buffer* buffer,
+                internal::String_Data** output);
+void Deserialize_(internal::String_Data* input, String* output);
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/template_util.h b/mojo/public/cpp/bindings/lib/template_util.h
new file mode 100644
index 0000000..5991266
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/template_util.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+
+namespace mojo {
+namespace internal {
+
+template<class T, T v>
+struct IntegralConstant {
+  static const T value = v;
+};
+
+template <class T, T v> const T IntegralConstant<T, v>::value;
+
+typedef IntegralConstant<bool, true> TrueType;
+typedef IntegralConstant<bool, false> FalseType;
+
+template <class T> struct IsConst : FalseType {};
+template <class T> struct IsConst<const T> : TrueType {};
+
+template<bool B, typename T = void>
+struct EnableIf {};
+
+template<typename T>
+struct EnableIf<true, T> { typedef T type; };
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+  YesType dummy[2];
+};
+
+// A helper template to determine if given type is non-const move-only-type,
+// i.e. if a value of the given type should be passed via .Pass() in a
+// destructive way.
+template <typename T> struct IsMoveOnlyType {
+  template <typename U>
+  static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
+
+  template <typename U>
+  static NoType Test(...);
+
+  static const bool value = sizeof(Test<T>(0)) == sizeof(YesType) &&
+                            !IsConst<T>::value;
+};
+
+template <typename T>
+typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) {
+  return t;
+}
+
+template <typename T>
+typename EnableIf<IsMoveOnlyType<T>::value, T>::type Forward(T& t) {
+  return t.Pass();
+}
+
+// This goop is a trick used to implement a template that can be used to
+// determine if a given class is the base class of another given class.
+template<typename, typename> struct IsSame {
+  static bool const value = false;
+};
+template<typename A> struct IsSame<A, A> {
+  static bool const value = true;
+};
+template<typename Base, typename Derived> struct IsBaseOf {
+ private:
+  // This class doesn't work correctly with forward declarations.
+  // Because sizeof cannot be applied to incomplete types, this line prevents us
+  // from passing in forward declarations.
+  typedef char (*EnsureTypesAreComplete)[sizeof(Base) + sizeof(Derived)];
+
+  static Derived* CreateDerived();
+  static char (&Check(Base*))[1];
+  static char (&Check(...))[2];
+
+ public:
+  static bool const value = sizeof Check(CreateDerived()) == 1 &&
+                            !IsSame<Base const, void const>::value;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc
new file mode 100644
index 0000000..47bcf02
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.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/public/cpp/bindings/lib/validation_errors.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+ValidationErrorObserverForTesting* g_validation_error_observer = nullptr;
+SerializationWarningObserverForTesting* g_serialization_warning_observer =
+    nullptr;
+
+}  // namespace
+
+const char* ValidationErrorToString(ValidationError error) {
+  switch (error) {
+    case VALIDATION_ERROR_NONE:
+      return "VALIDATION_ERROR_NONE";
+    case VALIDATION_ERROR_MISALIGNED_OBJECT:
+      return "VALIDATION_ERROR_MISALIGNED_OBJECT";
+    case VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE:
+      return "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE";
+    case VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER:
+      return "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER";
+    case VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER:
+      return "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER";
+    case VALIDATION_ERROR_ILLEGAL_HANDLE:
+      return "VALIDATION_ERROR_ILLEGAL_HANDLE";
+    case VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE:
+      return "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE";
+    case VALIDATION_ERROR_ILLEGAL_POINTER:
+      return "VALIDATION_ERROR_ILLEGAL_POINTER";
+    case VALIDATION_ERROR_UNEXPECTED_NULL_POINTER:
+      return "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER";
+    case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
+      return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION";
+    case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID:
+      return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID";
+  }
+
+  return "Unknown error";
+}
+
+void ReportValidationError(ValidationError error, const char* description) {
+  if (g_validation_error_observer) {
+    g_validation_error_observer->set_last_error(error);
+  } else if (description) {
+    MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error)
+                    << " (" << description << ")";
+  } else {
+    MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
+  }
+}
+
+ValidationErrorObserverForTesting::ValidationErrorObserverForTesting()
+    : last_error_(VALIDATION_ERROR_NONE) {
+  MOJO_DCHECK(!g_validation_error_observer);
+  g_validation_error_observer = this;
+}
+
+ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() {
+  MOJO_DCHECK(g_validation_error_observer == this);
+  g_validation_error_observer = nullptr;
+}
+
+bool ReportSerializationWarning(ValidationError error) {
+  if (g_serialization_warning_observer) {
+    g_serialization_warning_observer->set_last_warning(error);
+    return true;
+  }
+
+  return false;
+}
+
+SerializationWarningObserverForTesting::SerializationWarningObserverForTesting()
+    : last_warning_(VALIDATION_ERROR_NONE) {
+  MOJO_DCHECK(!g_serialization_warning_observer);
+  g_serialization_warning_observer = this;
+}
+
+SerializationWarningObserverForTesting::
+~SerializationWarningObserverForTesting() {
+  MOJO_DCHECK(g_serialization_warning_observer == this);
+  g_serialization_warning_observer = nullptr;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h
new file mode 100644
index 0000000..6152e60
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+enum ValidationError {
+  // There is no validation error.
+  VALIDATION_ERROR_NONE,
+  // An object (struct or array) is not 8-byte aligned.
+  VALIDATION_ERROR_MISALIGNED_OBJECT,
+  // An object is not contained inside the message data, or it overlaps other
+  // objects.
+  VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE,
+  // A struct header doesn't make sense, for example:
+  // - |num_bytes| is smaller than the size of the oldest version that we
+  // support.
+  // - |num_fields| is smaller than the field number of the oldest version that
+  // we support.
+  // - |num_bytes| and |num_fields| don't match.
+  VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER,
+  // An array header doesn't make sense, for example:
+  // - |num_bytes| is smaller than the size of the header plus the size required
+  // to store |num_elements| elements.
+  // - For fixed-size arrays, |num_elements| is different than the specified
+  // size.
+  VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+  // An encoded handle is illegal.
+  VALIDATION_ERROR_ILLEGAL_HANDLE,
+  // A non-nullable handle field is set to invalid handle.
+  VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+  // An encoded pointer is illegal.
+  VALIDATION_ERROR_ILLEGAL_POINTER,
+  // A non-nullable pointer field is set to null.
+  VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+  // |flags| in the message header is an invalid flag combination.
+  VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION,
+  // |flags| in the message header indicates that a request ID is required but
+  // there isn't one.
+  VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID,
+};
+
+const char* ValidationErrorToString(ValidationError error);
+
+void ReportValidationError(ValidationError error,
+                           const char* description = nullptr);
+
+// Only used by validation tests and when there is only one thread doing message
+// validation.
+class ValidationErrorObserverForTesting {
+ public:
+  ValidationErrorObserverForTesting();
+  ~ValidationErrorObserverForTesting();
+
+  ValidationError last_error() const { return last_error_; }
+  void set_last_error(ValidationError error) { last_error_ = error; }
+
+ private:
+  ValidationError last_error_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ValidationErrorObserverForTesting);
+};
+
+// Used only by MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING. Don't use it directly.
+//
+// The function returns true if the error is recorded (by a
+// SerializationWarningObserverForTesting object), false otherwise.
+bool ReportSerializationWarning(ValidationError error);
+
+// Only used by serialization tests and when there is only one thread doing
+// message serialization.
+class SerializationWarningObserverForTesting {
+ public:
+  SerializationWarningObserverForTesting();
+  ~SerializationWarningObserverForTesting();
+
+  ValidationError last_warning() const { return last_warning_; }
+  void set_last_warning(ValidationError error) { last_warning_ = error; }
+
+ private:
+  ValidationError last_warning_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(SerializationWarningObserverForTesting);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+// In debug build, logs a serialization warning if |condition| evaluates to
+// true:
+//   - if there is a SerializationWarningObserverForTesting object alive,
+//     records |error| in it;
+//   - otherwise, logs a fatal-level message.
+// |error| is the validation error that will be triggered by the receiver
+// of the serialzation result.
+//
+// In non-debug build, does nothing (not even compiling |condition|).
+#define MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( \
+    condition, error, description) \
+  MOJO_DLOG_IF(FATAL, (condition) && !ReportSerializationWarning(error)) \
+      << "The outgoing message will trigger " \
+      << ValidationErrorToString(error) << " at the receiving side (" \
+      << description << ").";
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
new file mode 100644
index 0000000..62801f1
--- /dev/null
+++ b/mojo/public/cpp/bindings/message.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_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+// Message is a holder for the data and handles to be sent over a MessagePipe.
+// Message owns its data and handles, but a consumer of Message is free to
+// mutate the data and handles. The message's data is comprised of a header
+// followed by payload.
+class Message {
+ public:
+  Message();
+  ~Message();
+
+  // These may only be called on a newly created Message object.
+  void AllocUninitializedData(uint32_t num_bytes);
+  void AdoptData(uint32_t num_bytes, internal::MessageData* data);
+
+  // Swaps data and handles between this Message and another.
+  void Swap(Message* other);
+
+  uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+  // Access the raw bytes of the message.
+  const uint8_t* data() const { return
+    reinterpret_cast<const uint8_t*>(data_);
+  }
+  uint8_t* mutable_data() { return reinterpret_cast<uint8_t*>(data_); }
+
+  // Access the header.
+  const internal::MessageHeader* header() const { return &data_->header; }
+
+  uint32_t name() const { return data_->header.name; }
+  bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); }
+
+  // Access the request_id field (if present).
+  bool has_request_id() const { return data_->header.num_fields >= 3; }
+  uint64_t request_id() const {
+    MOJO_DCHECK(has_request_id());
+    return static_cast<const internal::MessageHeaderWithRequestID*>(
+        &data_->header)->request_id;
+  }
+  void set_request_id(uint64_t request_id) {
+    MOJO_DCHECK(has_request_id());
+    static_cast<internal::MessageHeaderWithRequestID*>(&data_->header)->
+        request_id = request_id;
+  }
+
+  // Access the payload.
+  const uint8_t* payload() const {
+    return reinterpret_cast<const uint8_t*>(data_) + data_->header.num_bytes;
+  }
+  uint8_t* mutable_payload() {
+    return reinterpret_cast<uint8_t*>(data_) + data_->header.num_bytes;
+  }
+  uint32_t payload_num_bytes() const {
+    MOJO_DCHECK(data_num_bytes_ >= data_->header.num_bytes);
+    return data_num_bytes_ - data_->header.num_bytes;
+  }
+
+  // Access the handles.
+  const std::vector<Handle>* handles() const { return &handles_; }
+  std::vector<Handle>* mutable_handles() { return &handles_; }
+
+ private:
+  uint32_t data_num_bytes_;
+  internal::MessageData* data_;  // Heap-allocated using malloc.
+  std::vector<Handle> handles_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Message);
+};
+
+class MessageReceiver {
+ public:
+  virtual ~MessageReceiver() {}
+
+  // The receiver may mutate the given message.  Returns true if the message
+  // was accepted and false otherwise, indicating that the message was invalid
+  // or malformed.
+  virtual bool Accept(Message* message) MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+class MessageReceiverWithResponder : public MessageReceiver {
+ public:
+  virtual ~MessageReceiverWithResponder() {}
+
+  // A variant on Accept that registers a MessageReceiver (known as the
+  // responder) to handle the response message generated from the given
+  // message. The responder's Accept method may be called during
+  // AcceptWithResponder or some time after its return.
+  //
+  // NOTE: Upon returning true, AcceptWithResponder assumes ownership of
+  // |responder| and will delete it after calling |responder->Accept| or upon
+  // its own destruction.
+  //
+  virtual bool AcceptWithResponder(
+      Message* message, MessageReceiver* responder) MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+// Read a single message from the pipe and dispatch to the given receiver.  The
+// receiver may be null, in which case the message is simply discarded.
+// Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to
+// become readable. Returns MOJO_RESULT_OK if a message was dispatched and
+// otherwise returns an error code if something went wrong.
+//
+// NOTE: The message hasn't been validated and may be malformed!
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+                                  MessageReceiver* receiver,
+                                  bool* receiver_result);
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
diff --git a/mojo/public/cpp/bindings/message_filter.h b/mojo/public/cpp/bindings/message_filter.h
new file mode 100644
index 0000000..f4faafb
--- /dev/null
+++ b/mojo/public/cpp/bindings/message_filter.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_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is the base class for message filters. Subclasses should
+// implement the pure virtual method Accept() inherited from MessageReceiver to
+// process messages and/or forward them to |sink_|.
+class MessageFilter : public MessageReceiver {
+ public:
+  // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+  // this object is alive.
+  explicit MessageFilter(MessageReceiver* sink = nullptr);
+  virtual ~MessageFilter();
+
+  void set_sink(MessageReceiver* sink) { sink_ = sink; }
+
+ protected:
+  MessageReceiver* sink_;
+};
+
+// A trivial filter that simply forwards every message it receives to |sink_|.
+class PassThroughFilter : public MessageFilter {
+ public:
+  explicit PassThroughFilter(MessageReceiver* sink = nullptr);
+
+  virtual bool Accept(Message* message) override;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_FILTER_H_
diff --git a/mojo/public/cpp/bindings/no_interface.h b/mojo/public/cpp/bindings/no_interface.h
new file mode 100644
index 0000000..07587bd
--- /dev/null
+++ b/mojo/public/cpp/bindings/no_interface.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_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// NoInterface is for use in cases when a non-existent or empty interface is
+// needed (e.g., when the Mojom "Peer" attribute is not present).
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+  static const char* Name_;
+  typedef NoInterfaceProxy Proxy_;
+  typedef NoInterfaceStub Stub_;
+  typedef PassThroughFilter RequestValidator_;
+  typedef PassThroughFilter ResponseValidator_;
+  typedef NoInterface Client;
+  virtual ~NoInterface() {}
+};
+
+class NoInterfaceProxy : public NoInterface {
+ public:
+  explicit NoInterfaceProxy(MessageReceiver* receiver) {}
+};
+
+class NoInterfaceStub : public MessageReceiverWithResponder {
+ public:
+  NoInterfaceStub() {}
+  void set_sink(NoInterface* sink) {}
+  NoInterface* sink() { return nullptr; }
+  virtual bool Accept(Message* message) override;
+  virtual bool AcceptWithResponder(Message* message,
+                                   MessageReceiver* responder) override;
+};
+
+
+// AnyInterface is for use in cases where any interface would do (e.g., see the
+// Shell::Connect method).
+
+typedef NoInterface AnyInterface;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h
new file mode 100644
index 0000000..cd7db26
--- /dev/null
+++ b/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_BINDINGS_STRING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+class String {
+ public:
+  typedef internal::String_Data Data_;
+
+  String() : is_null_(true) {}
+  String(const std::string& str) : value_(str), is_null_(false) {}
+  String(const char* chars) : is_null_(!chars) {
+    if (chars)
+      value_ = chars;
+  }
+  String(const char* chars, size_t num_chars)
+      : value_(chars, num_chars),
+        is_null_(false) {
+  }
+  template <size_t N>
+  String(const char chars[N]) : value_(chars, N-1), is_null_(false) {}
+
+  template <typename U>
+  static String From(const U& other) {
+    return TypeConverter<String, U>::Convert(other);
+  }
+
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, String>::Convert(*this);
+  }
+
+  String& operator=(const std::string& str) {
+    value_ = str;
+    is_null_ = false;
+    return *this;
+  }
+  String& operator=(const char* chars) {
+    is_null_ = !chars;
+    if (chars) {
+      value_ = chars;
+    } else {
+      value_.clear();
+    }
+    return *this;
+  }
+
+  void reset() {
+    value_.clear();
+    is_null_ = true;
+  }
+
+  bool is_null() const { return is_null_; }
+
+  size_t size() const { return value_.size(); }
+
+  const char* data() const { return value_.data(); }
+
+  const char& at(size_t offset) const { return value_.at(offset); }
+  const char& operator[](size_t offset) const { return value_[offset]; }
+
+  const std::string& get() const { return value_; }
+  operator const std::string&() const { return value_; }
+
+  void Swap(String* other) {
+    std::swap(is_null_, other->is_null_);
+    value_.swap(other->value_);
+  }
+
+  void Swap(std::string* other) {
+    is_null_ = false;
+    value_.swap(*other);
+  }
+
+ private:
+  typedef std::string String::*Testable;
+
+ public:
+  operator Testable() const { return is_null_ ? 0 : &String::value_; }
+
+ private:
+  std::string value_;
+  bool is_null_;
+};
+
+inline bool operator==(const String& a, const String& b) {
+  return a.is_null() == b.is_null() && a.get() == b.get();
+}
+inline bool operator==(const char* a, const String& b) {
+  return !b.is_null() && a == b.get();
+}
+inline bool operator==(const String& a, const char* b) {
+  return !a.is_null() && a.get() == b;
+}
+inline bool operator!=(const String& a, const String& b) { return !(a == b); }
+inline bool operator!=(const char* a, const String& b) { return !(a == b); }
+inline bool operator!=(const String& a, const char* b) { return !(a == b); }
+
+inline std::ostream& operator<<(std::ostream& out, const String& s) {
+  return out << s.get();
+}
+
+// TODO(darin): Add similar variants of operator<,<=,>,>=
+
+template <>
+struct TypeConverter<String, std::string> {
+  static String Convert(const std::string& input) { return String(input); }
+};
+
+template <>
+struct TypeConverter<std::string, String> {
+  static std::string Convert(const String& input) { return input; }
+};
+
+template <size_t N>
+struct TypeConverter<String, char[N]> {
+  static String Convert(const char input[N]) {
+    MOJO_DCHECK(input);
+    return String(input, N-1);
+  }
+};
+
+// Appease MSVC.
+template <size_t N>
+struct TypeConverter<String, const char[N]> {
+  static String Convert(const char input[N]) {
+    MOJO_DCHECK(input);
+    return String(input, N-1);
+  }
+};
+
+template <>
+struct TypeConverter<String, const char*> {
+  // |input| may be null, in which case a null String will be returned.
+  static String Convert(const char* input) { return String(input); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
diff --git a/mojo/public/cpp/bindings/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h
new file mode 100644
index 0000000..46729dd
--- /dev/null
+++ b/mojo/public/cpp/bindings/struct_ptr.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_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+
+#include <new>
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Struct>
+class StructHelper {
+ public:
+  template <typename Ptr>
+  static void Initialize(Ptr* ptr) { ptr->Initialize(); }
+};
+
+}  // namespace internal
+
+template <typename Struct>
+class StructPtr {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(StructPtr, RValue);
+ public:
+  typedef typename Struct::Data_ Data_;
+
+  StructPtr() : ptr_(nullptr) {}
+  ~StructPtr() {
+    delete ptr_;
+  }
+
+  StructPtr(RValue other) : ptr_(nullptr) { Take(other.object); }
+  StructPtr& operator=(RValue other) {
+    Take(other.object);
+    return *this;
+  }
+
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, StructPtr>::Convert(*this);
+  }
+
+  void reset() {
+    if (ptr_) {
+      delete ptr_;
+      ptr_ = nullptr;
+    }
+  }
+
+  bool is_null() const { return ptr_ == nullptr; }
+
+  Struct& operator*() const {
+    MOJO_DCHECK(ptr_);
+    return *ptr_;
+  }
+  Struct* operator->() const {
+    MOJO_DCHECK(ptr_);
+    return ptr_;
+  }
+  Struct* get() const { return ptr_; }
+
+  void Swap(StructPtr* other) {
+    std::swap(ptr_, other->ptr_);
+  }
+
+  // Please note that calling this method will fail compilation if the value
+  // type |Struct| doesn't have a Clone() method defined (which usually means
+  // that it contains Mojo handles).
+  StructPtr Clone() const {
+    return is_null() ? StructPtr() : ptr_->Clone();
+  }
+
+ private:
+  typedef Struct* StructPtr::*Testable;
+
+ public:
+  operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
+
+ private:
+  friend class internal::StructHelper<Struct>;
+  void Initialize() {
+    MOJO_DCHECK(!ptr_);
+    ptr_ = new Struct();
+  }
+
+  void Take(StructPtr* other) {
+    reset();
+    Swap(other);
+  }
+
+  Struct* ptr_;
+};
+
+// Designed to be used when Struct is small and copyable.
+template <typename Struct>
+class InlinedStructPtr {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InlinedStructPtr, RValue);
+ public:
+  typedef typename Struct::Data_ Data_;
+
+  InlinedStructPtr() : is_null_(true) {}
+  ~InlinedStructPtr() {}
+
+  InlinedStructPtr(RValue other) : is_null_(true) { Take(other.object); }
+  InlinedStructPtr& operator=(RValue other) {
+    Take(other.object);
+    return *this;
+  }
+
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, InlinedStructPtr>::Convert(*this);
+  }
+
+  void reset() {
+    is_null_ = true;
+    value_.~Struct();
+    new (&value_) Struct();
+  }
+
+  bool is_null() const { return is_null_; }
+
+  Struct& operator*() const {
+    MOJO_DCHECK(!is_null_);
+    return value_;
+  }
+  Struct* operator->() const {
+    MOJO_DCHECK(!is_null_);
+    return &value_;
+  }
+  Struct* get() const { return &value_; }
+
+  void Swap(InlinedStructPtr* other) {
+    std::swap(value_, other->value_);
+    std::swap(is_null_, other->is_null_);
+  }
+
+  InlinedStructPtr Clone() const {
+    return is_null() ? InlinedStructPtr() : value_.Clone();
+  }
+
+ private:
+  typedef Struct InlinedStructPtr::*Testable;
+
+ public:
+  operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
+
+ private:
+  friend class internal::StructHelper<Struct>;
+  void Initialize() { is_null_ = false; }
+
+  void Take(InlinedStructPtr* other) {
+    reset();
+    Swap(other);
+  }
+
+  mutable Struct value_;
+  bool is_null_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
new file mode 100644
index 0000000..6a4b2df
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_bindings_unittests
+test("mojo_public_bindings_unittests") {
+  sources = [
+    "array_unittest.cc",
+    "bounds_checker_unittest.cc",
+    "buffer_unittest.cc",
+    "connector_unittest.cc",
+    "handle_passing_unittest.cc",
+    "interface_ptr_unittest.cc",
+    "request_response_unittest.cc",
+    "router_unittest.cc",
+    "sample_service_unittest.cc",
+    "serialization_warning_unittest.cc",
+    "string_unittest.cc",
+    "struct_unittest.cc",
+    "type_conversion_unittest.cc",
+    "validation_unittest.cc",
+  ]
+
+  deps = [
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//testing/gtest",
+    ":mojo_public_bindings_test_utils",
+  ]
+}
+
+source_set("mojo_public_bindings_test_utils") {
+  sources = [
+    "validation_test_input_parser.cc",
+    "validation_test_input_parser.h",
+  ]
+
+  deps = [
+    "//mojo/public/c/system",
+  ]
+}
diff --git a/mojo/public/cpp/bindings/tests/DEPS b/mojo/public/cpp/bindings/tests/DEPS
new file mode 100644
index 0000000..b99d520
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+mojo/public/cpp/environment",
+  "+mojo/public/interfaces/bindings/tests",
+]
diff --git a/mojo/public/cpp/bindings/tests/array_unittest.cc b/mojo/public/cpp/bindings/tests/array_unittest.cc
new file mode 100644
index 0000000..82a705f
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/array_unittest.cc
@@ -0,0 +1,486 @@
+// Copyright 2014 The Chromium 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/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::Array_Data;
+using mojo::internal::ArrayValidateParams;
+using mojo::internal::FixedBuffer;
+using mojo::internal::NoValidateParams;
+using mojo::internal::String_Data;
+
+class CopyableType {
+ public:
+  CopyableType() : copied_(false), ptr_(this) { num_instances_++; }
+  CopyableType(const CopyableType& other) : copied_(true), ptr_(other.ptr()) {
+    num_instances_++;
+  }
+  CopyableType& operator=(const CopyableType& other) {
+    copied_ = true;
+    ptr_ = other.ptr();
+    return *this;
+  }
+  ~CopyableType() { num_instances_--; }
+
+  bool copied() const { return copied_; }
+  static size_t num_instances() { return num_instances_; }
+  CopyableType* ptr() const { return ptr_; }
+  void ResetCopied() { copied_ = false; }
+
+ private:
+  bool copied_;
+  static size_t num_instances_;
+  CopyableType* ptr_;
+};
+
+size_t CopyableType::num_instances_ = 0;
+
+class MoveOnlyType {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(MoveOnlyType, RValue)
+ public:
+  typedef MoveOnlyType Data_;
+  MoveOnlyType() : moved_(false), ptr_(this) { num_instances_++; }
+  MoveOnlyType(RValue other) : moved_(true), ptr_(other.object->ptr()) {
+    num_instances_++;
+  }
+  MoveOnlyType& operator=(RValue other) {
+    moved_ = true;
+    ptr_ = other.object->ptr();
+    return *this;
+  }
+  ~MoveOnlyType() { num_instances_--; }
+
+  bool moved() const { return moved_; }
+  static size_t num_instances() { return num_instances_; }
+  MoveOnlyType* ptr() const { return ptr_; }
+  void ResetMoved() { moved_ = false; }
+
+ private:
+  bool moved_;
+  static size_t num_instances_;
+  MoveOnlyType* ptr_;
+};
+
+size_t MoveOnlyType::num_instances_ = 0;
+
+class ArrayTest : public testing::Test {
+ public:
+  virtual ~ArrayTest() {}
+
+ private:
+  Environment env_;
+};
+
+// Tests that basic Array operations work.
+TEST_F(ArrayTest, Basic) {
+  Array<char> array(8);
+  for (size_t i = 0; i < array.size(); ++i) {
+    char val = static_cast<char>(i*2);
+    array[i] = val;
+    EXPECT_EQ(val, array.at(i));
+  }
+}
+
+// Tests that basic Array<bool> operations work.
+TEST_F(ArrayTest, Bool) {
+  Array<bool> array(64);
+  for (size_t i = 0; i < array.size(); ++i) {
+    bool val = i % 3 == 0;
+    array[i] = val;
+    EXPECT_EQ(val, array.at(i));
+  }
+}
+
+// Tests that Array<ScopedMessagePipeHandle> supports transferring handles.
+TEST_F(ArrayTest, Handle) {
+  MessagePipe pipe;
+  Array<ScopedMessagePipeHandle> handles(2);
+  handles[0] = pipe.handle0.Pass();
+  handles[1].reset(pipe.handle1.release());
+
+  EXPECT_FALSE(pipe.handle0.is_valid());
+  EXPECT_FALSE(pipe.handle1.is_valid());
+
+  Array<ScopedMessagePipeHandle> handles2 = handles.Pass();
+  EXPECT_TRUE(handles2[0].is_valid());
+  EXPECT_TRUE(handles2[1].is_valid());
+
+  ScopedMessagePipeHandle pipe_handle = handles2[0].Pass();
+  EXPECT_TRUE(pipe_handle.is_valid());
+  EXPECT_FALSE(handles2[0].is_valid());
+}
+
+// Tests that Array<ScopedMessagePipeHandle> supports closing handles.
+TEST_F(ArrayTest, HandlesAreClosed) {
+  MessagePipe pipe;
+  MojoHandle pipe0_value = pipe.handle0.get().value();
+  MojoHandle pipe1_value = pipe.handle0.get().value();
+
+  {
+    Array<ScopedMessagePipeHandle> handles(2);
+    handles[0] = pipe.handle0.Pass();
+    handles[1].reset(pipe.handle0.release());
+  }
+
+  // We expect the pipes to have been closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value));
+}
+
+TEST_F(ArrayTest, Clone) {
+  {
+    // Test POD.
+    Array<int32_t> array(3);
+    for (size_t i = 0; i < array.size(); ++i)
+      array[i] = static_cast<int32_t>(i);
+
+    Array<int32_t> clone_array = array.Clone();
+    EXPECT_EQ(array.size(), clone_array.size());
+    for (size_t i = 0; i < array.size(); ++i)
+      EXPECT_EQ(array[i], clone_array[i]);
+  }
+
+  {
+    // Test copyable object.
+    Array<String> array(2);
+    array[0] = "hello";
+    array[1] = "world";
+
+    Array<String> clone_array = array.Clone();
+    EXPECT_EQ(array.size(), clone_array.size());
+    for (size_t i = 0; i < array.size(); ++i)
+      EXPECT_EQ(array[i], clone_array[i]);
+  }
+
+  {
+    // Test struct.
+    Array<RectPtr> array(2);
+    array[1] = Rect::New();
+    array[1]->x = 1;
+    array[1]->y = 2;
+    array[1]->width = 3;
+    array[1]->height = 4;
+
+    Array<RectPtr> clone_array = array.Clone();
+    EXPECT_EQ(array.size(), clone_array.size());
+    EXPECT_TRUE(clone_array[0].is_null());
+    EXPECT_EQ(array[1]->x, clone_array[1]->x);
+    EXPECT_EQ(array[1]->y, clone_array[1]->y);
+    EXPECT_EQ(array[1]->width, clone_array[1]->width);
+    EXPECT_EQ(array[1]->height, clone_array[1]->height);
+  }
+
+  {
+    // Test array of array.
+    Array<Array<int8_t>> array(2);
+    array[1] = Array<int8_t>(2);
+    array[1][0] = 0;
+    array[1][1] = 1;
+
+    Array<Array<int8_t>> clone_array = array.Clone();
+    EXPECT_EQ(array.size(), clone_array.size());
+    EXPECT_TRUE(clone_array[0].is_null());
+    EXPECT_EQ(array[1].size(), clone_array[1].size());
+    EXPECT_EQ(array[1][0], clone_array[1][0]);
+    EXPECT_EQ(array[1][1], clone_array[1][1]);
+  }
+
+  {
+    // Test that array of handles still works although Clone() is not available.
+    Array<ScopedMessagePipeHandle> array(10);
+    EXPECT_FALSE(array[0].is_valid());
+  }
+}
+
+TEST_F(ArrayTest, Serialization_ArrayOfPOD) {
+  Array<int32_t> array(4);
+  for (size_t i = 0; i < array.size(); ++i)
+    array[i] = static_cast<int32_t>(i);
+
+  size_t size = GetSerializedSize_(array);
+  EXPECT_EQ(8U + 4*4U, size);
+
+  FixedBuffer buf(size);
+  Array_Data<int32_t>* data;
+  SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>(
+      array.Pass(), &buf, &data);
+
+  Array<int32_t> array2;
+  Deserialize_(data, &array2);
+
+  EXPECT_EQ(4U, array2.size());
+  for (size_t i = 0; i < array2.size(); ++i)
+    EXPECT_EQ(static_cast<int32_t>(i), array2[i]);
+}
+
+TEST_F(ArrayTest, Serialization_ArrayOfArrayOfPOD) {
+  Array<Array<int32_t>> array(2);
+  for (size_t j = 0; j < array.size(); ++j) {
+    Array<int32_t> inner(4);
+    for (size_t i = 0; i < inner.size(); ++i)
+      inner[i] = static_cast<int32_t>(i + (j * 10));
+    array[j] = inner.Pass();
+  }
+
+  size_t size = GetSerializedSize_(array);
+  EXPECT_EQ(8U + 2*8U + 2*(8U + 4*4U), size);
+
+  FixedBuffer buf(size);
+  Array_Data<Array_Data<int32_t>*>* data;
+  SerializeArray_<ArrayValidateParams<0, false,
+                  ArrayValidateParams<0, false,
+                  NoValidateParams>>>(
+      array.Pass(), &buf, &data);
+
+  Array<Array<int32_t>> array2;
+  Deserialize_(data, &array2);
+
+  EXPECT_EQ(2U, array2.size());
+  for (size_t j = 0; j < array2.size(); ++j) {
+    const Array<int32_t>& inner = array2[j];
+    EXPECT_EQ(4U, inner.size());
+    for (size_t i = 0; i < inner.size(); ++i)
+      EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]);
+  }
+}
+
+TEST_F(ArrayTest, Serialization_ArrayOfBool) {
+  Array<bool> array(10);
+  for (size_t i = 0; i < array.size(); ++i)
+    array[i] = i % 2 ? true : false;
+
+  size_t size = GetSerializedSize_(array);
+  EXPECT_EQ(8U + 8U, size);
+
+  FixedBuffer buf(size);
+  Array_Data<bool>* data;
+  SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>(
+      array.Pass(), &buf, &data);
+
+  Array<bool> array2;
+  Deserialize_(data, &array2);
+
+  EXPECT_EQ(10U, array2.size());
+  for (size_t i = 0; i < array2.size(); ++i)
+    EXPECT_EQ(i % 2 ? true : false, array2[i]);
+}
+
+TEST_F(ArrayTest, Serialization_ArrayOfString) {
+  Array<String> array(10);
+  for (size_t i = 0; i < array.size(); ++i) {
+    char c = 'A' + static_cast<char>(i);
+    array[i] = String(&c, 1);
+  }
+
+  size_t size = GetSerializedSize_(array);
+  EXPECT_EQ(8U +     // array header
+            10*8U +  // array payload (10 pointers)
+            10*(8U +  // string header
+                8U),  // string length of 1 padded to 8
+            size);
+
+  FixedBuffer buf(size);
+  Array_Data<String_Data*>* data;
+  SerializeArray_<ArrayValidateParams<0, false,
+                  ArrayValidateParams<0, false,
+                  NoValidateParams>>>(
+      array.Pass(), &buf, &data);
+
+  Array<String> array2;
+  Deserialize_(data, &array2);
+
+  EXPECT_EQ(10U, array2.size());
+  for (size_t i = 0; i < array2.size(); ++i) {
+    char c = 'A' + static_cast<char>(i);
+    EXPECT_EQ(String(&c, 1), array2[i]);
+  }
+}
+
+TEST_F(ArrayTest, Resize_Copyable) {
+  ASSERT_EQ(0u, CopyableType::num_instances());
+  mojo::Array<CopyableType> array(3);
+  std::vector<CopyableType*> value_ptrs;
+  value_ptrs.push_back(array[0].ptr());
+  value_ptrs.push_back(array[1].ptr());
+
+  for (size_t i = 0; i < array.size(); i++)
+    array[i].ResetCopied();
+
+  array.resize(2);
+  ASSERT_EQ(2u, array.size());
+  EXPECT_EQ(array.size(), CopyableType::num_instances());
+  for (size_t i = 0; i < array.size(); i++) {
+    EXPECT_FALSE(array[i].copied());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+
+  array.resize(3);
+  array[2].ResetCopied();
+  ASSERT_EQ(3u, array.size());
+  EXPECT_EQ(array.size(), CopyableType::num_instances());
+  for (size_t i = 0; i < array.size(); i++)
+    EXPECT_FALSE(array[i].copied());
+  value_ptrs.push_back(array[2].ptr());
+
+  size_t capacity = array.storage().capacity();
+  array.resize(capacity);
+  ASSERT_EQ(capacity, array.size());
+  EXPECT_EQ(array.size(), CopyableType::num_instances());
+  for (size_t i = 0; i < 3; i++)
+    EXPECT_FALSE(array[i].copied());
+  for (size_t i = 3; i < array.size(); i++) {
+    array[i].ResetCopied();
+    value_ptrs.push_back(array[i].ptr());
+  }
+
+  array.resize(capacity + 2);
+  ASSERT_EQ(capacity + 2, array.size());
+  EXPECT_EQ(array.size(), CopyableType::num_instances());
+  for (size_t i = 0; i < capacity; i++) {
+    EXPECT_TRUE(array[i].copied());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+  array.reset();
+  EXPECT_EQ(0u, CopyableType::num_instances());
+  EXPECT_FALSE(array);
+  array.resize(0);
+  EXPECT_EQ(0u, CopyableType::num_instances());
+  EXPECT_TRUE(array);
+}
+
+TEST_F(ArrayTest, Resize_MoveOnly) {
+  ASSERT_EQ(0u, MoveOnlyType::num_instances());
+  mojo::Array<MoveOnlyType> array(3);
+  std::vector<MoveOnlyType*> value_ptrs;
+  value_ptrs.push_back(array[0].ptr());
+  value_ptrs.push_back(array[1].ptr());
+
+  for (size_t i = 0; i < array.size(); i++)
+    EXPECT_FALSE(array[i].moved());
+
+  array.resize(2);
+  ASSERT_EQ(2u, array.size());
+  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+  for (size_t i = 0; i < array.size(); i++) {
+    EXPECT_FALSE(array[i].moved());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+
+  array.resize(3);
+  ASSERT_EQ(3u, array.size());
+  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+  for (size_t i = 0; i < array.size(); i++)
+    EXPECT_FALSE(array[i].moved());
+  value_ptrs.push_back(array[2].ptr());
+
+  size_t capacity = array.storage().capacity();
+  array.resize(capacity);
+  ASSERT_EQ(capacity, array.size());
+  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+  for (size_t i = 0; i < array.size(); i++)
+    EXPECT_FALSE(array[i].moved());
+  for (size_t i = 3; i < array.size(); i++)
+    value_ptrs.push_back(array[i].ptr());
+
+  array.resize(capacity + 2);
+  ASSERT_EQ(capacity + 2, array.size());
+  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+  for (size_t i = 0; i < capacity; i++) {
+    EXPECT_TRUE(array[i].moved());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+  for (size_t i = capacity; i < array.size(); i++)
+    EXPECT_FALSE(array[i].moved());
+
+  array.reset();
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+  EXPECT_FALSE(array);
+  array.resize(0);
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+  EXPECT_TRUE(array);
+}
+
+TEST_F(ArrayTest, PushBack_Copyable) {
+  ASSERT_EQ(0u, CopyableType::num_instances());
+  mojo::Array<CopyableType> array(2);
+  array.reset();
+  std::vector<CopyableType*> value_ptrs;
+  size_t capacity = array.storage().capacity();
+  for (size_t i = 0; i < capacity; i++) {
+    CopyableType value;
+    value_ptrs.push_back(value.ptr());
+    array.push_back(value);
+    ASSERT_EQ(i + 1, array.size());
+    ASSERT_EQ(i + 1, value_ptrs.size());
+    EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
+    EXPECT_TRUE(array[i].copied());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+    array[i].ResetCopied();
+    EXPECT_TRUE(array);
+  }
+  {
+    CopyableType value;
+    value_ptrs.push_back(value.ptr());
+    array.push_back(value);
+    EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
+  }
+  ASSERT_EQ(capacity + 1, array.size());
+  EXPECT_EQ(array.size(), CopyableType::num_instances());
+
+  for (size_t i = 0; i < array.size(); i++) {
+    EXPECT_TRUE(array[i].copied());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+  array.reset();
+  EXPECT_EQ(0u, CopyableType::num_instances());
+}
+
+TEST_F(ArrayTest, PushBack_MoveOnly) {
+  ASSERT_EQ(0u, MoveOnlyType::num_instances());
+  mojo::Array<MoveOnlyType> array(2);
+  array.reset();
+  std::vector<MoveOnlyType*> value_ptrs;
+  size_t capacity = array.storage().capacity();
+  for (size_t i = 0; i < capacity; i++) {
+    MoveOnlyType value;
+    value_ptrs.push_back(value.ptr());
+    array.push_back(value.Pass());
+    ASSERT_EQ(i + 1, array.size());
+    ASSERT_EQ(i + 1, value_ptrs.size());
+    EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
+    EXPECT_TRUE(array[i].moved());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+    array[i].ResetMoved();
+    EXPECT_TRUE(array);
+  }
+  {
+    MoveOnlyType value;
+    value_ptrs.push_back(value.ptr());
+    array.push_back(value.Pass());
+    EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
+  }
+  ASSERT_EQ(capacity + 1, array.size());
+  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+
+  for (size_t i = 0; i < array.size(); i++) {
+    EXPECT_TRUE(array[i].moved());
+    EXPECT_EQ(value_ptrs[i], array[i].ptr());
+  }
+  array.reset();
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc
new file mode 100644
index 0000000..8378cdd
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2014 The Chromium 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 <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+const void* ToPtr(uintptr_t ptr) {
+  return reinterpret_cast<const void*>(ptr);
+}
+
+#ifdef NDEBUG
+TEST(BoundsCheckerTest, ConstructorRangeOverflow) {
+  {
+    // Test memory range overflow.
+    internal::BoundsChecker
+        checker(ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0);
+
+    EXPECT_FALSE(checker.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
+    EXPECT_FALSE(checker.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
+  }
+
+  if (sizeof(size_t) > sizeof(uint32_t)) {
+    // Test handle index range overflow.
+    size_t num_handles =
+        static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5;
+    internal::BoundsChecker checker(ToPtr(0), 0, num_handles);
+
+    EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+    EXPECT_FALSE(
+        checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+  }
+}
+#endif
+
+TEST(BoundsCheckerTest, IsValidRange) {
+  {
+    internal::BoundsChecker checker(ToPtr(1234), 100, 0);
+
+    // Basics.
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(100), 5));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1230), 50));
+    EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 5));
+    EXPECT_TRUE(checker.IsValidRange(ToPtr(1240), 50));
+    EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 100));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 101));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 100));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1333), 5));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 5));
+
+    // ClaimMemory() updates the valid range.
+    EXPECT_TRUE(checker.ClaimMemory(ToPtr(1254), 10));
+
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1254), 10));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 1));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 10));
+    EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 10));
+    EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 70));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1264), 71));
+  }
+
+  {
+    internal::BoundsChecker checker(ToPtr(1234), 100, 0);
+    // Should return false for empty ranges.
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(0), 0));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1200), 0));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 0));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 0));
+  }
+
+  {
+    // The valid memory range is empty.
+    internal::BoundsChecker checker(ToPtr(1234), 0, 0);
+
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1));
+    EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0));
+  }
+
+  {
+    internal::BoundsChecker
+        checker(ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0);
+
+    // Test overflow.
+    EXPECT_FALSE(checker.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 4000));
+    EXPECT_FALSE(checker.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500),
+        std::numeric_limits<uint32_t>::max()));
+
+    // This should be fine.
+    EXPECT_TRUE(checker.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 200));
+  }
+}
+
+TEST(BoundsCheckerTest, ClaimHandle) {
+  {
+    internal::BoundsChecker checker(ToPtr(0), 0, 10);
+
+    // Basics.
+    EXPECT_TRUE(checker.ClaimHandle(Handle(0)));
+    EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+    EXPECT_TRUE(checker.ClaimHandle(Handle(9)));
+    EXPECT_FALSE(checker.ClaimHandle(Handle(10)));
+
+    // Should fail because it is smaller than the max index that has been
+    // claimed.
+    EXPECT_FALSE(checker.ClaimHandle(Handle(8)));
+
+    // Should return true for invalid handle.
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+  }
+
+  {
+    // No handle to claim.
+    internal::BoundsChecker checker(ToPtr(0), 0, 0);
+
+    EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+    // Should still return true for invalid handle.
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+  }
+
+  {
+    // Test the case that |num_handles| is the same value as
+    // |internal::kEncodedInvalidHandleValue|.
+    EXPECT_EQ(internal::kEncodedInvalidHandleValue,
+              std::numeric_limits<uint32_t>::max());
+    internal::BoundsChecker checker(ToPtr(0), 0,
+                                    std::numeric_limits<uint32_t>::max());
+
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+    EXPECT_FALSE(
+        checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+    EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+    // Should still return true for invalid handle.
+    EXPECT_TRUE(
+        checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+  }
+}
+
+TEST(BoundsCheckerTest, ClaimMemory) {
+  {
+    internal::BoundsChecker checker(ToPtr(1000), 2000, 0);
+
+    // Basics.
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(500), 100));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(800), 300));
+    EXPECT_TRUE(checker.ClaimMemory(ToPtr(1000), 100));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(1099), 100));
+    EXPECT_TRUE(checker.ClaimMemory(ToPtr(1100), 200));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 1001));
+    EXPECT_TRUE(checker.ClaimMemory(ToPtr(2000), 500));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 500));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(1400), 100));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(3000), 1));
+    EXPECT_TRUE(checker.ClaimMemory(ToPtr(2500), 500));
+  }
+
+  {
+    // No memory to claim.
+    internal::BoundsChecker checker(ToPtr(10000), 0, 0);
+
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 1));
+    EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 0));
+  }
+
+  {
+    internal::BoundsChecker
+        checker(ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0);
+
+    // Test overflow.
+    EXPECT_FALSE(checker.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 4000));
+    EXPECT_FALSE(checker.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 750),
+        std::numeric_limits<uint32_t>::max()));
+
+    // This should be fine.
+    EXPECT_TRUE(checker.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 200));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/buffer_unittest.cc b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
new file mode 100644
index 0000000..61424c0
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
@@ -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.
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+bool IsZero(void* p_buf, size_t size) {
+  char* buf = reinterpret_cast<char*>(p_buf);
+  for (size_t i = 0; i < size; ++i) {
+    if (buf[i] != 0)
+      return false;
+  }
+  return true;
+}
+
+// Tests that FixedBuffer allocates memory aligned to 8 byte boundaries.
+TEST(FixedBufferTest, Alignment) {
+  internal::FixedBuffer buf(internal::Align(10) * 2);
+  ASSERT_EQ(buf.size(), 16u * 2);
+
+  void* a = buf.Allocate(10);
+  ASSERT_TRUE(a);
+  EXPECT_TRUE(IsZero(a, 10));
+  EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(a) % 8);
+
+  void* b = buf.Allocate(10);
+  ASSERT_TRUE(b);
+  EXPECT_TRUE(IsZero(b, 10));
+  EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(b) % 8);
+
+  // Any more allocations would result in an assert, but we can't test that.
+}
+
+// Tests that FixedBuffer::Leak passes ownership to the caller.
+TEST(FixedBufferTest, Leak) {
+  void* ptr = nullptr;
+  void* buf_ptr = nullptr;
+  {
+    internal::FixedBuffer buf(8);
+    ASSERT_EQ(8u, buf.size());
+
+    ptr = buf.Allocate(8);
+    ASSERT_TRUE(ptr);
+    buf_ptr = buf.Leak();
+
+    // The buffer should point to the first element allocated.
+    // TODO(mpcomplete): Is this a reasonable expectation?
+    EXPECT_EQ(ptr, buf_ptr);
+
+    // The FixedBuffer should be empty now.
+    EXPECT_EQ(0u, buf.size());
+    EXPECT_FALSE(buf.Leak());
+  }
+
+  // Since we called Leak, ptr is still writable after FixedBuffer went out of
+  // scope.
+  memset(ptr, 1, 8);
+  free(buf_ptr);
+}
+
+#ifdef NDEBUG
+TEST(FixedBufferTest, TooBig) {
+  internal::FixedBuffer buf(24);
+
+  // A little bit too large.
+  EXPECT_EQ(reinterpret_cast<void*>(0), buf.Allocate(32));
+
+  // Move the cursor forward.
+  EXPECT_NE(reinterpret_cast<void*>(0), buf.Allocate(16));
+
+  // A lot too large.
+  EXPECT_EQ(reinterpret_cast<void*>(0),
+            buf.Allocate(std::numeric_limits<size_t>::max() - 1024u));
+
+  // A lot too large, leading to possible integer overflow.
+  EXPECT_EQ(reinterpret_cast<void*>(0),
+            buf.Allocate(std::numeric_limits<size_t>::max() - 8u));
+}
+#endif
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
new file mode 100644
index 0000000..317f4c8
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -0,0 +1,402 @@
+// 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 <stdlib.h>
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+  MessageAccumulator() {
+  }
+
+  virtual bool Accept(Message* message) override {
+    queue_.Push(message);
+    return true;
+  }
+
+  bool IsEmpty() const {
+    return queue_.IsEmpty();
+  }
+
+  void Pop(Message* message) {
+    queue_.Pop(message);
+  }
+
+ private:
+  internal::MessageQueue queue_;
+};
+
+class ConnectorDeletingMessageAccumulator : public MessageAccumulator {
+ public:
+  ConnectorDeletingMessageAccumulator(internal::Connector** connector)
+      : connector_(connector) {}
+
+  virtual bool Accept(Message* message) override {
+    delete *connector_;
+    *connector_ = 0;
+    return MessageAccumulator::Accept(message);
+  }
+
+ private:
+  internal::Connector** connector_;
+};
+
+class ReentrantMessageAccumulator : public MessageAccumulator {
+ public:
+  ReentrantMessageAccumulator(internal::Connector* connector)
+      : connector_(connector), number_of_calls_(0) {}
+
+  virtual bool Accept(Message* message) override {
+    if (!MessageAccumulator::Accept(message))
+      return false;
+    number_of_calls_++;
+    if (number_of_calls_ == 1) {
+      return connector_->WaitForIncomingMessage();
+    }
+    return true;
+  }
+
+  int number_of_calls() { return number_of_calls_; }
+
+ private:
+  internal::Connector* connector_;
+  int number_of_calls_;
+};
+
+class ConnectorTest : public testing::Test {
+ public:
+  ConnectorTest() {
+  }
+
+  virtual void SetUp() override {
+    CreateMessagePipe(nullptr, &handle0_, &handle1_);
+  }
+
+  virtual void TearDown() override {}
+
+  void AllocMessage(const char* text, Message* message) {
+    size_t payload_size = strlen(text) + 1;  // Plus null terminator.
+    internal::MessageBuilder builder(1, payload_size);
+    memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+    builder.Finish(message);
+  }
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+ protected:
+  ScopedMessagePipeHandle handle0_;
+  ScopedMessagePipeHandle handle1_;
+
+ private:
+  Environment env_;
+  RunLoop loop_;
+};
+
+TEST_F(ConnectorTest, Basic) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  PumpMessages();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_Synchronous) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  connector1.WaitForIncomingMessage();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  PumpMessages();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_TwoMessages) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char* kText[] = { "hello", "world" };
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  PumpMessages();
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+    ASSERT_FALSE(accumulator.IsEmpty());
+
+    Message message_received;
+    accumulator.Pop(&message_received);
+
+    EXPECT_EQ(
+        std::string(kText[i]),
+        std::string(reinterpret_cast<const char*>(message_received.payload())));
+  }
+}
+
+TEST_F(ConnectorTest, Basic_TwoMessages_Synchronous) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char* kText[] = { "hello", "world" };
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  connector1.WaitForIncomingMessage();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText[0]),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+
+  ASSERT_TRUE(accumulator.IsEmpty());
+}
+
+TEST_F(ConnectorTest, WriteToClosedPipe) {
+  internal::Connector connector0(handle0_.Pass());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  // Close the other end of the pipe.
+  handle1_.reset();
+
+  // Not observed yet because we haven't spun the RunLoop yet.
+  EXPECT_FALSE(connector0.encountered_error());
+
+  // Write failures are not reported.
+  bool ok = connector0.Accept(&message);
+  EXPECT_TRUE(ok);
+
+  // Still not observed.
+  EXPECT_FALSE(connector0.encountered_error());
+
+  // Spin the RunLoop, and then we should start observing the closed pipe.
+  PumpMessages();
+
+  EXPECT_TRUE(connector0.encountered_error());
+}
+
+TEST_F(ConnectorTest, MessageWithHandles) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char kText[] = "hello world";
+
+  Message message1;
+  AllocMessage(kText, &message1);
+
+  MessagePipe pipe;
+  message1.mutable_handles()->push_back(pipe.handle0.release());
+
+  connector0.Accept(&message1);
+
+  // The message should have been transferred, releasing the handles.
+  EXPECT_TRUE(message1.handles()->empty());
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  PumpMessages();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+  ASSERT_EQ(1U, message_received.handles()->size());
+
+  // Now send a message to the transferred handle and confirm it's sent through
+  // to the orginal pipe.
+  // TODO(vtl): Do we need a better way of "downcasting" the handle types?
+  ScopedMessagePipeHandle smph;
+  smph.reset(MessagePipeHandle(message_received.handles()->front().value()));
+  message_received.mutable_handles()->front() = Handle();
+  // |smph| now owns this handle.
+
+  internal::Connector connector_received(smph.Pass());
+  internal::Connector connector_original(pipe.handle1.Pass());
+
+  Message message2;
+  AllocMessage(kText, &message2);
+
+  connector_received.Accept(&message2);
+  connector_original.set_incoming_receiver(&accumulator);
+  PumpMessages();
+
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithError) {
+  internal::Connector connector0(handle0_.Pass());
+  // Close the other end of the pipe.
+  handle1_.reset();
+  ASSERT_FALSE(connector0.WaitForIncomingMessage());
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector* connector1 = new internal::Connector(handle1_.Pass());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  ConnectorDeletingMessageAccumulator accumulator(&connector1);
+  connector1->set_incoming_receiver(&accumulator);
+
+  connector1->WaitForIncomingMessage();
+
+  ASSERT_FALSE(connector1);
+  ASSERT_FALSE(accumulator.IsEmpty());
+
+  Message message_received;
+  accumulator.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithReentrancy) {
+  internal::Connector connector0(handle0_.Pass());
+  internal::Connector connector1(handle1_.Pass());
+
+  const char* kText[] = { "hello", "world" };
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  ReentrantMessageAccumulator accumulator(&connector1);
+  connector1.set_incoming_receiver(&accumulator);
+
+  PumpMessages();
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+    ASSERT_FALSE(accumulator.IsEmpty());
+
+    Message message_received;
+    accumulator.Pop(&message_received);
+
+    EXPECT_EQ(
+        std::string(kText[i]),
+        std::string(reinterpret_cast<const char*>(message_received.payload())));
+  }
+
+  ASSERT_EQ(2, accumulator.number_of_calls());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
new file mode 100644
index 0000000..d4cffb4
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -0,0 +1,350 @@
+// 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/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/sample_factory.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+const char kText1[] = "hello";
+const char kText2[] = "world";
+
+class StringRecorder {
+ public:
+  explicit StringRecorder(std::string* buf) : buf_(buf) {
+  }
+  void Run(const String& a) const {
+    *buf_ = a.To<std::string>();
+  }
+ private:
+  std::string* buf_;
+};
+
+class ImportedInterfaceImpl
+    : public InterfaceImpl<imported::ImportedInterface> {
+ public:
+  virtual void DoSomething() override { do_something_count_++; }
+
+  static int do_something_count() { return do_something_count_; }
+
+ private:
+  static int do_something_count_;
+};
+int ImportedInterfaceImpl::do_something_count_ = 0;
+
+class SampleNamedObjectImpl : public InterfaceImpl<sample::NamedObject> {
+ public:
+  virtual void SetName(const mojo::String& name) override { name_ = name; }
+
+  virtual void GetName(
+      const mojo::Callback<void(mojo::String)>& callback) override {
+    callback.Run(name_);
+  }
+
+ private:
+  std::string name_;
+};
+
+class SampleFactoryImpl : public InterfaceImpl<sample::Factory> {
+ public:
+  virtual void DoStuff(sample::RequestPtr request,
+                       ScopedMessagePipeHandle pipe) override {
+    std::string text1;
+    if (pipe.is_valid())
+      EXPECT_TRUE(ReadTextMessage(pipe.get(), &text1));
+
+    std::string text2;
+    if (request->pipe.is_valid()) {
+      EXPECT_TRUE(ReadTextMessage(request->pipe.get(), &text2));
+
+      // Ensure that simply accessing request->pipe does not close it.
+      EXPECT_TRUE(request->pipe.is_valid());
+    }
+
+    ScopedMessagePipeHandle pipe0;
+    if (!text2.empty()) {
+      CreateMessagePipe(nullptr, &pipe0, &pipe1_);
+      EXPECT_TRUE(WriteTextMessage(pipe1_.get(), text2));
+    }
+
+    sample::ResponsePtr response(sample::Response::New());
+    response->x = 2;
+    response->pipe = pipe0.Pass();
+    client()->DidStuff(response.Pass(), text1);
+
+    if (request->obj)
+      request->obj->DoSomething();
+  }
+
+  virtual void DoStuff2(ScopedDataPipeConsumerHandle pipe) override {
+    // Read the data from the pipe, writing the response (as a string) to
+    // DidStuff2().
+    ASSERT_TRUE(pipe.is_valid());
+    uint32_t data_size = 0;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              ReadDataRaw(pipe.get(), nullptr, &data_size,
+                          MOJO_READ_DATA_FLAG_QUERY));
+    ASSERT_NE(0, static_cast<int>(data_size));
+    char data[64];
+    ASSERT_LT(static_cast<int>(data_size), 64);
+    ASSERT_EQ(MOJO_RESULT_OK,
+              ReadDataRaw(pipe.get(), data, &data_size,
+                          MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+    client()->DidStuff2(data);
+  }
+
+  virtual void CreateNamedObject(
+      InterfaceRequest<sample::NamedObject> object_request) override {
+    EXPECT_TRUE(object_request.is_pending());
+    BindToRequest(new SampleNamedObjectImpl(), &object_request);
+  }
+
+  // These aren't called or implemented, but exist here to test that the
+  // methods are generated with the correct argument types for imported
+  // interfaces.
+  virtual void RequestImportedInterface(
+      InterfaceRequest<imported::ImportedInterface> imported,
+      const mojo::Callback<void(InterfaceRequest<imported::ImportedInterface>)>&
+          callback) override {}
+  virtual void TakeImportedInterface(
+      imported::ImportedInterfacePtr imported,
+      const mojo::Callback<void(imported::ImportedInterfacePtr)>& callback)
+      override {}
+
+ private:
+  ScopedMessagePipeHandle pipe1_;
+};
+
+class SampleFactoryClientImpl : public sample::FactoryClient {
+ public:
+  SampleFactoryClientImpl() : got_response_(false) {
+  }
+
+  void set_expected_text_reply(const std::string& expected_text_reply) {
+    expected_text_reply_ = expected_text_reply;
+  }
+
+  bool got_response() const {
+    return got_response_;
+  }
+
+  virtual void DidStuff(sample::ResponsePtr response,
+                        const String& text_reply) override {
+    EXPECT_EQ(expected_text_reply_, text_reply);
+
+    if (response->pipe.is_valid()) {
+      std::string text2;
+      EXPECT_TRUE(ReadTextMessage(response->pipe.get(), &text2));
+
+      // Ensure that simply accessing response.pipe does not close it.
+      EXPECT_TRUE(response->pipe.is_valid());
+
+      EXPECT_EQ(std::string(kText2), text2);
+
+      // Do some more tests of handle passing:
+      ScopedMessagePipeHandle p = response->pipe.Pass();
+      EXPECT_TRUE(p.is_valid());
+      EXPECT_FALSE(response->pipe.is_valid());
+    }
+
+    got_response_ = true;
+  }
+
+  virtual void DidStuff2(const String& text_reply) override {
+    got_response_ = true;
+    EXPECT_EQ(expected_text_reply_, text_reply);
+  }
+
+ private:
+  ScopedMessagePipeHandle pipe1_;
+  ScopedMessagePipeHandle pipe3_;
+  std::string expected_text_reply_;
+  bool got_response_;
+};
+
+class HandlePassingTest : public testing::Test {
+ public:
+  virtual void TearDown() {
+    PumpMessages();
+  }
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+ private:
+  Environment env_;
+  RunLoop loop_;
+};
+
+TEST_F(HandlePassingTest, Basic) {
+  sample::FactoryPtr factory;
+  BindToProxy(new SampleFactoryImpl(), &factory);
+
+  SampleFactoryClientImpl factory_client;
+  factory_client.set_expected_text_reply(kText1);
+
+  factory.set_client(&factory_client);
+
+  MessagePipe pipe0;
+  EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1));
+
+  MessagePipe pipe1;
+  EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2));
+
+  imported::ImportedInterfacePtr imported;
+  BindToProxy(new ImportedInterfaceImpl(), &imported);
+
+  sample::RequestPtr request(sample::Request::New());
+  request->x = 1;
+  request->pipe = pipe1.handle0.Pass();
+  request->obj = imported.Pass();
+  factory->DoStuff(request.Pass(), pipe0.handle0.Pass());
+
+  EXPECT_FALSE(factory_client.got_response());
+  int count_before = ImportedInterfaceImpl::do_something_count();
+
+  PumpMessages();
+
+  EXPECT_TRUE(factory_client.got_response());
+  EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before);
+}
+
+TEST_F(HandlePassingTest, PassInvalid) {
+  sample::FactoryPtr factory;
+  BindToProxy(new SampleFactoryImpl(), &factory);
+
+  SampleFactoryClientImpl factory_client;
+  factory.set_client(&factory_client);
+
+  sample::RequestPtr request(sample::Request::New());
+  request->x = 1;
+  factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass());
+
+  EXPECT_FALSE(factory_client.got_response());
+
+  PumpMessages();
+
+  EXPECT_TRUE(factory_client.got_response());
+}
+
+// Verifies DataPipeConsumer can be passed and read from.
+TEST_F(HandlePassingTest, DataPipe) {
+  sample::FactoryPtr factory;
+  BindToProxy(new SampleFactoryImpl(), &factory);
+
+  SampleFactoryClientImpl factory_client;
+  factory.set_client(&factory_client);
+
+  // Writes a string to a data pipe and passes the data pipe (consumer) to the
+  // factory.
+  ScopedDataPipeProducerHandle producer_handle;
+  ScopedDataPipeConsumerHandle consumer_handle;
+  MojoCreateDataPipeOptions options = {
+      sizeof(MojoCreateDataPipeOptions),
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+      1,
+      1024};
+  ASSERT_EQ(MOJO_RESULT_OK,
+            CreateDataPipe(&options, &producer_handle, &consumer_handle));
+  std::string expected_text_reply = "got it";
+  factory_client.set_expected_text_reply(expected_text_reply);
+  // +1 for \0.
+  uint32_t data_size = static_cast<uint32_t>(expected_text_reply.size() + 1);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(producer_handle.get(), expected_text_reply.c_str(),
+                         &data_size, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+
+  factory->DoStuff2(consumer_handle.Pass());
+
+  EXPECT_FALSE(factory_client.got_response());
+
+  PumpMessages();
+
+  EXPECT_TRUE(factory_client.got_response());
+}
+
+TEST_F(HandlePassingTest, PipesAreClosed) {
+  sample::FactoryPtr factory;
+  BindToProxy(new SampleFactoryImpl(), &factory);
+
+  SampleFactoryClientImpl factory_client;
+  factory.set_client(&factory_client);
+
+  MessagePipe extra_pipe;
+
+  MojoHandle handle0_value = extra_pipe.handle0.get().value();
+  MojoHandle handle1_value = extra_pipe.handle1.get().value();
+
+  {
+    Array<ScopedMessagePipeHandle> pipes(2);
+    pipes[0] = extra_pipe.handle0.Pass();
+    pipes[1] = extra_pipe.handle1.Pass();
+
+    sample::RequestPtr request(sample::Request::New());
+    request->more_pipes = pipes.Pass();
+
+    factory->DoStuff(request.Pass(), ScopedMessagePipeHandle());
+  }
+
+  // We expect the pipes to have been closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle0_value));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle1_value));
+}
+
+TEST_F(HandlePassingTest, IsHandle) {
+  // Validate that mojo::internal::IsHandle<> works as expected since this.
+  // template is key to ensuring that we don't leak handles.
+  EXPECT_TRUE(internal::IsHandle<Handle>::value);
+  EXPECT_TRUE(internal::IsHandle<MessagePipeHandle>::value);
+  EXPECT_TRUE(internal::IsHandle<DataPipeConsumerHandle>::value);
+  EXPECT_TRUE(internal::IsHandle<DataPipeProducerHandle>::value);
+  EXPECT_TRUE(internal::IsHandle<SharedBufferHandle>::value);
+
+  // Basic sanity checks...
+  EXPECT_FALSE(internal::IsHandle<int>::value);
+  EXPECT_FALSE(internal::IsHandle<sample::FactoryPtr>::value);
+  EXPECT_FALSE(internal::IsHandle<String>::value);
+}
+
+TEST_F(HandlePassingTest, CreateNamedObject) {
+  sample::FactoryPtr factory;
+  BindToProxy(new SampleFactoryImpl(), &factory);
+
+  sample::NamedObjectPtr object1;
+  EXPECT_FALSE(object1);
+
+  InterfaceRequest<sample::NamedObject> object1_request = Get(&object1);
+  EXPECT_TRUE(object1_request.is_pending());
+  factory->CreateNamedObject(object1_request.Pass());
+  EXPECT_FALSE(object1_request.is_pending());  // We've passed the request.
+
+  ASSERT_TRUE(object1);
+  object1->SetName("object1");
+
+  sample::NamedObjectPtr object2;
+  factory->CreateNamedObject(Get(&object2));
+  object2->SetName("object2");
+
+  std::string name1;
+  object1->GetName(StringRecorder(&name1));
+
+  std::string name2;
+  object2->GetName(StringRecorder(&name2));
+
+  PumpMessages();  // Yield for results.
+
+  EXPECT_EQ(std::string("object1"), name1);
+  EXPECT_EQ(std::string("object2"), name2);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
new file mode 100644
index 0000000..ded6d47
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -0,0 +1,384 @@
+// 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/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ErrorObserver : public ErrorHandler {
+ public:
+  ErrorObserver() : encountered_error_(false) {
+  }
+
+  bool encountered_error() const { return encountered_error_; }
+
+  virtual void OnConnectionError() override { encountered_error_ = true; }
+
+ private:
+  bool encountered_error_;
+};
+
+class MathCalculatorImpl : public InterfaceImpl<math::Calculator> {
+ public:
+  virtual ~MathCalculatorImpl() {}
+
+  MathCalculatorImpl()
+      : total_(0.0),
+        got_connection_(false) {
+  }
+
+  virtual void OnConnectionEstablished() override { got_connection_ = true; }
+
+  virtual void Clear() override { client()->Output(total_); }
+
+  virtual void Add(double value) override {
+    total_ += value;
+    client()->Output(total_);
+  }
+
+  virtual void Multiply(double value) override {
+    total_ *= value;
+    client()->Output(total_);
+  }
+
+  bool got_connection() const {
+    return got_connection_;
+  }
+
+ private:
+  double total_;
+  bool got_connection_;
+};
+
+class MathCalculatorUIImpl : public math::CalculatorUI {
+ public:
+  explicit MathCalculatorUIImpl(math::CalculatorPtr calculator)
+      : calculator_(calculator.Pass()),
+        output_(0.0) {
+    calculator_.set_client(this);
+  }
+
+  bool WaitForIncomingMethodCall() {
+    return calculator_.WaitForIncomingMethodCall();
+  }
+
+  bool encountered_error() const {
+    return calculator_.encountered_error();
+  }
+
+  void Add(double value) {
+    calculator_->Add(value);
+  }
+
+  void Subtract(double value) {
+    calculator_->Add(-value);
+  }
+
+  void Multiply(double value) {
+    calculator_->Multiply(value);
+  }
+
+  void Divide(double value) {
+    calculator_->Multiply(1.0 / value);
+  }
+
+  double GetOutput() const {
+    return output_;
+  }
+
+ private:
+  // math::CalculatorUI implementation:
+  virtual void Output(double value) override { output_ = value; }
+
+  math::CalculatorPtr calculator_;
+  double output_;
+};
+
+class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI {
+ public:
+  explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator)
+      : calculator_(calculator.Pass()),
+        nesting_level_(0) {
+    ++num_instances_;
+    calculator_.set_client(this);
+  }
+
+  void BeginTest(bool nested) {
+    nesting_level_ = nested ? 2 : 1;
+    calculator_->Add(1.0);
+  }
+
+  static int num_instances() { return num_instances_; }
+
+ private:
+  virtual ~SelfDestructingMathCalculatorUIImpl() {
+    --num_instances_;
+  }
+
+  virtual void Output(double value) override {
+    if (--nesting_level_ > 0) {
+      // Add some more and wait for re-entrant call to Output!
+      calculator_->Add(1.0);
+      RunLoop::current()->RunUntilIdle();
+    } else {
+      delete this;
+    }
+  }
+
+  math::CalculatorPtr calculator_;
+  int nesting_level_;
+  static int num_instances_;
+};
+
+// static
+int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0;
+
+class ReentrantServiceImpl : public InterfaceImpl<sample::Service> {
+ public:
+  virtual ~ReentrantServiceImpl() {}
+
+  ReentrantServiceImpl()
+      : got_connection_(false), call_depth_(0), max_call_depth_(0) {}
+
+  virtual void OnConnectionEstablished() override { got_connection_ = true; }
+
+  bool got_connection() const {
+    return got_connection_;
+  }
+
+  int max_call_depth() { return max_call_depth_; }
+
+  virtual void Frobinate(sample::FooPtr foo,
+                         sample::Service::BazOptions baz,
+                         sample::PortPtr port) override {
+    max_call_depth_ = std::max(++call_depth_, max_call_depth_);
+    if (call_depth_ == 1) {
+      EXPECT_TRUE(WaitForIncomingMethodCall());
+    }
+    call_depth_--;
+  }
+
+  virtual void GetPort(mojo::InterfaceRequest<sample::Port> port) override {}
+
+ private:
+  bool got_connection_;
+  int call_depth_;
+  int max_call_depth_;
+};
+
+class InterfacePtrTest : public testing::Test {
+ public:
+  virtual ~InterfacePtrTest() {
+    loop_.RunUntilIdle();
+  }
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+ private:
+  Environment env_;
+  RunLoop loop_;
+};
+
+TEST_F(InterfacePtrTest, EndToEnd) {
+  math::CalculatorPtr calc;
+  MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc);
+  EXPECT_TRUE(impl->got_connection());
+
+  // Suppose this is instantiated in a process that has pipe1_.
+  MathCalculatorUIImpl calculator_ui(calc.Pass());
+
+  calculator_ui.Add(2.0);
+  calculator_ui.Multiply(5.0);
+
+  PumpMessages();
+
+  EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
+  math::CalculatorPtr calc;
+  MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc);
+  EXPECT_TRUE(impl->got_connection());
+
+  // Suppose this is instantiated in a process that has pipe1_.
+  MathCalculatorUIImpl calculator_ui(calc.Pass());
+
+  EXPECT_EQ(0.0, calculator_ui.GetOutput());
+
+  calculator_ui.Add(2.0);
+  EXPECT_EQ(0.0, calculator_ui.GetOutput());
+  impl->WaitForIncomingMethodCall();
+  calculator_ui.WaitForIncomingMethodCall();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+
+  calculator_ui.Multiply(5.0);
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  impl->WaitForIncomingMethodCall();
+  calculator_ui.WaitForIncomingMethodCall();
+  EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, Movable) {
+  math::CalculatorPtr a;
+  math::CalculatorPtr b;
+  BindToProxy(new MathCalculatorImpl(), &b);
+
+  EXPECT_TRUE(!a);
+  EXPECT_FALSE(!b);
+
+  a = b.Pass();
+
+  EXPECT_FALSE(!a);
+  EXPECT_TRUE(!b);
+}
+
+TEST_F(InterfacePtrTest, Resettable) {
+  math::CalculatorPtr a;
+
+  EXPECT_TRUE(!a);
+
+  MessagePipe pipe;
+
+  // Save this so we can test it later.
+  Handle handle = pipe.handle0.get();
+
+  a = MakeProxy<math::Calculator>(pipe.handle0.Pass());
+
+  EXPECT_FALSE(!a);
+
+  a.reset();
+
+  EXPECT_TRUE(!a);
+  EXPECT_FALSE(a.internal_state()->router_for_testing());
+
+  // Test that handle was closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle));
+}
+
+TEST_F(InterfacePtrTest, EncounteredError) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy);
+
+  MathCalculatorUIImpl calculator_ui(proxy.Pass());
+
+  calculator_ui.Add(2.0);
+  PumpMessages();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  calculator_ui.Multiply(5.0);
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  // Close the server.
+  server->internal_state()->router()->CloseMessagePipe();
+
+  // The state change isn't picked up locally yet.
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  PumpMessages();
+
+  // OK, now we see the error.
+  EXPECT_TRUE(calculator_ui.encountered_error());
+}
+
+TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy);
+
+  ErrorObserver error_observer;
+  proxy.set_error_handler(&error_observer);
+
+  MathCalculatorUIImpl calculator_ui(proxy.Pass());
+
+  calculator_ui.Add(2.0);
+  PumpMessages();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  calculator_ui.Multiply(5.0);
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  // Close the server.
+  server->internal_state()->router()->CloseMessagePipe();
+
+  // The state change isn't picked up locally yet.
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  PumpMessages();
+
+  // OK, now we see the error.
+  EXPECT_TRUE(calculator_ui.encountered_error());
+
+  // We should have also been able to observe the error through the
+  // ErrorHandler interface.
+  EXPECT_TRUE(error_observer.encountered_error());
+}
+
+TEST_F(InterfacePtrTest, NoClientAttribute) {
+  // This is a test to ensure the following compiles. The sample::Port interface
+  // does not have an explicit Client attribute.
+  sample::PortPtr port;
+  MessagePipe pipe;
+  port.Bind(pipe.handle0.Pass());
+}
+
+TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) {
+  math::CalculatorPtr proxy;
+  BindToProxy(new MathCalculatorImpl(), &proxy);
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+
+  SelfDestructingMathCalculatorUIImpl* impl =
+      new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
+  impl->BeginTest(false);
+
+  PumpMessages();
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+}
+
+TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) {
+  math::CalculatorPtr proxy;
+  BindToProxy(new MathCalculatorImpl(), &proxy);
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+
+  SelfDestructingMathCalculatorUIImpl* impl =
+      new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
+  impl->BeginTest(true);
+
+  PumpMessages();
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+}
+
+TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
+  sample::ServicePtr proxy;
+  ReentrantServiceImpl* impl = BindToProxy(new ReentrantServiceImpl(), &proxy);
+  EXPECT_TRUE(impl->got_connection());
+
+  proxy->Frobinate(sample::FooPtr(),
+                   sample::Service::BAZ_OPTIONS_REGULAR,
+                   sample::PortPtr());
+  proxy->Frobinate(sample::FooPtr(),
+                   sample::Service::BAZ_OPTIONS_REGULAR,
+                   sample::PortPtr());
+
+  PumpMessages();
+
+  EXPECT_EQ(2, impl->max_call_depth());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
new file mode 100644
index 0000000..3755e64
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium 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/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/sample_import.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ProviderImpl : public InterfaceImpl<sample::Provider> {
+ public:
+  virtual void EchoString(const String& a,
+                          const Callback<void(String)>& callback) override {
+    Callback<void(String)> callback_copy;
+    // Make sure operator= is used.
+    callback_copy = callback;
+    callback_copy.Run(a);
+  }
+
+  virtual void EchoStrings(
+      const String& a,
+      const String& b,
+      const Callback<void(String, String)>& callback) override {
+    callback.Run(a, b);
+  }
+
+  virtual void EchoMessagePipeHandle(
+      ScopedMessagePipeHandle a,
+      const Callback<void(ScopedMessagePipeHandle)>& callback) override {
+    callback.Run(a.Pass());
+  }
+
+  virtual void EchoEnum(sample::Enum a,
+                        const Callback<void(sample::Enum)>& callback) override {
+    callback.Run(a);
+  }
+};
+
+class StringRecorder {
+ public:
+  explicit StringRecorder(std::string* buf) : buf_(buf) {
+  }
+  void Run(const String& a) const {
+    *buf_ = a;
+  }
+  void Run(const String& a, const String& b) const {
+    *buf_ = a.get() + b.get();
+  }
+ private:
+  std::string* buf_;
+};
+
+class EnumRecorder {
+ public:
+  explicit EnumRecorder(sample::Enum* value) : value_(value) {
+  }
+  void Run(sample::Enum a) const {
+    *value_ = a;
+  }
+ private:
+  sample::Enum* value_;
+};
+
+class MessagePipeWriter {
+ public:
+  explicit MessagePipeWriter(const char* text) : text_(text) {
+  }
+  void Run(ScopedMessagePipeHandle handle) const {
+    WriteTextMessage(handle.get(), text_);
+  }
+ private:
+  std::string text_;
+};
+
+class RequestResponseTest : public testing::Test {
+ public:
+  virtual ~RequestResponseTest() {
+    loop_.RunUntilIdle();
+  }
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+ private:
+  Environment env_;
+  RunLoop loop_;
+};
+
+TEST_F(RequestResponseTest, EchoString) {
+  sample::ProviderPtr provider;
+  BindToProxy(new ProviderImpl(), &provider);
+
+  std::string buf;
+  provider->EchoString(String::From("hello"), StringRecorder(&buf));
+
+  PumpMessages();
+
+  EXPECT_EQ(std::string("hello"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoStrings) {
+  sample::ProviderPtr provider;
+  BindToProxy(new ProviderImpl(), &provider);
+
+  std::string buf;
+  provider->EchoStrings(
+      String::From("hello"), String::From(" world"), StringRecorder(&buf));
+
+  PumpMessages();
+
+  EXPECT_EQ(std::string("hello world"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoMessagePipeHandle) {
+  sample::ProviderPtr provider;
+  BindToProxy(new ProviderImpl(), &provider);
+
+  MessagePipe pipe2;
+  provider->EchoMessagePipeHandle(pipe2.handle1.Pass(),
+                                  MessagePipeWriter("hello"));
+
+  PumpMessages();
+
+  std::string value;
+  ReadTextMessage(pipe2.handle0.get(), &value);
+
+  EXPECT_EQ(std::string("hello"), value);
+}
+
+TEST_F(RequestResponseTest, EchoEnum) {
+  sample::ProviderPtr provider;
+  BindToProxy(new ProviderImpl(), &provider);
+
+  sample::Enum value;
+  provider->EchoEnum(sample::ENUM_VALUE, EnumRecorder(&value));
+
+  PumpMessages();
+
+  EXPECT_EQ(sample::ENUM_VALUE, value);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/router_unittest.cc b/mojo/public/cpp/bindings/tests/router_unittest.cc
new file mode 100644
index 0000000..d74e525
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2014 The Chromium 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 <stdlib.h>
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+void AllocRequestMessage(uint32_t name, const char* text, Message* message) {
+  size_t payload_size = strlen(text) + 1;  // Plus null terminator.
+  internal::RequestMessageBuilder builder(name, payload_size);
+  memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+  builder.Finish(message);
+}
+
+void AllocResponseMessage(uint32_t name, const char* text,
+                          uint64_t request_id, Message* message) {
+  size_t payload_size = strlen(text) + 1;  // Plus null terminator.
+  internal::ResponseMessageBuilder builder(name, payload_size, request_id);
+  memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+  builder.Finish(message);
+}
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+  explicit MessageAccumulator(internal::MessageQueue* queue) : queue_(queue) {
+  }
+
+  virtual bool Accept(Message* message) override {
+    queue_->Push(message);
+    return true;
+  }
+
+ private:
+  internal::MessageQueue* queue_;
+};
+
+class ResponseGenerator : public MessageReceiverWithResponder {
+ public:
+  ResponseGenerator() {
+  }
+
+  virtual bool Accept(Message* message) override { return false; }
+
+  virtual bool AcceptWithResponder(Message* message,
+                                   MessageReceiver* responder) override {
+    EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse));
+
+    return SendResponse(message->name(), message->request_id(), responder);
+  }
+
+  bool SendResponse(uint32_t name, uint64_t request_id,
+                    MessageReceiver* responder) {
+    Message response;
+    AllocResponseMessage(name, "world", request_id, &response);
+
+    bool result = responder->Accept(&response);
+    delete responder;
+    return result;
+  }
+};
+
+class LazyResponseGenerator : public ResponseGenerator {
+ public:
+  LazyResponseGenerator() : responder_(nullptr), name_(0), request_id_(0) {
+  }
+
+  virtual ~LazyResponseGenerator() {
+    delete responder_;
+  }
+
+  virtual bool AcceptWithResponder(Message* message,
+                                   MessageReceiver* responder) override {
+    name_ = message->name();
+    request_id_ = message->request_id();
+    responder_ = responder;
+    return true;
+  }
+
+  bool has_responder() const { return !!responder_; }
+
+  void Complete() {
+    SendResponse(name_, request_id_, responder_);
+    responder_ = nullptr;
+  }
+
+ private:
+  MessageReceiver* responder_;
+  uint32_t name_;
+  uint32_t request_id_;
+};
+
+class RouterTest : public testing::Test {
+ public:
+  RouterTest() {
+  }
+
+  virtual void SetUp() override {
+    CreateMessagePipe(nullptr, &handle0_, &handle1_);
+  }
+
+  virtual void TearDown() override {}
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+ protected:
+  ScopedMessagePipeHandle handle0_;
+  ScopedMessagePipeHandle handle1_;
+
+ private:
+  Environment env_;
+  RunLoop loop_;
+};
+
+TEST_F(RouterTest, BasicRequestResponse) {
+  internal::Router router0(handle0_.Pass(), internal::FilterChain());
+  internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+  ResponseGenerator generator;
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  internal::MessageQueue message_queue;
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  PumpMessages();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("world"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
+  internal::Router router0(handle0_.Pass(), internal::FilterChain());
+  internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+  ResponseGenerator generator;
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  internal::MessageQueue message_queue;
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  router1.WaitForIncomingMessage();
+  router0.WaitForIncomingMessage();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("world"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, RequestWithNoReceiver) {
+  internal::Router router0(handle0_.Pass(), internal::FilterChain());
+  internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+  // Without an incoming receiver set on router1, we expect router0 to observe
+  // an error as a result of sending a message.
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  internal::MessageQueue message_queue;
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  PumpMessages();
+
+  EXPECT_TRUE(router0.encountered_error());
+  EXPECT_TRUE(router1.encountered_error());
+  EXPECT_TRUE(message_queue.IsEmpty());
+}
+
+TEST_F(RouterTest, LateResponse) {
+  // Test that things won't blow up if we try to send a message to a
+  // MessageReceiver, which was given to us via AcceptWithResponder,
+  // after the router has gone away.
+
+  LazyResponseGenerator generator;
+  {
+    internal::Router router0(handle0_.Pass(), internal::FilterChain());
+    internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+    router1.set_incoming_receiver(&generator);
+
+    Message request;
+    AllocRequestMessage(1, "hello", &request);
+
+    internal::MessageQueue message_queue;
+    router0.AcceptWithResponder(&request,
+                                new MessageAccumulator(&message_queue));
+
+    PumpMessages();
+
+    EXPECT_TRUE(generator.has_responder());
+
+  }
+
+  generator.Complete();  // This should end up doing nothing.
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
new file mode 100644
index 0000000..2385772
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -0,0 +1,377 @@
+// Copyright 2014 The Chromium 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 <algorithm>
+#include <ostream>
+#include <string>
+
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<int32_t, sample::BarPtr> {
+  static int32_t Convert(const sample::BarPtr& bar) {
+    return static_cast<int32_t>(bar->alpha) << 16 |
+           static_cast<int32_t>(bar->beta) << 8 |
+           static_cast<int32_t>(bar->gamma);
+  }
+};
+
+}  // namespace mojo
+
+namespace sample {
+namespace {
+
+// Set this variable to true to print the message in hex.
+bool g_dump_message_as_hex = false;
+
+// Set this variable to true to print the message in human readable form.
+bool g_dump_message_as_text = false;
+
+// Make a sample |Foo|.
+FooPtr MakeFoo() {
+  mojo::String name("foopy");
+
+  BarPtr bar(Bar::New());
+  bar->alpha = 20;
+  bar->beta = 40;
+  bar->gamma = 60;
+  bar->type = Bar::TYPE_VERTICAL;
+
+  mojo::Array<BarPtr> extra_bars(3);
+  for (size_t i = 0; i < extra_bars.size(); ++i) {
+    Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL;
+    BarPtr bar(Bar::New());
+    uint8_t base = static_cast<uint8_t>(i * 100);
+    bar->alpha = base;
+    bar->beta = base + 20;
+    bar->gamma = base + 40;
+    bar->type = type;
+    extra_bars[i] = bar.Pass();
+  }
+
+  mojo::Array<uint8_t> data(10);
+  for (size_t i = 0; i < data.size(); ++i)
+    data[i] = static_cast<uint8_t>(data.size() - i);
+
+  mojo::Array<mojo::ScopedDataPipeConsumerHandle> input_streams(2);
+  mojo::Array<mojo::ScopedDataPipeProducerHandle> output_streams(2);
+  for (size_t i = 0; i < input_streams.size(); ++i) {
+    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 = 1024;
+    mojo::ScopedDataPipeProducerHandle producer;
+    mojo::ScopedDataPipeConsumerHandle consumer;
+    mojo::CreateDataPipe(&options, &producer, &consumer);
+    input_streams[i] = consumer.Pass();
+    output_streams[i] = producer.Pass();
+  }
+
+  mojo::Array<mojo::Array<bool> > array_of_array_of_bools(2);
+  for (size_t i = 0; i < 2; ++i) {
+    mojo::Array<bool> array_of_bools(2);
+    for (size_t j = 0; j < 2; ++j)
+      array_of_bools[j] = j;
+    array_of_array_of_bools[i] = array_of_bools.Pass();
+  }
+
+  mojo::MessagePipe pipe;
+  FooPtr foo(Foo::New());
+  foo->name = name;
+  foo->x = 1;
+  foo->y = 2;
+  foo->a = false;
+  foo->b = true;
+  foo->c = false;
+  foo->bar = bar.Pass();
+  foo->extra_bars = extra_bars.Pass();
+  foo->data = data.Pass();
+  foo->source = pipe.handle1.Pass();
+  foo->input_streams = input_streams.Pass();
+  foo->output_streams = output_streams.Pass();
+  foo->array_of_array_of_bools = array_of_array_of_bools.Pass();
+
+  return foo.Pass();
+}
+
+// Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+void CheckFoo(const Foo& foo) {
+  const std::string kName("foopy");
+  ASSERT_FALSE(foo.name.is_null());
+  EXPECT_EQ(kName.size(), foo.name.size());
+  for (size_t i = 0; i < std::min(kName.size(), foo.name.size()); i++) {
+    // Test both |operator[]| and |at|.
+    EXPECT_EQ(kName[i], foo.name.at(i)) << i;
+    EXPECT_EQ(kName[i], foo.name[i]) << i;
+  }
+  EXPECT_EQ(kName, foo.name.get());
+
+  EXPECT_EQ(1, foo.x);
+  EXPECT_EQ(2, foo.y);
+  EXPECT_FALSE(foo.a);
+  EXPECT_TRUE(foo.b);
+  EXPECT_FALSE(foo.c);
+
+  EXPECT_EQ(20, foo.bar->alpha);
+  EXPECT_EQ(40, foo.bar->beta);
+  EXPECT_EQ(60, foo.bar->gamma);
+  EXPECT_EQ(Bar::TYPE_VERTICAL, foo.bar->type);
+
+  EXPECT_EQ(3u, foo.extra_bars.size());
+  for (size_t i = 0; i < foo.extra_bars.size(); i++) {
+    uint8_t base = static_cast<uint8_t>(i * 100);
+    Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL;
+    EXPECT_EQ(base, foo.extra_bars[i]->alpha) << i;
+    EXPECT_EQ(base + 20, foo.extra_bars[i]->beta) << i;
+    EXPECT_EQ(base + 40, foo.extra_bars[i]->gamma) << i;
+    EXPECT_EQ(type, foo.extra_bars[i]->type) << i;
+  }
+
+  EXPECT_EQ(10u, foo.data.size());
+  for (size_t i = 0; i < foo.data.size(); ++i) {
+    EXPECT_EQ(static_cast<uint8_t>(foo.data.size() - i), foo.data[i]) << i;
+  }
+
+  EXPECT_FALSE(foo.input_streams.is_null());
+  EXPECT_EQ(2u, foo.input_streams.size());
+
+  EXPECT_FALSE(foo.output_streams.is_null());
+  EXPECT_EQ(2u, foo.output_streams.size());
+
+  EXPECT_EQ(2u, foo.array_of_array_of_bools.size());
+  for (size_t i = 0; i < foo.array_of_array_of_bools.size(); ++i) {
+    EXPECT_EQ(2u, foo.array_of_array_of_bools[i].size());
+    for (size_t j = 0; j < foo.array_of_array_of_bools[i].size(); ++j) {
+      EXPECT_EQ(bool(j), foo.array_of_array_of_bools[i][j]);
+    }
+  }
+}
+
+void PrintSpacer(int depth) {
+  for (int i = 0; i < depth; ++i)
+    std::cout << "   ";
+}
+
+void Print(int depth, const char* name, bool value) {
+  PrintSpacer(depth);
+  std::cout << name << ": " << (value ? "true" : "false") << std::endl;
+}
+
+void Print(int depth, const char* name, int32_t value) {
+  PrintSpacer(depth);
+  std::cout << name << ": " << value << std::endl;
+}
+
+void Print(int depth, const char* name, uint8_t value) {
+  PrintSpacer(depth);
+  std::cout << name << ": " << uint32_t(value) << std::endl;
+}
+
+template <typename H>
+void Print(int depth, const char* name,
+           const mojo::ScopedHandleBase<H>& value) {
+  PrintSpacer(depth);
+  std::cout << name << ": 0x" << std::hex << value.get().value() << std::endl;
+}
+
+void Print(int depth, const char* name, const mojo::String& str) {
+  PrintSpacer(depth);
+  std::cout << name << ": \"" << str.get() << "\"" << std::endl;
+}
+
+void Print(int depth, const char* name, const BarPtr& bar) {
+  PrintSpacer(depth);
+  std::cout << name << ":" << std::endl;
+  if (!bar.is_null()) {
+    ++depth;
+    Print(depth, "alpha", bar->alpha);
+    Print(depth, "beta", bar->beta);
+    Print(depth, "gamma", bar->gamma);
+    Print(depth, "packed", bar.To<int32_t>());
+    --depth;
+  }
+}
+
+template <typename T>
+void Print(int depth, const char* name, const mojo::Array<T>& array) {
+  PrintSpacer(depth);
+  std::cout << name << ":" << std::endl;
+  if (!array.is_null()) {
+    ++depth;
+    for (size_t i = 0; i < array.size(); ++i) {
+      std::stringstream buf;
+      buf << i;
+      Print(depth, buf.str().data(), array.at(i));
+    }
+    --depth;
+  }
+}
+
+void Print(int depth, const char* name, const FooPtr& foo) {
+  PrintSpacer(depth);
+  std::cout << name << ":" << std::endl;
+  if (!foo.is_null()) {
+    ++depth;
+    Print(depth, "name", foo->name);
+    Print(depth, "x", foo->x);
+    Print(depth, "y", foo->y);
+    Print(depth, "a", foo->a);
+    Print(depth, "b", foo->b);
+    Print(depth, "c", foo->c);
+    Print(depth, "bar", foo->bar);
+    Print(depth, "extra_bars", foo->extra_bars);
+    Print(depth, "data", foo->data);
+    Print(depth, "source", foo->source);
+    Print(depth, "input_streams", foo->input_streams);
+    Print(depth, "output_streams", foo->output_streams);
+    Print(depth, "array_of_array_of_bools", foo->array_of_array_of_bools);
+    --depth;
+  }
+}
+
+void DumpHex(const uint8_t* bytes, uint32_t num_bytes) {
+  for (uint32_t i = 0; i < num_bytes; ++i) {
+    std::cout << std::setw(2) << std::setfill('0') << std::hex <<
+        uint32_t(bytes[i]);
+
+    if (i % 16 == 15) {
+      std::cout << std::endl;
+      continue;
+    }
+
+    if (i % 2 == 1)
+      std::cout << " ";
+    if (i % 8 == 7)
+      std::cout << " ";
+  }
+}
+
+class ServiceImpl : public Service {
+ public:
+  virtual void Frobinate(FooPtr foo, BazOptions baz, PortPtr port) override {
+    // Users code goes here to handle the incoming Frobinate message.
+
+    // We mainly check that we're given the expected arguments.
+    EXPECT_FALSE(foo.is_null());
+    if (!foo.is_null())
+      CheckFoo(*foo);
+    EXPECT_EQ(BAZ_OPTIONS_EXTRA, baz);
+
+    if (g_dump_message_as_text) {
+      // Also dump the Foo structure and all of its members.
+      std::cout << "Frobinate:" << std::endl;
+      int depth = 1;
+      Print(depth, "foo", foo);
+      Print(depth, "baz", baz);
+      Print(depth, "port", port.get());
+    }
+  }
+
+  virtual void GetPort(mojo::InterfaceRequest<Port> port_request) override {}
+};
+
+class ServiceProxyImpl : public ServiceProxy {
+ public:
+  explicit ServiceProxyImpl(mojo::MessageReceiverWithResponder* receiver)
+      : ServiceProxy(receiver) {
+  }
+};
+
+class SimpleMessageReceiver : public mojo::MessageReceiverWithResponder {
+ public:
+  virtual bool Accept(mojo::Message* message) override {
+    // Imagine some IPC happened here.
+
+    if (g_dump_message_as_hex) {
+      DumpHex(reinterpret_cast<const uint8_t*>(message->data()),
+              message->data_num_bytes());
+    }
+
+    // In the receiving process, an implementation of ServiceStub is known to
+    // the system. It receives the incoming message.
+    ServiceImpl impl;
+
+    ServiceStub stub;
+    stub.set_sink(&impl);
+    return stub.Accept(message);
+  }
+
+  virtual bool AcceptWithResponder(mojo::Message* message,
+                                   mojo::MessageReceiver* responder) override {
+    return false;
+  }
+};
+
+class BindingsSampleTest : public testing::Test {
+ public:
+  BindingsSampleTest() {}
+  virtual ~BindingsSampleTest() {}
+
+ private:
+  mojo::Environment env_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(BindingsSampleTest);
+};
+
+TEST_F(BindingsSampleTest, Basic) {
+  SimpleMessageReceiver receiver;
+
+  // User has a proxy to a Service somehow.
+  Service* service = new ServiceProxyImpl(&receiver);
+
+  // User constructs a message to send.
+
+  // Notice that it doesn't matter in what order the structs / arrays are
+  // allocated. Here, the various members of Foo are allocated before Foo is
+  // allocated.
+
+  FooPtr foo = MakeFoo();
+  CheckFoo(*foo);
+
+  PortPtr port;
+  service->Frobinate(foo.Pass(), Service::BAZ_OPTIONS_EXTRA, port.Pass());
+
+  delete service;
+}
+
+TEST_F(BindingsSampleTest, DefaultValues) {
+  DefaultsTestPtr defaults(DefaultsTest::New());
+  EXPECT_EQ(-12, defaults->a0);
+  EXPECT_EQ(kTwelve, defaults->a1);
+  EXPECT_EQ(1234, defaults->a2);
+  EXPECT_EQ(34567U, defaults->a3);
+  EXPECT_EQ(123456, defaults->a4);
+  EXPECT_EQ(3456789012U, defaults->a5);
+  EXPECT_EQ(-111111111111LL, defaults->a6);
+  EXPECT_EQ(9999999999999999999ULL, defaults->a7);
+  EXPECT_EQ(0x12345, defaults->a8);
+  EXPECT_EQ(-0x12345, defaults->a9);
+  EXPECT_EQ(1234, defaults->a10);
+  EXPECT_TRUE(defaults->a11);
+  EXPECT_FALSE(defaults->a12);
+  EXPECT_FLOAT_EQ(123.25f, defaults->a13);
+  EXPECT_DOUBLE_EQ(1234567890.123, defaults->a14);
+  EXPECT_DOUBLE_EQ(1E10, defaults->a15);
+  EXPECT_DOUBLE_EQ(-1.2E+20, defaults->a16);
+  EXPECT_DOUBLE_EQ(1.23E-20, defaults->a17);
+  EXPECT_TRUE(defaults->a18.is_null());
+  EXPECT_TRUE(defaults->a19.is_null());
+  EXPECT_EQ(Bar::TYPE_BOTH, defaults->a20);
+  EXPECT_TRUE(defaults->a21.is_null());
+  ASSERT_FALSE(defaults->a22.is_null());
+  EXPECT_EQ(imported::SHAPE_RECTANGLE, defaults->a22->shape);
+  EXPECT_EQ(imported::COLOR_BLACK, defaults->a22->color);
+  EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, defaults->a23);
+  EXPECT_EQ(0x123456789, defaults->a24);
+  EXPECT_EQ(-0x123456789, defaults->a25);
+}
+
+}  // namespace
+}  // namespace sample
diff --git a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
new file mode 100644
index 0000000..8709460
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
@@ -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.
+
+// Serialization warnings are only recorded in debug build.
+#ifndef NDEBUG
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::ArrayValidateParams;
+using mojo::internal::NoValidateParams;
+
+// Creates an array of arrays of handles (2 X 3) for testing.
+Array<Array<ScopedHandle> > CreateTestNestedHandleArray() {
+  Array<Array<ScopedHandle> > array(2);
+  for (size_t i = 0; i < array.size(); ++i) {
+    Array<ScopedHandle> nested_array(3);
+    for (size_t j = 0; j < nested_array.size(); ++j) {
+      MessagePipe pipe;
+      nested_array[j] = ScopedHandle::From(pipe.handle1.Pass());
+    }
+    array[i] = nested_array.Pass();
+  }
+
+  return array.Pass();
+}
+
+class SerializationWarningTest : public testing::Test {
+ public:
+  virtual ~SerializationWarningTest() {}
+
+ protected:
+  template <typename T>
+  void TestWarning(T obj, mojo::internal::ValidationError expected_warning) {
+    warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+    mojo::internal::FixedBuffer buf(GetSerializedSize_(obj));
+    typename T::Data_* data;
+    Serialize_(obj.Pass(), &buf, &data);
+
+    EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+  }
+
+  template <typename ValidateParams, typename T>
+  void TestArrayWarning(T obj,
+                        mojo::internal::ValidationError expected_warning) {
+    warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+    mojo::internal::FixedBuffer buf(GetSerializedSize_(obj));
+    typename T::Data_* data;
+    SerializeArray_<ValidateParams>(obj.Pass(), &buf, &data);
+
+    EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+  }
+
+  mojo::internal::SerializationWarningObserverForTesting warning_observer_;
+  Environment env_;
+};
+
+TEST_F(SerializationWarningTest, HandleInStruct) {
+  Struct2Ptr test_struct(Struct2::New());
+  EXPECT_FALSE(test_struct->hdl.is_valid());
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+
+  test_struct = Struct2::New();
+  MessagePipe pipe;
+  test_struct->hdl = ScopedHandle::From(pipe.handle1.Pass());
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, StructInStruct) {
+  Struct3Ptr test_struct(Struct3::New());
+  EXPECT_TRUE(!test_struct->struct_1);
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct3::New();
+  test_struct->struct_1 = Struct1::New();
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStructsInStruct) {
+  Struct4Ptr test_struct(Struct4::New());
+  EXPECT_TRUE(!test_struct->data);
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct4::New();
+  test_struct->data.resize(1);
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct4::New();
+  test_struct->data.resize(0);
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+  test_struct = Struct4::New();
+  test_struct->data.resize(1);
+  test_struct->data[0] = Struct1::New();
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, FixedArrayOfStructsInStruct) {
+  Struct5Ptr test_struct(Struct5::New());
+  EXPECT_TRUE(!test_struct->pair);
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct5::New();
+  test_struct->pair.resize(1);
+  test_struct->pair[0] = Struct1::New();
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+
+  test_struct = Struct5::New();
+  test_struct->pair.resize(2);
+  test_struct->pair[0] = Struct1::New();
+  test_struct->pair[1] = Struct1::New();
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, StringInStruct) {
+  Struct6Ptr test_struct(Struct6::New());
+  EXPECT_TRUE(!test_struct->str);
+
+  TestWarning(test_struct.Pass(),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct6::New();
+  test_struct->str = "hello world";
+
+  TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfArraysOfHandles) {
+  Array<Array<ScopedHandle> > test_array = CreateTestNestedHandleArray();
+  test_array[0] = Array<ScopedHandle>();
+  test_array[1][0] = ScopedHandle();
+
+  TestArrayWarning<ArrayValidateParams<0, true,
+                   ArrayValidateParams<0, true, NoValidateParams> > >(
+      test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+  test_array = CreateTestNestedHandleArray();
+  test_array[0] = Array<ScopedHandle>();
+  TestArrayWarning<ArrayValidateParams<0, false,
+                   ArrayValidateParams<0, true, NoValidateParams> > >(
+      test_array.Pass(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_array = CreateTestNestedHandleArray();
+  test_array[1][0] = ScopedHandle();
+  TestArrayWarning<ArrayValidateParams<0, true,
+                   ArrayValidateParams<0, false, NoValidateParams> > >(
+      test_array.Pass(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStrings) {
+  Array<String> test_array(3);
+  for (size_t i = 0; i < test_array.size(); ++i)
+    test_array[i] = "hello";
+
+  TestArrayWarning<ArrayValidateParams<0, true,
+                   ArrayValidateParams<0, false, NoValidateParams> > >(
+      test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+  test_array = Array<String>(3);
+  TestArrayWarning<ArrayValidateParams<0, false,
+                   ArrayValidateParams<0, false, NoValidateParams> > >(
+      test_array.Pass(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_array = Array<String>(2);
+  TestArrayWarning<ArrayValidateParams<3, true,
+                   ArrayValidateParams<0, false, NoValidateParams> > >(
+      test_array.Pass(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
+
+#endif
diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc
new file mode 100644
index 0000000..6f85443
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/string_unittest.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(StringTest, DefaultIsNull) {
+  String s;
+  EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, ConstructedWithNULL) {
+  String s(nullptr);
+  EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, ConstructedWithNullCharPointer) {
+  const char* null = nullptr;
+  String s(null);
+  EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, AssignedNULL) {
+  String s("");
+  EXPECT_FALSE(s.is_null());
+  s = nullptr;
+  EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, Empty) {
+  String s("");
+  EXPECT_FALSE(s.is_null());
+  EXPECT_TRUE(s.get().empty());
+}
+
+TEST(StringTest, Basic) {
+  String s("hello world");
+  EXPECT_EQ(std::string("hello world"), s.get());
+}
+
+TEST(StringTest, Assignment) {
+  String s("hello world");
+  String t = s;  // Makes a copy.
+  EXPECT_FALSE(t.is_null());
+  EXPECT_EQ(std::string("hello world"), t.get());
+  EXPECT_FALSE(s.is_null());
+}
+
+TEST(StringTest, Equality) {
+  String s("hello world");
+  String t("hello world");
+  EXPECT_EQ(s, t);
+  EXPECT_TRUE(s == t);
+  EXPECT_TRUE("hello world" == s);
+  EXPECT_TRUE(s == "hello world");
+  EXPECT_TRUE("not" != s);
+  EXPECT_TRUE(s != "not");
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc
new file mode 100644
index 0000000..7d74498
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2014 The Chromium 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/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+RectPtr MakeRect(int32_t factor = 1) {
+  RectPtr rect(Rect::New());
+  rect->x = 1 * factor;
+  rect->y = 2 * factor;
+  rect->width = 10 * factor;
+  rect->height = 20 * factor;
+  return rect.Pass();
+}
+
+void CheckRect(const Rect& rect, int32_t factor = 1) {
+  EXPECT_EQ(1 * factor, rect.x);
+  EXPECT_EQ(2 * factor, rect.y);
+  EXPECT_EQ(10 * factor, rect.width);
+  EXPECT_EQ(20 * factor, rect.height);
+}
+
+class StructTest : public testing::Test {
+ public:
+  virtual ~StructTest() {}
+
+ private:
+  Environment env_;
+};
+
+}  // namespace
+
+TEST_F(StructTest, Rect) {
+  RectPtr rect;
+  EXPECT_TRUE(rect.is_null());
+  EXPECT_TRUE(!rect);
+  EXPECT_FALSE(rect);
+
+  rect = MakeRect();
+  EXPECT_FALSE(rect.is_null());
+  EXPECT_FALSE(!rect);
+  EXPECT_TRUE(rect);
+
+  CheckRect(*rect);
+}
+
+TEST_F(StructTest, Clone) {
+  NamedRegionPtr region;
+
+  NamedRegionPtr clone_region = region.Clone();
+  EXPECT_TRUE(clone_region.is_null());
+
+  region = NamedRegion::New();
+  clone_region = region.Clone();
+  EXPECT_TRUE(clone_region->name.is_null());
+  EXPECT_TRUE(clone_region->rects.is_null());
+
+  region->name = "hello world";
+  clone_region = region.Clone();
+  EXPECT_EQ(region->name, clone_region->name);
+
+  region->rects = Array<RectPtr>(2);
+  region->rects[1] = MakeRect();
+  clone_region = region.Clone();
+  EXPECT_EQ(2u, clone_region->rects.size());
+  EXPECT_TRUE(clone_region->rects[0].is_null());
+  CheckRect(*clone_region->rects[1]);
+
+  // NoDefaultFieldValues contains handles, so Clone() is not available, but
+  // NoDefaultFieldValuesPtr should still compile.
+  NoDefaultFieldValuesPtr no_default_field_values(NoDefaultFieldValues::New());
+  EXPECT_FALSE(no_default_field_values->f13.is_valid());
+}
+
+// Serialization test of a struct with no pointer or handle members.
+TEST_F(StructTest, Serialization_Basic) {
+  RectPtr rect(MakeRect());
+
+  size_t size = GetSerializedSize_(rect);
+  EXPECT_EQ(8U + 16U, size);
+
+  mojo::internal::FixedBuffer buf(size);
+  internal::Rect_Data* data;
+  Serialize_(rect.Pass(), &buf, &data);
+
+  RectPtr rect2;
+  Deserialize_(data, &rect2);
+
+  CheckRect(*rect2);
+}
+
+// Serialization test of a struct with struct pointers.
+TEST_F(StructTest, Serialization_StructPointers) {
+  RectPairPtr pair(RectPair::New());
+  pair->first = MakeRect();
+  pair->second = MakeRect();
+
+  size_t size = GetSerializedSize_(pair);
+  EXPECT_EQ(8U + 16U + 2*(8U + 16U), size);
+
+  mojo::internal::FixedBuffer buf(size);
+  internal::RectPair_Data* data;
+  Serialize_(pair.Pass(), &buf, &data);
+
+  RectPairPtr pair2;
+  Deserialize_(data, &pair2);
+
+  CheckRect(*pair2->first);
+  CheckRect(*pair2->second);
+}
+
+// Serialization test of a struct with an array member.
+TEST_F(StructTest, Serialization_ArrayPointers) {
+  NamedRegionPtr region(NamedRegion::New());
+  region->name = "region";
+  region->rects = Array<RectPtr>::New(4);
+  for (size_t i = 0; i < region->rects.size(); ++i)
+    region->rects[i] = MakeRect(static_cast<int32_t>(i) + 1);
+
+  size_t size = GetSerializedSize_(region);
+  EXPECT_EQ(8U +  // header
+            8U +  // name pointer
+            8U +  // rects pointer
+            8U +  // name header
+            8U +  // name payload (rounded up)
+            8U +    // rects header
+            4*8U +  // rects payload (four pointers)
+            4*(8U +   // rect header
+               16U),  // rect payload (four ints)
+            size);
+
+  mojo::internal::FixedBuffer buf(size);
+  internal::NamedRegion_Data* data;
+  Serialize_(region.Pass(), &buf, &data);
+
+  NamedRegionPtr region2;
+  Deserialize_(data, &region2);
+
+  EXPECT_EQ(String("region"), region2->name);
+
+  EXPECT_EQ(4U, region2->rects.size());
+  for (size_t i = 0; i < region2->rects.size(); ++i)
+    CheckRect(*region2->rects[i], static_cast<int32_t>(i) + 1);
+}
+
+// Serialization test of a struct with null array pointers.
+TEST_F(StructTest, Serialization_NullArrayPointers) {
+  NamedRegionPtr region(NamedRegion::New());
+  EXPECT_TRUE(region->name.is_null());
+  EXPECT_TRUE(region->rects.is_null());
+
+  size_t size = GetSerializedSize_(region);
+  EXPECT_EQ(8U +  // header
+            8U +  // name pointer
+            8U,   // rects pointer
+            size);
+
+  mojo::internal::FixedBuffer buf(size);
+  internal::NamedRegion_Data* data;
+  Serialize_(region.Pass(), &buf, &data);
+
+  NamedRegionPtr region2;
+  Deserialize_(data, &region2);
+
+  EXPECT_TRUE(region2->name.is_null());
+  EXPECT_TRUE(region2->rects.is_null());
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
new file mode 100644
index 0000000..9500839
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
@@ -0,0 +1,208 @@
+// 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/public/cpp/environment/environment.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+struct RedmondRect {
+  int32_t left;
+  int32_t top;
+  int32_t right;
+  int32_t bottom;
+};
+
+struct RedmondNamedRegion {
+  std::string name;
+  std::vector<RedmondRect> rects;
+};
+
+bool AreEqualRectArrays(const Array<test::RectPtr>& rects1,
+                        const Array<test::RectPtr>& rects2) {
+  if (rects1.size() != rects2.size())
+    return false;
+
+  for (size_t i = 0; i < rects1.size(); ++i) {
+    if (rects1[i]->x != rects2[i]->x ||
+        rects1[i]->y != rects2[i]->y ||
+        rects1[i]->width != rects2[i]->width ||
+        rects1[i]->height != rects2[i]->height) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+template <>
+struct TypeConverter<test::RectPtr, RedmondRect> {
+  static test::RectPtr Convert(const RedmondRect& input) {
+    test::RectPtr rect(test::Rect::New());
+    rect->x = input.left;
+    rect->y = input.top;
+    rect->width = input.right - input.left;
+    rect->height = input.bottom - input.top;
+    return rect.Pass();
+  }
+};
+
+template <>
+struct TypeConverter<RedmondRect, test::RectPtr> {
+  static RedmondRect Convert(const test::RectPtr& input) {
+    RedmondRect rect;
+    rect.left = input->x;
+    rect.top = input->y;
+    rect.right = input->x + input->width;
+    rect.bottom = input->y + input->height;
+    return rect;
+  }
+};
+
+template <>
+struct TypeConverter<test::NamedRegionPtr, RedmondNamedRegion> {
+  static test::NamedRegionPtr Convert(const RedmondNamedRegion& input) {
+    test::NamedRegionPtr region(test::NamedRegion::New());
+    region->name = input.name;
+    region->rects = Array<test::RectPtr>::From(input.rects);
+    return region.Pass();
+  }
+};
+
+template <>
+struct TypeConverter<RedmondNamedRegion, test::NamedRegionPtr> {
+  static RedmondNamedRegion Convert(const test::NamedRegionPtr& input) {
+    RedmondNamedRegion region;
+    region.name = input->name;
+    region.rects = input->rects.To<std::vector<RedmondRect> >();
+    return region;
+  }
+};
+
+namespace test {
+namespace {
+
+TEST(TypeConversionTest, String) {
+  const char kText[6] = "hello";
+
+  String a = std::string(kText);
+  String b(kText);
+  String c(static_cast<const char*>(kText));
+
+  EXPECT_EQ(std::string(kText), a.To<std::string>());
+  EXPECT_EQ(std::string(kText), b.To<std::string>());
+  EXPECT_EQ(std::string(kText), c.To<std::string>());
+}
+
+TEST(TypeConversionTest, String_Null) {
+  String a;
+  EXPECT_TRUE(a.is_null());
+  EXPECT_EQ(std::string(), a.To<std::string>());
+
+  String b = String::From(static_cast<const char*>(nullptr));
+  EXPECT_TRUE(b.is_null());
+}
+
+TEST(TypeConversionTest, String_Empty) {
+  String a = "";
+  EXPECT_EQ(std::string(), a.To<std::string>());
+
+  String b = std::string();
+  EXPECT_FALSE(b.is_null());
+  EXPECT_EQ(std::string(), b.To<std::string>());
+}
+
+TEST(TypeConversionTest, StringWithEmbeddedNull) {
+  const std::string kText("hel\0lo", 6);
+
+  String a(kText);
+  EXPECT_EQ(kText, a.To<std::string>());
+
+  // Expect truncation:
+  String b(kText.c_str());
+  EXPECT_EQ(std::string("hel"), b.To<std::string>());
+}
+
+TEST(TypeConversionTest, CustomTypeConverter) {
+  RectPtr rect(Rect::New());
+  rect->x = 10;
+  rect->y = 20;
+  rect->width = 50;
+  rect->height = 45;
+
+  RedmondRect rr = rect.To<RedmondRect>();
+  EXPECT_EQ(10, rr.left);
+  EXPECT_EQ(20, rr.top);
+  EXPECT_EQ(60, rr.right);
+  EXPECT_EQ(65, rr.bottom);
+
+  RectPtr rect2(Rect::From(rr));
+  EXPECT_EQ(rect->x, rect2->x);
+  EXPECT_EQ(rect->y, rect2->y);
+  EXPECT_EQ(rect->width, rect2->width);
+  EXPECT_EQ(rect->height, rect2->height);
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Array_Null) {
+  Array<RectPtr> rects;
+
+  std::vector<RedmondRect> redmond_rects =
+      rects.To<std::vector<RedmondRect> >();
+
+  EXPECT_TRUE(redmond_rects.empty());
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Array) {
+  const RedmondRect kBase = { 10, 20, 30, 40 };
+
+  Array<RectPtr> rects(10);
+  for (size_t i = 0; i < rects.size(); ++i) {
+    RedmondRect rr = kBase;
+    rr.left += static_cast<int32_t>(i);
+    rr.top += static_cast<int32_t>(i);
+    rects[i] = Rect::From(rr);
+  }
+
+  std::vector<RedmondRect> redmond_rects =
+      rects.To<std::vector<RedmondRect> >();
+
+  Array<RectPtr> rects2 = Array<RectPtr>::From(redmond_rects);
+  EXPECT_TRUE(AreEqualRectArrays(rects, rects2));
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Nested) {
+  RedmondNamedRegion redmond_region;
+  redmond_region.name = "foopy";
+
+  const RedmondRect kBase = { 10, 20, 30, 40 };
+
+  for (size_t i = 0; i < 10; ++i) {
+    RedmondRect rect = kBase;
+    rect.left += static_cast<int32_t>(i);
+    rect.top += static_cast<int32_t>(i);
+    redmond_region.rects.push_back(rect);
+  }
+
+  // Round-trip through generated struct and TypeConverter.
+
+  NamedRegionPtr copy = NamedRegion::From(redmond_region);
+  RedmondNamedRegion redmond_region2 = copy.To<RedmondNamedRegion>();
+
+  EXPECT_EQ(redmond_region.name, redmond_region2.name);
+  EXPECT_EQ(redmond_region.rects.size(), redmond_region2.rects.size());
+  for (size_t i = 0; i < redmond_region.rects.size(); ++i) {
+    EXPECT_EQ(redmond_region.rects[i].left, redmond_region2.rects[i].left);
+    EXPECT_EQ(redmond_region.rects[i].top, redmond_region2.rects[i].top);
+    EXPECT_EQ(redmond_region.rects[i].right, redmond_region2.rects[i].right);
+    EXPECT_EQ(redmond_region.rects[i].bottom, redmond_region2.rects[i].bottom);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
new file mode 100644
index 0000000..f5b5a01
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
@@ -0,0 +1,406 @@
+// Copyright 2014 The Chromium 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/public/cpp/bindings/tests/validation_test_input_parser.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <map>
+#include <set>
+#include <utility>
+
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ValidationTestInputParser {
+ public:
+  ValidationTestInputParser(const std::string& input,
+                            std::vector<uint8_t>* data,
+                            size_t* num_handles,
+                            std::string* error_message);
+  ~ValidationTestInputParser();
+
+  bool Run();
+
+ private:
+  struct DataType;
+
+  typedef std::pair<const char*, const char*> Range;
+
+  typedef bool (ValidationTestInputParser::*ParseDataFunc)(
+      const DataType& type, const std::string& value_string);
+
+  struct DataType {
+    const char* name;
+    size_t name_size;
+    size_t data_size;
+    ParseDataFunc parse_data_func;
+  };
+
+  // A dist4/8 item that hasn't been matched with an anchr item.
+  struct PendingDistanceItem {
+    // Where this data item is located in |data_|.
+    size_t pos;
+    // Either 4 or 8 (bytes).
+    size_t data_size;
+  };
+
+  bool GetNextItem(Range* range);
+
+  bool ParseItem(const Range& range);
+
+  bool ParseUnsignedInteger(const DataType& type,
+                            const std::string& value_string);
+  bool ParseSignedInteger(const DataType& type,
+                          const std::string& value_string);
+  bool ParseFloat(const DataType& type, const std::string& value_string);
+  bool ParseDouble(const DataType& type, const std::string& value_string);
+  bool ParseBinarySequence(const DataType& type,
+                           const std::string& value_string);
+  bool ParseDistance(const DataType& type, const std::string& value_string);
+  bool ParseAnchor(const DataType& type, const std::string& value_string);
+  bool ParseHandles(const DataType& type, const std::string& value_string);
+
+  bool StartsWith(const Range& range, const char* prefix, size_t prefix_length);
+
+  bool ConvertToUnsignedInteger(const std::string& value_string,
+                                unsigned long long int* value);
+
+  template <typename T>
+  void AppendData(T data) {
+    size_t pos = data_->size();
+    data_->resize(pos + sizeof(T));
+    memcpy(&(*data_)[pos], &data, sizeof(T));
+  }
+
+  template <typename TargetType, typename InputType>
+  bool ConvertAndAppendData(InputType value) {
+    if (value > std::numeric_limits<TargetType>::max() ||
+        value < std::numeric_limits<TargetType>::min()) {
+      return false;
+    }
+    AppendData(static_cast<TargetType>(value));
+    return true;
+  }
+
+  template <typename TargetType, typename InputType>
+  bool ConvertAndFillData(size_t pos, InputType value) {
+    if (value > std::numeric_limits<TargetType>::max() ||
+        value < std::numeric_limits<TargetType>::min()) {
+      return false;
+    }
+    TargetType target_value = static_cast<TargetType>(value);
+    assert(pos + sizeof(TargetType) <= data_->size());
+    memcpy(&(*data_)[pos], &target_value, sizeof(TargetType));
+    return true;
+  }
+
+  static const DataType kDataTypes[];
+  static const size_t kDataTypeCount;
+
+  const std::string& input_;
+  size_t input_cursor_;
+
+  std::vector<uint8_t>* data_;
+  size_t* num_handles_;
+  std::string* error_message_;
+
+  std::map<std::string, PendingDistanceItem> pending_distance_items_;
+  std::set<std::string> anchors_;
+};
+
+#define DATA_TYPE(name, data_size, parse_data_func) \
+    {name, sizeof(name) - 1, data_size, parse_data_func}
+
+const ValidationTestInputParser::DataType
+    ValidationTestInputParser::kDataTypes[] = {
+  DATA_TYPE("[u1]", 1, &ValidationTestInputParser::ParseUnsignedInteger),
+  DATA_TYPE("[u2]", 2, &ValidationTestInputParser::ParseUnsignedInteger),
+  DATA_TYPE("[u4]", 4, &ValidationTestInputParser::ParseUnsignedInteger),
+  DATA_TYPE("[u8]", 8, &ValidationTestInputParser::ParseUnsignedInteger),
+  DATA_TYPE("[s1]", 1, &ValidationTestInputParser::ParseSignedInteger),
+  DATA_TYPE("[s2]", 2, &ValidationTestInputParser::ParseSignedInteger),
+  DATA_TYPE("[s4]", 4, &ValidationTestInputParser::ParseSignedInteger),
+  DATA_TYPE("[s8]", 8, &ValidationTestInputParser::ParseSignedInteger),
+  DATA_TYPE("[b]", 1, &ValidationTestInputParser::ParseBinarySequence),
+  DATA_TYPE("[f]", 4, &ValidationTestInputParser::ParseFloat),
+  DATA_TYPE("[d]", 8, &ValidationTestInputParser::ParseDouble),
+  DATA_TYPE("[dist4]", 4, &ValidationTestInputParser::ParseDistance),
+  DATA_TYPE("[dist8]", 8, &ValidationTestInputParser::ParseDistance),
+  DATA_TYPE("[anchr]", 0, &ValidationTestInputParser::ParseAnchor),
+  DATA_TYPE("[handles]", 0, &ValidationTestInputParser::ParseHandles)
+};
+
+const size_t ValidationTestInputParser::kDataTypeCount =
+    sizeof(ValidationTestInputParser::kDataTypes) /
+    sizeof(ValidationTestInputParser::kDataTypes[0]);
+
+ValidationTestInputParser::ValidationTestInputParser(
+    const std::string& input,
+    std::vector<uint8_t>* data,
+    size_t* num_handles,
+    std::string* error_message)
+    : input_(input),
+      input_cursor_(0),
+      data_(data),
+      num_handles_(num_handles),
+      error_message_(error_message) {
+  assert(data_);
+  assert(num_handles_);
+  assert(error_message_);
+  data_->clear();
+  *num_handles_ = 0;
+  error_message_->clear();
+}
+
+ValidationTestInputParser::~ValidationTestInputParser() {
+}
+
+bool ValidationTestInputParser::Run() {
+  Range range;
+  bool result = true;
+  while (result && GetNextItem(&range))
+    result = ParseItem(range);
+
+  if (!result) {
+    *error_message_ = "Error occurred when parsing " +
+        std::string(range.first, range.second);
+  } else if (!pending_distance_items_.empty()) {
+    // We have parsed all the contents in |input_| successfully, but there are
+    // unmatched dist4/8 items.
+    *error_message_ = "Error occurred when matching [dist4/8] and [anchr].";
+    result = false;
+  }
+  if (!result) {
+    data_->clear();
+    *num_handles_ = 0;
+  } else {
+    assert(error_message_->empty());
+  }
+
+  return result;
+}
+
+bool ValidationTestInputParser::GetNextItem(Range* range) {
+  const char kWhitespaceChars[] = " \t\n\r";
+  const char kItemDelimiters[] = " \t\n\r/";
+  const char kEndOfLineChars[] = "\n\r";
+  while (true) {
+    // Skip leading whitespaces.
+    // If there are no non-whitespace characters left, |input_cursor_| will be
+    // set to std::npos.
+    input_cursor_ = input_.find_first_not_of(kWhitespaceChars, input_cursor_);
+
+    if (input_cursor_ >= input_.size())
+      return false;
+
+    if (StartsWith(Range(&input_[0] + input_cursor_,
+                         &input_[0] + input_.size()),
+                   "//", 2)) {
+      // Skip contents until the end of the line.
+      input_cursor_ = input_.find_first_of(kEndOfLineChars, input_cursor_);
+    } else {
+      range->first = &input_[0] + input_cursor_;
+      input_cursor_ = input_.find_first_of(kItemDelimiters, input_cursor_);
+      range->second = input_cursor_ >= input_.size() ?
+          &input_[0] + input_.size() : &input_[0] + input_cursor_;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool ValidationTestInputParser::ParseItem(const Range& range) {
+  for (size_t i = 0; i < kDataTypeCount; ++i) {
+    if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) {
+      return (this->*kDataTypes[i].parse_data_func)(
+          kDataTypes[i],
+          std::string(range.first + kDataTypes[i].name_size, range.second));
+    }
+  }
+
+  // "[u1]" is optional.
+  return ParseUnsignedInteger(kDataTypes[0],
+                              std::string(range.first, range.second));
+}
+
+bool ValidationTestInputParser::ParseUnsignedInteger(
+    const DataType& type, const std::string& value_string) {
+  unsigned long long int value;
+  if (!ConvertToUnsignedInteger(value_string, &value))
+    return false;
+
+  switch (type.data_size) {
+    case 1:
+      return ConvertAndAppendData<uint8_t>(value);
+    case 2:
+      return ConvertAndAppendData<uint16_t>(value);
+    case 4:
+      return ConvertAndAppendData<uint32_t>(value);
+    case 8:
+      return ConvertAndAppendData<uint64_t>(value);
+    default:
+      assert(false);
+      return false;
+  }
+}
+
+bool ValidationTestInputParser::ParseSignedInteger(
+    const DataType& type, const std::string& value_string) {
+  long long int value;
+  if (sscanf(value_string.c_str(), "%lli", &value) != 1)
+    return false;
+
+  switch (type.data_size) {
+    case 1:
+      return ConvertAndAppendData<int8_t>(value);
+    case 2:
+      return ConvertAndAppendData<int16_t>(value);
+    case 4:
+      return ConvertAndAppendData<int32_t>(value);
+    case 8:
+      return ConvertAndAppendData<int64_t>(value);
+    default:
+      assert(false);
+      return false;
+  }
+}
+
+bool ValidationTestInputParser::ParseFloat(
+    const DataType& type, const std::string& value_string) {
+  MOJO_COMPILE_ASSERT(sizeof(float) == 4, float_size_is_not_4);
+
+  float value;
+  if (sscanf(value_string.c_str(), "%f", &value) != 1)
+    return false;
+
+  AppendData(value);
+  return true;
+}
+
+bool ValidationTestInputParser::ParseDouble(const DataType& type,
+                                            const std::string& value_string) {
+  MOJO_COMPILE_ASSERT(sizeof(double) == 8, double_size_is_not_8);
+
+  double value;
+  if (sscanf(value_string.c_str(), "%lf", &value) != 1)
+    return false;
+
+  AppendData(value);
+  return true;
+}
+
+bool ValidationTestInputParser::ParseBinarySequence(
+    const DataType& type, const std::string& value_string) {
+  if (value_string.size() != 8)
+    return false;
+
+  uint8_t value = 0;
+  for (std::string::const_iterator iter = value_string.begin();
+       iter != value_string.end();
+       ++iter) {
+    value <<= 1;
+    if (*iter == '1')
+      value++;
+    else if (*iter != '0')
+      return false;
+  }
+  AppendData(value);
+  return true;
+}
+
+bool ValidationTestInputParser::ParseDistance(const DataType& type,
+                                              const std::string& value_string) {
+  if (pending_distance_items_.find(value_string) !=
+      pending_distance_items_.end())
+    return false;
+
+  PendingDistanceItem item = {data_->size(), type.data_size};
+  data_->resize(data_->size() + type.data_size);
+  pending_distance_items_[value_string] = item;
+
+  return true;
+}
+
+bool ValidationTestInputParser::ParseAnchor(const DataType& type,
+                                            const std::string& value_string) {
+  if (anchors_.find(value_string) != anchors_.end())
+    return false;
+  anchors_.insert(value_string);
+
+  std::map<std::string, PendingDistanceItem>::iterator iter =
+      pending_distance_items_.find(value_string);
+  if (iter == pending_distance_items_.end())
+    return false;
+
+  PendingDistanceItem dist_item = iter->second;
+  pending_distance_items_.erase(iter);
+
+  size_t distance = data_->size() - dist_item.pos;
+  switch (dist_item.data_size) {
+    case 4:
+      return ConvertAndFillData<uint32_t>(dist_item.pos, distance);
+    case 8:
+      return ConvertAndFillData<uint64_t>(dist_item.pos, distance);
+    default:
+      assert(false);
+      return false;
+  }
+}
+
+bool ValidationTestInputParser::ParseHandles(const DataType& type,
+                                             const std::string& value_string) {
+  // It should be the first item.
+  if (!data_->empty())
+    return false;
+
+  unsigned long long int value;
+  if (!ConvertToUnsignedInteger(value_string, &value))
+    return false;
+
+  if (value > std::numeric_limits<size_t>::max())
+    return false;
+
+  *num_handles_ = static_cast<size_t>(value);
+  return true;
+}
+
+bool ValidationTestInputParser::StartsWith(const Range& range,
+                                           const char* prefix,
+                                           size_t prefix_length) {
+  if (static_cast<size_t>(range.second - range.first) < prefix_length)
+    return false;
+
+  return memcmp(range.first, prefix, prefix_length) == 0;
+}
+
+bool ValidationTestInputParser::ConvertToUnsignedInteger(
+    const std::string& value_string,
+    unsigned long long int* value) {
+  const char* format = nullptr;
+  if (value_string.find_first_of("xX") != std::string::npos)
+    format = "%llx";
+  else
+    format = "%llu";
+  return sscanf(value_string.c_str(), format, value) == 1;
+}
+
+}  // namespace
+
+bool ParseValidationTestInput(const std::string& input,
+                              std::vector<uint8_t>* data,
+                              size_t* num_handles,
+                              std::string* error_message) {
+  ValidationTestInputParser parser(input, data, num_handles, error_message);
+  return parser.Run();
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.h b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h
new file mode 100644
index 0000000..c8821cd
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+namespace mojo {
+namespace test {
+
+// Input Format of Mojo Message Validation Tests.
+//
+// Data items are separated by whitespaces:
+//   - ' ' (0x20) space;
+//   - '\t' (0x09) horizontal tab;
+//   - '\n' (0x0a) newline;
+//   - '\r' (0x0d) carriage return.
+// A comment starts with //, extending to the end of the line.
+// Each data item is of the format [<type>]<value>. The types defined and the
+// corresponding value formats are described below.
+//
+// Type: u1 / u2 / u4 / u8
+// Description: Little-endian 1/2/4/8-byte unsigned integer.
+// Value Format:
+//   - Decimal integer: 0|[1-9][0-9]*
+//   - Hexadecimal integer: 0[xX][0-9a-fA-F]+
+//   - The type prefix (including the square brackets) of 1-byte unsigned
+//   integer is optional.
+//
+// Type: s1 / s2 / s4 / s8
+// Description: Little-endian 1/2/4/8-byte signed integer.
+// Value Format:
+//   - Decimal integer: [-+]?(0|[1-9][0-9]*)
+//   - Hexadecimal integer: [-+]?0[xX][0-9a-fA-F]+
+//
+// Type: b
+// Description: Binary sequence of 1 byte.
+// Value Format: [01]{8}
+//
+// Type: f / d
+// Description: Little-endian IEEE-754 format of float (4 bytes) and double (8
+// bytes).
+// Value Format: [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
+//
+// Type: dist4 / dist8
+// Description: Little-endian 4/8-byte unsigned integer. The actual value is set
+// to the byte distance from the location of this integer to the location of the
+// anchr item with the same ID. A dist8 and anchr pair can be used to easily
+// represent an encoded pointer. A dist4 and anchr pair can be used to easily
+// calculate struct/array size.
+// Value Format: The value is an ID: [0-9a-zA-Z_]+
+//
+// Type: anchr
+// Description: Mark an anchor location. It doesn’t translate into any actual
+// data.
+// Value Format: The value is an ID of the same format as that of dist4/8.
+//
+// Type: handles
+// Description: The number of handles that are associated with the message. This
+// special item is not part of the message data. If specified, it should be the
+// first item.
+// Value Format: The same format as u1/2/4/8.
+//
+// EXAMPLE:
+//
+// Suppose you have the following Mojo types defined:
+//   struct Bar {
+//     int32 a;
+//     bool b;
+//     bool c;
+//   };
+//   struct Foo {
+//     Bar x;
+//     uint32 y;
+//   };
+//
+// The following describes a valid message whose payload is a Foo struct:
+//   // message header
+//   [dist4]message_header   // num_bytes
+//   [u4]3                   // num_fields
+//   [u4]0                   // type
+//   [u4]1                   // flags
+//   [u8]1234                // request_id
+//   [anchr]message_header
+//
+//   // payload
+//   [dist4]foo      // num_bytes
+//   [u4]2           // num_fields
+//   [dist8]bar_ptr  // x
+//   [u4]0xABCD      // y
+//   [u4]0           // padding
+//   [anchr]foo
+//
+//   [anchr]bar_ptr
+//   [dist4]bar   // num_bytes
+//   [u4]3        // num_fields
+//   [s4]-1       // a
+//   [b]00000010  // b and c
+//   0 0 0        // padding
+//   [anchr]bar
+
+// Parses validation test input.
+// On success, |data| and |num_handles| store the parsing result,
+// |error_message| is cleared; on failure, |error_message| is set to a message
+// describing the error, |data| is cleared and |num_handles| set to 0.
+// Note: For now, this method only works on little-endian platforms.
+bool ParseValidationTestInput(const std::string& input,
+                              std::vector<uint8_t>* data,
+                              size_t* num_handles,
+                              std::string* error_message);
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
new file mode 100644
index 0000000..bf0894c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -0,0 +1,434 @@
+// Copyright 2014 The Chromium 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 <stdio.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename T>
+void Append(std::vector<uint8_t>* data_vector, T data) {
+  size_t pos = data_vector->size();
+  data_vector->resize(pos + sizeof(T));
+  memcpy(&(*data_vector)[pos], &data, sizeof(T));
+}
+
+bool TestInputParser(const std::string& input,
+                     bool expected_result,
+                     const std::vector<uint8_t>& expected_data,
+                     size_t expected_num_handles) {
+  std::vector<uint8_t> data;
+  size_t num_handles;
+  std::string error_message;
+
+  bool result = ParseValidationTestInput(input, &data, &num_handles,
+                                         &error_message);
+  if (expected_result) {
+    if (result && error_message.empty() &&
+        expected_data == data && expected_num_handles == num_handles) {
+      return true;
+    }
+
+    // Compare with an empty string instead of checking |error_message.empty()|,
+    // so that the message will be printed out if the two are not equal.
+    EXPECT_EQ(std::string(), error_message);
+    EXPECT_EQ(expected_data, data);
+    EXPECT_EQ(expected_num_handles, num_handles);
+    return false;
+  }
+
+  EXPECT_FALSE(error_message.empty());
+  return !result && !error_message.empty();
+}
+
+std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names,
+                                          const std::string& prefix) {
+  const std::string suffix = ".data";
+  std::vector<std::string> tests;
+  for (size_t i = 0; i < names.size(); ++i) {
+    if (names[i].size() >= suffix.size() &&
+        names[i].substr(0, prefix.size()) == prefix &&
+        names[i].substr(names[i].size() - suffix.size()) == suffix)
+      tests.push_back(names[i].substr(0, names[i].size() - suffix.size()));
+  }
+  return tests;
+}
+
+bool ReadFile(const std::string& path, std::string* result) {
+  FILE* fp = OpenSourceRootRelativeFile(path.c_str());
+  if (!fp) {
+    ADD_FAILURE() << "File not found: " << path;
+    return false;
+  }
+  fseek(fp, 0, SEEK_END);
+  size_t size = static_cast<size_t>(ftell(fp));
+  if (size == 0) {
+    result->clear();
+    fclose(fp);
+    return true;
+  }
+  fseek(fp, 0, SEEK_SET);
+  result->resize(size);
+  size_t size_read = fread(&result->at(0), 1, size, fp);
+  fclose(fp);
+  return size == size_read;
+}
+
+bool ReadAndParseDataFile(const std::string& path,
+                          std::vector<uint8_t>* data,
+                          size_t* num_handles) {
+  std::string input;
+  if (!ReadFile(path, &input))
+    return false;
+
+  std::string error_message;
+  if (!ParseValidationTestInput(input, data, num_handles, &error_message)) {
+    ADD_FAILURE() << error_message;
+    return false;
+  }
+
+  return true;
+}
+
+bool ReadResultFile(const std::string& path, std::string* result) {
+  if (!ReadFile(path, result))
+    return false;
+
+  // Result files are new-line delimited text files. Remove any CRs.
+  result->erase(std::remove(result->begin(), result->end(), '\r'),
+                result->end());
+
+  // Remove trailing LFs.
+  size_t pos = result->find_last_not_of('\n');
+  if (pos == std::string::npos)
+    result->clear();
+  else
+    result->resize(pos + 1);
+
+  return true;
+}
+
+std::string GetPath(const std::string& root, const std::string& suffix) {
+  return "mojo/public/interfaces/bindings/tests/data/validation/" +
+      root + suffix;
+}
+
+// |message| should be a newly created object.
+bool ReadTestCase(const std::string& test,
+                  Message* message,
+                  std::string* expected) {
+  std::vector<uint8_t> data;
+  size_t num_handles;
+  if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) ||
+      !ReadResultFile(GetPath(test, ".expected"), expected)) {
+    return false;
+  }
+
+  message->AllocUninitializedData(static_cast<uint32_t>(data.size()));
+  if (!data.empty())
+    memcpy(message->mutable_data(), &data[0], data.size());
+  message->mutable_handles()->resize(num_handles);
+
+  return true;
+}
+
+void RunValidationTests(const std::string& prefix,
+                        MessageReceiver* test_message_receiver) {
+  std::vector<std::string> names =
+      EnumerateSourceRootRelativeDirectory(GetPath("", ""));
+  std::vector<std::string> tests = GetMatchingTests(names, prefix);
+
+  for (size_t i = 0; i < tests.size(); ++i) {
+    Message message;
+    std::string expected;
+    ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected));
+
+    std::string result;
+    mojo::internal::ValidationErrorObserverForTesting observer;
+    bool unused MOJO_ALLOW_UNUSED = test_message_receiver->Accept(&message);
+    if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE)
+      result = "PASS";
+    else
+      result = mojo::internal::ValidationErrorToString(observer.last_error());
+
+    EXPECT_EQ(expected, result) << "failed test: " << tests[i];
+  }
+}
+
+class DummyMessageReceiver : public MessageReceiver {
+ public:
+  virtual bool Accept(Message* message) override {
+    return true;  // Any message is OK.
+  }
+};
+
+class ValidationTest : public testing::Test {
+ public:
+  virtual ~ValidationTest() {
+  }
+
+ private:
+  Environment env_;
+};
+
+class ValidationIntegrationTest : public ValidationTest {
+ public:
+  ValidationIntegrationTest() : test_message_receiver_(nullptr) {
+  }
+
+  virtual ~ValidationIntegrationTest() {
+  }
+
+  virtual void SetUp() override {
+    ScopedMessagePipeHandle tester_endpoint;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_));
+    test_message_receiver_ =
+        new TestMessageReceiver(this, tester_endpoint.Pass());
+  }
+
+  virtual void TearDown() override {
+    delete test_message_receiver_;
+    test_message_receiver_ = nullptr;
+
+    // Make sure that the other end receives the OnConnectionError()
+    // notification.
+    PumpMessages();
+  }
+
+  MessageReceiver* test_message_receiver() {
+    return test_message_receiver_;
+  }
+
+  ScopedMessagePipeHandle testee_endpoint() {
+    return testee_endpoint_.Pass();
+  }
+
+ private:
+  class TestMessageReceiver : public MessageReceiver {
+   public:
+    TestMessageReceiver(ValidationIntegrationTest* owner,
+                        ScopedMessagePipeHandle handle)
+        : owner_(owner),
+          connector_(handle.Pass()) {
+    }
+    virtual ~TestMessageReceiver() {
+    }
+
+    virtual bool Accept(Message* message) override {
+      bool rv = connector_.Accept(message);
+      owner_->PumpMessages();
+      return rv;
+    }
+
+   public:
+    ValidationIntegrationTest* owner_;
+    mojo::internal::Connector connector_;
+  };
+
+  void PumpMessages() {
+    loop_.RunUntilIdle();
+  }
+
+  RunLoop loop_;
+  TestMessageReceiver* test_message_receiver_;
+  ScopedMessagePipeHandle testee_endpoint_;
+};
+
+class IntegrationTestInterface1Client : public IntegrationTestInterface1 {
+ public:
+  virtual ~IntegrationTestInterface1Client() {
+  }
+
+  virtual void Method0(BasicStructPtr param0) override {}
+};
+
+class IntegrationTestInterface1Impl
+    : public InterfaceImpl<IntegrationTestInterface1> {
+ public:
+  virtual ~IntegrationTestInterface1Impl() {
+  }
+
+  virtual void Method0(BasicStructPtr param0) override {}
+};
+
+TEST_F(ValidationTest, InputParser) {
+  {
+    // The parser, as well as Append() defined above, assumes that this code is
+    // running on a little-endian platform. Test whether that is true.
+    uint16_t x = 1;
+    ASSERT_EQ(1, *(reinterpret_cast<char*>(&x)));
+  }
+  {
+    // Test empty input.
+    std::string input;
+    std::vector<uint8_t> expected;
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    // Test input that only consists of comments and whitespaces.
+    std::string input = "    \t  // hello world \n\r \t// the answer is 42   ";
+    std::vector<uint8_t> expected;
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "[u1]0x10// hello world !! \n\r  \t [u2]65535 \n"
+                        "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
+    std::vector<uint8_t> expected;
+    Append(&expected, static_cast<uint8_t>(0x10));
+    Append(&expected, static_cast<uint16_t>(65535));
+    Append(&expected, static_cast<uint32_t>(65536));
+    Append(&expected, static_cast<uint64_t>(0xffffffffffffffff));
+    Append(&expected, static_cast<uint8_t>(0));
+    Append(&expected, static_cast<uint8_t>(0xff));
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
+    std::vector<uint8_t> expected;
+    Append(&expected, -static_cast<int64_t>(0x800));
+    Append(&expected, static_cast<int8_t>(-128));
+    Append(&expected, static_cast<int16_t>(0));
+    Append(&expected, static_cast<int32_t>(-40));
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "[b]00001011 [b]10000000  // hello world\r [b]00000000";
+    std::vector<uint8_t> expected;
+    Append(&expected, static_cast<uint8_t>(11));
+    Append(&expected, static_cast<uint8_t>(128));
+    Append(&expected, static_cast<uint8_t>(0));
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "[f]+.3e9 [d]-10.03";
+    std::vector<uint8_t> expected;
+    Append(&expected, +.3e9f);
+    Append(&expected, -10.03);
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
+    std::vector<uint8_t> expected;
+    Append(&expected, static_cast<uint32_t>(14));
+    Append(&expected, static_cast<uint8_t>(0));
+    Append(&expected, static_cast<uint64_t>(9));
+    Append(&expected, static_cast<uint8_t>(0));
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+  }
+  {
+    std::string input = "// This message has handles! \n[handles]50 [u8]2";
+    std::vector<uint8_t> expected;
+    Append(&expected, static_cast<uint64_t>(2));
+
+    EXPECT_TRUE(TestInputParser(input, true, expected, 50));
+  }
+
+  // Test some failure cases.
+  {
+    const char* error_inputs[] = {
+      "/ hello world",
+      "[u1]x",
+      "[u2]-1000",
+      "[u1]0x100",
+      "[s2]-0x8001",
+      "[b]1",
+      "[b]1111111k",
+      "[dist4]unmatched",
+      "[anchr]hello [dist8]hello",
+      "[dist4]a [dist4]a [anchr]a",
+      "[dist4]a [anchr]a [dist4]a [anchr]a",
+      "0 [handles]50",
+      nullptr
+    };
+
+    for (size_t i = 0; error_inputs[i]; ++i) {
+      std::vector<uint8_t> expected;
+      if (!TestInputParser(error_inputs[i], false, expected, 0))
+        ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i];
+    }
+  }
+}
+
+TEST_F(ValidationTest, Conformance) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::internal::MessageHeaderValidator>();
+  validators.Append<ConformanceTestInterface::RequestValidator_>();
+
+  RunValidationTests("conformance_", validators.GetHead());
+}
+
+TEST_F(ValidationTest, NotImplemented) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::internal::MessageHeaderValidator>();
+  validators.Append<ConformanceTestInterface::RequestValidator_>();
+
+  RunValidationTests("not_implemented_", validators.GetHead());
+}
+
+TEST_F(ValidationIntegrationTest, InterfacePtr) {
+  // Test that InterfacePtr<X> applies the correct validators and they don't
+  // conflict with each other:
+  //   - MessageHeaderValidator
+  //   - X::Client::RequestValidator_
+  //   - X::ResponseValidator_
+
+  IntegrationTestInterface1Client interface1_client;
+  IntegrationTestInterface2Ptr interface2_ptr =
+      MakeProxy<IntegrationTestInterface2>(testee_endpoint().Pass());
+  interface2_ptr.set_client(&interface1_client);
+  interface2_ptr.internal_state()->router_for_testing()->EnableTestingMode();
+
+  RunValidationTests("integration_", test_message_receiver());
+}
+
+TEST_F(ValidationIntegrationTest, InterfaceImpl) {
+  // Test that InterfaceImpl<X> applies the correct validators and they don't
+  // conflict with each other:
+  //   - MessageHeaderValidator
+  //   - X::RequestValidator_
+  //   - X::Client::ResponseValidator_
+
+  // |interface1_impl| will delete itself when the pipe is closed.
+  IntegrationTestInterface1Impl* interface1_impl =
+      BindToPipe(new IntegrationTestInterface1Impl(), testee_endpoint().Pass());
+  interface1_impl->internal_state()->router()->EnableTestingMode();
+
+  RunValidationTests("integration_", test_message_receiver());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h
new file mode 100644
index 0000000..ff94cda
--- /dev/null
+++ b/mojo/public/cpp/bindings/type_converter.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+
+namespace mojo {
+
+// Specialize the following class:
+//   template <typename T, typename U> struct TypeConverter;
+// to perform type conversion for Mojom-defined structs and arrays. Here, T is
+// the target type; U is the input type.
+//
+// Specializations should implement the following interfaces:
+//   namespace mojo {
+//   template <>
+//   struct TypeConverter<X, Y> {
+//     static X Convert(const Y& input);
+//   };
+//   template <>
+//   struct TypeConverter<Y, X> {
+//     static Y Convert(const X& input);
+//   };
+//   }
+//
+// EXAMPLE:
+//
+// Suppose you have the following Mojom-defined struct:
+//
+//   module geometry {
+//   struct Point {
+//     int32 x;
+//     int32 y;
+//   };
+//   }
+//
+// Now, imagine you wanted to write a TypeConverter specialization for
+// gfx::Point. It might look like this:
+//
+//   namespace mojo {
+//   template <>
+//   struct TypeConverter<geometry::PointPtr, gfx::Point> {
+//     static geometry::PointPtr Convert(const gfx::Point& input) {
+//       geometry::PointPtr result;
+//       result->x = input.x();
+//       result->y = input.y();
+//       return result.Pass();
+//     }
+//   };
+//   template <>
+//   struct TypeConverter<gfx::Point, geometry::PointPtr> {
+//     static gfx::Point Convert(const geometry::PointPtr& input) {
+//       return input ? gfx::Point(input->x, input->y) : gfx::Point();
+//     }
+//   };
+//   }
+//
+// With the above TypeConverter defined, it is possible to write code like this:
+//
+//   void AcceptPoint(const geometry::PointPtr& input) {
+//     // With an explicit cast using the .To<> method.
+//     gfx::Point pt = input.To<gfx::Point>();
+//
+//     // With an explicit cast using the static From() method.
+//     geometry::PointPtr output = geometry::Point::From(pt);
+//
+//     // Inferring the input type using the ConvertTo helper function.
+//     gfx::Point pt2 = ConvertTo<gfx::Point>(input);
+//   }
+//
+template <typename T, typename U>
+struct TypeConverter;
+
+// The following specialization is useful when you are converting between
+// Array<POD> and std::vector<POD>.
+template <typename T>
+struct TypeConverter<T, T> {
+  static T Convert(const T& obj) { return obj; }
+};
+
+// The following helper function is useful for shorthand. The compiler can infer
+// the input type, so you can write:
+//   OutputType out = ConvertTo<OutputType>(input);
+template <typename T, typename U>
+inline T ConvertTo(const U& obj) {
+  return TypeConverter<T, U>::Convert(obj);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
diff --git a/mojo/public/cpp/environment/BUILD.gn b/mojo/public/cpp/environment/BUILD.gn
new file mode 100644
index 0000000..a0d6ca7
--- /dev/null
+++ b/mojo/public/cpp/environment/BUILD.gn
@@ -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.
+
+source_set("environment") {
+  sources = [
+    "logging.h",
+    "environment.h",
+  ]
+
+  public_deps = [ "//mojo/public/c/environment" ]
+
+  deps = [ "//mojo/public/cpp/system" ]
+}
+
+source_set("standalone") {
+  sources = [
+    "lib/default_async_waiter.cc",
+    "lib/default_async_waiter.h",
+    "lib/default_logger.cc",
+    "lib/default_logger.h",
+    "lib/environment.cc",
+    "lib/logging.cc",
+  ]
+
+  public_deps = [ ":environment" ]
+
+  deps = [
+    "//mojo/public/c/environment",
+    "//mojo/public/cpp/utility",
+  ]
+}
diff --git a/mojo/public/cpp/environment/environment.h b/mojo/public/cpp/environment/environment.h
new file mode 100644
index 0000000..48f4c26
--- /dev/null
+++ b/mojo/public/cpp/environment/environment.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_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+struct MojoAsyncWaiter;
+struct MojoLogger;
+
+namespace mojo {
+
+// Other parts of the Mojo C++ APIs use the *static* methods of this class.
+//
+// The "standalone" implementation of this class requires that this class (in
+// the lib/ subdirectory) be instantiated (and remain so) while using the Mojo
+// C++ APIs. I.e., the static methods depend on things set up by the constructor
+// and torn down by the destructor.
+//
+// Other implementations may not have this requirement.
+class Environment {
+ public:
+  Environment();
+  // This constructor allows the standard implementations to be overridden (set
+  // a parameter to null to get the standard implementation).
+  Environment(const MojoAsyncWaiter* default_async_waiter,
+              const MojoLogger* default_logger);
+  ~Environment();
+
+  static const MojoAsyncWaiter* GetDefaultAsyncWaiter();
+  static const MojoLogger* GetDefaultLogger();
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Environment);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
diff --git a/mojo/public/cpp/environment/lib/DEPS b/mojo/public/cpp/environment/lib/DEPS
new file mode 100644
index 0000000..1889e1f
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+mojo/public/cpp/environment",
+  "+mojo/public/cpp/utility",
+]
diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.cc b/mojo/public/cpp/environment/lib/default_async_waiter.cc
new file mode 100644
index 0000000..b03623e
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_async_waiter.cc
@@ -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.
+
+#include "mojo/public/cpp/environment/lib/default_async_waiter.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+
+namespace {
+
+// RunLoopHandler implementation used for a request to AsyncWait(). There are
+// two ways RunLoopHandlerImpl is deleted:
+// . when the handle is ready (or errored).
+// . when CancelWait() is invoked.
+class RunLoopHandlerImpl : public RunLoopHandler {
+ public:
+  RunLoopHandlerImpl(const Handle& handle,
+                     MojoAsyncWaitCallback callback,
+                     void* closure)
+      : handle_(handle),
+        callback_(callback),
+        closure_(closure) {
+  }
+
+  virtual ~RunLoopHandlerImpl() {
+    RunLoop::current()->RemoveHandler(handle_);
+  }
+
+  // RunLoopHandler:
+  virtual void OnHandleReady(const Handle& handle) override {
+    NotifyCallback(MOJO_RESULT_OK);
+  }
+
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    NotifyCallback(result);
+  }
+
+ private:
+  void NotifyCallback(MojoResult result) {
+    // Delete this to unregister the handle. That way if the callback
+    // reregisters everything is ok.
+    MojoAsyncWaitCallback callback = callback_;
+    void* closure = closure_;
+    delete this;
+
+    callback(closure, result);
+  }
+
+  const Handle handle_;
+  MojoAsyncWaitCallback callback_;
+  void* closure_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopHandlerImpl);
+};
+
+MojoAsyncWaitID AsyncWait(MojoHandle handle,
+                          MojoHandleSignals signals,
+                          MojoDeadline deadline,
+                          MojoAsyncWaitCallback callback,
+                          void* closure) {
+  RunLoop* run_loop = RunLoop::current();
+  assert(run_loop);
+
+  // |run_loop_handler| is destroyed either when the handle is ready or if
+  // CancelWait is invoked.
+  RunLoopHandlerImpl* run_loop_handler =
+      new RunLoopHandlerImpl(Handle(handle), callback, closure);
+  run_loop->AddHandler(run_loop_handler, Handle(handle), signals, deadline);
+  return reinterpret_cast<MojoAsyncWaitID>(run_loop_handler);
+}
+
+void CancelWait(MojoAsyncWaitID wait_id) {
+  delete reinterpret_cast<RunLoopHandlerImpl*>(wait_id);
+}
+
+}  // namespace
+
+namespace internal {
+
+const MojoAsyncWaiter kDefaultAsyncWaiter = {
+  AsyncWait,
+  CancelWait
+};
+
+}  // namespace internal
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.h b/mojo/public/cpp/environment/lib/default_async_waiter.h
new file mode 100644
index 0000000..49ce233
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_async_waiter.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+
+struct MojoAsyncWaiter;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoAsyncWaiter kDefaultAsyncWaiter;
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
diff --git a/mojo/public/cpp/environment/lib/default_logger.cc b/mojo/public/cpp/environment/lib/default_logger.cc
new file mode 100644
index 0000000..af4a628
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_logger.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium 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/public/cpp/environment/lib/default_logger.h"
+
+#include <stdio.h>
+#include <stdlib.h>  // For |abort()|.
+
+#include <algorithm>
+
+#include "mojo/public/c/environment/logger.h"
+
+namespace mojo {
+
+namespace {
+
+MojoLogLevel g_minimum_log_level = MOJO_LOG_LEVEL_INFO;
+
+const char* GetLogLevelString(MojoLogLevel log_level) {
+  if (log_level <= MOJO_LOG_LEVEL_VERBOSE-3)
+    return "VERBOSE4+";
+  switch (log_level) {
+    case MOJO_LOG_LEVEL_VERBOSE-2:
+      return "VERBOSE3";
+    case MOJO_LOG_LEVEL_VERBOSE-1:
+      return "VERBOSE2";
+    case MOJO_LOG_LEVEL_VERBOSE:
+      return "VERBOSE1";
+    case MOJO_LOG_LEVEL_INFO:
+      return "INFO";
+    case MOJO_LOG_LEVEL_WARNING:
+      return "WARNING";
+    case MOJO_LOG_LEVEL_ERROR:
+      return "ERROR";
+  }
+  // Consider everything higher to be fatal.
+  return "FATAL";
+}
+
+void LogMessage(MojoLogLevel log_level, const char* message) {
+  if (log_level < g_minimum_log_level)
+    return;
+
+  // TODO(vtl): Add timestamp also?
+  fprintf(stderr, "%s: %s\n", GetLogLevelString(log_level), message);
+  if (log_level >= MOJO_LOG_LEVEL_FATAL)
+    abort();
+}
+
+MojoLogLevel GetMinimumLogLevel() {
+  return g_minimum_log_level;
+}
+
+void SetMinimumLogLevel(MojoLogLevel minimum_log_level) {
+  g_minimum_log_level = std::min(minimum_log_level, MOJO_LOG_LEVEL_FATAL);
+}
+
+}  // namespace
+
+namespace internal {
+
+const MojoLogger kDefaultLogger = {
+  LogMessage,
+  GetMinimumLogLevel,
+  SetMinimumLogLevel
+};
+
+}  // namespace internal
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_logger.h b/mojo/public/cpp/environment/lib/default_logger.h
new file mode 100644
index 0000000..4db3233
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_logger.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+
+struct MojoLogger;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoLogger kDefaultLogger;
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
diff --git a/mojo/public/cpp/environment/lib/environment.cc b/mojo/public/cpp/environment/lib/environment.cc
new file mode 100644
index 0000000..5fde8f7
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/environment.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/environment/environment.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/lib/default_async_waiter.h"
+#include "mojo/public/cpp/environment/lib/default_logger.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+namespace {
+
+const MojoAsyncWaiter* g_default_async_waiter = nullptr;
+const MojoLogger* g_default_logger = nullptr;
+
+void Init(const MojoAsyncWaiter* default_async_waiter,
+          const MojoLogger* default_logger) {
+  g_default_async_waiter =
+      default_async_waiter ? default_async_waiter :
+                             &internal::kDefaultAsyncWaiter;
+  g_default_logger = default_logger ? default_logger :
+                                      &internal::kDefaultLogger;
+
+  RunLoop::SetUp();
+}
+
+}  // namespace
+
+Environment::Environment() {
+  Init(nullptr, nullptr);
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+                         const MojoLogger* default_logger) {
+  Init(default_async_waiter, default_logger);
+}
+
+Environment::~Environment() {
+  RunLoop::TearDown();
+
+  // TODO(vtl): Maybe we should allow nesting, and restore previous default
+  // async waiters and loggers?
+  g_default_async_waiter = nullptr;
+  g_default_logger = nullptr;
+}
+
+// static
+const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() {
+  assert(g_default_async_waiter);  // Fails if not "inside" |Environment|.
+  return g_default_async_waiter;
+}
+
+// static
+const MojoLogger* Environment::GetDefaultLogger() {
+  assert(g_default_logger);  // Fails if not "inside" |Environment|.
+  return g_default_logger;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/logging.cc b/mojo/public/cpp/environment/lib/logging.cc
new file mode 100644
index 0000000..990626d
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/logging.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/public/cpp/environment/logging.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+// Gets a pointer to the filename portion of |s|. Assumes that the filename
+// follows the last slash or backslash in |s|, or is |s| if no slash or
+// backslash is present.
+//
+// E.g., a pointer to "foo.cc" is returned for the following inputs: "foo.cc",
+// "./foo.cc", ".\foo.cc", "/absolute/path/to/foo.cc",
+// "relative/path/to/foo.cc", "C:\absolute\path\to\foo.cc", etc.
+const char* GetFilename(const char* s) {
+  const char* rv = s;
+  while (*s) {
+    if (*s == '/' || *s == '\\')
+      rv = s + 1;
+    s++;
+  }
+  return rv;
+}
+
+}  // namespace
+
+LogMessage::LogMessage(const char* file, int line, MojoLogLevel log_level)
+    : log_level_(log_level) {
+  // Note: Don't include the log level in the message, since that's passed on.
+  stream_ << GetFilename(file) << '(' << line << "): ";
+}
+
+LogMessage::~LogMessage() {
+  Environment::GetDefaultLogger()->LogMessage(log_level_,
+                                              stream_.str().c_str());
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/logging.h b/mojo/public/cpp/environment/logging.h
new file mode 100644
index 0000000..a3e2cef
--- /dev/null
+++ b/mojo/public/cpp/environment/logging.h
@@ -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.
+
+// Logging macros, similar to Chromium's base/logging.h, except with |MOJO_|
+// prefixes and missing some features (notably |CHECK_EQ()|, etc.).
+
+// TODO(vtl): It's weird that this is in the environment directory, since its
+// implementation (in environment/lib) is meant to be used by any implementation
+// of the environment.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+
+#include <sstream>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+#define MOJO_LOG_STREAM(level) \
+    ::mojo::internal::LogMessage(__FILE__, __LINE__, \
+                                 MOJO_LOG_LEVEL_ ## level).stream()
+
+#define MOJO_LAZY_LOG_STREAM(level, condition) \
+    !(condition) ? \
+        (void) 0 : \
+        ::mojo::internal::VoidifyOstream() & MOJO_LOG_STREAM(level)
+
+#define MOJO_SHOULD_LOG(level) \
+    (MOJO_LOG_LEVEL_ ## level >= \
+     ::mojo::Environment::GetDefaultLogger()->GetMinimumLogLevel())
+
+#define MOJO_LOG(level) \
+    MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level))
+
+#define MOJO_LOG_IF(level, condition) \
+    MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level) && (condition))
+
+#define MOJO_CHECK(condition) \
+    MOJO_LAZY_LOG_STREAM(FATAL, !(condition)) \
+        << "Check failed: " #condition ". "
+
+// Note: For non-debug builds, |MOJO_DLOG_IF()| *eliminates* (i.e., doesn't
+// compile) the condition, whereas |MOJO_DCHECK()| "neuters" the condition
+// (i.e., compiles, but doesn't evaluate).
+#ifdef NDEBUG
+
+#define MOJO_DLOG(level) MOJO_LAZY_LOG_STREAM(level, false)
+#define MOJO_DLOG_IF(level, condition) MOJO_LAZY_LOG_STREAM(level, false)
+#define MOJO_DCHECK(condition) MOJO_LAZY_LOG_STREAM(FATAL, false && (condition))
+
+#else
+
+#define MOJO_DLOG(level) MOJO_LOG(level)
+#define MOJO_DLOG_IF(level, condition) MOJO_LOG_IF(level, condition)
+#define MOJO_DCHECK(condition) MOJO_CHECK(condition)
+
+#endif  // NDEBUG
+
+namespace mojo {
+namespace internal {
+
+class LogMessage {
+ public:
+  LogMessage(const char* file, int line, MojoLogLevel log_level);
+  ~LogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  const MojoLogLevel log_level_;
+  std::ostringstream stream_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Used to ignore a stream.
+struct VoidifyOstream {
+  // Use & since it has precedence lower than << but higher than ?:.
+  void operator&(std::ostream&) {}
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
diff --git a/mojo/public/cpp/environment/tests/BUILD.gn b/mojo/public/cpp/environment/tests/BUILD.gn
new file mode 100644
index 0000000..55ded10
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_environment_unittests
+test("mojo_public_environment_unittests") {
+  sources = [
+    "async_waiter_unittest.cc",
+    "logger_unittest.cc",
+    "logging_unittest.cc",
+  ]
+
+  deps = [
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/public/c/environment",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/cpp/utility",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/public/cpp/environment/tests/async_waiter_unittest.cc b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
new file mode 100644
index 0000000..0992f89
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestAsyncWaitCallback {
+ public:
+  TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {
+  }
+  virtual ~TestAsyncWaitCallback() {}
+
+  int result_count() const { return result_count_; }
+
+  MojoResult last_result() const { return last_result_; }
+
+  // MojoAsyncWaitCallback:
+  static void OnHandleReady(void* closure, MojoResult result) {
+    TestAsyncWaitCallback* self = static_cast<TestAsyncWaitCallback*>(closure);
+    self->result_count_++;
+    self->last_result_ = result;
+  }
+
+ private:
+  int result_count_;
+  MojoResult last_result_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback);
+};
+
+MojoAsyncWaitID CallAsyncWait(const Handle& handle,
+                              MojoHandleSignals signals,
+                              TestAsyncWaitCallback* callback) {
+  return Environment::GetDefaultAsyncWaiter()->AsyncWait(
+      handle.value(),
+      signals,
+      MOJO_DEADLINE_INDEFINITE,
+      &TestAsyncWaitCallback::OnHandleReady,
+      callback);
+}
+
+void CallCancelWait(MojoAsyncWaitID wait_id) {
+  Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id);
+}
+
+class AsyncWaiterTest : public testing::Test {
+ public:
+  AsyncWaiterTest() {}
+
+ private:
+  Environment environment_;
+  RunLoop run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterTest);
+};
+
+// Verifies AsyncWaitCallback is notified when pipe is ready.
+TEST_F(AsyncWaiterTest, CallbackNotified) {
+  TestAsyncWaitCallback callback;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  CallAsyncWait(test_pipe.handle0.get(),
+                MOJO_HANDLE_SIGNAL_READABLE,
+                &callback);
+  RunLoop::current()->Run();
+  EXPECT_EQ(1, callback.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback.last_result());
+}
+
+// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready.
+TEST_F(AsyncWaiterTest, TwoCallbacksNotified) {
+  TestAsyncWaitCallback callback1;
+  TestAsyncWaitCallback callback2;
+  MessagePipe test_pipe1;
+  MessagePipe test_pipe2;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string()));
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string()));
+
+  CallAsyncWait(test_pipe1.handle0.get(),
+                MOJO_HANDLE_SIGNAL_READABLE,
+                &callback1);
+  CallAsyncWait(test_pipe2.handle0.get(),
+                MOJO_HANDLE_SIGNAL_READABLE,
+                &callback2);
+
+  RunLoop::current()->Run();
+  EXPECT_EQ(1, callback1.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result());
+  EXPECT_EQ(1, callback2.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result());
+}
+
+// Verifies cancel works.
+TEST_F(AsyncWaiterTest, CancelCallback) {
+  TestAsyncWaitCallback callback;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  CallCancelWait(
+      CallAsyncWait(test_pipe.handle0.get(),
+                    MOJO_HANDLE_SIGNAL_READABLE,
+                    &callback));
+  RunLoop::current()->Run();
+  EXPECT_EQ(0, callback.result_count());
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/logger_unittest.cc b/mojo/public/cpp/environment/tests/logger_unittest.cc
new file mode 100644
index 0000000..e30a84b
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logger_unittest.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 "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(LoggerTest, Basic) {
+  Environment environment;
+  const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+  logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE-1, "Logged at VERBOSE-1 level");
+  logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, "Logged at VERBOSE level");
+  logger->LogMessage(MOJO_LOG_LEVEL_INFO, "Logged at INFO level");
+  logger->LogMessage(MOJO_LOG_LEVEL_WARNING, "Logged at WARNING level");
+  logger->LogMessage(MOJO_LOG_LEVEL_ERROR, "Logged at ERROR level");
+
+  // This should kill us:
+  EXPECT_DEATH_IF_SUPPORTED({
+    logger->LogMessage(MOJO_LOG_LEVEL_FATAL, "Logged at FATAL level");
+  }, "");
+}
+
+TEST(LoggerTest, LogLevels) {
+  Environment environment;
+  const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+  for (MojoLogLevel log_level = MOJO_LOG_LEVEL_VERBOSE-1;
+       log_level <= MOJO_LOG_LEVEL_FATAL+1;
+       log_level++) {
+    logger->SetMinimumLogLevel(log_level);
+
+    if (log_level <= MOJO_LOG_LEVEL_FATAL)
+      EXPECT_EQ(log_level, logger->GetMinimumLogLevel());
+    else
+      EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, logger->GetMinimumLogLevel());
+
+    logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE-1, "Logged at VERBOSE-1 level");
+    logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, "Logged at VERBOSE level");
+    logger->LogMessage(MOJO_LOG_LEVEL_INFO, "Logged at INFO level");
+    logger->LogMessage(MOJO_LOG_LEVEL_WARNING, "Logged at WARNING level");
+    logger->LogMessage(MOJO_LOG_LEVEL_ERROR, "Logged at ERROR level");
+
+    // This should kill us:
+    EXPECT_DEATH_IF_SUPPORTED({
+      logger->LogMessage(MOJO_LOG_LEVEL_FATAL, "Logged at FATAL level");
+    }, "");
+  }
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/logging_unittest.cc b/mojo/public/cpp/environment/tests/logging_unittest.cc
new file mode 100644
index 0000000..45f527e
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logging_unittest.cc
@@ -0,0 +1,474 @@
+// Copyright 2014 The Chromium 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 <stdlib.h>
+
+#include <sstream>
+#include <string>
+
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A macro, so it can be automatically joined with other string literals. (Not
+// simply __FILE__, since that may contain a path.)
+#define OUR_FILENAME "logging_unittest.cc"
+
+namespace mojo {
+namespace {
+
+class PtrToMemberHelper {
+ public:
+  int member;
+};
+
+bool DcheckTestHelper(bool* was_called) {
+  *was_called = true;
+  return false;
+}
+
+class LoggingTest : public testing::Test {
+ public:
+  LoggingTest() : environment_(nullptr, &kMockLogger) {
+    minimum_log_level_ = MOJO_LOG_LEVEL_INFO;
+    ResetMockLogger();
+  }
+  virtual ~LoggingTest() {}
+
+ protected:
+  // Note: Does not reset |minimum_log_level_|.
+  static void ResetMockLogger() {
+    log_message_was_called_ = false;
+    last_log_level_ = MOJO_LOG_LEVEL_INFO;
+    last_message_.clear();
+  }
+
+  // A function returning |bool| that shouldn't be called.
+  static bool NotCalledCondition() {
+    not_called_condition_was_called_ = true;
+    return false;
+  }
+
+  static bool log_message_was_called() { return log_message_was_called_; }
+  static MojoLogLevel last_log_level() { return last_log_level_; }
+  static const std::string& last_message() { return last_message_; }
+  static bool not_called_condition_was_called() {
+    return not_called_condition_was_called_;
+  }
+
+ private:
+  // Note: We record calls even if |log_level| is below |minimum_log_level_|
+  // (since the macros should mostly avoid this, and we want to be able to check
+  // that they do).
+  static void MockLogMessage(MojoLogLevel log_level, const char* message) {
+    log_message_was_called_ = true;
+    last_log_level_ = log_level;
+    last_message_ = message;
+  }
+
+  static MojoLogLevel MockGetMinimumLogLevel() {
+    return minimum_log_level_;
+  }
+
+  static void MockSetMinimumLogLevel(MojoLogLevel minimum_log_level) {
+    minimum_log_level_ = minimum_log_level;
+  }
+
+  Environment environment_;
+
+  static const MojoLogger kMockLogger;
+  static MojoLogLevel minimum_log_level_;
+  static bool log_message_was_called_;
+  static MojoLogLevel last_log_level_;
+  static std::string last_message_;
+  static bool not_called_condition_was_called_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(LoggingTest);
+};
+
+// static
+const MojoLogger LoggingTest::kMockLogger = {
+  &LoggingTest::MockLogMessage,
+  &LoggingTest::MockGetMinimumLogLevel,
+  &LoggingTest::MockSetMinimumLogLevel
+};
+
+// static
+MojoLogLevel LoggingTest::minimum_log_level_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+bool LoggingTest::log_message_was_called_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+MojoLogLevel LoggingTest::last_log_level_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+std::string LoggingTest::last_message_;
+
+// static
+bool LoggingTest::not_called_condition_was_called_ = false;
+
+std::string ExpectedLogMessage(int line, const char* message) {
+  std::ostringstream s;
+  s << OUR_FILENAME "(" << line << "): " << message;
+  return s.str();
+}
+
+TEST_F(LoggingTest, InternalLogMessage) {
+  internal::LogMessage("foo.cc", 123, MOJO_LOG_LEVEL_INFO).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage("./path/to/foo.cc", 123, MOJO_LOG_LEVEL_WARNING).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage("/path/to/foo.cc", 123, MOJO_LOG_LEVEL_ERROR).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage("path/to/foo.cc", 123, MOJO_LOG_LEVEL_FATAL).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage(".\\xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage("xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE-1).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE-1, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage("C:\\xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE-9).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE-9, last_log_level());
+  EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+  ResetMockLogger();
+
+  internal::LogMessage(__FILE__, 123, MOJO_LOG_LEVEL_INFO).stream()
+      << "hello " << "world";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(OUR_FILENAME "(123): hello world", last_message());
+}
+
+TEST_F(LoggingTest, LogStream) {
+  MOJO_LOG_STREAM(INFO) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+
+  ResetMockLogger();
+
+  MOJO_LOG_STREAM(ERROR) << "hi " << 123;
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hi 123"), last_message());
+}
+
+TEST_F(LoggingTest, LazyLogStream) {
+  MOJO_LAZY_LOG_STREAM(INFO, true) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+
+  ResetMockLogger();
+
+  MOJO_LAZY_LOG_STREAM(ERROR, true) << "hi " << 123;
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hi 123"), last_message());
+
+  ResetMockLogger();
+
+  MOJO_LAZY_LOG_STREAM(INFO, false) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LAZY_LOG_STREAM(FATAL, false) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  PtrToMemberHelper helper;
+  helper.member = 1;
+  int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member;
+
+  // This probably fails to compile if we forget to parenthesize the condition
+  // in the macro (.* has lower precedence than !, which can't apply to
+  // |helper|).
+  MOJO_LAZY_LOG_STREAM(ERROR, helper.*member_ptr == 1) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LAZY_LOG_STREAM(WARNING, helper.*member_ptr == 0) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, ShouldLog) {
+  // We start at |MOJO_LOG_LEVEL_INFO|.
+  EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(INFO));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+
+  Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+  EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE));
+  EXPECT_FALSE(MOJO_SHOULD_LOG(INFO));
+  EXPECT_FALSE(MOJO_SHOULD_LOG(WARNING));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+
+  Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_VERBOSE-1);
+  EXPECT_TRUE(MOJO_SHOULD_LOG(VERBOSE));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(INFO));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+  EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+}
+
+TEST_F(LoggingTest, Log) {
+  // We start at |MOJO_LOG_LEVEL_INFO|.
+  MOJO_LOG(VERBOSE) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG(INFO) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+
+  ResetMockLogger();
+
+  MOJO_LOG(ERROR) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+
+  ResetMockLogger();
+
+  Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+
+  MOJO_LOG(VERBOSE) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG(INFO) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG(ERROR) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+}
+
+TEST_F(LoggingTest, LogIf) {
+  // We start at |MOJO_LOG_LEVEL_INFO|.
+  MOJO_LOG_IF(VERBOSE, true) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG_IF(VERBOSE, false) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+  Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+
+  bool x = true;
+  // Also try to make sure that we parenthesize the condition properly.
+  MOJO_LOG_IF(INFO, false || x) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG_IF(INFO, 0 != 1) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG_IF(WARNING, 1 + 1 == 2) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_LOG_IF(ERROR, 1 * 2 == 2) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 3, "hello"), last_message());
+
+  ResetMockLogger();
+
+  MOJO_LOG_IF(FATAL, 1 * 2 == 3) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  // |MOJO_LOG_IF()| shouldn't evaluate its condition if the level is below the
+  // minimum.
+  MOJO_LOG_IF(INFO, NotCalledCondition()) << "hello";
+  EXPECT_FALSE(not_called_condition_was_called());
+  EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, Check) {
+  MOJO_CHECK(true) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  PtrToMemberHelper helper;
+  helper.member = 0;
+  int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member;
+
+  // Also try to make sure that we parenthesize the condition properly.
+  MOJO_CHECK(helper.*member_ptr == 1) << "hello";
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+  // Different compilers have different ideas about the line number of a split
+  // line.
+  int line = __LINE__;
+  EXPECT_EQ(ExpectedLogMessage(line - 5,
+                               "Check failed: helper.*member_ptr == 1. hello"),
+            last_message());
+
+  ResetMockLogger();
+
+  // Also test a "naked" |MOJO_CHECK()|s.
+  MOJO_CHECK(1 + 2 == 3);
+  EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, Dlog) {
+  // We start at |MOJO_LOG_LEVEL_INFO|.
+  MOJO_DLOG(VERBOSE) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_DLOG(INFO) << "hello";
+#ifdef NDEBUG
+  EXPECT_FALSE(log_message_was_called());
+#else
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 6, "hello"), last_message());
+#endif
+}
+
+TEST_F(LoggingTest, DlogIf) {
+  // We start at |MOJO_LOG_LEVEL_INFO|. It shouldn't evaluate the condition in
+  // this case.
+  MOJO_DLOG_IF(VERBOSE, NotCalledCondition()) << "hello";
+  EXPECT_FALSE(not_called_condition_was_called());
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_DLOG_IF(INFO, 1 == 0) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_DLOG_IF(INFO, 1 == 1) << "hello";
+#ifdef NDEBUG
+  EXPECT_FALSE(log_message_was_called());
+#else
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 6, "hello"), last_message());
+#endif
+
+  ResetMockLogger();
+
+  // |MOJO_DLOG_IF()| shouldn't compile its condition for non-debug builds.
+#ifndef NDEBUG
+  bool debug_only = true;
+#endif
+  MOJO_DLOG_IF(WARNING, debug_only) << "hello";
+#ifdef NDEBUG
+  EXPECT_FALSE(log_message_was_called());
+#else
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level());
+  EXPECT_EQ(ExpectedLogMessage(__LINE__ - 6, "hello"), last_message());
+#endif
+}
+
+TEST_F(LoggingTest, Dcheck) {
+  MOJO_DCHECK(true);
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  MOJO_DCHECK(true) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+
+  ResetMockLogger();
+
+  // |MOJO_DCHECK()| should compile (but not evaluate) its condition even for
+  // non-debug builds. (Hopefully, we'll get an unused variable error if it
+  // fails to compile the condition.)
+  bool was_called = false;
+  MOJO_DCHECK(DcheckTestHelper(&was_called)) << "hello";
+#ifdef NDEBUG
+  EXPECT_FALSE(was_called);
+  EXPECT_FALSE(log_message_was_called());
+#else
+  EXPECT_TRUE(was_called);
+  EXPECT_TRUE(log_message_was_called());
+  EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+  // Different compilers have different ideas about the line number of a split
+  // line.
+  int line = __LINE__;
+  EXPECT_EQ(
+      ExpectedLogMessage(line - 10,
+                         "Check failed: DcheckTestHelper(&was_called). hello"),
+      last_message());
+#endif
+
+  ResetMockLogger();
+
+  // Also try to make sure that we parenthesize the condition properly.
+  bool x = true;
+  MOJO_DCHECK(false || x) << "hello";
+  EXPECT_FALSE(log_message_was_called());
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
new file mode 100644
index 0000000..046ede0
--- /dev/null
+++ b/mojo/public/cpp/system/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.
+
+source_set("system") {
+  sources = [
+    "buffer.h",
+    "core.h",
+    "data_pipe.h",
+    "functions.h",
+    "handle.h",
+    "macros.h",
+    "message_pipe.h"
+  ]
+
+  public_deps = [ "//mojo/public/c/system" ]
+}
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
new file mode 100644
index 0000000..ad9966a
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.h
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// SharedBufferHandle ----------------------------------------------------------
+
+class SharedBufferHandle : public Handle {
+ public:
+  SharedBufferHandle() {}
+  explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(SharedBufferHandle) == sizeof(Handle),
+                    bad_size_for_cpp_SharedBufferHandle);
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedSharedBufferHandle) ==
+                        sizeof(SharedBufferHandle),
+                    bad_size_for_cpp_ScopedSharedBufferHandle);
+
+inline MojoResult CreateSharedBuffer(
+    const MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    ScopedSharedBufferHandle* shared_buffer) {
+  assert(shared_buffer);
+  SharedBufferHandle handle;
+  MojoResult rv = MojoCreateSharedBuffer(options, num_bytes,
+                                         handle.mutable_value());
+  // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+  // will be used).
+  shared_buffer->reset(handle);
+  return rv;
+}
+
+// TODO(vtl): This (and also the functions below) are templatized to allow for
+// future/other buffer types. A bit "safer" would be to overload this function
+// manually. (The template enforces that the in and out handles to be of the
+// same type.)
+template <class BufferHandleType>
+inline MojoResult DuplicateBuffer(
+    BufferHandleType buffer,
+    const MojoDuplicateBufferHandleOptions* options,
+    ScopedHandleBase<BufferHandleType>* new_buffer) {
+  assert(new_buffer);
+  BufferHandleType handle;
+  MojoResult rv = MojoDuplicateBufferHandle(
+      buffer.value(), options, handle.mutable_value());
+  // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+  // will be used).
+  new_buffer->reset(handle);
+  return rv;
+}
+
+template <class BufferHandleType>
+inline MojoResult MapBuffer(BufferHandleType buffer,
+                            uint64_t offset,
+                            uint64_t num_bytes,
+                            void** pointer,
+                            MojoMapBufferFlags flags) {
+  assert(buffer.is_valid());
+  return MojoMapBuffer(buffer.value(), offset, num_bytes, pointer, flags);
+}
+
+inline MojoResult UnmapBuffer(void* pointer) {
+  assert(pointer);
+  return MojoUnmapBuffer(pointer);
+}
+
+// A wrapper class that automatically creates a shared buffer and owns the
+// handle.
+class SharedBuffer {
+ public:
+  explicit SharedBuffer(uint64_t num_bytes);
+  SharedBuffer(uint64_t num_bytes,
+               const MojoCreateSharedBufferOptions& options);
+  ~SharedBuffer();
+
+  ScopedSharedBufferHandle handle;
+};
+
+inline SharedBuffer::SharedBuffer(uint64_t num_bytes) {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateSharedBuffer(nullptr, num_bytes, &handle);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::SharedBuffer(
+    uint64_t num_bytes,
+    const MojoCreateSharedBufferOptions& options) {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateSharedBuffer(&options, num_bytes, &handle);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::~SharedBuffer() {
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
diff --git a/mojo/public/cpp/system/core.h b/mojo/public/cpp/system/core.h
new file mode 100644
index 0000000..b08a5a6
--- /dev/null
+++ b/mojo/public/cpp/system/core.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/functions.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
diff --git a/mojo/public/cpp/system/data_pipe.h b/mojo/public/cpp/system/data_pipe.h
new file mode 100644
index 0000000..b2f6e68
--- /dev/null
+++ b/mojo/public/cpp/system/data_pipe.h
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// DataPipeProducerHandle and DataPipeConsumerHandle ---------------------------
+
+class DataPipeProducerHandle : public Handle {
+ public:
+  DataPipeProducerHandle() {}
+  explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+                    bad_size_for_cpp_DataPipeProducerHandle);
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeProducerHandle) ==
+                        sizeof(DataPipeProducerHandle),
+                    bad_size_for_cpp_ScopedDataPipeProducerHandle);
+
+class DataPipeConsumerHandle : public Handle {
+ public:
+  DataPipeConsumerHandle() {}
+  explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+                    bad_size_for_cpp_DataPipeConsumerHandle);
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeConsumerHandle) ==
+                        sizeof(DataPipeConsumerHandle),
+                    bad_size_for_cpp_ScopedDataPipeConsumerHandle);
+
+inline MojoResult CreateDataPipe(
+    const MojoCreateDataPipeOptions* options,
+    ScopedDataPipeProducerHandle* data_pipe_producer,
+    ScopedDataPipeConsumerHandle* data_pipe_consumer) {
+  assert(data_pipe_producer);
+  assert(data_pipe_consumer);
+  DataPipeProducerHandle producer_handle;
+  DataPipeConsumerHandle consumer_handle;
+  MojoResult rv = MojoCreateDataPipe(options, producer_handle.mutable_value(),
+                                     consumer_handle.mutable_value());
+  // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+  // will be used).
+  data_pipe_producer->reset(producer_handle);
+  data_pipe_consumer->reset(consumer_handle);
+  return rv;
+}
+
+inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+                               const void* elements,
+                               uint32_t* num_bytes,
+                               MojoWriteDataFlags flags) {
+  return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags);
+}
+
+inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+                                    void** buffer,
+                                    uint32_t* buffer_num_bytes,
+                                    MojoWriteDataFlags flags) {
+  return MojoBeginWriteData(data_pipe_producer.value(), buffer,
+                            buffer_num_bytes, flags);
+}
+
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+                                  uint32_t num_bytes_written) {
+  return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+                              void* elements,
+                              uint32_t* num_bytes,
+                              MojoReadDataFlags flags) {
+  return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags);
+}
+
+inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+                                   const void** buffer,
+                                   uint32_t* buffer_num_bytes,
+                                   MojoReadDataFlags flags) {
+  return MojoBeginReadData(data_pipe_consumer.value(), buffer, buffer_num_bytes,
+                           flags);
+}
+
+inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+                                 uint32_t num_bytes_read) {
+  return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read);
+}
+
+// A wrapper class that automatically creates a data pipe and owns both handles.
+// TODO(vtl): Make an even more friendly version? (Maybe templatized for a
+// particular type instead of some "element"? Maybe functions that take
+// vectors?)
+class DataPipe {
+ public:
+  DataPipe();
+  explicit DataPipe(const MojoCreateDataPipeOptions& options);
+  ~DataPipe();
+
+  ScopedDataPipeProducerHandle producer_handle;
+  ScopedDataPipeConsumerHandle consumer_handle;
+};
+
+inline DataPipe::DataPipe() {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateDataPipe(&options, &producer_handle, &consumer_handle);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::~DataPipe() {
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/public/cpp/system/functions.h b/mojo/public/cpp/system/functions.h
new file mode 100644
index 0000000..d73d27a
--- /dev/null
+++ b/mojo/public/cpp/system/functions.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+// Standalone functions --------------------------------------------------------
+
+inline MojoTimeTicks GetTimeTicksNow() {
+  return MojoGetTimeTicksNow();
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
new file mode 100644
index 0000000..72b14c8
--- /dev/null
+++ b/mojo/public/cpp/system/handle.h
@@ -0,0 +1,246 @@
+// Copyright 2014 The Chromium 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_PUBLIC_CPP_SYSTEM_HANDLE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
+
+#include <assert.h>
+#include <limits>
+
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// OVERVIEW
+//
+// |Handle| and |...Handle|:
+//
+// |Handle| is a simple, copyable wrapper for the C type |MojoHandle| (which is
+// just an integer). Its purpose is to increase type-safety, not provide
+// lifetime management. For the same purpose, we have trivial *subclasses* of
+// |Handle|, e.g., |MessagePipeHandle| and |DataPipeProducerHandle|. |Handle|
+// and its subclasses impose *no* extra overhead over using |MojoHandle|s
+// directly.
+//
+// Note that though we provide constructors for |Handle|/|...Handle| from a
+// |MojoHandle|, we do not provide, e.g., a constructor for |MessagePipeHandle|
+// from a |Handle|. This is for type safety: If we did, you'd then be able to
+// construct a |MessagePipeHandle| from, e.g., a |DataPipeProducerHandle| (since
+// it's a |Handle|).
+//
+// |ScopedHandleBase| and |Scoped...Handle|:
+//
+// |ScopedHandleBase<HandleType>| is a templated scoped wrapper, for the handle
+// types above (in the same sense that a C++11 |unique_ptr<T>| is a scoped
+// wrapper for a |T*|). It provides lifetime management, closing its owned
+// handle on destruction. It also provides (emulated) move semantics, again
+// along the lines of C++11's |unique_ptr| (and exactly like Chromium's
+// |scoped_ptr|).
+//
+// |ScopedHandle| is just (a typedef of) a |ScopedHandleBase<Handle>|.
+// Similarly, |ScopedMessagePipeHandle| is just a
+// |ScopedHandleBase<MessagePipeHandle>|. Etc. Note that a
+// |ScopedMessagePipeHandle| is *not* a (subclass of) |ScopedHandle|.
+//
+// Wrapper functions:
+//
+// We provide simple wrappers for the |Mojo...()| functions (in
+// mojo/public/c/system/core.h -- see that file for details on individual
+// functions).
+//
+// The general guideline is functions that imply ownership transfer of a handle
+// should take (or produce) an appropriate |Scoped...Handle|, while those that
+// don't take a |...Handle|. For example, |CreateMessagePipe()| has two
+// |ScopedMessagePipe| "out" parameters, whereas |Wait()| and |WaitMany()| take
+// |Handle| parameters. Some, have both: e.g., |DuplicatedBuffer()| takes a
+// suitable (unscoped) handle (e.g., |SharedBufferHandle|) "in" parameter and
+// produces a suitable scoped handle (e.g., |ScopedSharedBufferHandle| a.k.a.
+// |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter.
+//
+// An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a
+// |Handle|, leaving the user to discard the handle.
+//
+// More significantly, |WriteMessageRaw()| exposes the full API complexity of
+// |MojoWriteMessage()| (but doesn't require any extra overhead). It takes a raw
+// array of |Handle|s as input, and takes ownership of them (i.e., invalidates
+// them) on *success* (but not on failure). There are a number of reasons for
+// this. First, C++03 |std::vector|s cannot contain the move-only
+// |Scoped...Handle|s. Second, |std::vector|s impose extra overhead
+// (necessitating heap-allocation of the buffer). Third, |std::vector|s wouldn't
+// provide the desired level of flexibility/safety: a vector of handles would
+// have to be all of the same type (probably |Handle|/|ScopedHandle|). Fourth,
+// it's expected to not be used directly, but instead be used by generated
+// bindings.
+//
+// Other |...Raw()| functions expose similar rough edges, e.g., dealing with raw
+// pointers (and lengths) instead of taking |std::vector|s or similar.
+
+// ScopedHandleBase ------------------------------------------------------------
+
+// Scoper for the actual handle types defined further below. It's move-only,
+// like the C++11 |unique_ptr|.
+template <class HandleType>
+class ScopedHandleBase {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(ScopedHandleBase, RValue)
+
+ public:
+  ScopedHandleBase() {}
+  explicit ScopedHandleBase(HandleType handle) : handle_(handle) {}
+  ~ScopedHandleBase() { CloseIfNecessary(); }
+
+  template <class CompatibleHandleType>
+  explicit ScopedHandleBase(ScopedHandleBase<CompatibleHandleType> other)
+      : handle_(other.release()) {
+  }
+
+  // Move-only constructor and operator=.
+  ScopedHandleBase(RValue other) : handle_(other.object->release()) {}
+  ScopedHandleBase& operator=(RValue other) {
+    if (other.object != this) {
+      CloseIfNecessary();
+      handle_ = other.object->release();
+    }
+    return *this;
+  }
+
+  const HandleType& get() const { return handle_; }
+
+  template <typename PassedHandleType>
+  static ScopedHandleBase<HandleType> From(
+      ScopedHandleBase<PassedHandleType> other) {
+    MOJO_COMPILE_ASSERT(
+        sizeof(static_cast<PassedHandleType*>(static_cast<HandleType*>(0))),
+        HandleType_is_not_a_subtype_of_PassedHandleType);
+    return ScopedHandleBase<HandleType>(
+        static_cast<HandleType>(other.release().value()));
+  }
+
+  void swap(ScopedHandleBase& other) {
+    handle_.swap(other.handle_);
+  }
+
+  HandleType release() MOJO_WARN_UNUSED_RESULT {
+    HandleType rv;
+    rv.swap(handle_);
+    return rv;
+  }
+
+  void reset(HandleType handle = HandleType()) {
+    CloseIfNecessary();
+    handle_ = handle;
+  }
+
+  bool is_valid() const {
+    return handle_.is_valid();
+  }
+
+ private:
+  void CloseIfNecessary() {
+    if (!handle_.is_valid())
+      return;
+    MojoResult result MOJO_ALLOW_UNUSED = MojoClose(handle_.value());
+    assert(result == MOJO_RESULT_OK);
+  }
+
+  HandleType handle_;
+};
+
+template <typename HandleType>
+inline ScopedHandleBase<HandleType> MakeScopedHandle(HandleType handle) {
+  return ScopedHandleBase<HandleType>(handle);
+}
+
+// Handle ----------------------------------------------------------------------
+
+const MojoHandle kInvalidHandleValue = MOJO_HANDLE_INVALID;
+
+// Wrapper base class for |MojoHandle|.
+class Handle {
+ public:
+  Handle() : value_(kInvalidHandleValue) {}
+  explicit Handle(MojoHandle value) : value_(value) {}
+  ~Handle() {}
+
+  void swap(Handle& other) {
+    MojoHandle temp = value_;
+    value_ = other.value_;
+    other.value_ = temp;
+  }
+
+  bool is_valid() const {
+    return value_ != kInvalidHandleValue;
+  }
+
+  const MojoHandle& value() const { return value_; }
+  MojoHandle* mutable_value() { return &value_; }
+  void set_value(MojoHandle value) { value_ = value; }
+
+ private:
+  MojoHandle value_;
+
+  // Copying and assignment allowed.
+};
+
+// Should have zero overhead.
+MOJO_COMPILE_ASSERT(sizeof(Handle) == sizeof(MojoHandle),
+                    bad_size_for_cpp_Handle);
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedHandle) == sizeof(Handle),
+                    bad_size_for_cpp_ScopedHandle);
+
+inline MojoResult Wait(Handle handle,
+                       MojoHandleSignals signals,
+                       MojoDeadline deadline) {
+  return MojoWait(handle.value(), signals, deadline);
+}
+
+// |HandleVectorType| and |FlagsVectorType| should be similar enough to
+// |std::vector<Handle>| and |std::vector<MojoHandleSignals>|, respectively:
+//  - They should have a (const) |size()| method that returns an unsigned type.
+//  - They must provide contiguous storage, with access via (const) reference to
+//    that storage provided by a (const) |operator[]()| (by reference).
+template <class HandleVectorType, class FlagsVectorType>
+inline MojoResult WaitMany(const HandleVectorType& handles,
+                           const FlagsVectorType& signals,
+                           MojoDeadline deadline) {
+  if (signals.size() != handles.size())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (handles.size() > std::numeric_limits<uint32_t>::max())
+    return MOJO_RESULT_OUT_OF_RANGE;
+
+  if (handles.size() == 0)
+    return MojoWaitMany(nullptr, nullptr, 0, deadline);
+
+  const Handle& first_handle = handles[0];
+  const MojoHandleSignals& first_signals = signals[0];
+  return MojoWaitMany(
+      reinterpret_cast<const MojoHandle*>(&first_handle),
+      reinterpret_cast<const MojoHandleSignals*>(&first_signals),
+      static_cast<uint32_t>(handles.size()),
+      deadline);
+}
+
+// |Close()| takes ownership of the handle, since it'll invalidate it.
+// Note: There's nothing to do, since the argument will be destroyed when it
+// goes out of scope.
+template <class HandleType>
+inline void Close(ScopedHandleBase<HandleType> /*handle*/) {}
+
+// Most users should typically use |Close()| (above) instead.
+inline MojoResult CloseRaw(Handle handle) {
+  return MojoClose(handle.value());
+}
+
+// Strict weak ordering, so that |Handle|s can be used as keys in |std::map|s,
+inline bool operator<(const Handle a, const Handle b) {
+  return a.value() < b.value();
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
diff --git a/mojo/public/cpp/system/macros.h b/mojo/public/cpp/system/macros.h
new file mode 100644
index 0000000..605906e
--- /dev/null
+++ b/mojo/public/cpp/system/macros.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_PUBLIC_CPP_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+
+#include "mojo/public/c/system/macros.h"
+
+// Define a set of C++ specific macros.
+// Mojo C++ API users can assume that mojo/public/cpp/system/macros.h
+// includes mojo/public/c/system/macros.h.
+
+// A macro to disallow the copy constructor and operator= functions.
+// This should be used in the private: declarations for a class.
+#define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+    TypeName(const TypeName&); \
+    void operator=(const TypeName&)
+
+// Used to calculate the number of elements in an array.
+// (See |arraysize()| in Chromium's base/basictypes.h for more details.)
+namespace mojo {
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#if !defined(_MSC_VER)
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+}  // namespace mojo
+#define MOJO_ARRAYSIZE(array) (sizeof(::mojo::ArraySizeHelper(array)))
+
+// Used to make a type move-only in C++03. See Chromium's base/move.h for more
+// details.
+#define MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
+   private: \
+    struct rvalue_type { \
+      explicit rvalue_type(type* object) : object(object) {} \
+      type* object; \
+    }; \
+    type(type&); \
+    void operator=(type&); \
+   public: \
+    operator rvalue_type() { return rvalue_type(this); } \
+    type Pass() { return type(rvalue_type(this)); } \
+    typedef void MoveOnlyTypeForCPP03; \
+   private:
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
new file mode 100644
index 0000000..7ef6314
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// MessagePipeHandle -----------------------------------------------------------
+
+class MessagePipeHandle : public Handle {
+ public:
+  MessagePipeHandle() {}
+  explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(MessagePipeHandle) == sizeof(Handle),
+                    bad_size_for_cpp_MessagePipeHandle);
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedMessagePipeHandle) ==
+                        sizeof(MessagePipeHandle),
+                    bad_size_for_cpp_ScopedMessagePipeHandle);
+
+inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+                                    ScopedMessagePipeHandle* message_pipe0,
+                                    ScopedMessagePipeHandle* message_pipe1) {
+  assert(message_pipe0);
+  assert(message_pipe1);
+  MessagePipeHandle handle0;
+  MessagePipeHandle handle1;
+  MojoResult rv = MojoCreateMessagePipe(options,
+                                        handle0.mutable_value(),
+                                        handle1.mutable_value());
+  // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+  // will be used).
+  message_pipe0->reset(handle0);
+  message_pipe1->reset(handle1);
+  return rv;
+}
+
+// These "raw" versions fully expose the underlying API, but don't help with
+// ownership of handles (especially when writing messages).
+// TODO(vtl): Write "baked" versions.
+inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
+                                  const void* bytes,
+                                  uint32_t num_bytes,
+                                  const MojoHandle* handles,
+                                  uint32_t num_handles,
+                                  MojoWriteMessageFlags flags) {
+  return MojoWriteMessage(message_pipe.value(), bytes, num_bytes, handles,
+                          num_handles, flags);
+}
+
+inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
+                                 void* bytes,
+                                 uint32_t* num_bytes,
+                                 MojoHandle* handles,
+                                 uint32_t* num_handles,
+                                 MojoReadMessageFlags flags) {
+  return MojoReadMessage(message_pipe.value(), bytes, num_bytes, handles,
+                         num_handles, flags);
+}
+
+// A wrapper class that automatically creates a message pipe and owns both
+// handles.
+class MessagePipe {
+ public:
+  MessagePipe();
+  explicit MessagePipe(const MojoCreateMessagePipeOptions& options);
+  ~MessagePipe();
+
+  ScopedMessagePipeHandle handle0;
+  ScopedMessagePipeHandle handle1;
+};
+
+inline MessagePipe::MessagePipe() {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateMessagePipe(nullptr, &handle0, &handle1);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+  MojoResult result MOJO_ALLOW_UNUSED =
+      CreateMessagePipe(&options, &handle0, &handle1);
+  assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::~MessagePipe() {
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
new file mode 100644
index 0000000..3226662
--- /dev/null
+++ b/mojo/public/cpp/system/tests/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_system_unittests
+test("mojo_public_system_unittests") {
+  deps = [
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/public/c/system/tests",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "core_unittest.cc",
+    "macros_unittest.cc",
+  ]
+}
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc
new file mode 100644
index 0000000..a44bc07
--- /dev/null
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -0,0 +1,419 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file tests the C++ Mojo system core wrappers.
+// TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets
+// compiled into a different binary from the C API tests.
+
+#include "mojo/public/cpp/system/core.h"
+
+#include <map>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(CoreCppTest, GetTimeTicksNow) {
+  const MojoTimeTicks start = GetTimeTicksNow();
+  EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+      << "GetTimeTicksNow should return nonzero value";
+}
+
+TEST(CoreCppTest, Basic) {
+  // Basic |Handle| implementation:
+  {
+    EXPECT_EQ(MOJO_HANDLE_INVALID, kInvalidHandleValue);
+
+    Handle h0;
+    EXPECT_EQ(kInvalidHandleValue, h0.value());
+    EXPECT_EQ(kInvalidHandleValue, *h0.mutable_value());
+    EXPECT_FALSE(h0.is_valid());
+
+    Handle h1(static_cast<MojoHandle>(123));
+    EXPECT_EQ(static_cast<MojoHandle>(123), h1.value());
+    EXPECT_EQ(static_cast<MojoHandle>(123), *h1.mutable_value());
+    EXPECT_TRUE(h1.is_valid());
+    *h1.mutable_value() = static_cast<MojoHandle>(456);
+    EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+    EXPECT_TRUE(h1.is_valid());
+
+    h1.swap(h0);
+    EXPECT_EQ(static_cast<MojoHandle>(456), h0.value());
+    EXPECT_TRUE(h0.is_valid());
+    EXPECT_FALSE(h1.is_valid());
+
+    h1.set_value(static_cast<MojoHandle>(789));
+    h0.swap(h1);
+    EXPECT_EQ(static_cast<MojoHandle>(789), h0.value());
+    EXPECT_TRUE(h0.is_valid());
+    EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+    EXPECT_TRUE(h1.is_valid());
+
+    // Make sure copy constructor works.
+    Handle h2(h0);
+    EXPECT_EQ(static_cast<MojoHandle>(789), h2.value());
+    // And assignment.
+    h2 = h1;
+    EXPECT_EQ(static_cast<MojoHandle>(456), h2.value());
+
+    // Make sure that we can put |Handle|s into |std::map|s.
+    h0 = Handle(static_cast<MojoHandle>(987));
+    h1 = Handle(static_cast<MojoHandle>(654));
+    h2 = Handle(static_cast<MojoHandle>(321));
+    Handle h3;
+    std::map<Handle, int> handle_to_int;
+    handle_to_int[h0] = 0;
+    handle_to_int[h1] = 1;
+    handle_to_int[h2] = 2;
+    handle_to_int[h3] = 3;
+
+    EXPECT_EQ(4u, handle_to_int.size());
+    EXPECT_FALSE(handle_to_int.find(h0) == handle_to_int.end());
+    EXPECT_EQ(0, handle_to_int[h0]);
+    EXPECT_FALSE(handle_to_int.find(h1) == handle_to_int.end());
+    EXPECT_EQ(1, handle_to_int[h1]);
+    EXPECT_FALSE(handle_to_int.find(h2) == handle_to_int.end());
+    EXPECT_EQ(2, handle_to_int[h2]);
+    EXPECT_FALSE(handle_to_int.find(h3) == handle_to_int.end());
+    EXPECT_EQ(3, handle_to_int[h3]);
+    EXPECT_TRUE(handle_to_int.find(Handle(static_cast<MojoHandle>(13579))) ==
+                    handle_to_int.end());
+
+    // TODO(vtl): With C++11, support |std::unordered_map|s, etc. (Or figure out
+    // how to support the variations of |hash_map|.)
+  }
+
+  // |Handle|/|ScopedHandle| functions:
+  {
+    ScopedHandle h;
+
+    EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+    // This should be a no-op.
+    Close(h.Pass());
+
+    // It should still be invalid.
+    EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              Wait(h.get(), ~MOJO_HANDLE_SIGNAL_NONE, 1000000));
+
+    std::vector<Handle> wh;
+    wh.push_back(h.get());
+    std::vector<MojoHandleSignals> sigs;
+    sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE);
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE));
+  }
+
+  // |MakeScopedHandle| (just compilation tests):
+  {
+    EXPECT_FALSE(MakeScopedHandle(Handle()).is_valid());
+    EXPECT_FALSE(MakeScopedHandle(MessagePipeHandle()).is_valid());
+    EXPECT_FALSE(MakeScopedHandle(DataPipeProducerHandle()).is_valid());
+    EXPECT_FALSE(MakeScopedHandle(DataPipeConsumerHandle()).is_valid());
+    EXPECT_FALSE(MakeScopedHandle(SharedBufferHandle()).is_valid());
+  }
+
+  // |MessagePipeHandle|/|ScopedMessagePipeHandle| functions:
+  {
+    MessagePipeHandle h_invalid;
+    EXPECT_FALSE(h_invalid.is_valid());
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              WriteMessageRaw(h_invalid,
+                              nullptr, 0,
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    char buffer[10] = { 0 };
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              WriteMessageRaw(h_invalid,
+                              buffer, sizeof(buffer),
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              ReadMessageRaw(h_invalid,
+                             nullptr, nullptr,
+                             nullptr, nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+    uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              ReadMessageRaw(h_invalid,
+                             buffer, &buffer_size,
+                             nullptr, nullptr,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+
+    // Basic tests of waiting and closing.
+    MojoHandle hv0 = kInvalidHandleValue;
+    {
+      ScopedMessagePipeHandle h0;
+      ScopedMessagePipeHandle h1;
+      EXPECT_FALSE(h0.get().is_valid());
+      EXPECT_FALSE(h1.get().is_valid());
+
+      CreateMessagePipe(nullptr, &h0, &h1);
+      EXPECT_TRUE(h0.get().is_valid());
+      EXPECT_TRUE(h1.get().is_valid());
+      EXPECT_NE(h0.get().value(), h1.get().value());
+      // Save the handle values, so we can check that things got closed
+      // correctly.
+      hv0 = h0.get().value();
+      MojoHandle hv1 = h1.get().value();
+
+      EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+                Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0));
+      std::vector<Handle> wh;
+      wh.push_back(h0.get());
+      wh.push_back(h1.get());
+      std::vector<MojoHandleSignals> sigs;
+      sigs.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+      sigs.push_back(MOJO_HANDLE_SIGNAL_WRITABLE);
+      EXPECT_EQ(1, WaitMany(wh, sigs, 1000));
+
+      // Test closing |h1| explicitly.
+      Close(h1.Pass());
+      EXPECT_FALSE(h1.get().is_valid());
+
+      // Make sure |h1| is closed.
+      EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                MojoWait(hv1, ~MOJO_HANDLE_SIGNAL_NONE,
+                         MOJO_DEADLINE_INDEFINITE));
+
+      EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+                Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE));
+    }
+    // |hv0| should have been closed when |h0| went out of scope, so this close
+    // should fail.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+
+    // Actually test writing/reading messages.
+    {
+      ScopedMessagePipeHandle h0;
+      ScopedMessagePipeHandle h1;
+      CreateMessagePipe(nullptr, &h0, &h1);
+
+      const char kHello[] = "hello";
+      const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                WriteMessageRaw(h0.get(),
+                                kHello, kHelloSize,
+                                nullptr, 0,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE));
+      char buffer[10] = { 0 };
+      uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                ReadMessageRaw(h1.get(),
+                               buffer, &buffer_size,
+                               nullptr, nullptr,
+                               MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(kHelloSize, buffer_size);
+      EXPECT_STREQ(kHello, buffer);
+
+      // Send a handle over the previously-establish message pipe. Use the
+      // |MessagePipe| wrapper (to test it), which automatically creates a
+      // message pipe.
+      MessagePipe mp;
+
+      // Write a message to |mp.handle0|, before we send |mp.handle1|.
+      const char kWorld[] = "world!";
+      const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                WriteMessageRaw(mp.handle0.get(),
+                                kWorld, kWorldSize,
+                                nullptr, 0,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+      // Send |mp.handle1| over |h1| to |h0|.
+      MojoHandle handles[5];
+      handles[0] = mp.handle1.release().value();
+      EXPECT_NE(kInvalidHandleValue, handles[0]);
+      EXPECT_FALSE(mp.handle1.get().is_valid());
+      uint32_t handles_count = 1;
+      EXPECT_EQ(MOJO_RESULT_OK,
+                WriteMessageRaw(h1.get(),
+                                kHello, kHelloSize,
+                                handles, handles_count,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+      // |handles[0]| should actually be invalid now.
+      EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0]));
+
+      // Read "hello" and the sent handle.
+      EXPECT_EQ(MOJO_RESULT_OK,
+                Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE));
+      memset(buffer, 0, sizeof(buffer));
+      buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++)
+        handles[i] = kInvalidHandleValue;
+      handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                ReadMessageRaw(h0.get(),
+                               buffer, &buffer_size,
+                               handles, &handles_count,
+                               MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(kHelloSize, buffer_size);
+      EXPECT_STREQ(kHello, buffer);
+      EXPECT_EQ(1u, handles_count);
+      EXPECT_NE(kInvalidHandleValue, handles[0]);
+
+      // Read from the sent/received handle.
+      mp.handle1.reset(MessagePipeHandle(handles[0]));
+      // Save |handles[0]| to check that it gets properly closed.
+      hv0 = handles[0];
+      EXPECT_EQ(MOJO_RESULT_OK,
+                Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE));
+      memset(buffer, 0, sizeof(buffer));
+      buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++)
+        handles[i] = kInvalidHandleValue;
+      handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                ReadMessageRaw(mp.handle1.get(),
+                               buffer, &buffer_size,
+                               handles, &handles_count,
+                               MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(kWorldSize, buffer_size);
+      EXPECT_STREQ(kWorld, buffer);
+      EXPECT_EQ(0u, handles_count);
+    }
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+  }
+
+  // TODO(vtl): Test |CloseRaw()|.
+  // TODO(vtl): Test |reset()| more thoroughly?
+}
+
+TEST(CoreCppTest, TearDownWithMessagesEnqueued) {
+  // Tear down a message pipe which still has a message enqueued, with the
+  // message also having a valid message pipe handle.
+  {
+    ScopedMessagePipeHandle h0;
+    ScopedMessagePipeHandle h1;
+    CreateMessagePipe(nullptr, &h0, &h1);
+
+    // Send a handle over the previously-establish message pipe.
+    ScopedMessagePipeHandle h2;
+    ScopedMessagePipeHandle h3;
+    CreateMessagePipe(nullptr, &h2, &h3);
+
+    // Write a message to |h2|, before we send |h3|.
+    const char kWorld[] = "world!";
+    const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h2.get(),
+                              kWorld, kWorldSize,
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    // And also a message to |h3|.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h3.get(),
+                              kWorld, kWorldSize,
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Send |h3| over |h1| to |h0|.
+    const char kHello[] = "hello";
+    const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+    MojoHandle h3_value;
+    h3_value = h3.release().value();
+    EXPECT_NE(kInvalidHandleValue, h3_value);
+    EXPECT_FALSE(h3.get().is_valid());
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h1.get(),
+                              kHello, kHelloSize,
+                              &h3_value, 1,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    // |h3_value| should actually be invalid now.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+  }
+
+  // Do this in a different order: make the enqueued message pipe handle only
+  // half-alive.
+  {
+    ScopedMessagePipeHandle h0;
+    ScopedMessagePipeHandle h1;
+    CreateMessagePipe(nullptr, &h0, &h1);
+
+    // Send a handle over the previously-establish message pipe.
+    ScopedMessagePipeHandle h2;
+    ScopedMessagePipeHandle h3;
+    CreateMessagePipe(nullptr, &h2, &h3);
+
+    // Write a message to |h2|, before we send |h3|.
+    const char kWorld[] = "world!";
+    const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h2.get(),
+                              kWorld, kWorldSize,
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    // And also a message to |h3|.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h3.get(),
+                              kWorld, kWorldSize,
+                              nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Send |h3| over |h1| to |h0|.
+    const char kHello[] = "hello";
+    const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+    MojoHandle h3_value;
+    h3_value = h3.release().value();
+    EXPECT_NE(kInvalidHandleValue, h3_value);
+    EXPECT_FALSE(h3.get().is_valid());
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WriteMessageRaw(h1.get(),
+                              kHello, kHelloSize,
+                              &h3_value, 1,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE));
+    // |h3_value| should actually be invalid now.
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+  }
+}
+
+TEST(CoreCppTest, ScopedHandleMoveCtor) {
+  ScopedSharedBufferHandle buffer1;
+  EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1));
+  EXPECT_TRUE(buffer1.is_valid());
+
+  ScopedSharedBufferHandle buffer2;
+  EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer2));
+  EXPECT_TRUE(buffer2.is_valid());
+
+  // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will
+  // assert.
+  buffer1 = buffer2.Pass();
+
+  EXPECT_TRUE(buffer1.is_valid());
+  EXPECT_FALSE(buffer2.is_valid());
+}
+
+TEST(CoreCppTest, ScopedHandleMoveCtorSelf) {
+  ScopedSharedBufferHandle buffer1;
+  EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1));
+  EXPECT_TRUE(buffer1.is_valid());
+
+  buffer1 = buffer1.Pass();
+
+  EXPECT_TRUE(buffer1.is_valid());
+}
+
+// TODO(vtl): Write data pipe tests.
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/tests/macros_unittest.cc b/mojo/public/cpp/system/tests/macros_unittest.cc
new file mode 100644
index 0000000..49a8005
--- /dev/null
+++ b/mojo/public/cpp/system/tests/macros_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file tests the C++ Mojo system macros and consists of "positive" tests,
+// i.e., those verifying that things work (without compile errors, or even
+// warnings if warnings are treated as errors).
+// TODO(vtl): Maybe rename "MacrosCppTest" -> "MacrosTest" if/when this gets
+// compiled into a different binary from the C API tests.
+// TODO(vtl): Fix no-compile tests (which are all disabled; crbug.com/105388)
+// and write some "negative" tests.
+
+#include "mojo/public/cpp/system/macros.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+struct TestOverrideBaseClass {
+  virtual ~TestOverrideBaseClass() {}
+  virtual void ToBeOverridden() {}
+  virtual void AlsoToBeOverridden() = 0;
+};
+
+struct TestOverrideSubclass : public TestOverrideBaseClass {
+  virtual ~TestOverrideSubclass() {}
+  virtual void ToBeOverridden() override {}
+  virtual void AlsoToBeOverridden() override {}
+};
+
+TEST(MacrosCppTest, Override) {
+  TestOverrideSubclass x;
+  x.ToBeOverridden();
+  x.AlsoToBeOverridden();
+}
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+class TestDisallowCopyAndAssignClass {
+ public:
+  TestDisallowCopyAndAssignClass() {}
+  explicit TestDisallowCopyAndAssignClass(int) {}
+  void NoOp() {}
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestDisallowCopyAndAssignClass);
+};
+
+TEST(MacrosCppTest, DisallowCopyAndAssign) {
+  TestDisallowCopyAndAssignClass x;
+  x.NoOp();
+  TestDisallowCopyAndAssignClass y(789);
+  y.NoOp();
+}
+
+// Test that |MOJO_ARRAYSIZE()| works in a |MOJO_COMPILE_ASSERT()|.
+const int kGlobalArray[5] = { 1, 2, 3, 4, 5 };
+MOJO_COMPILE_ASSERT(MOJO_ARRAYSIZE(kGlobalArray) == 5u,
+                    mojo_array_size_failed_in_compile_assert);
+
+TEST(MacrosCppTest, ArraySize) {
+  double local_array[4] = { 6.7, 7.8, 8.9, 9.0 };
+  EXPECT_EQ(4u, MOJO_ARRAYSIZE(local_array));
+}
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+class MoveOnlyInt {
+  MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(MoveOnlyInt, RValue)
+
+ public:
+  MoveOnlyInt() : is_set_(false), value_() {}
+  explicit MoveOnlyInt(int value) : is_set_(true), value_(value) {}
+  ~MoveOnlyInt() {}
+
+  // Move-only constructor and operator=.
+  MoveOnlyInt(RValue other) { *this = other; }
+  MoveOnlyInt& operator=(RValue other) {
+    if (other.object != this) {
+      is_set_ = other.object->is_set_;
+      value_ = other.object->value_;
+      other.object->is_set_ = false;
+    }
+    return *this;
+  }
+
+  int value() const {
+    assert(is_set());
+    return value_;
+  }
+  bool is_set() const { return is_set_; }
+
+ private:
+  bool is_set_;
+  int value_;
+};
+
+TEST(MacrosCppTest, MoveOnlyTypeForCpp03) {
+  MoveOnlyInt x(123);
+  EXPECT_TRUE(x.is_set());
+  EXPECT_EQ(123, x.value());
+  MoveOnlyInt y;
+  EXPECT_FALSE(y.is_set());
+  y = x.Pass();
+  EXPECT_FALSE(x.is_set());
+  EXPECT_TRUE(y.is_set());
+  EXPECT_EQ(123, y.value());
+  MoveOnlyInt z(y.Pass());
+  EXPECT_FALSE(y.is_set());
+  EXPECT_TRUE(z.is_set());
+  EXPECT_EQ(123, z.value());
+  z = z.Pass();
+  EXPECT_TRUE(z.is_set());
+  EXPECT_EQ(123, z.value());
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
new file mode 100644
index 0000000..6c73c29
--- /dev/null
+++ b/mojo/public/cpp/test_support/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_test_utils
+source_set("test_utils") {
+  testonly = true
+  deps = [
+    "//base",
+    "//mojo/public/c/test_support",
+    "//mojo/public/cpp/system",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "lib/test_support.cc",
+    "lib/test_utils.cc",
+    "test_utils.h",
+  ]
+}
diff --git a/mojo/public/cpp/test_support/DEPS b/mojo/public/cpp/test_support/DEPS
new file mode 100644
index 0000000..6dc5394
--- /dev/null
+++ b/mojo/public/cpp/test_support/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/c/test_support",
+]
diff --git a/mojo/public/cpp/test_support/lib/test_support.cc b/mojo/public/cpp/test_support/lib/test_support.cc
new file mode 100644
index 0000000..0b6035b
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_support.cc
@@ -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.
+
+#include "mojo/public/cpp/test_support/test_support.h"
+
+#include <stdlib.h>
+
+namespace mojo {
+namespace test {
+
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+    const std::string& relative_path) {
+  char** names = MojoTestSupportEnumerateSourceRootRelativeDirectory(
+      relative_path.c_str());
+  std::vector<std::string> results;
+  for (char** ptr = names; *ptr != nullptr; ++ptr) {
+    results.push_back(*ptr);
+    free(*ptr);
+  }
+  free(names);
+  return results;
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc
new file mode 100644
index 0000000..8491c4b
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -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.
+
+#include "mojo/public/cpp/test_support/test_utils.h"
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+bool WriteTextMessage(const MessagePipeHandle& handle,
+                      const std::string& text) {
+  MojoResult rv = WriteMessageRaw(handle,
+                                  text.data(),
+                                  static_cast<uint32_t>(text.size()),
+                                  nullptr,
+                                  0,
+                                  MOJO_WRITE_MESSAGE_FLAG_NONE);
+  return rv == MOJO_RESULT_OK;
+}
+
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text) {
+  MojoResult rv;
+  bool did_wait = false;
+
+  uint32_t num_bytes = 0, num_handles = 0;
+  for (;;) {
+    rv = ReadMessageRaw(handle,
+                        nullptr,
+                        &num_bytes,
+                        nullptr,
+                        &num_handles,
+                        MOJO_READ_MESSAGE_FLAG_NONE);
+    if (rv == MOJO_RESULT_SHOULD_WAIT) {
+      if (did_wait) {
+        assert(false);  // Looping endlessly!?
+        return false;
+      }
+      rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+      if (rv != MOJO_RESULT_OK)
+        return false;
+      did_wait = true;
+    } else {
+      assert(!num_handles);
+      break;
+    }
+  }
+
+  text->resize(num_bytes);
+  rv = ReadMessageRaw(handle,
+                      &text->at(0),
+                      &num_bytes,
+                      nullptr,
+                      &num_handles,
+                      MOJO_READ_MESSAGE_FLAG_NONE);
+  return rv == MOJO_RESULT_OK;
+}
+
+bool DiscardMessage(const MessagePipeHandle& handle) {
+  MojoResult rv = ReadMessageRaw(handle, nullptr, nullptr, nullptr, nullptr,
+                                 MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+  return rv == MOJO_RESULT_OK;
+}
+
+void IterateAndReportPerf(const char* test_name,
+                          PerfTestSingleIteration single_iteration,
+                          void* closure) {
+  // TODO(vtl): These should be specifiable using command-line flags.
+  static const size_t kGranularity = 100;
+  static const MojoTimeTicks kPerftestTimeMicroseconds = 3 * 1000000;
+
+  const MojoTimeTicks start_time = GetTimeTicksNow();
+  MojoTimeTicks end_time;
+  size_t iterations = 0;
+  do {
+    for (size_t i = 0; i < kGranularity; i++)
+      (*single_iteration)(closure);
+    iterations += kGranularity;
+
+    end_time = GetTimeTicksNow();
+  } while (end_time - start_time < kPerftestTimeMicroseconds);
+
+  MojoTestSupportLogPerfResult(test_name,
+                               1000000.0 * iterations / (end_time - start_time),
+                               "iterations/second");
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/test_support/test_support.h b/mojo/public/cpp/test_support/test_support.h
new file mode 100644
index 0000000..eb4d4be
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_support.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_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/c/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+inline void LogPerfResult(const char* test_name,
+                          double value,
+                          const char* units) {
+  MojoTestSupportLogPerfResult(test_name, value, units);
+}
+
+// Opens text file relative to the source root for reading.
+inline FILE* OpenSourceRootRelativeFile(const std::string& relative_path) {
+  return MojoTestSupportOpenSourceRootRelativeFile(relative_path.c_str());
+}
+
+// Returns the list of regular files in a directory relative to the source root.
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+    const std::string& relative_path);
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/mojo/public/cpp/test_support/test_utils.h b/mojo/public/cpp/test_support/test_utils.h
new file mode 100644
index 0000000..43a3ea9
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -0,0 +1,39 @@
+// 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_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace test {
+
+// Writes a message to |handle| with message data |text|. Returns true on
+// success.
+bool WriteTextMessage(const MessagePipeHandle& handle, const std::string& text);
+
+// Reads a message from |handle|, putting its contents into |*text|. Returns
+// true on success. (This blocks if necessary and will call |MojoReadMessage()|
+// multiple times, e.g., to query the size of the message.)
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text);
+
+// Discards a message from |handle|. Returns true on success. (This does not
+// block. It will fail if no message is available to discard.)
+bool DiscardMessage(const MessagePipeHandle& handle);
+
+// Run |single_iteration| an appropriate number of times and report its
+// performance appropriately. (This actually runs |single_iteration| for a fixed
+// amount of time and reports the number of iterations per unit time.)
+typedef void (*PerfTestSingleIteration)(void* closure);
+void IterateAndReportPerf(const char* test_name,
+                          PerfTestSingleIteration single_iteration,
+                          void* closure);
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
diff --git a/mojo/public/cpp/utility/BUILD.gn b/mojo/public/cpp/utility/BUILD.gn
new file mode 100644
index 0000000..a0898b9
--- /dev/null
+++ b/mojo/public/cpp/utility/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.
+
+source_set("utility") {
+  sources = [
+    "mutex.h",
+    "run_loop.h",
+    "run_loop_handler.h",
+    "thread.h",
+    "lib/mutex.cc",
+    "lib/run_loop.cc",
+    "lib/thread.cc",
+    "lib/thread_local.h",
+    "lib/thread_local_posix.cc",
+    "lib/thread_local_win.cc",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/bindings:callback",
+    "//mojo/public/cpp/system",
+  ]
+
+  if (is_win) {
+    # See crbug.com/342893:
+    sources -= [
+      "mutex.h",
+      "thread.h",
+      "lib/mutex.cc",
+      "lib/thread.cc",
+    ]
+  }
+}
diff --git a/mojo/public/cpp/utility/DEPS b/mojo/public/cpp/utility/DEPS
new file mode 100644
index 0000000..a9dfbd1
--- /dev/null
+++ b/mojo/public/cpp/utility/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/cpp/bindings/callback.h"
+]
diff --git a/mojo/public/cpp/utility/lib/mutex.cc b/mojo/public/cpp/utility/lib/mutex.cc
new file mode 100644
index 0000000..23370e1
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/mutex.cc
@@ -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.
+
+#include "mojo/public/cpp/utility/mutex.h"
+
+#include <assert.h>
+#include <errno.h>
+
+namespace mojo {
+
+// Release builds have inlined (non-error-checking) definitions in the header.
+#if !defined(NDEBUG)
+Mutex::Mutex() {
+  pthread_mutexattr_t mutexattr;
+  int rv = pthread_mutexattr_init(&mutexattr);
+  assert(rv == 0);
+  rv = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
+  assert(rv == 0);
+  rv = pthread_mutex_init(&mutex_, &mutexattr);
+  assert(rv == 0);
+  rv = pthread_mutexattr_destroy(&mutexattr);
+  assert(rv == 0);
+}
+
+Mutex::~Mutex() {
+  int rv = pthread_mutex_destroy(&mutex_);
+  assert(rv == 0);
+}
+
+void Mutex::Lock() {
+  int rv = pthread_mutex_lock(&mutex_);
+  assert(rv == 0);
+}
+
+void Mutex::Unlock() {
+  int rv = pthread_mutex_unlock(&mutex_);
+  assert(rv == 0);
+}
+
+bool Mutex::TryLock() {
+  int rv = pthread_mutex_trylock(&mutex_);
+  assert(rv == 0 || rv == EBUSY);
+  return rv == 0;
+}
+
+void Mutex::AssertHeld() {
+  assert(pthread_mutex_lock(&mutex_) == EDEADLK);
+}
+#endif  // !defined(NDEBUG)
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/run_loop.cc b/mojo/public/cpp/utility/lib/run_loop.cc
new file mode 100644
index 0000000..bcbc623
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/run_loop.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 "mojo/public/cpp/utility/run_loop.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "mojo/public/cpp/utility/lib/thread_local.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+namespace {
+
+internal::ThreadLocalPointer<RunLoop> current_run_loop;
+
+const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0);
+
+}  // namespace
+
+// State needed for one iteration of WaitMany().
+struct RunLoop::WaitState {
+  WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {}
+
+  std::vector<Handle> handles;
+  std::vector<MojoHandleSignals> handle_signals;
+  MojoDeadline deadline;
+};
+
+struct RunLoop::RunState {
+  RunState() : should_quit(false) {}
+
+  bool should_quit;
+};
+
+RunLoop::RunLoop()
+    : run_state_(NULL), next_handler_id_(0), next_sequence_number_(0) {
+  assert(!current());
+  current_run_loop.Set(this);
+}
+
+RunLoop::~RunLoop() {
+  assert(current() == this);
+  NotifyHandlers(MOJO_RESULT_ABORTED, IGNORE_DEADLINE);
+  current_run_loop.Set(NULL);
+}
+
+// static
+void RunLoop::SetUp() {
+  current_run_loop.Allocate();
+}
+
+// static
+void RunLoop::TearDown() {
+  assert(!current());
+  current_run_loop.Free();
+}
+
+// static
+RunLoop* RunLoop::current() {
+  return current_run_loop.Get();
+}
+
+void RunLoop::AddHandler(RunLoopHandler* handler,
+                         const Handle& handle,
+                         MojoHandleSignals handle_signals,
+                         MojoDeadline deadline) {
+  assert(current() == this);
+  assert(handler);
+  assert(handle.is_valid());
+  // Assume it's an error if someone tries to reregister an existing handle.
+  assert(0u == handler_data_.count(handle));
+  HandlerData handler_data;
+  handler_data.handler = handler;
+  handler_data.handle_signals = handle_signals;
+  handler_data.deadline = (deadline == MOJO_DEADLINE_INDEFINITE) ?
+      kInvalidTimeTicks :
+      GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline);
+  handler_data.id = next_handler_id_++;
+  handler_data_[handle] = handler_data;
+}
+
+void RunLoop::RemoveHandler(const Handle& handle) {
+  assert(current() == this);
+  handler_data_.erase(handle);
+}
+
+bool RunLoop::HasHandler(const Handle& handle) const {
+  return handler_data_.find(handle) != handler_data_.end();
+}
+
+void RunLoop::Run() {
+  RunInternal(UNTIL_EMPTY);
+}
+
+void RunLoop::RunUntilIdle() {
+  RunInternal(UNTIL_IDLE);
+}
+
+void RunLoop::RunInternal(RunMode run_mode) {
+  assert(current() == this);
+  RunState* old_state = run_state_;
+  RunState run_state;
+  run_state_ = &run_state;
+  for (;;) {
+    bool did_work = DoDelayedWork();
+    if (run_state.should_quit)
+      break;
+    did_work |= Wait(run_mode == UNTIL_IDLE);
+    if (run_state.should_quit)
+      break;
+    if (!did_work && run_mode == UNTIL_IDLE)
+      break;
+  }
+  run_state_ = old_state;
+}
+
+bool RunLoop::DoDelayedWork() {
+  MojoTimeTicks now = GetTimeTicksNow();
+  if (!delayed_tasks_.empty() && delayed_tasks_.top().run_time <= now) {
+    PendingTask task = delayed_tasks_.top();
+    delayed_tasks_.pop();
+    task.task.Run();
+    return true;
+  }
+  return false;
+}
+
+void RunLoop::Quit() {
+  assert(current() == this);
+  if (run_state_)
+    run_state_->should_quit = true;
+}
+
+void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) {
+  assert(current() == this);
+  MojoTimeTicks run_time = delay + GetTimeTicksNow();
+  delayed_tasks_.push(PendingTask(task, run_time, next_sequence_number_++));
+}
+
+bool RunLoop::Wait(bool non_blocking) {
+  const WaitState wait_state = GetWaitState(non_blocking);
+  if (wait_state.handles.empty() && delayed_tasks_.empty()) {
+    Quit();
+    return false;
+  }
+
+  const MojoResult result = WaitMany(wait_state.handles,
+                                     wait_state.handle_signals,
+                                     wait_state.deadline);
+  if (result >= 0) {
+    const size_t index = static_cast<size_t>(result);
+    assert(handler_data_.find(wait_state.handles[index]) !=
+           handler_data_.end());
+    handler_data_[wait_state.handles[index]].handler->OnHandleReady(
+        wait_state.handles[index]);
+    return true;
+  }
+
+  switch (result) {
+    case MOJO_RESULT_INVALID_ARGUMENT:
+    case MOJO_RESULT_FAILED_PRECONDITION:
+      return RemoveFirstInvalidHandle(wait_state);
+    case MOJO_RESULT_DEADLINE_EXCEEDED:
+      return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE);
+  }
+
+  assert(false);
+  return false;
+}
+
+bool RunLoop::NotifyHandlers(MojoResult error, CheckDeadline check) {
+  bool notified = false;
+
+  // Make a copy in case someone tries to add/remove new handlers as part of
+  // notifying.
+  const HandleToHandlerData cloned_handlers(handler_data_);
+  const MojoTimeTicks now(GetTimeTicksNow());
+  for (HandleToHandlerData::const_iterator i = cloned_handlers.begin();
+       i != cloned_handlers.end(); ++i) {
+    // Only check deadline exceeded if that's what we're notifying.
+    if (check == CHECK_DEADLINE && (i->second.deadline == kInvalidTimeTicks ||
+                                    i->second.deadline > now)) {
+      continue;
+    }
+
+    // Since we're iterating over a clone of the handlers, verify the handler
+    // is still valid before notifying.
+    if (handler_data_.find(i->first) == handler_data_.end() ||
+        handler_data_[i->first].id != i->second.id) {
+      continue;
+    }
+
+    RunLoopHandler* handler = i->second.handler;
+    handler_data_.erase(i->first);
+    handler->OnHandleError(i->first, error);
+    notified = true;
+  }
+
+  return notified;
+}
+
+bool RunLoop::RemoveFirstInvalidHandle(const WaitState& wait_state) {
+  for (size_t i = 0; i < wait_state.handles.size(); ++i) {
+    const MojoResult result =
+        mojo::Wait(wait_state.handles[i], wait_state.handle_signals[i],
+                   static_cast<MojoDeadline>(0));
+    if (result == MOJO_RESULT_INVALID_ARGUMENT ||
+        result == MOJO_RESULT_FAILED_PRECONDITION) {
+      // Remove the handle first, this way if OnHandleError() tries to remove
+      // the handle our iterator isn't invalidated.
+      assert(handler_data_.find(wait_state.handles[i]) != handler_data_.end());
+      RunLoopHandler* handler =
+          handler_data_[wait_state.handles[i]].handler;
+      handler_data_.erase(wait_state.handles[i]);
+      handler->OnHandleError(wait_state.handles[i], result);
+      return true;
+    }
+    assert(MOJO_RESULT_DEADLINE_EXCEEDED == result);
+  }
+  return false;
+}
+
+RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const {
+  WaitState wait_state;
+  MojoTimeTicks min_time = kInvalidTimeTicks;
+  for (HandleToHandlerData::const_iterator i = handler_data_.begin();
+       i != handler_data_.end(); ++i) {
+    wait_state.handles.push_back(i->first);
+    wait_state.handle_signals.push_back(i->second.handle_signals);
+    if (!non_blocking && i->second.deadline != kInvalidTimeTicks &&
+        (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) {
+      min_time = i->second.deadline;
+    }
+  }
+  if (!delayed_tasks_.empty()) {
+    MojoTimeTicks delayed_min_time = delayed_tasks_.top().run_time;
+    if (min_time == kInvalidTimeTicks)
+      min_time = delayed_min_time;
+    else
+      min_time = std::min(min_time, delayed_min_time);
+  }
+  if (non_blocking) {
+    wait_state.deadline = static_cast<MojoDeadline>(0);
+  } else if (min_time != kInvalidTimeTicks) {
+    const MojoTimeTicks now = GetTimeTicksNow();
+    if (min_time < now)
+      wait_state.deadline = static_cast<MojoDeadline>(0);
+    else
+      wait_state.deadline = static_cast<MojoDeadline>(min_time - now);
+  }
+  return wait_state;
+}
+
+RunLoop::PendingTask::PendingTask(const Closure& task,
+                                  MojoTimeTicks run_time,
+                                  uint64_t sequence_number)
+    : task(task), run_time(run_time), sequence_number(sequence_number) {
+}
+
+RunLoop::PendingTask::~PendingTask() {
+}
+
+bool RunLoop::PendingTask::operator<(const RunLoop::PendingTask& other) const {
+  if (run_time != other.run_time) {
+    // std::priority_queue<> puts the least element at the end of the queue. We
+    // want the soonest eligible task to be at the head of the queue, so
+    // run_times further in the future are considered lesser.
+    return run_time > other.run_time;
+  }
+
+  return sequence_number > other.sequence_number;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread.cc b/mojo/public/cpp/utility/lib/thread.cc
new file mode 100644
index 0000000..da33497
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread.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/public/cpp/utility/thread.h"
+
+#include <assert.h>
+
+namespace mojo {
+
+Thread::Thread()
+    : options_(),
+      thread_(),
+      started_(false),
+      joined_(false) {
+}
+
+Thread::Thread(const Options& options)
+    : options_(options),
+      thread_(),
+      started_(false),
+      joined_(false) {
+}
+
+Thread::~Thread() {
+  // If it was started, it must have been joined.
+  assert(!started_ || joined_);
+}
+
+void Thread::Start() {
+  assert(!started_);
+  assert(!joined_);
+
+  pthread_attr_t attr;
+  int rv MOJO_ALLOW_UNUSED = pthread_attr_init(&attr);
+  assert(rv == 0);
+
+  // Non-default stack size?
+  if (options_.stack_size() != 0) {
+    rv = pthread_attr_setstacksize(&attr, options_.stack_size());
+    assert(rv == 0);
+  }
+
+  started_ = true;
+  rv = pthread_create(&thread_, &attr, &ThreadRunTrampoline, this);
+  assert(rv == 0);
+
+  rv = pthread_attr_destroy(&attr);
+  assert(rv == 0);
+}
+
+void Thread::Join() {
+  // Must have been started but not yet joined.
+  assert(started_);
+  assert(!joined_);
+
+  joined_ = true;
+  int rv MOJO_ALLOW_UNUSED = pthread_join(thread_, NULL);
+  assert(rv == 0);
+}
+
+// static
+void* Thread::ThreadRunTrampoline(void* arg) {
+  Thread* self = static_cast<Thread*>(arg);
+  self->Run();
+  return NULL;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread_local.h b/mojo/public/cpp/utility/lib/thread_local.h
new file mode 100644
index 0000000..4c3625d
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
+#define MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
+
+#ifndef _WIN32
+#include <pthread.h>
+#endif
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Helper functions that abstract the cross-platform APIs.
+struct ThreadLocalPlatform {
+#ifdef _WIN32
+  typedef unsigned long SlotType;
+#else
+  typedef pthread_key_t SlotType;
+#endif
+
+  static void AllocateSlot(SlotType* slot);
+  static void FreeSlot(SlotType slot);
+  static void* GetValueFromSlot(SlotType slot);
+  static void SetValueInSlot(SlotType slot, void* value);
+};
+
+// This class is intended to be statically allocated.
+template <typename P>
+class ThreadLocalPointer {
+ public:
+  ThreadLocalPointer() : slot_() {
+  }
+
+  void Allocate() {
+    ThreadLocalPlatform::AllocateSlot(&slot_);
+  }
+
+  void Free() {
+    ThreadLocalPlatform::FreeSlot(slot_);
+  }
+
+  P* Get() {
+    return static_cast<P*>(ThreadLocalPlatform::GetValueFromSlot(slot_));
+  }
+
+  void Set(P* value) {
+    ThreadLocalPlatform::SetValueInSlot(slot_, value);
+  }
+
+ private:
+  ThreadLocalPlatform::SlotType slot_;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
diff --git a/mojo/public/cpp/utility/lib/thread_local_posix.cc b/mojo/public/cpp/utility/lib/thread_local_posix.cc
new file mode 100644
index 0000000..b33dfc6
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local_posix.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/public/cpp/utility/lib/thread_local.h"
+
+#include <assert.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+  if (pthread_key_create(slot, NULL) != 0) {
+    assert(false);
+  }
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+  if (pthread_key_delete(slot) != 0) {
+    assert(false);
+  }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+  return pthread_getspecific(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+  if (pthread_setspecific(slot, value) != 0) {
+    assert(false);
+  }
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread_local_win.cc b/mojo/public/cpp/utility/lib/thread_local_win.cc
new file mode 100644
index 0000000..98841f7
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local_win.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/public/cpp/utility/lib/thread_local.h"
+
+#include <assert.h>
+#include <windows.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+  *slot = TlsAlloc();
+  assert(*slot != TLS_OUT_OF_INDEXES);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+  if (!TlsFree(slot)) {
+    assert(false);
+  }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+  return TlsGetValue(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+  if (!TlsSetValue(slot, value)) {
+    assert(false);
+  }
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/mutex.h b/mojo/public/cpp/utility/mutex.h
new file mode 100644
index 0000000..35611c2
--- /dev/null
+++ b/mojo/public/cpp/utility/mutex.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_PUBLIC_CPP_UTILITY_MUTEX_H_
+#define MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+#ifdef NDEBUG
+// Note: Make a C++ constant for |PTHREAD_MUTEX_INITIALIZER|. (We can't directly
+// use the C macro in an initializer list, since it might expand to |{ ... }|.)
+namespace internal {
+const pthread_mutex_t kPthreadMutexInitializer = PTHREAD_MUTEX_INITIALIZER;
+}
+#endif
+
+class Mutex {
+ public:
+#ifdef NDEBUG
+  Mutex() : mutex_(internal::kPthreadMutexInitializer) {}
+  ~Mutex() { pthread_mutex_destroy(&mutex_); }
+
+  void Lock() { pthread_mutex_lock(&mutex_); }
+  void Unlock() { pthread_mutex_unlock(&mutex_); }
+  bool TryLock() { return pthread_mutex_trylock(&mutex_) == 0; }
+
+  void AssertHeld() {}
+#else
+  Mutex();
+  ~Mutex();
+
+  void Lock();
+  void Unlock();
+  bool TryLock();
+
+  void AssertHeld();
+#endif
+
+ private:
+  pthread_mutex_t mutex_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+class MutexLock {
+ public:
+  explicit MutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); }
+  ~MutexLock() { mutex_->Unlock(); }
+
+ private:
+  Mutex* const mutex_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLock);
+};
+
+// Catch bug where variable name is omitted (e.g., |MutexLock (&mu)|).
+#define MutexLock(x) MOJO_COMPILE_ASSERT(0, mutex_lock_missing_variable_name);
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
diff --git a/mojo/public/cpp/utility/run_loop.h b/mojo/public/cpp/utility/run_loop.h
new file mode 100644
index 0000000..359208d
--- /dev/null
+++ b/mojo/public/cpp/utility/run_loop.h
@@ -0,0 +1,155 @@
+// 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_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
+
+#include <map>
+#include <queue>
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class RunLoopHandler;
+
+class RunLoop {
+ public:
+  RunLoop();
+  ~RunLoop();
+
+  // Sets up state needed for RunLoop. This must be invoked before creating a
+  // RunLoop.
+  static void SetUp();
+
+  // Cleans state created by Setup().
+  static void TearDown();
+
+  // Returns the RunLoop for the current thread. Returns NULL if not yet
+  // created.
+  static RunLoop* current();
+
+  // Registers a RunLoopHandler for the specified handle. Only one handler can
+  // be registered for a specified handle.
+  void AddHandler(RunLoopHandler* handler,
+                  const Handle& handle,
+                  MojoHandleSignals handle_signals,
+                  MojoDeadline deadline);
+  void RemoveHandler(const Handle& handle);
+  bool HasHandler(const Handle& handle) const;
+
+  // Runs the loop servicing handles and tasks as they are ready. This returns
+  // when Quit() is invoked, or there are no more handles nor tasks.
+  void Run();
+
+  // Runs the loop servicing any handles and tasks that are ready. Does not wait
+  // for handles or tasks to become ready before returning. Returns early if
+  // Quit() is invoked.
+  void RunUntilIdle();
+
+  void Quit();
+
+  // Adds a task to be performed after delay has elapsed. Must be posted to the
+  // current thread's RunLoop.
+  void PostDelayedTask(const Closure& task, MojoTimeTicks delay);
+
+ private:
+  struct RunState;
+  struct WaitState;
+
+  // Contains the data needed to track a request to AddHandler().
+  struct HandlerData {
+    HandlerData()
+        : handler(NULL),
+          handle_signals(MOJO_HANDLE_SIGNAL_NONE),
+          deadline(0),
+          id(0) {}
+
+    RunLoopHandler* handler;
+    MojoHandleSignals handle_signals;
+    MojoTimeTicks deadline;
+    // See description of |RunLoop::next_handler_id_| for details.
+    int id;
+  };
+
+  typedef std::map<Handle, HandlerData> HandleToHandlerData;
+
+  // Used for NotifyHandlers to specify whether HandlerData's |deadline|
+  // should be checked prior to notifying.
+  enum CheckDeadline {
+    CHECK_DEADLINE,
+    IGNORE_DEADLINE
+  };
+
+  // Mode of operation of the run loop.
+  enum RunMode {
+    UNTIL_EMPTY,
+    UNTIL_IDLE
+  };
+
+  // Runs the loop servicing any handles and tasks that are ready. If
+  // |run_mode| is |UNTIL_IDLE|, does not wait for handles or tasks to become
+  // ready before returning. Returns early if Quit() is invoked.
+  void RunInternal(RunMode run_mode);
+
+  // Do one unit of delayed work, if eligible. Returns true is a task was run.
+  bool DoDelayedWork();
+
+  // Waits for a handle to be ready or until the next task must be run. Returns
+  // after servicing at least one handle (or there are no more handles) unless
+  // a task must be run or |non_blocking| is true, in which case it will also
+  // return if no task is registered and servicing at least one handle would
+  // require blocking. Returns true if a RunLoopHandler was notified.
+  bool Wait(bool non_blocking);
+
+  // Notifies handlers of |error|.  If |check| == CHECK_DEADLINE, this will
+  // only notify handlers whose deadline has expired and skips the rest.
+  // Returns true if a RunLoopHandler was notified.
+  bool NotifyHandlers(MojoResult error, CheckDeadline check);
+
+  // Removes the first invalid handle. This is called if MojoWaitMany() finds an
+  // invalid handle. Returns true if a RunLoopHandler was notified.
+  bool RemoveFirstInvalidHandle(const WaitState& wait_state);
+
+  // Returns the state needed to pass to WaitMany().
+  WaitState GetWaitState(bool non_blocking) const;
+
+  HandleToHandlerData handler_data_;
+
+  // If non-NULL we're running (inside Run()). Member references a value on the
+  // stack.
+  RunState* run_state_;
+
+  // An ever increasing value assigned to each HandlerData::id. Used to detect
+  // uniqueness while notifying. That is, while notifying expired timers we copy
+  // |handler_data_| and only notify handlers whose id match. If the id does not
+  // match it means the handler was removed then added so that we shouldn't
+  // notify it.
+  int next_handler_id_;
+
+  struct PendingTask {
+    PendingTask(const Closure& task,
+                MojoTimeTicks runtime,
+                uint64_t sequence_number);
+    ~PendingTask();
+
+    bool operator<(const PendingTask& other) const;
+
+    Closure task;
+    MojoTimeTicks run_time;
+    uint64_t sequence_number;
+  };
+  // An ever increasing sequence number attached to each pending task in order
+  // to preserve relative order of tasks posted at the 'same' time.
+  uint64_t next_sequence_number_;
+  typedef std::priority_queue<PendingTask> DelayedTaskQueue;
+  DelayedTaskQueue delayed_tasks_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoop);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
diff --git a/mojo/public/cpp/utility/run_loop_handler.h b/mojo/public/cpp/utility/run_loop_handler.h
new file mode 100644
index 0000000..69838d5
--- /dev/null
+++ b/mojo/public/cpp/utility/run_loop_handler.h
@@ -0,0 +1,25 @@
+// 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_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// Used by RunLoop to notify when a handle is either ready or has become
+// invalid.
+class RunLoopHandler {
+ public:
+  virtual void OnHandleReady(const Handle& handle) = 0;
+  virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+  virtual ~RunLoopHandler() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
diff --git a/mojo/public/cpp/utility/tests/BUILD.gn b/mojo/public/cpp/utility/tests/BUILD.gn
new file mode 100644
index 0000000..225bf07
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_utility_unittests
+test("mojo_public_utility_unittests") {
+  deps = [
+    "//mojo/common/test:run_all_unittests",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/cpp/utility",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "mutex_unittest.cc",
+    "run_loop_unittest.cc",
+    "thread_unittest.cc",
+  ]
+}
diff --git a/mojo/public/cpp/utility/tests/mutex_unittest.cc b/mojo/public/cpp/utility/tests/mutex_unittest.cc
new file mode 100644
index 0000000..eb5a330
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/mutex_unittest.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/public/cpp/utility/mutex.h"
+
+#include <stdlib.h>  // For |rand()|.
+#include <time.h>  // For |nanosleep()| (defined by POSIX).
+
+#include <vector>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(MutexTest, TrivialSingleThreaded) {
+  Mutex mutex;
+
+  mutex.Lock();
+  mutex.AssertHeld();
+  mutex.Unlock();
+
+  EXPECT_TRUE(mutex.TryLock());
+  mutex.AssertHeld();
+  mutex.Unlock();
+
+  {
+    MutexLock lock(&mutex);
+    mutex.AssertHeld();
+  }
+
+  EXPECT_TRUE(mutex.TryLock());
+  mutex.Unlock();
+}
+
+class Fiddler {
+ public:
+  enum Type { kTypeLock, kTypeTry };
+  Fiddler(size_t times_to_lock,
+          Type type,
+          bool should_sleep,
+          Mutex* mutex,
+          int* shared_value)
+      : times_to_lock_(times_to_lock),
+        type_(type),
+        should_sleep_(should_sleep),
+        mutex_(mutex),
+        shared_value_(shared_value) {
+  }
+
+  ~Fiddler() {
+  }
+
+  void Fiddle() {
+    for (size_t i = 0; i < times_to_lock_;) {
+      switch (type_) {
+        case kTypeLock: {
+          mutex_->Lock();
+          int old_shared_value = *shared_value_;
+          if (should_sleep_)
+            SleepALittle();
+          *shared_value_ = old_shared_value + 1;
+          mutex_->Unlock();
+          i++;
+          break;
+        }
+        case kTypeTry:
+          if (mutex_->TryLock()) {
+            int old_shared_value = *shared_value_;
+            if (should_sleep_)
+              SleepALittle();
+            *shared_value_ = old_shared_value + 1;
+            mutex_->Unlock();
+            i++;
+          } else {
+            SleepALittle();  // Don't spin.
+          }
+          break;
+      }
+    }
+  }
+
+ private:
+  static void SleepALittle() {
+    static const long kNanosPerMilli = 1000000;
+    struct timespec req = {
+      0,  // Seconds.
+      (rand() % 10) * kNanosPerMilli // Nanoseconds.
+    };
+    int rv MOJO_ALLOW_UNUSED = nanosleep(&req, NULL);
+    assert(rv == 0);
+  }
+
+  const size_t times_to_lock_;
+  const Type type_;
+  const bool should_sleep_;
+  Mutex* const mutex_;
+  int* const shared_value_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler);
+};
+
+class FiddlerThread : public Thread {
+ public:
+  // Takes ownership of |fiddler|.
+  FiddlerThread(Fiddler* fiddler)
+      : fiddler_(fiddler) {
+  }
+
+  virtual ~FiddlerThread() {
+    delete fiddler_;
+  }
+
+  virtual void Run() override { fiddler_->Fiddle(); }
+
+ private:
+  Fiddler* const fiddler_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread);
+};
+
+// This does a stress test (that also checks exclusion).
+TEST(MutexTest, ThreadedStress) {
+  static const size_t kNumThreads = 20;
+  static const int kTimesToLockEach = 20;
+  assert(kNumThreads % 4 == 0);
+
+  Mutex mutex;
+  int shared_value = 0;
+
+  std::vector<FiddlerThread*> fiddler_threads;
+
+  for (size_t i = 0; i < kNumThreads; i += 4) {
+    fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+        kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value)));
+    fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+        kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value)));
+    fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+        kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)));
+    fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+        kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value)));
+  }
+
+  for (size_t i = 0; i < kNumThreads; i++)
+    fiddler_threads[i]->Start();
+
+  // Do some fiddling ourselves.
+  Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)
+      .Fiddle();
+
+  // Join.
+  for (size_t i = 0; i < kNumThreads; i++)
+    fiddler_threads[i]->Join();
+
+  EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value);
+
+  // Delete.
+  for (size_t i = 0; i < kNumThreads; i++)
+    delete fiddler_threads[i];
+  fiddler_threads.clear();
+}
+
+class TryThread : public Thread {
+ public:
+  explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {}
+  virtual ~TryThread() {}
+
+  virtual void Run() override {
+    try_lock_succeeded_ = mutex_->TryLock();
+    if (try_lock_succeeded_)
+      mutex_->Unlock();
+  }
+
+  bool try_lock_succeeded() const { return try_lock_succeeded_; }
+
+ private:
+  Mutex* const mutex_;
+  bool try_lock_succeeded_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread);
+};
+
+TEST(MutexTest, TryLock) {
+  Mutex mutex;
+
+  // |TryLock()| should succeed -- we don't have the lock.
+  {
+    TryThread thread(&mutex);
+    thread.Start();
+    thread.Join();
+    EXPECT_TRUE(thread.try_lock_succeeded());
+  }
+
+  // Take the lock.
+  ASSERT_TRUE(mutex.TryLock());
+
+  // Now it should fail.
+  {
+    TryThread thread(&mutex);
+    thread.Start();
+    thread.Join();
+    EXPECT_FALSE(thread.try_lock_succeeded());
+  }
+
+  // Release the lock.
+  mutex.Unlock();
+
+  // It should succeed again.
+  {
+    TryThread thread(&mutex);
+    thread.Start();
+    thread.Join();
+    EXPECT_TRUE(thread.try_lock_succeeded());
+  }
+}
+
+
+// Tests of assertions for Debug builds.
+#if !defined(NDEBUG)
+// Test |AssertHeld()| (which is an actual user API).
+TEST(MutexTest, DebugAssertHeldFailure) {
+  Mutex mutex;
+  EXPECT_DEATH_IF_SUPPORTED(mutex.AssertHeld(), "");
+}
+
+// Test other consistency checks.
+TEST(MutexTest, DebugAssertionFailures) {
+  // Unlock without lock held.
+  EXPECT_DEATH_IF_SUPPORTED({
+    Mutex mutex;
+    mutex.Unlock();
+  }, "");
+
+  // Lock with lock held (on same thread).
+  EXPECT_DEATH_IF_SUPPORTED({
+    Mutex mutex;
+    mutex.Lock();
+    mutex.Lock();
+  }, "");
+
+  // Try lock with lock held.
+  EXPECT_DEATH_IF_SUPPORTED({
+    Mutex mutex;
+    mutex.Lock();
+    mutex.TryLock();
+  }, "");
+
+  // Destroy lock with lock held.
+  EXPECT_DEATH_IF_SUPPORTED({
+    Mutex mutex;
+    mutex.Lock();
+  }, "");
+}
+#endif  // !defined(NDEBUG)
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/tests/run_loop_unittest.cc b/mojo/public/cpp/utility/tests/run_loop_unittest.cc
new file mode 100644
index 0000000..c6199d7
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/run_loop_unittest.cc
@@ -0,0 +1,431 @@
+// Copyright 2014 The Chromium 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/public/cpp/utility/run_loop.h"
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestRunLoopHandler : public RunLoopHandler {
+ public:
+  TestRunLoopHandler()
+      : ready_count_(0),
+        error_count_(0),
+        last_error_result_(MOJO_RESULT_OK) {
+  }
+  virtual ~TestRunLoopHandler() {}
+
+  void clear_ready_count() { ready_count_ = 0; }
+  int ready_count() const { return ready_count_; }
+
+  void clear_error_count() { error_count_ = 0; }
+  int error_count() const { return error_count_; }
+
+  MojoResult last_error_result() const { return last_error_result_; }
+
+  // RunLoopHandler:
+  virtual void OnHandleReady(const Handle& handle) override { ready_count_++; }
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    error_count_++;
+    last_error_result_ = result;
+  }
+
+ private:
+  int ready_count_;
+  int error_count_;
+  MojoResult last_error_result_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestRunLoopHandler);
+};
+
+class RunLoopTest : public testing::Test {
+ public:
+  RunLoopTest() {}
+
+  virtual void SetUp() override {
+    Test::SetUp();
+    RunLoop::SetUp();
+  }
+  virtual void TearDown() override {
+    RunLoop::TearDown();
+    Test::TearDown();
+  }
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopTest);
+};
+
+// Trivial test to verify Run() with no added handles returns.
+TEST_F(RunLoopTest, ExitsWithNoHandles) {
+  RunLoop run_loop;
+  run_loop.Run();
+}
+
+class RemoveOnReadyRunLoopHandler : public TestRunLoopHandler {
+ public:
+  RemoveOnReadyRunLoopHandler() : run_loop_(NULL) {
+  }
+  virtual ~RemoveOnReadyRunLoopHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+  // RunLoopHandler:
+  virtual void OnHandleReady(const Handle& handle) override {
+    run_loop_->RemoveHandler(handle);
+    TestRunLoopHandler::OnHandleReady(handle);
+  }
+
+ private:
+  RunLoop* run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveOnReadyRunLoopHandler);
+};
+
+// Verifies RunLoop quits when no more handles (handle is removed when ready).
+TEST_F(RunLoopTest, HandleReady) {
+  RemoveOnReadyRunLoopHandler handler;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  RunLoop run_loop;
+  handler.set_run_loop(&run_loop);
+  run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+                      MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+  run_loop.Run();
+  EXPECT_EQ(1, handler.ready_count());
+  EXPECT_EQ(0, handler.error_count());
+  EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+class QuitOnReadyRunLoopHandler : public TestRunLoopHandler {
+ public:
+  QuitOnReadyRunLoopHandler() : run_loop_(NULL) {
+  }
+  virtual ~QuitOnReadyRunLoopHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+  // RunLoopHandler:
+  virtual void OnHandleReady(const Handle& handle) override {
+    run_loop_->Quit();
+    TestRunLoopHandler::OnHandleReady(handle);
+  }
+
+ private:
+  RunLoop* run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnReadyRunLoopHandler);
+};
+
+// Verifies Quit() from OnHandleReady() quits the loop.
+TEST_F(RunLoopTest, QuitFromReady) {
+  QuitOnReadyRunLoopHandler handler;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  RunLoop run_loop;
+  handler.set_run_loop(&run_loop);
+  run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+                      MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+  run_loop.Run();
+  EXPECT_EQ(1, handler.ready_count());
+  EXPECT_EQ(0, handler.error_count());
+  EXPECT_TRUE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+class QuitOnErrorRunLoopHandler : public TestRunLoopHandler {
+ public:
+  QuitOnErrorRunLoopHandler() : run_loop_(NULL) {
+  }
+  virtual ~QuitOnErrorRunLoopHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+  // RunLoopHandler:
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    run_loop_->Quit();
+    TestRunLoopHandler::OnHandleError(handle, result);
+  }
+
+ private:
+  RunLoop* run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnErrorRunLoopHandler);
+};
+
+// Verifies Quit() when the deadline is reached works.
+TEST_F(RunLoopTest, QuitWhenDeadlineExpired) {
+  QuitOnErrorRunLoopHandler handler;
+  MessagePipe test_pipe;
+  RunLoop run_loop;
+  handler.set_run_loop(&run_loop);
+  run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+                      MOJO_HANDLE_SIGNAL_READABLE,
+                      static_cast<MojoDeadline>(10000));
+  run_loop.Run();
+  EXPECT_EQ(0, handler.ready_count());
+  EXPECT_EQ(1, handler.error_count());
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, handler.last_error_result());
+  EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+// Test that handlers are notified of loop destruction.
+TEST_F(RunLoopTest, Destruction) {
+  TestRunLoopHandler handler;
+  MessagePipe test_pipe;
+  {
+    RunLoop run_loop;
+    run_loop.AddHandler(&handler,
+                        test_pipe.handle0.get(),
+                        MOJO_HANDLE_SIGNAL_READABLE,
+                        MOJO_DEADLINE_INDEFINITE);
+  }
+  EXPECT_EQ(1, handler.error_count());
+  EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result());
+}
+
+class RemoveManyRunLoopHandler : public TestRunLoopHandler {
+ public:
+  RemoveManyRunLoopHandler() : run_loop_(NULL) {
+  }
+  virtual ~RemoveManyRunLoopHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+  void add_handle(const Handle& handle) { handles_.push_back(handle); }
+
+  // RunLoopHandler:
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    for (size_t i = 0; i < handles_.size(); i++)
+      run_loop_->RemoveHandler(handles_[i]);
+    TestRunLoopHandler::OnHandleError(handle, result);
+  }
+
+ private:
+  std::vector<Handle> handles_;
+  RunLoop* run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveManyRunLoopHandler);
+};
+
+// Test that handlers are notified of loop destruction.
+TEST_F(RunLoopTest, MultipleHandleDestruction) {
+  RemoveManyRunLoopHandler odd_handler;
+  TestRunLoopHandler even_handler;
+  MessagePipe test_pipe1, test_pipe2, test_pipe3;
+  {
+    RunLoop run_loop;
+    odd_handler.set_run_loop(&run_loop);
+    odd_handler.add_handle(test_pipe1.handle0.get());
+    odd_handler.add_handle(test_pipe3.handle0.get());
+    run_loop.AddHandler(&odd_handler,
+                        test_pipe1.handle0.get(),
+                        MOJO_HANDLE_SIGNAL_READABLE,
+                        MOJO_DEADLINE_INDEFINITE);
+    run_loop.AddHandler(&even_handler,
+                        test_pipe2.handle0.get(),
+                        MOJO_HANDLE_SIGNAL_READABLE,
+                        MOJO_DEADLINE_INDEFINITE);
+    run_loop.AddHandler(&odd_handler,
+                        test_pipe3.handle0.get(),
+                        MOJO_HANDLE_SIGNAL_READABLE,
+                        MOJO_DEADLINE_INDEFINITE);
+  }
+  EXPECT_EQ(1, odd_handler.error_count());
+  EXPECT_EQ(1, even_handler.error_count());
+  EXPECT_EQ(MOJO_RESULT_ABORTED, odd_handler.last_error_result());
+  EXPECT_EQ(MOJO_RESULT_ABORTED, even_handler.last_error_result());
+}
+
+class AddHandlerOnErrorHandler : public TestRunLoopHandler {
+ public:
+  AddHandlerOnErrorHandler() : run_loop_(NULL) {
+  }
+  virtual ~AddHandlerOnErrorHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+  // RunLoopHandler:
+  virtual void OnHandleError(const Handle& handle, MojoResult result) override {
+    run_loop_->AddHandler(this, handle,
+                          MOJO_HANDLE_SIGNAL_READABLE,
+                          MOJO_DEADLINE_INDEFINITE);
+    TestRunLoopHandler::OnHandleError(handle, result);
+  }
+
+ private:
+  RunLoop* run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AddHandlerOnErrorHandler);
+};
+
+TEST_F(RunLoopTest, AddHandlerOnError) {
+  AddHandlerOnErrorHandler handler;
+  MessagePipe test_pipe;
+  {
+    RunLoop run_loop;
+    handler.set_run_loop(&run_loop);
+    run_loop.AddHandler(&handler,
+                        test_pipe.handle0.get(),
+                        MOJO_HANDLE_SIGNAL_READABLE,
+                        MOJO_DEADLINE_INDEFINITE);
+  }
+  EXPECT_EQ(1, handler.error_count());
+  EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result());
+}
+
+TEST_F(RunLoopTest, Current) {
+  EXPECT_TRUE(RunLoop::current() == NULL);
+  {
+    RunLoop run_loop;
+    EXPECT_EQ(&run_loop, RunLoop::current());
+  }
+  EXPECT_TRUE(RunLoop::current() == NULL);
+}
+
+class NestingRunLoopHandler : public TestRunLoopHandler {
+ public:
+  static const size_t kDepthLimit;
+  static const char kSignalMagic;
+
+  NestingRunLoopHandler()
+      : run_loop_(NULL),
+        pipe_(NULL),
+        depth_(0),
+        reached_depth_limit_(false) {}
+
+  virtual ~NestingRunLoopHandler() {}
+
+  void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+  void set_pipe(MessagePipe* pipe) { pipe_ = pipe; }
+  bool reached_depth_limit() const { return reached_depth_limit_; }
+
+  // RunLoopHandler:
+  virtual void OnHandleReady(const Handle& handle) override {
+    TestRunLoopHandler::OnHandleReady(handle);
+    EXPECT_EQ(handle.value(), pipe_->handle0.get().value());
+
+    ReadSignal();
+    size_t current_depth = ++depth_;
+    if (current_depth < kDepthLimit) {
+      WriteSignal();
+      run_loop_->Run();
+      if (current_depth == kDepthLimit - 1) {
+        // The topmost loop Quit()-ed, so its parent takes back the
+        // control without exeeding deadline.
+        EXPECT_EQ(error_count(), 0);
+      } else {
+        EXPECT_EQ(error_count(), 1);
+      }
+
+    } else {
+      EXPECT_EQ(current_depth, kDepthLimit);
+      reached_depth_limit_ = true;
+      run_loop_->Quit();
+    }
+    --depth_;
+  }
+
+  void WriteSignal() {
+    char write_byte = kSignalMagic;
+    MojoResult write_result = WriteMessageRaw(
+        pipe_->handle1.get(),
+        &write_byte, 1, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+    EXPECT_EQ(write_result, MOJO_RESULT_OK);
+  }
+
+  void ReadSignal() {
+    char read_byte = 0;
+    uint32_t bytes_read = 1;
+    uint32_t handles_read = 0;
+    MojoResult read_result = ReadMessageRaw(
+        pipe_->handle0.get(),
+        &read_byte, &bytes_read, NULL, &handles_read,
+        MOJO_READ_MESSAGE_FLAG_NONE);
+    EXPECT_EQ(read_result, MOJO_RESULT_OK);
+    EXPECT_EQ(read_byte, kSignalMagic);
+  }
+
+ private:
+  RunLoop* run_loop_;
+  MessagePipe* pipe_;
+  size_t depth_;
+  bool reached_depth_limit_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(NestingRunLoopHandler);
+};
+
+const size_t NestingRunLoopHandler::kDepthLimit = 10;
+const char NestingRunLoopHandler::kSignalMagic = 'X';
+
+TEST_F(RunLoopTest, NestedRun) {
+  NestingRunLoopHandler handler;
+  MessagePipe test_pipe;
+  RunLoop run_loop;
+  handler.set_run_loop(&run_loop);
+  handler.set_pipe(&test_pipe);
+  run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+                      MOJO_HANDLE_SIGNAL_READABLE,
+                      static_cast<MojoDeadline>(10000));
+  handler.WriteSignal();
+  run_loop.Run();
+
+  EXPECT_TRUE(handler.reached_depth_limit());
+  // Got MOJO_RESULT_DEADLINE_EXCEEDED once then removed from the
+  // RunLoop's handler list.
+  EXPECT_EQ(handler.error_count(), 1);
+  EXPECT_EQ(handler.last_error_result(), MOJO_RESULT_DEADLINE_EXCEEDED);
+}
+
+struct Task {
+  Task(int num, std::vector<int>* sequence) : num(num), sequence(sequence) {}
+
+  void Run() const { sequence->push_back(num); }
+
+  int num;
+  std::vector<int>* sequence;
+};
+
+TEST_F(RunLoopTest, DelayedTaskOrder) {
+  std::vector<int> sequence;
+  RunLoop run_loop;
+  run_loop.PostDelayedTask(Closure(Task(1, &sequence)), 0);
+  run_loop.PostDelayedTask(Closure(Task(2, &sequence)), 0);
+  run_loop.PostDelayedTask(Closure(Task(3, &sequence)), 0);
+  run_loop.RunUntilIdle();
+
+  ASSERT_EQ(3u, sequence.size());
+  EXPECT_EQ(1, sequence[0]);
+  EXPECT_EQ(2, sequence[1]);
+  EXPECT_EQ(3, sequence[2]);
+}
+
+struct QuittingTask {
+  explicit QuittingTask(RunLoop* run_loop) : run_loop(run_loop) {}
+
+  void Run() const { run_loop->Quit(); }
+
+  RunLoop* run_loop;
+};
+
+TEST_F(RunLoopTest, QuitFromDelayedTask) {
+  TestRunLoopHandler handler;
+  MessagePipe test_pipe;
+  RunLoop run_loop;
+  run_loop.AddHandler(&handler,
+                      test_pipe.handle0.get(),
+                      MOJO_HANDLE_SIGNAL_READABLE,
+                      MOJO_DEADLINE_INDEFINITE);
+  run_loop.PostDelayedTask(Closure(QuittingTask(&run_loop)), 0);
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/tests/thread_unittest.cc b/mojo/public/cpp/utility/tests/thread_unittest.cc
new file mode 100644
index 0000000..0d81ef8
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/thread_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium 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/public/cpp/utility/thread.h"
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class SetIntThread : public Thread {
+ public:
+  SetIntThread(int* int_to_set, int value)
+      : int_to_set_(int_to_set),
+        value_(value) {
+  }
+  SetIntThread(const Options& options, int* int_to_set, int value)
+      : Thread(options),
+        int_to_set_(int_to_set),
+        value_(value) {
+  }
+
+  virtual ~SetIntThread() {
+  }
+
+  virtual void Run() override { *int_to_set_ = value_; }
+
+ private:
+  int* const int_to_set_;
+  const int value_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(SetIntThread);
+};
+
+TEST(ThreadTest, CreateAndJoin) {
+  int value = 0;
+
+  // Not starting the thread should result in a no-op.
+  {
+    SetIntThread thread(&value, 1234567);
+  }
+  EXPECT_EQ(0, value);
+
+  // Start and join.
+  {
+    SetIntThread thread(&value, 12345678);
+    thread.Start();
+    thread.Join();
+    EXPECT_EQ(12345678, value);
+  }
+
+  // Ditto, with non-default (but reasonable) stack size.
+  {
+    Thread::Options options;
+    options.set_stack_size(1024 * 1024);  // 1 MB.
+    SetIntThread thread(options, &value, 12345678);
+    thread.Start();
+    thread.Join();
+    EXPECT_EQ(12345678, value);
+  }
+}
+
+// Tests of assertions for Debug builds.
+// Note: It's okay to create threads, despite gtest having to fork. (The threads
+// are in the child process.)
+#if !defined(NDEBUG)
+TEST(ThreadTest, DebugAssertionFailures) {
+  // Can only start once.
+  EXPECT_DEATH_IF_SUPPORTED({
+    int value = 0;
+    SetIntThread thread(&value, 1);
+    thread.Start();
+    thread.Start();
+  }, "");
+
+  // Must join (if you start).
+  EXPECT_DEATH_IF_SUPPORTED({
+    int value = 0;
+    SetIntThread thread(&value, 2);
+    thread.Start();
+  }, "");
+
+  // Can only join once.
+  EXPECT_DEATH_IF_SUPPORTED({
+    int value = 0;
+    SetIntThread thread(&value, 3);
+    thread.Start();
+    thread.Join();
+    thread.Join();
+  }, "");
+
+  // Stack too big (we're making certain assumptions here).
+  EXPECT_DEATH_IF_SUPPORTED({
+    int value = 0;
+    Thread::Options options;
+    options.set_stack_size(static_cast<size_t>(-1));
+    SetIntThread thread(options, &value, 4);
+    thread.Start();
+    thread.Join();
+  }, "");
+}
+#endif  // !defined(NDEBUG)
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/utility/thread.h b/mojo/public/cpp/utility/thread.h
new file mode 100644
index 0000000..b7d10ee
--- /dev/null
+++ b/mojo/public/cpp/utility/thread.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_PUBLIC_CPP_UTILITY_THREAD_H_
+#define MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+#include <stddef.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is thread-friendly, not thread-safe (e.g., you mustn't call
+// |Join()| from multiple threads and/or simultaneously try to destroy the
+// object).
+class Thread {
+ public:
+  // TODO(vtl): Support non-joinable? priority?
+  class Options {
+   public:
+    Options() : stack_size_(0) {}
+
+    // A stack size of 0 means the default.
+    size_t stack_size() const { return stack_size_; }
+    void set_stack_size(size_t stack_size) { stack_size_ = stack_size; }
+
+   private:
+    size_t stack_size_;
+
+    // Copy and assign allowed.
+  };
+
+  // TODO(vtl): Add name or name prefix?
+  Thread();
+  explicit Thread(const Options& options);
+  virtual ~Thread();
+
+  void Start();
+  void Join();
+
+  virtual void Run() = 0;
+
+ private:
+  static void* ThreadRunTrampoline(void* arg);
+
+  const Options options_;
+  pthread_t thread_;
+  bool started_;
+  bool joined_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
diff --git a/mojo/public/gles2/BUILD.gn b/mojo/public/gles2/BUILD.gn
new file mode 100644
index 0000000..5c21e78
--- /dev/null
+++ b/mojo/public/gles2/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.
+
+# In an is_component_build build, everything can link against //mojo/gles2
+# because it is built as a shared library. However, in a static build,
+# //mojo/gles2 is linked into an executable (e.g., mojo_shell), and must be
+# injected into other shared libraries (i.e., Mojo Apps) that need the mojo
+# gles2 API.
+#
+# For component targets, add //mojo/public/gles2:for_component to your deps
+# section.
+#
+# For shared_library targets (e.g., a Mojo App), add
+# //mojo/public/gles2:for_shared_library to your deps
+
+group("for_shared_library") {
+  public_configs = [ "//third_party/khronos:khronos_headers" ]
+  public_deps = [ "//mojo/public/c/gles2" ]
+
+  if (is_component_build) {
+    deps = [ "//mojo/gles2" ]
+  } else {
+    deps = [ "//mojo/public/platform/native:gles2_thunks" ]
+  }
+}
+
+group("for_component") {
+  public_configs = [ "//third_party/khronos:khronos_headers" ]
+  public_deps = [ "//mojo/public/c/gles2" ]
+
+  if (is_component_build) {
+    deps = [ "//mojo/gles2" ]
+  }
+}
diff --git a/mojo/public/go/mojo/system/core.go b/mojo/public/go/mojo/system/core.go
new file mode 100644
index 0000000..8049d37
--- /dev/null
+++ b/mojo/public/go/mojo/system/core.go
@@ -0,0 +1,9 @@
+// Copyright 2014 The Chromium 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 system;
+
+type Core interface {
+	GetTimeTicksNow() int64
+}
diff --git a/mojo/public/interfaces/application/BUILD.gn b/mojo/public/interfaces/application/BUILD.gn
new file mode 100644
index 0000000..91e2447
--- /dev/null
+++ b/mojo/public/interfaces/application/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_public.gypi:mojo_application_bindings
+mojom("application") {
+  sources = [
+    "application.mojom",
+    "service_provider.mojom",
+    "shell.mojom",
+  ]
+}
diff --git a/mojo/public/interfaces/application/application.mojom b/mojo/public/interfaces/application/application.mojom
new file mode 100644
index 0000000..758a053
--- /dev/null
+++ b/mojo/public/interfaces/application/application.mojom
@@ -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/interfaces/application/service_provider.mojom"
+
+module mojo {
+
+// Applications vend Services through the ServiceProvider interface. Services
+// implement Interfaces.
+[Client=Shell]
+interface Application {
+  // Initialize is guaranteed to be called before any AcceptConnection calls.
+  Initialize(array<string>? args);
+
+  AcceptConnection(string? requestor_url, ServiceProvider? provider);
+};
+
+}
diff --git a/mojo/public/interfaces/application/service_provider.mojom b/mojo/public/interfaces/application/service_provider.mojom
new file mode 100644
index 0000000..00eb057
--- /dev/null
+++ b/mojo/public/interfaces/application/service_provider.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 {
+
+// ServiceProvider is used to establish connections to services in a bi-
+// directional fashion between two applications.
+[Client=ServiceProvider]
+interface ServiceProvider {
+  // Connect the given message pipe handle to the named service. If the named
+  // service does not exist, then the handle will be closed.
+  ConnectToService(string? interface_name, handle<message_pipe>? pipe);
+};
+
+}
diff --git a/mojo/public/interfaces/application/shell.mojom b/mojo/public/interfaces/application/shell.mojom
new file mode 100644
index 0000000..a9ce05f
--- /dev/null
+++ b/mojo/public/interfaces/application/shell.mojom
@@ -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/interfaces/application/service_provider.mojom"
+
+module mojo {
+
+// The Shell is the finder and launcher of Applications. An Application uses
+// it's Shell interface to connect to other Applications.
+[Client=Application]
+interface Shell {
+  // Loads url. mojo:{service} will result in the user of the value of the
+  // --origin flag to the shell being used.
+  ConnectToApplication(string? application_url, ServiceProvider&? provider);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
new file mode 100644
index 0000000..e56f9b5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("test_interfaces") {
+  testonly = true
+  sources = [
+    "math_calculator.mojom",
+    "no_module.mojom",
+    "rect.mojom",
+    "regression_tests.mojom",
+    "sample_factory.mojom",
+    "sample_import.mojom",
+    "sample_import2.mojom",
+    "sample_interfaces.mojom",
+    "sample_service.mojom",
+    "serialization_test_structs.mojom",
+    "test_structs.mojom",
+    "validation_test_interfaces.mojom",
+  ]
+}
diff --git a/mojo/public/interfaces/bindings/tests/data/message_data b/mojo/public/interfaces/bindings/tests/data/message_data
new file mode 100644
index 0000000..b288878
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/message_data
@@ -0,0 +1,25 @@
+// File generated by mojo_message_generator.
+0X10
+0X00
+0X00
+0X00
+0X02
+0X00
+0X00
+0X00
+0X15
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X09
+0X08
+0X07
+0X06
+0X00
+0X00
+0X00
+0X00
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data
new file mode 100644
index 0000000..ee5ecdb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data
@@ -0,0 +1,2 @@
+[u4]16  // num_bytes: Bigger than the total size of the message.
+[u4]2   // num_fields
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data
new file mode 100644
index 0000000..21e7fbc
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data
@@ -0,0 +1 @@
+0x00
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data
new file mode 100644
index 0000000..9d66188
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data
@@ -0,0 +1,6 @@
+[dist4]message_header  // num_bytes
+[u4]3                  // num_fields
+[u4]0x80000000         // name
+[u4]3                  // flags: This combination is illegal.
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected
new file mode 100644
index 0000000..696c78d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data
new file mode 100644
index 0000000..2414431
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data
@@ -0,0 +1,6 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0x80000000         // name
+[u4]1                  // flags: This is a response message which expects to
+                       // have a request ID.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected
new file mode 100644
index 0000000..083db1a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data
new file mode 100644
index 0000000..ad3b005
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data
@@ -0,0 +1,4 @@
+[u4]0xFFFFFFFF  // num_bytes: Test whether a huge value will cause overflow.
+[u4]2           // num_fields
+[u4]0x80000000  // name
+[u4]0           // flags
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data
new file mode 100644
index 0000000..353b4e8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data
@@ -0,0 +1,4 @@
+[dist4]message_header  // num_bytes: Less than the minimal size of message
+                       // header.
+[u4]2                  // num_fields
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data
new file mode 100644
index 0000000..3a94448
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data
@@ -0,0 +1,4 @@
+[u4]0           // num_bytes
+[u4]0           // num_fields
+[u4]0x80000000  // name
+[u4]0           // flags
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data
new file mode 100644
index 0000000..7e8a714
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data
@@ -0,0 +1,7 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0x80000000         // name
+[u4]0                  // flags
+[u8]0                  // Extra bytes that result in mismatched |num_bytes| and
+                       // |num_fields|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data
new file mode 100644
index 0000000..fd8b4b4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]3                  // num_fields
+[u4]0x80000000         // name
+[u4]0                  // flags
+[u8]0                  // request_id
+[u8]0                  // Extra bytes that result in mismatched |num_bytes| and
+                       // |num_fields|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data
new file mode 100644
index 0000000..c4b46ab
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data
@@ -0,0 +1,5 @@
+[dist4]message_header  // num_bytes
+[u4]8                  // num_fields: |num_bytes| is too small for |num_fields|.
+[u4]0x80000000         // name
+[u4]0                  // flags
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data
new file mode 100644
index 0000000..a94e6ce
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data
@@ -0,0 +1,6 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // num_fields: Less than the minimal number of fields
+                       // that we expect.
+[u4]0x80000000         // name
+[u4]0                  // flags
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data
new file mode 100644
index 0000000..1a5603e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data
@@ -0,0 +1,11 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]1                  // num_fields
+[f]-1                  // param0
+[u4]0                  // padding
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data
new file mode 100644
index 0000000..5556808
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[u4]16  // num_bytes: Incomplete struct.
+[u4]1   // num_fields
+[f]-1   // param0
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data
new file mode 100644
index 0000000..9a362f6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data
@@ -0,0 +1,7 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[u4]16  // num_bytes: Incomplete struct header.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data
new file mode 100644
index 0000000..52e4540
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[u4]0xFFFFFFFF  // num_bytes: Test whether a huge value will cause overflow.
+[u4]1           // num_fields
+[f]-1           // param0
+[u4]0           // padding
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data
new file mode 100644
index 0000000..7c966f8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes: Less than the minimal size that we expect.
+[u4]1                  // num_fields
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data
new file mode 100644
index 0000000..af20941
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[u4]4                  // num_bytes: Less than the size of struct header.
+[u4]1                  // num_fields
+[f]-1                  // param0
+[u4]0                  // padding
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data
new file mode 100644
index 0000000..d67a72f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data
@@ -0,0 +1,16 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]1                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method1_params
+
+[anchr]param0_ptr
+[dist4]struct_a  // num_bytes
+[u4]1            // num_fields
+[u8]1234         // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data
new file mode 100644
index 0000000..051dccc
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data
@@ -0,0 +1,18 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]1                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method1_params
+
+[u1]0  // Causes the following struct to be misaligned.
+
+[anchr]param0_ptr
+[dist4]struct_a  // num_bytes
+[u4]1            // num_fields
+[u8]1234         // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected
new file mode 100644
index 0000000..acca999
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MISALIGNED_OBJECT
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data
new file mode 100644
index 0000000..98a21c4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data
@@ -0,0 +1,11 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]1                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method1_params   // num_bytes
+[u4]1                   // num_fields
+[u8]0xFFFFFFFFFFFFFFFF  // param0: Test whether decoding the pointer causes
+                        // overflow.
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected
new file mode 100644
index 0000000..23abb8c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data
new file mode 100644
index 0000000..c53a78b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]1                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]1                  // num_fields
+[u8]0                  // param0: An unexpected null pointer.
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data
new file mode 100644
index 0000000..38e20ab
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]2                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]1               // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+[u8]0  // Having extra bytes in the middle is okay if the following objects are
+       // still properly alignmented.
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member  // num_bytes
+[u4]1                   // num_fields
+[u8]12345               // i
+[anchr]struct_a_member
+
+[anchr]param1_ptr
+[dist4]struct_a_param  // num_bytes
+[u4]1                  // num_fields
+[u8]67890              // i
+[anchr]struct_a_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data
new file mode 100644
index 0000000..a12121b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data
@@ -0,0 +1,25 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]2                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]1               // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+// There are two pointers pointing to the same struct.
+[anchr]struct_a_ptr
+[anchr]param1_ptr
+[dist4]struct_a  // num_bytes
+[u4]1            // num_fields
+[u8]12345        // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data
new file mode 100644
index 0000000..a916f03
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data
@@ -0,0 +1,30 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]2                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]1               // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member  // num_bytes
+[u4]1                   // num_fields
+
+[anchr]param1_ptr
+// The following |num_bytes| and |num_fields| fields are also the |i| field
+// of the previous struct.
+[dist4]struct_a_param   // num_bytes
+[u4]1                   // num_fields
+[anchr]struct_a_member
+[u8]67890               // i
+[anchr]struct_a_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data
new file mode 100644
index 0000000..7145ccd
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]2                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]1               // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+// The following two structs are arranged in wrong order.
+
+[anchr]param1_ptr
+[dist4]struct_a_param  // num_bytes
+[u4]1                  // num_fields
+[u8]67890              // i
+[anchr]struct_a_param
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member  // num_bytes
+[u4]1                   // num_fields
+[u8]12345               // i
+[anchr]struct_a_member
+
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data
new file mode 100644
index 0000000..ca7651a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data
@@ -0,0 +1,16 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]0xFFFFFFFF  // num_bytes: Test whether a huge value will cause overflow.
+[u4]12          // num_elements
+[b]01010101
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data
new file mode 100644
index 0000000..9bc1fc3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data
@@ -0,0 +1,16 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]7   // num_bytes: Less than the size of array header.
+[u4]12  // num_elements
+[b]01010101
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data
new file mode 100644
index 0000000..bed891c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,18 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]array  // num_bytes: Less than the size needed (array header + 12 boolean
+              // values).
+[u4]12        // num_elements
+[b]01010101
+[anchr]array
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data
new file mode 100644
index 0000000..f8d7644
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data
@@ -0,0 +1,11 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params   // num_bytes
+[u4]1                   // num_fields
+[u8]0xFFFFFFFFFFFFFFFF  // param0: Test whether decoding the pointer causes
+                        // overflow.
+[anchr]method3_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected
new file mode 100644
index 0000000..23abb8c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data
new file mode 100644
index 0000000..cc7dd38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]array  // num_bytes
+[u4]12        // num_elements
+[b]01010101
+[b]00001111
+[anchr]array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data
new file mode 100644
index 0000000..7850893
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data
@@ -0,0 +1,14 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]16  // num_bytes
+[u1]0   // num_elements: Incomplete array.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data
new file mode 100644
index 0000000..5de9fe6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]16  // num_bytes: Incomplete array header.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data
new file mode 100644
index 0000000..40a13e0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[u2]0  // Causes the following array to be misaligned.
+
+[anchr]param0_ptr
+[dist4]array  // num_bytes
+[u4]12        // num_elements
+[b]01010101
+[b]00001111
+[anchr]array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected
new file mode 100644
index 0000000..acca999
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MISALIGNED_OBJECT
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data
new file mode 100644
index 0000000..0edab4b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]3                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]1                  // num_fields
+[u8]0                  // param0: An unexpected null pointer.
+[anchr]method3_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data
new file mode 100644
index 0000000..46f7502
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]4                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes: Larger than what we know is okay.
+[u4]3                  // num_fields: Larger than what we know is okay.
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[u8]0                  // unknown
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]1             // num_fields
+[dist8]array_ptr  // array
+[anchr]struct_c
+
+[anchr]array_ptr
+[dist4]array_member  // num_bytes
+[u4]3                // num_elements
+0 1 2
+[anchr]array_member
+
+[u4]0 [u1]0  // Padding to make the next array aligned properly.
+
+[anchr]param1_ptr
+[dist4]array_param  // num_bytes
+[u4]10              // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data
new file mode 100644
index 0000000..f3a0e9f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data
@@ -0,0 +1,24 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]4                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]1             // num_fields
+[dist8]array_ptr  // array
+[anchr]struct_c
+
+[anchr]param1_ptr
+[anchr]array_ptr
+[dist4]array_member  // num_bytes
+[u4]3                // num_elements
+0 1 2
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data
new file mode 100644
index 0000000..d819857
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data
@@ -0,0 +1,30 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]4                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]1             // num_fields
+[dist8]array_ptr  // array
+[anchr]struct_c
+
+[anchr]array_ptr
+[dist4]array_member  // num_bytes
+[u4]3                // num_elements
+
+[anchr]param1_ptr
+// The first three bytes of |num_bytes| are also the elements of the previous
+// array.
+[dist4]array_param  // num_bytes
+[u4]10              // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data
new file mode 100644
index 0000000..fdc925f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data
@@ -0,0 +1,33 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]4                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]1             // num_fields
+[dist8]array_ptr  // array
+[anchr]struct_c
+
+// The following two arrays are arranged in wrong order.
+
+[anchr]param1_ptr
+[dist4]array_param  // num_bytes
+[u4]10              // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
+
+[u4]0 [u2]0  // Padding to make the next array aligned properly.
+
+[anchr]array_ptr
+[dist4]array_member  // num_bytes
+[u4]3                // num_elements
+0 1 2
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data
new file mode 100644
index 0000000..0518c14
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data
@@ -0,0 +1,35 @@
+[handles]10  // Larger than the number of handles that we know about is okay.
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[u4]3                // data_pipe_consumer
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]2                      // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data
new file mode 100644
index 0000000..43cad7c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data
@@ -0,0 +1,36 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]10                 // param1: It is outside of the valid encoded handle
+                       // range [0, 10).
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[u4]3                // data_pipe_consumer
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]2                      // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data
new file mode 100644
index 0000000..8b39c00
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data
@@ -0,0 +1,35 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[u4]4                // data_pipe_consumer: The same value as |param1| above.
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]2                      // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data
new file mode 100644
index 0000000..95a356b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data
@@ -0,0 +1,35 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[u4]3                // data_pipe_consumer
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]2                      // num_elements
+[u4]1                      // The two message pipe handles have the same value.
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data
new file mode 100644
index 0000000..ef49285
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data
@@ -0,0 +1,34 @@
+[handles]5
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[s4]-1               // data_pipe_consumer: An unexpected invalid handle.
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]1                      // num_elements
+[u4]2
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected
new file mode 100644
index 0000000..6768236
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data
new file mode 100644
index 0000000..c6fa8ea
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data
@@ -0,0 +1,36 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]5                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[u4]9                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]2                // num_fields
+[dist8]struct_d_ptr  // struct_d
+[u4]1                // data_pipe_consumer: It is smaller than those handles
+                     // in |message_pipe_array|, which is wrong.
+[u4]0                // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d           // num_bytes
+[u4]1                     // num_fields
+[dist8]message_pipes_ptr  // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array  // num_bytes
+[u4]2                      // num_elements
+[u4]3
+[u4]4
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data
new file mode 100644
index 0000000..4f34c0b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data
@@ -0,0 +1,22 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]6                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method6_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method6_params
+
+[anchr]param0_ptr
+[dist4]array_param  // num_bytes
+[u4]1               // num_elements
+[dist8]element_ptr
+[anchr]array_param
+
+[anchr]element_ptr
+[dist4]array_element  // num_bytes
+[u4]10                // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_element
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data
new file mode 100644
index 0000000..a58396c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,22 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]6                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method6_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method6_params
+
+[anchr]param0_ptr
+[dist4]array_param  // num_bytes
+[u4]1               // num_elements
+[dist8]element_ptr
+[anchr]array_param
+
+[anchr]element_ptr
+[dist4]array_element  // num_bytes: It is insufficient to store 12 elements.
+[u4]12                // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_element
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data
new file mode 100644
index 0000000..d6562af
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]7                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[u8]0                  // unknown
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]1                  // num_fields
+[dist8]array_ptr       // fixed_size_array
+[anchr]struct_f
+
+[anchr]array_ptr
+[dist4]array_member    // num_bytes
+[u4]3                  // num_elements
+0 1 2
+[anchr]array_member
+
+[u4]0 [u1]0            // Padding to make the next array aligned properly.
+
+[anchr]param1_ptr
+[dist4]array_param     // num_bytes
+[u4]5                  // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data
new file mode 100644
index 0000000..bf1ddd5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data
@@ -0,0 +1,31 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]7                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]1                  // num_fields
+[dist8]array_ptr       // fixed_size_array
+[anchr]struct_f
+
+[anchr]array_ptr
+[dist4]array_member    // num_bytes
+[u4]2                  // num_elements: Too few elements.
+0 1
+[anchr]array_member
+
+[u4]0 [u1]0 [u1]0      // Padding for alignment of next array.
+
+[anchr]param1_ptr
+[dist4]array_param     // num_bytes
+[u4]5                  // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data
new file mode 100644
index 0000000..ab69175
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data
@@ -0,0 +1,23 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]7                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]2                  // num_fields
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]1                  // num_fields
+[u8]0                  // fixed_size_array: An unexpected null pointer.
+[anchr]struct_f
+
+[anchr]param1_ptr
+[dist4]array_param     // num_bytes
+[u4]5                  // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data
new file mode 100644
index 0000000..059e538
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]8                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param  // num_bytes
+[u4]0x20000001      // num_elements: The corresponding array size should be
+                    // 0x20000001 * 8 + 8 = 0x100000010 which is
+                    // 2^32 + 16 (base-10), while |num_bytes| is a 32-bit
+                    // unsigned integer and its value is 16.
+[u8]0
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data
new file mode 100644
index 0000000..fd5e174
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data
@@ -0,0 +1,30 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]8                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param       // num_bytes
+[u4]3                    // num_elements
+[u8]0                    // A null pointer, which is okay.
+[dist8]nested_array_ptr
+[u8]0                    // A null pointer, which is okay.
+[anchr]array_param
+
+[anchr]nested_array_ptr
+[dist4]nested_array  // num_bytes
+[u4]1                // num_elements
+[dist8]string_ptr
+[anchr]nested_array
+
+[anchr]string_ptr
+[dist4]string  // num_bytes
+[u4]5          // num_elements
+0 1 2 3 4
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data
new file mode 100644
index 0000000..f1d8718
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]8                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]1                  // num_fields
+[u8]0                  // param0: An unexpected null pointer.
+[anchr]method8_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data
new file mode 100644
index 0000000..104dddb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data
@@ -0,0 +1,31 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]8                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param       // num_bytes
+[u4]3                    // num_elements
+[u8]0                    // A null pointer, which is okay.
+[dist8]nested_array_ptr
+[u8]0                    // A null pointer, which is okay.
+[anchr]array_param
+
+[anchr]nested_array_ptr
+[dist4]nested_array  // num_bytes
+[u4]2                // num_elements
+[dist8]string_ptr
+[u8]0                // An unexpected null pointer.
+[anchr]nested_array
+
+[anchr]string_ptr
+[dist4]string  // num_bytes
+[u4]5          // num_elements
+0 1 2 3 4
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data
new file mode 100644
index 0000000..c548906
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data
@@ -0,0 +1,34 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]9                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method9_params
+
+[anchr]param0_ptr
+[dist4]array_param         // num_bytes
+[u4]2                      // num_elements
+[dist8]nested_array_ptr_0
+[dist8]nested_array_ptr_1
+[anchr]array_param
+
+[anchr]nested_array_ptr_0
+[dist4]nested_array_0  // num_bytes
+[u4]2                  // num_elements
+[u4]0
+[s4]-1                 // An invalid handle, which is okay.
+[anchr]nested_array_0
+
+[anchr]nested_array_ptr_1
+[dist4]nested_array_1  // num_bytes
+[u4]3                  // num_elements
+[u4]2
+[s4]-1                 // An invalid handle, which is okay.
+[u4]3
+[anchr]nested_array_1
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data
new file mode 100644
index 0000000..f0967d5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data
@@ -0,0 +1,12 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]9                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]1                  // num_fields
+[u8]0                  // param0: A null pointer, which is okay.
+[anchr]method9_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data
new file mode 100644
index 0000000..9c8f478
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data
@@ -0,0 +1,25 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]9                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method9_params
+
+[anchr]param0_ptr
+[dist4]array_param         // num_bytes
+[u4]2                      // num_elements
+[dist8]nested_array_ptr_0
+[u8]0                      // An unexpected null pointer.
+[anchr]array_param
+
+[anchr]nested_array_ptr_0
+[dist4]nested_array_0  // num_bytes
+[u4]1                  // num_elements
+[u4]0
+[anchr]nested_array_0
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data
new file mode 100644
index 0000000..df7b7e8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]basic_struct  // num_bytes
+[u4]1                // num_fields
+[s4]-1               // a
+[u4]0                // padding
+[anchr]basic_struct
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data
new file mode 100644
index 0000000..3dcca9d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data
@@ -0,0 +1,14 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[u4]0  // num_bytes: The struct size is too small.
+[u4]0  // num_fields
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data
new file mode 100644
index 0000000..f4b6c40
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]3                  // num_fields
+[u4]0                  // name
+[u4]2                  // flags: Is response.
+[u8]1                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]uint8_array  // num_bytes
+[u4]1               // num_elements
+[u1]0
+[anchr]uint8_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data
new file mode 100644
index 0000000..a357013
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]3                  // num_fields
+[u4]0                  // name
+[u4]2                  // flags: Is response.
+[u8]1                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]1                  // num_fields
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]uint8_array  // num_bytes
+[u4]2               // num_elements: The size is too small to hold 2 elements.
+[u1]0
+[anchr]uint8_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data
new file mode 100644
index 0000000..6d2cb4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data
@@ -0,0 +1,6 @@
+[dist4]message_header  // num_bytes
+[u4]3                  // num_fields
+[u4]0xffffffff         // name
+[u4]3                  // flags: This combination is illegal.
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected
new file mode 100644
index 0000000..696c78d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data
new file mode 100644
index 0000000..ffcafd3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]2                  // num_fields
+[u4]0                  // name
+[u4]0                  // flags
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // num_fields: Less than the minimal number of fields
+                       // that we expect.
+[f]-1                  // param0
+[u4]0                  // padding
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/math_calculator.mojom b/mojo/public/interfaces/bindings/tests/math_calculator.mojom
new file mode 100644
index 0000000..a18add1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/math_calculator.mojom
@@ -0,0 +1,19 @@
+// 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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.math"]
+module math {
+
+[Client=CalculatorUI]
+interface Calculator {
+  Clear@0();
+  Add@1(double value@0);
+  Multiply@2(double value@0);
+};
+
+interface CalculatorUI {
+  Output@0(double value@0);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/no_module.mojom b/mojo/public/interfaces/bindings/tests/no_module.mojom
new file mode 100644
index 0000000..f380011
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/no_module.mojom
@@ -0,0 +1,9 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Entities without module
+
+enum EnumWithoutModule {
+  A
+};
diff --git a/mojo/public/interfaces/bindings/tests/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom
new file mode 100644
index 0000000..324ce48
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/rect.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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"]
+module mojo.test {
+
+struct Rect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/regression_tests.mojom b/mojo/public/interfaces/bindings/tests/regression_tests.mojom
new file mode 100644
index 0000000..49ab69a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/regression_tests.mojom
@@ -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.
+
+// Module containing entities for regression tests of the generator. Entities
+// must never be modified, instead new entity must be added to add new tests.
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.regression_tests"]
+module regression_tests {
+
+interface CheckMethodWithEmptyResponse {
+WithouParameterAndEmptyResponse() => ();
+WithParameterAndEmptyResponse(bool b) => ();
+};
+
+interface CheckNameCollision {
+WithNameCollision(bool message, bool response) => (bool message, bool response);
+};
+
+enum EnumWithReference {
+  k_STEREO_AND_KEYBOARD_MIC = 30,
+  k_MAX = k_STEREO_AND_KEYBOARD_MIC
+};
+
+enum EnumWithLowercase {
+  PlanarF16,
+  PlanarF32
+};
+
+enum EnumWithNumbers {
+  k_2_1 = 4
+};
+
+enum EnumWithK {
+  K = 0
+};
+
+}  // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_factory.mojom b/mojo/public/interfaces/bindings/tests/sample_factory.mojom
new file mode 100644
index 0000000..567cf23
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_factory.mojom
@@ -0,0 +1,48 @@
+// 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.
+
+import "sample_import.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"]
+module sample {
+
+// This sample shows how handles to MessagePipes can be sent as both parameters
+// to methods as well as fields on structs.
+
+struct Request {
+  int32 x;
+  handle<message_pipe>? pipe;
+  array<handle<message_pipe>>? more_pipes;
+
+  // Interfaces can be used as members.
+  imported.ImportedInterface? obj;
+};
+
+struct Response {
+  int32 x;
+  handle<message_pipe>? pipe;
+};
+
+interface NamedObject {
+  SetName(string name);
+  GetName() => (string name);
+};
+
+[Client=FactoryClient]
+interface Factory {
+  DoStuff(Request request, handle<message_pipe>? pipe);
+  DoStuff2(handle<data_pipe_consumer> pipe);
+  CreateNamedObject(NamedObject& obj);
+  RequestImportedInterface(
+      imported.ImportedInterface& obj) => (imported.ImportedInterface& obj);
+  TakeImportedInterface(
+      imported.ImportedInterface obj) => (imported.ImportedInterface obj);
+};
+
+interface FactoryClient {
+  DidStuff(Response response, string text);
+  DidStuff2(string text);
+};
+
+}  // module sample
diff --git a/mojo/public/interfaces/bindings/tests/sample_import.mojom b/mojo/public/interfaces/bindings/tests/sample_import.mojom
new file mode 100644
index 0000000..659b74d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import.mojom
@@ -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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"]
+module imported {
+
+// This sample just defines some types that are imported into
+// sample_service.mojom, to show how import works.
+
+enum Shape {
+  RECTANGLE = 1,
+  CIRCLE,
+  TRIANGLE,
+  LAST = TRIANGLE,
+};
+
+// These enum values should not interfere with those of Shape above.
+enum AnotherShape {
+  RECTANGLE = 10,
+  CIRCLE,
+  TRIANGLE,
+};
+
+enum YetAnotherShape {
+  RECTANGLE = 20,
+  CIRCLE,
+  TRIANGLE,
+};
+
+struct Point {
+  int32 x;
+  int32 y;
+};
+
+interface ImportedInterface {
+  DoSomething();
+};
+
+}  // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_import2.mojom b/mojo/public/interfaces/bindings/tests/sample_import2.mojom
new file mode 100644
index 0000000..63ddb5a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import2.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 "sample_import.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"]
+module imported {
+
+// This sample adds more types and constants to the "imported" namespace,
+// to test a bug with importing multiple modules with the same namespace.
+
+enum Color {
+  RED,
+  BLACK,
+};
+
+struct Size {
+  int32 width;
+  int32 height;
+};
+
+struct Thing {
+  imported.Shape shape = RECTANGLE;
+  int32 color = Color.BLACK;
+  Point location;
+  Size size;
+};
+
+}  // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom
new file mode 100644
index 0000000..01e78ed
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_interfaces.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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample",
+ JavaConstantsClassName="InterfaceConstants",
+ Foo = "hello world"]
+module sample {
+
+const uint64 kLong = 4405;
+
+enum Enum {
+  VALUE
+};
+
+[Client=ProviderClient]
+interface Provider {
+  EchoString(string a) => (string a);
+  EchoStrings(string a, string b) => (string a, string b);
+  EchoMessagePipeHandle(handle<message_pipe> a) => (handle<message_pipe> a);
+  EchoEnum(Enum a) => (Enum a);
+};
+
+// TODO(darin): We shouldn't need this, but JS bindings don't work without it.
+interface ProviderClient {
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/sample_service.mojom b/mojo/public/interfaces/bindings/tests/sample_service.mojom
new file mode 100644
index 0000000..baf03de
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_service.mojom
@@ -0,0 +1,128 @@
+// 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.
+
+import "sample_import.mojom"
+import "sample_import2.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"]
+module sample {
+
+const uint8 kTwelve = 12;
+const uint64 kTooBigForSignedInt64 = 9999999999999999999;
+
+const double kDoubleInfinity = double.INFINITY;
+const double kDoubleNegativeInfinity = double.NEGATIVE_INFINITY;
+const double kDoubleNaN = double.NAN;
+
+const float kFloatInfinity = float.INFINITY;
+const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY;
+const float kFloatNaN = float.NAN;
+
+struct Bar {
+  enum Type {
+    VERTICAL = 1,
+    HORIZONTAL,
+    BOTH,
+    INVALID
+  };
+  uint8 alpha@0 = 0xff;
+  uint8 beta@1;
+  uint8 gamma@2;
+  Type type@3 = sample.Bar.Type.VERTICAL;
+};
+
+[RequiredFields=7]
+struct Foo {
+  const string kFooby = "Fooby";
+  string name@8 = kFooby;
+  int32 x@0;
+  int32 y@1;
+  bool a@2 = true;
+  bool b@3;
+  bool c@4;
+  Bar? bar@5;
+  array<Bar>? extra_bars@7;
+  array<uint8>? data@6;
+  handle<message_pipe>? source@9;
+  array<handle<data_pipe_consumer>>? input_streams@10;
+  array<handle<data_pipe_producer>>? output_streams@11;
+  array<array<bool>>? array_of_array_of_bools@12;
+  array<array<array<string>>>? multi_array_of_strings@13;
+  array<bool>? array_of_bools@14;
+};
+
+struct DefaultsTest {
+  int8 a0@0 = -12;
+  uint8 a1@1 = sample.kTwelve;
+  int16 a2@2 = 1234;
+  uint16 a3@3 = 34567;
+  int32 a4@4 = 123456;
+  uint32 a5@5 = 3456789012;
+  int64 a6@6 = -111111111111;
+  uint64 a7@7 = 9999999999999999999;
+  int32 a8@8 = 0x12345;
+  int32 a9@9 = -0x12345;
+  int32 a10@10 = +1234;
+  bool a11@11 = true;
+  bool a12@12 = false;
+  float a13@13 = 123.25;
+  double a14@14 = 1234567890.123;
+  double a15@15 = 1E10;
+  double a16@16 = -1.2E+20;
+  double a17@17 = +1.23E-20;
+
+  // TODO(vtl): Add tests for default vs null when those are implemented (for
+  // structs, arrays, and strings).
+  array<uint8> a18@18;
+  string a19@19;
+
+  Bar.Type a20@20 = BOTH;
+  imported.Point a21@21;
+  imported.Thing a22@22 = default;
+
+  uint64 a23@23 = 0xFFFFFFFFFFFFFFFF;
+  int64 a24@24 = 0x123456789;
+  int64 a25@25 = -0x123456789;
+
+  double a26@26 = double.INFINITY;
+  double a27@27 = double.NEGATIVE_INFINITY;
+  double a28@28 = double.NAN;
+  float a29@29 = float.INFINITY;
+  float a30@30 = float.NEGATIVE_INFINITY;
+  float a31@31 = float.NAN;
+};
+
+struct StructWithHoleV1 {
+  int32 v1 = 1;
+  int64 v2 = 2;
+};
+
+struct StructWithHoleV2 {
+  int32 v1 = 1;
+  int64 v2 = 2;
+  int32 v3 = 3;
+};
+
+[Client=ServiceClient]
+interface Service {
+  enum BazOptions {
+    REGULAR = 0,
+    EXTRA
+  };
+  const uint8 kFavoriteBaz = 1;
+  Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2);
+  GetPort@1(Port& port @0);
+};
+
+interface ServiceClient {
+  DidFrobinate@0(int32 result@0);
+};
+
+// This interface is referenced above where it is defined. It also refers to
+// itself from a method.
+interface Port {
+  PostMessage@0(string message_text@0, Port port@1);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom
new file mode 100644
index 0000000..f51c197
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"]
+module mojo.test {
+
+struct Struct1 {
+  uint8 i;
+};
+
+struct Struct2 {
+  handle hdl;
+};
+
+struct Struct3 {
+  Struct1 struct_1;
+};
+
+struct Struct4 {
+  array<Struct1> data;
+};
+
+struct Struct5 {
+  array<Struct1, 2> pair;
+};
+
+struct Struct6 {
+  string str;
+};
+
+struct StructOfNullables {
+  handle? hdl;
+  Struct1? struct_1;
+  string? str;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/test_structs.mojom b/mojo/public/interfaces/bindings/tests/test_structs.mojom
new file mode 100644
index 0000000..d361da7
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom
@@ -0,0 +1,85 @@
+// 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.
+
+import "mojo/public/interfaces/bindings/tests/rect.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"]
+module mojo.test {
+
+struct NamedRegion {
+  string? name;
+  array<Rect>? rects;
+};
+
+struct RectPair {
+  Rect? first;
+  Rect? second;
+};
+
+struct EmptyStruct {
+};
+
+// Used to verify that struct fields which don't specify a deafult are
+// initialized to: false for bool, 0 for numbers, and null for strings,
+// handles, and structs. The "?" nullable suffix shouldn't have any 
+// impact on initial field values.
+
+struct NoDefaultFieldValues {
+  bool f0;
+  int8 f1;
+  uint8 f2;
+  int16 f3;
+  uint16 f4;
+  int32 f5;
+  uint32 f6;
+  int64 f7;
+  uint64 f8;
+  float f9;
+  double f10;
+  string f11;
+  string? f12;
+  handle<message_pipe> f13;
+  handle<data_pipe_consumer> f14;
+  handle<data_pipe_producer> f15;
+  handle<message_pipe>? f16;
+  handle<data_pipe_consumer>? f17;
+  handle<data_pipe_producer>? f18;
+  handle f19;
+  handle? f20;
+  handle<shared_buffer> f21;
+  handle<shared_buffer>? f22;
+  array<string> f23;
+  array<string?> f24;
+  array<string>? f25;
+  array<string?>? f26;
+  EmptyStruct f27;
+  EmptyStruct? f28;
+};
+
+// Used to verify that struct fields with an explicit default value
+// are initialized correctly. The "?" nullable suffix shouldn't have any 
+// impact on initial field values.
+
+struct DefaultFieldValues {
+  const string kFoo = "foo";
+  bool f0 = true;
+  int8 f1 = 100;
+  uint8 f2 = 100;
+  int16 f3 = 100;
+  uint16 f4 = 100;
+  int32 f5 = 100;
+  uint32 f6 = 100;
+  int64 f7 = 100;
+  uint64 f8 = 100;
+  float f9 = 100;
+  float f10 = 100.0;
+  double f11 = 100;
+  double f12 = 100.0;
+  string f13 = kFoo;
+  string? f14 = kFoo;
+  Rect f15 = default;
+  Rect? f16 = default;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
new file mode 100644
index 0000000..5cd9d0b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
@@ -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.
+
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"]
+module mojo.test {
+
+struct StructA {
+  uint64 i;
+};
+
+struct StructB {
+  StructA struct_a;
+};
+
+struct StructC {
+  array<uint8> data;
+};
+
+struct StructD {
+  array<handle<message_pipe>> message_pipes;
+};
+
+struct StructE {
+  StructD struct_d;
+  handle<data_pipe_consumer> data_pipe_consumer;
+};
+
+struct StructF {
+  array<uint8, 3> fixed_size_array;
+};
+
+interface ConformanceTestInterface {
+  Method0(float param0);
+  Method1(StructA param0);
+  Method2(StructB param0, StructA param1);
+  Method3(array<bool> param0);
+  Method4(StructC param0, array<uint8> param1);
+  Method5(StructE param0, handle<data_pipe_producer> param1);
+  Method6(array<array<uint8>> param0);
+  Method7(StructF param0, array<uint8, 5> param1);
+  Method8(array<array<string>?> param0);
+  Method9(array<array<handle?>>? param0);
+};
+
+struct BasicStruct {
+  int32 a;
+};
+
+[Client=IntegrationTestInterface2]
+interface IntegrationTestInterface1 {
+  Method0(BasicStruct param0);
+};
+
+[Client=IntegrationTestInterface1]
+interface IntegrationTestInterface2 {
+  Method0() => (array<uint8> param0);
+};
+
+}
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
new file mode 100644
index 0000000..6f5d8e9
--- /dev/null
+++ b/mojo/public/java/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+ 
+android_library("system") {
+  java_files = [
+    "system/src/org/chromium/mojo/system/AsyncWaiter.java",
+    "system/src/org/chromium/mojo/system/Core.java",
+    "system/src/org/chromium/mojo/system/DataPipe.java",
+    "system/src/org/chromium/mojo/system/Flags.java",
+    "system/src/org/chromium/mojo/system/Handle.java",
+    "system/src/org/chromium/mojo/system/InvalidHandle.java",
+    "system/src/org/chromium/mojo/system/MessagePipeHandle.java",
+    "system/src/org/chromium/mojo/system/MojoException.java",
+    "system/src/org/chromium/mojo/system/MojoResult.java",
+    "system/src/org/chromium/mojo/system/Pair.java",
+    "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
+    "system/src/org/chromium/mojo/system/UntypedHandle.java",
+  ]
+}
+
+android_library("bindings") {
+  java_files = [
+    "bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java",
+    "bindings/src/org/chromium/mojo/bindings/BindingsHelper.java",
+    "bindings/src/org/chromium/mojo/bindings/Callbacks.java",
+    "bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java",
+    "bindings/src/org/chromium/mojo/bindings/Connector.java",
+    "bindings/src/org/chromium/mojo/bindings/Decoder.java",
+    "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java",
+    "bindings/src/org/chromium/mojo/bindings/DeserializationException.java",
+    "bindings/src/org/chromium/mojo/bindings/Encoder.java",
+    "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java",
+    "bindings/src/org/chromium/mojo/bindings/HandleOwner.java",
+    "bindings/src/org/chromium/mojo/bindings/Interface.java",
+    "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java",
+    "bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java",
+    "bindings/src/org/chromium/mojo/bindings/MessageHeader.java",
+    "bindings/src/org/chromium/mojo/bindings/Message.java",
+    "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java",
+    "bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java",
+    "bindings/src/org/chromium/mojo/bindings/RouterImpl.java",
+    "bindings/src/org/chromium/mojo/bindings/Router.java",
+    "bindings/src/org/chromium/mojo/bindings/SerializationException.java",
+    "bindings/src/org/chromium/mojo/bindings/ServiceMessage.java",
+    "bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java",
+    "bindings/src/org/chromium/mojo/bindings/Struct.java",
+  ]
+
+  deps = [ ":system" ]
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
new file mode 100644
index 0000000..8a83be9
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper around {@link Router} that will close the connection when not referenced anymore.
+ */
+class AutoCloseableRouter implements Router {
+
+    /**
+     * The underlying router.
+     */
+    private final Router mRouter;
+
+    /**
+     * The executor to close the underlying router.
+     */
+    private final Executor mExecutor;
+
+    /**
+     * Flags to keep track if this router has been correctly closed.
+     */
+    private boolean mClosed;
+
+    /**
+     * Constructor.
+     */
+    public AutoCloseableRouter(Core core, Router router) {
+        mRouter = router;
+        mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
+    }
+
+    /**
+     * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+     */
+    @Override
+    public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+        mRouter.setIncomingMessageReceiver(incomingMessageReceiver);
+    }
+
+    /**
+     * @see HandleOwner#passHandle()
+     */
+    @Override
+    public MessagePipeHandle passHandle() {
+        return mRouter.passHandle();
+    }
+
+    /**
+     * @see MessageReceiver#accept(Message)
+     */
+    @Override
+    public boolean accept(Message message) {
+        return mRouter.accept(message);
+    }
+
+    /**
+     * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+     */
+    @Override
+    public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+        return mRouter.acceptWithResponder(message, responder);
+
+    }
+
+    /**
+     * @see Router#start()
+     */
+    @Override
+    public void start() {
+        mRouter.start();
+    }
+
+    /**
+     * @see Router#setErrorHandler(ConnectionErrorHandler)
+     */
+    @Override
+    public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+        mRouter.setErrorHandler(errorHandler);
+    }
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close() {
+        mRouter.close();
+        mClosed = true;
+    }
+
+    /**
+     * @see Object#finalize()
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        if (!mClosed) {
+            mExecutor.execute(new Runnable() {
+
+                @Override
+                public void run() {
+                    close();
+                }
+            });
+            throw new IllegalStateException("Warning: Router objects should be explicitly closed " +
+                    "when no longer required otherwise you may leak handles.");
+        }
+        super.finalize();
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
new file mode 100644
index 0000000..b18e5c5
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -0,0 +1,127 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Handle;
+
+/**
+ * Helper functions.
+ */
+public class BindingsHelper {
+    /**
+     * Alignment in bytes for mojo serialization.
+     */
+    public static final int ALIGNMENT = 8;
+
+    /**
+     * The size, in bytes, of a serialized handle. A handle is serialized as an int representing the
+     * offset of the handle in the list of handles.
+     */
+    public static final int SERIALIZED_HANDLE_SIZE = 4;
+
+    /**
+     * The size, in bytes, of a serialized pointer. A pointer is serializaed as an unsigned long
+     * representing the offset from its position to the pointed elemnt.
+     */
+    public static final int POINTER_SIZE = 8;
+
+    /**
+     * The value used for the expected length of a non-fixed size array.
+     */
+    public static final int UNSPECIFIED_ARRAY_LENGTH = -1;
+
+    /**
+     * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+     */
+    public static int align(int size) {
+        return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+    }
+
+    /**
+     * Passed as |arrayNullability| when neither the array nor its elements are nullable.
+     */
+    public static final int NOTHING_NULLABLE = 0;
+
+    /**
+     * "Array bit" of |arrayNullability| is set iff the array itself is nullable.
+     */
+    public static final int ARRAY_NULLABLE = (1 << 0);
+
+    /**
+     * "Element bit" of |arrayNullability| is set iff the array elements are nullable.
+     */
+    public static final int ELEMENT_NULLABLE = (1 << 1);
+
+    public static boolean isArrayNullable(int arrayNullability) {
+        return (arrayNullability & ARRAY_NULLABLE) > 0;
+    }
+
+    public static boolean isElementNullable(int arrayNullability) {
+        return (arrayNullability & ELEMENT_NULLABLE) > 0;
+    }
+
+    /**
+     * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+     */
+    public static long align(long size) {
+        return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+    }
+
+    /**
+     * Compute the size in bytes of the given string encoded as utf8.
+     */
+    public static int utf8StringSizeInBytes(String s) {
+        int res = 0;
+        for (int i = 0; i < s.length(); ++i) {
+            char c = s.charAt(i);
+            int codepoint = c;
+            if (isSurrogate(c)) {
+                i++;
+                char c2 = s.charAt(i);
+                codepoint = Character.toCodePoint(c, c2);
+            }
+            res += 1;
+            if (codepoint > 0x7f) {
+                res += 1;
+                if (codepoint > 0x7ff) {
+                    res += 1;
+                    if (codepoint > 0xffff) {
+                        res += 1;
+                        if (codepoint > 0x1fffff) {
+                            res += 1;
+                            if (codepoint > 0x3ffffff) {
+                                res += 1;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Determines if the given {@code char} value is a Unicode <i>surrogate code unit</i>. See
+     * {@link Character#isSurrogate}. Extracting here because the method only exists at API level
+     * 19.
+     */
+    private static boolean isSurrogate(char c) {
+        return c >= Character.MIN_SURROGATE && c < (Character.MAX_SURROGATE + 1);
+    }
+
+    /**
+     * Returns an {@link AsyncWaiter} to use with the given handle, or <code>null</code> if none if
+     * available.
+     */
+    static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) {
+        if (handle.getCore() != null) {
+            return handle.getCore().getDefaultAsyncWaiter();
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
new file mode 100644
index 0000000..c6b14c1
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
@@ -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.
+
+// This file was generated using
+//     mojo/tools/generate_java_callback_interfaces.py
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Contains a generic interface for callbacks.
+ */
+public interface Callbacks {
+
+    /**
+     * A generic callback.
+     */
+    interface Callback0 {
+        /**
+         * Call the callback.
+         */
+        public void call();
+    }
+
+    /**
+     * A generic 1-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+     */
+    interface Callback1<T1> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1);
+    }
+
+    /**
+     * A generic 2-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+     */
+    interface Callback2<T1, T2> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2);
+    }
+
+    /**
+     * A generic 3-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+      * @param <T3> the type of argument 3.
+     */
+    interface Callback3<T1, T2, T3> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2, T3 arg3);
+    }
+
+    /**
+     * A generic 4-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+      * @param <T3> the type of argument 3.
+      * @param <T4> the type of argument 4.
+     */
+    interface Callback4<T1, T2, T3, T4> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+    }
+
+    /**
+     * A generic 5-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+      * @param <T3> the type of argument 3.
+      * @param <T4> the type of argument 4.
+      * @param <T5> the type of argument 5.
+     */
+    interface Callback5<T1, T2, T3, T4, T5> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+    }
+
+    /**
+     * A generic 6-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+      * @param <T3> the type of argument 3.
+      * @param <T4> the type of argument 4.
+      * @param <T5> the type of argument 5.
+      * @param <T6> the type of argument 6.
+     */
+    interface Callback6<T1, T2, T3, T4, T5, T6> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+    }
+
+    /**
+     * A generic 7-argument callback.
+     *
+     * @param <T1> the type of argument 1.
+      * @param <T2> the type of argument 2.
+      * @param <T3> the type of argument 3.
+      * @param <T4> the type of argument 4.
+      * @param <T5> the type of argument 5.
+      * @param <T6> the type of argument 6.
+      * @param <T7> the type of argument 7.
+     */
+    interface Callback7<T1, T2, T3, T4, T5, T6, T7> {
+        /**
+         * Call the callback.
+         */
+        public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
new file mode 100644
index 0000000..abb0bdf
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MojoException;
+
+/**
+ * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over
+ * message pipes.
+ */
+interface ConnectionErrorHandler {
+    public void onConnectionError(MojoException e);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
new file mode 100644
index 0000000..cbb1924
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -0,0 +1,241 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A {@link Connector} owns a {@link MessagePipeHandle} and will send any received messages to the
+ * registered {@link MessageReceiver}. It also acts as a {@link MessageReceiver} and will send any
+ * message through the handle.
+ * <p>
+ * The method |start| must be called before the {@link Connector} will start listening to incoming
+ * messages.
+ */
+public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> {
+
+    /**
+     * The callback that is notified when the state of the owned handle changes.
+     */
+    private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback();
+
+    /**
+     * The owned message pipe.
+     */
+    private final MessagePipeHandle mMessagePipeHandle;
+
+    /**
+     * A waiter which is notified when a new message is available on the owned message pipe.
+     */
+    private final AsyncWaiter mAsyncWaiter;
+
+    /**
+     * The {@link MessageReceiver} to which received messages are sent.
+     */
+    private MessageReceiver mIncomingMessageReceiver;
+
+    /**
+     * The Cancellable for the current wait. Is |null| when not currently waiting for new messages.
+     */
+    private AsyncWaiter.Cancellable mCancellable;
+
+    /**
+     * The error handler to notify of errors.
+     */
+    private ConnectionErrorHandler mErrorHandler;
+
+    /**
+     * Create a new connector over a |messagePipeHandle|. The created connector will use the default
+     * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
+     */
+    public Connector(MessagePipeHandle messagePipeHandle) {
+        this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
+    }
+
+    /**
+     * Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get
+     * notified of changes on the handle.
+     */
+    public Connector(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+        mCancellable = null;
+        mMessagePipeHandle = messagePipeHandle;
+        mAsyncWaiter = asyncWaiter;
+    }
+
+    /**
+     * Set the {@link MessageReceiver} that will receive message from the owned message pipe.
+     */
+    public void setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver) {
+        mIncomingMessageReceiver = incomingMessageReceiver;
+    }
+
+    /**
+     * Set the {@link ConnectionErrorHandler} that will be notified of errors on the owned message
+     * pipe.
+     */
+    public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+        mErrorHandler = errorHandler;
+    }
+
+    /**
+     * Start listening for incoming messages.
+     */
+    public void start() {
+        assert mCancellable == null;
+        registerAsyncWaiterForRead();
+    }
+
+    /**
+     * @see MessageReceiver#accept(Message)
+     */
+    @Override
+    public boolean accept(Message message) {
+        try {
+            mMessagePipeHandle.writeMessage(message.getData(),
+                    message.getHandles(), MessagePipeHandle.WriteFlags.NONE);
+            return true;
+        } catch (MojoException e) {
+            onError(e);
+            return false;
+        }
+    }
+
+    /**
+     * Pass the owned handle of the connector. After this, the connector is disconnected. It cannot
+     * accept new message and it isn't listening to the handle anymore.
+     *
+     * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+     */
+    @Override
+    public MessagePipeHandle passHandle() {
+        cancelIfActive();
+        MessagePipeHandle handle = mMessagePipeHandle.pass();
+        if (mIncomingMessageReceiver != null) {
+            mIncomingMessageReceiver.close();
+        }
+        return handle;
+    }
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close() {
+        cancelIfActive();
+        mMessagePipeHandle.close();
+        if (mIncomingMessageReceiver != null) {
+            mIncomingMessageReceiver.close();
+        }
+    }
+
+    private class AsyncWaiterCallback implements AsyncWaiter.Callback {
+
+        /**
+         * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
+         */
+        @Override
+        public void onResult(int result) {
+            Connector.this.onAsyncWaiterResult(result);
+        }
+
+        /**
+         * @see org.chromium.mojo.system.AsyncWaiter.Callback#onError(MojoException)
+         */
+        @Override
+        public void onError(MojoException exception) {
+            Connector.this.onError(exception);
+        }
+
+    }
+
+    /**
+     * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
+     */
+    private void onAsyncWaiterResult(int result) {
+        mCancellable = null;
+        if (result == MojoResult.OK) {
+            readOutstandingMessages();
+        } else {
+            onError(new MojoException(result));
+        }
+    }
+
+    private void onError(MojoException exception) {
+        mCancellable = null;
+        close();
+        if (mErrorHandler != null) {
+            mErrorHandler.onConnectionError(exception);
+        }
+    }
+
+    /**
+     * Register to be called back when a new message is available on the owned message pipe.
+     */
+    private void registerAsyncWaiterForRead() {
+        assert mCancellable == null;
+        if (mAsyncWaiter != null) {
+            mCancellable = mAsyncWaiter.asyncWait(mMessagePipeHandle, Core.HandleSignals.READABLE,
+                    Core.DEADLINE_INFINITE, mAsyncWaiterCallback);
+        } else {
+            onError(new MojoException(MojoResult.INVALID_ARGUMENT));
+        }
+    }
+
+    /**
+     * Read all available messages on the owned message pipe.
+     */
+    private void readOutstandingMessages() {
+        int result;
+        do {
+            try {
+                result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver);
+            } catch (MojoException e) {
+                onError(e);
+                return;
+            }
+        } while (result == MojoResult.OK);
+        if (result == MojoResult.SHOULD_WAIT) {
+            registerAsyncWaiterForRead();
+        } else {
+            onError(new MojoException(result));
+        }
+    }
+
+    private void cancelIfActive() {
+        if (mCancellable != null) {
+            mCancellable.cancel();
+            mCancellable = null;
+        }
+    }
+
+    /**
+     * Read a message, and pass it to the given |MessageReceiver| if not null. If the
+     * |MessageReceiver| is null, the message is lost.
+     *
+     * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can
+     *            be <code>null</code>, in which case the message is discarded.
+     */
+    static int readAndDispatchMessage(MessagePipeHandle handle, MessageReceiver receiver) {
+        // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance.
+        ReadMessageResult result = handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
+        if (result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
+            return result.getMojoResult();
+        }
+        ByteBuffer buffer = ByteBuffer.allocateDirect(result.getMessageSize());
+        result = handle.readMessage(buffer, result.getHandlesCount(),
+                MessagePipeHandle.ReadFlags.NONE);
+        if (receiver != null && result.getMojoResult() == MojoResult.OK) {
+            receiver.accept(new Message(buffer, result.getHandles()));
+        }
+        return result.getMojoResult();
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
new file mode 100644
index 0000000..efb9842
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -0,0 +1,630 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.bindings.Interface.Proxy;
+import org.chromium.mojo.bindings.Struct.DataHeader;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.InvalidHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
+
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+/**
+ * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic
+ * types from a {@link Message} object at a given offset into it's byte buffer.
+ */
+public class Decoder {
+
+    /**
+     * Helper class to validate the decoded message.
+     */
+    static final class Validator {
+
+        /**
+         * Minimal value for the next handle to deserialize.
+         */
+        private int mMinNextClaimedHandle = 0;
+        /**
+         * Minimal value of the start of the next memory to claim.
+         */
+        private long mMinNextMemory = 0;
+
+        /**
+         * The maximal memory accessible.
+         */
+        private final long mMaxMemory;
+
+        /**
+         * The number of handles in the message.
+         */
+        private final long mNumberOfHandles;
+
+        /**
+         * Constructor.
+         */
+        Validator(long maxMemory, int numberOfHandles) {
+            mMaxMemory = maxMemory;
+            mNumberOfHandles = numberOfHandles;
+        }
+
+        public void claimHandle(int handle) {
+            if (handle < mMinNextClaimedHandle) {
+                throw new DeserializationException(
+                        "Trying to access handle out of order.");
+            }
+            if (handle >= mNumberOfHandles) {
+                throw new DeserializationException("Trying to access non present handle.");
+            }
+            mMinNextClaimedHandle = handle + 1;
+        }
+
+        public void claimMemory(long start, long end) {
+            if (start % BindingsHelper.ALIGNMENT != 0) {
+                throw new DeserializationException("Incorrect starting alignment: " + start + ".");
+            }
+            if (start < mMinNextMemory) {
+                throw new DeserializationException("Trying to access memory out of order.");
+            }
+            if (end < start) {
+                throw new DeserializationException("Incorrect memory range.");
+            }
+            if (end > mMaxMemory) {
+                throw new DeserializationException("Trying to access out of range memory.");
+            }
+            mMinNextMemory = BindingsHelper.align(end);
+        }
+    }
+
+    /**
+     * The message to deserialize from.
+     */
+    private final Message mMessage;
+
+    /**
+     * The base offset in the byte buffer.
+     */
+    private final int mBaseOffset;
+
+    /**
+     * Validator for the decoded message.
+     */
+    private final Validator mValidator;
+
+    /**
+     * Constructor.
+     *
+     * @param message The message to decode.
+     */
+    public Decoder(Message message) {
+        this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0);
+    }
+
+    private Decoder(Message message, Validator validator, int baseOffset) {
+        mMessage = message;
+        mMessage.getData().order(ByteOrder.nativeOrder());
+        mBaseOffset = baseOffset;
+        mValidator = validator;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset.
+     */
+    public DataHeader readDataHeader() {
+        // Claim the memory for the header.
+        mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
+        int size = readInt(DataHeader.SIZE_OFFSET);
+        int numFields = readInt(DataHeader.NUM_FIELDS_OFFSET);
+        if (size < 0) {
+            throw new DeserializationException(
+                    "Negative size. Unsigned integers are not valid for java.");
+        }
+        if (numFields < 0) {
+            throw new DeserializationException(
+                    "Negative number of fields. Unsigned integers are not valid for java.");
+        }
+
+        // Claim the remaining memory.
+        mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + size);
+        DataHeader res = new DataHeader(size, numFields);
+        return res;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array where element have the given size.
+     */
+    public DataHeader readDataHeaderForPointerArray(int expectedLength) {
+        return readDataHeaderForArray(8, expectedLength);
+    }
+
+    /**
+     * Deserializes a byte at the given offset.
+     */
+    public byte readByte(int offset) {
+        validateBufferSize(offset, 1);
+        return mMessage.getData().get(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes a boolean at the given offset, re-using any partially read byte.
+     */
+    public boolean readBoolean(int offset, int bit) {
+        validateBufferSize(offset, 1);
+        return (readByte(offset) & (1 << bit)) != 0;
+    }
+
+    /**
+     * Deserializes a short at the given offset.
+     */
+    public short readShort(int offset) {
+        validateBufferSize(offset, 2);
+        return mMessage.getData().getShort(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes an int at the given offset.
+     */
+    public int readInt(int offset) {
+        validateBufferSize(offset, 4);
+        return mMessage.getData().getInt(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes a float at the given offset.
+     */
+    public float readFloat(int offset) {
+        validateBufferSize(offset, 4);
+        return mMessage.getData().getFloat(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes a long at the given offset.
+     */
+    public long readLong(int offset) {
+        validateBufferSize(offset, 8);
+        return mMessage.getData().getLong(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes a double at the given offset.
+     */
+    public double readDouble(int offset) {
+        validateBufferSize(offset, 8);
+        return mMessage.getData().getDouble(mBaseOffset + offset);
+    }
+
+    /**
+     * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
+     * of the pointer.
+     */
+    public Decoder readPointer(int offset, boolean nullable) {
+        int basePosition = mBaseOffset + offset;
+        long pointerOffset = readLong(offset);
+        if (pointerOffset == 0) {
+            if (!nullable) {
+                throw new DeserializationException(
+                        "Trying to decode null pointer for a non-nullable type.");
+            }
+            return null;
+        }
+        int newPosition = (int) (basePosition + pointerOffset);
+        // The method |getDecoderAtPosition| will validate that the pointer address is valid.
+        return getDecoderAtPosition(newPosition);
+
+    }
+
+    /**
+     * Deserializes an array of boolean at the given offset.
+     */
+    public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForBooleanArray(expectedLength);
+        byte[] bytes = new byte[(si.numFields + 7) / BindingsHelper.ALIGNMENT];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().get(bytes);
+        boolean[] result = new boolean[si.numFields];
+        for (int i = 0; i < bytes.length; ++i) {
+            for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+                int booleanIndex = i * BindingsHelper.ALIGNMENT + j;
+                if (booleanIndex < result.length) {
+                    result[booleanIndex] = (bytes[i] & (1 << j)) != 0;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of bytes at the given offset.
+     */
+    public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(1, expectedLength);
+        byte[] result = new byte[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an array of shorts at the given offset.
+     */
+    public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(2, expectedLength);
+        short[] result = new short[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asShortBuffer().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an array of ints at the given offset.
+     */
+    public int[] readInts(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        int[] result = new int[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asIntBuffer().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an array of floats at the given offset.
+     */
+    public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        float[] result = new float[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asFloatBuffer().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an array of longs at the given offset.
+     */
+    public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+        long[] result = new long[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asLongBuffer().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an array of doubles at the given offset.
+     */
+    public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+        double[] result = new double[si.numFields];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asDoubleBuffer().get(result);
+        return result;
+    }
+
+    /**
+     * Deserializes an |Handle| at the given offset.
+     */
+    public Handle readHandle(int offset, boolean nullable) {
+        int index = readInt(offset);
+        if (index == -1) {
+            if (!nullable) {
+                throw new DeserializationException(
+                        "Trying to decode an invalid handle for a non-nullable type.");
+            }
+            return InvalidHandle.INSTANCE;
+        }
+        mValidator.claimHandle(index);
+        return mMessage.getHandles().get(index);
+    }
+
+    /**
+     * Deserializes an |UntypedHandle| at the given offset.
+     */
+    public UntypedHandle readUntypedHandle(int offset, boolean nullable) {
+        return readHandle(offset, nullable).toUntypedHandle();
+    }
+
+    /**
+     * Deserializes a |ConsumerHandle| at the given offset.
+     */
+    public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle();
+    }
+
+    /**
+     * Deserializes a |ProducerHandle| at the given offset.
+     */
+    public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toDataPipeProducerHandle();
+    }
+
+    /**
+     * Deserializes a |MessagePipeHandle| at the given offset.
+     */
+    public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toMessagePipeHandle();
+    }
+
+    /**
+     * Deserializes a |SharedBufferHandle| at the given offset.
+     */
+    public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toSharedBufferHandle();
+    }
+
+    /**
+     * Deserializes an interface at the given offset.
+     *
+     * @return a proxy to the service.
+     */
+    public <P extends Proxy> P readServiceInterface(int offset, boolean nullable,
+            Interface.Manager<?, P> manager) {
+        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+        if (!handle.isValid()) {
+            return null;
+        }
+        return manager.attachProxy(handle);
+    }
+
+    /**
+     * Deserializes a |InterfaceRequest| at the given offset.
+     */
+    public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset,
+            boolean nullable) {
+        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+        if (handle == null) {
+            return null;
+        }
+        return new InterfaceRequest<I>(handle);
+    }
+
+    /**
+     * Deserializes a string at the given offset.
+     */
+    public String readString(int offset, boolean nullable) {
+        final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
+        byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+        if (bytes == null) {
+            return null;
+        }
+        return new String(bytes, Charset.forName("utf8"));
+    }
+
+    /**
+     * Deserializes an array of |Handle| at the given offset.
+     */
+    public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        Handle[] result = new Handle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of |UntypedHandle| at the given offset.
+     */
+    public UntypedHandle[] readUntypedHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        UntypedHandle[] result = new UntypedHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readUntypedHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of |ConsumerHandle| at the given offset.
+     */
+    public DataPipe.ConsumerHandle[] readConsumerHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readConsumerHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of |ProducerHandle| at the given offset.
+     */
+    public DataPipe.ProducerHandle[] readProducerHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readProducerHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+
+    }
+
+    /**
+     * Deserializes an array of |MessagePipeHandle| at the given offset.
+     */
+    public MessagePipeHandle[] readMessagePipeHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        MessagePipeHandle[] result = new MessagePipeHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readMessagePipeHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+
+    }
+
+    /**
+     * Deserializes an array of |SharedBufferHandle| at the given offset.
+     */
+    public SharedBufferHandle[] readSharedBufferHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        SharedBufferHandle[] result = new SharedBufferHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readSharedBufferHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+
+    }
+
+    /**
+     * Deserializes an array of |ServiceHandle| at the given offset.
+     */
+    public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
+            int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        S[] result = manager.buildArray(si.numFields);
+        for (int i = 0; i < result.length; ++i) {
+            // This cast is necessary because java 6 doesn't handle wildcard correctly when using
+            // Manager<S, ? extends S>
+            @SuppressWarnings("unchecked")
+            S value = (S) d.readServiceInterface(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability), manager);
+            result[i] = value;
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of |InterfaceRequest| at the given offset.
+     */
+    public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        @SuppressWarnings("unchecked")
+        InterfaceRequest<I>[] result = new InterfaceRequest[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readInterfaceRequest(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+    }
+
+    /**
+     * Returns a view of this decoder at the offset |offset|.
+     */
+    private Decoder getDecoderAtPosition(int offset) {
+        return new Decoder(mMessage, mValidator, offset);
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array of booleans.
+     */
+    private DataHeader readDataHeaderForBooleanArray(int expectedLength) {
+        DataHeader dataHeader = readDataHeader();
+        if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.numFields + 7) / 8) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.numFields != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " +
+                    expectedLength + ", but got: " + dataHeader.numFields + ".");
+        }
+        return dataHeader;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} of an array at the given offset.
+     */
+    private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) {
+        DataHeader dataHeader = readDataHeader();
+        if (dataHeader.size < (DataHeader.HEADER_SIZE + elementSize * dataHeader.numFields)) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.numFields != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " +
+                    expectedLength + ", but got: " + dataHeader.numFields + ".");
+        }
+        return dataHeader;
+    }
+
+    private void validateBufferSize(int offset, int size) {
+        if (mMessage.getData().limit() < offset + size) {
+            throw new DeserializationException("Buffer is smaller than expected.");
+        }
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
new file mode 100644
index 0000000..2ee2ae7
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MojoException;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * A {@link ConnectionErrorHandler} that delegate the errors to a list of registered handlers. This
+ * class will use weak pointers to prevent keeping references to any handlers it delegates to.
+ */
+public class DelegatingConnectionErrorHandler implements ConnectionErrorHandler {
+
+    /**
+     * The registered handlers. This uses a {@link WeakHashMap} so that it doesn't prevent the
+     * handler from being garbage collected.
+     */
+    private final Set<ConnectionErrorHandler> mHandlers =
+            Collections.newSetFromMap(new WeakHashMap<ConnectionErrorHandler, Boolean>());
+
+    /**
+     * @see ConnectionErrorHandler#onConnectionError(MojoException)
+     */
+    @Override
+    public void onConnectionError(MojoException e) {
+        for (ConnectionErrorHandler handler : mHandlers) {
+            handler.onConnectionError(e);
+        }
+    }
+
+    /**
+     * Add a handler that will be notified of any error this object receives.
+     */
+    public void addConnectionErrorHandler(ConnectionErrorHandler handler) {
+        mHandlers.add(handler);
+    }
+
+    /**
+     * Remove a previously registered handler.
+     */
+    public void removeConnectionErrorHandler(ConnectionErrorHandler handler) {
+        mHandlers.remove(handler);
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
new file mode 100644
index 0000000..eeb511c
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Error when deserializing a mojo message.
+ */
+public class DeserializationException extends RuntimeException {
+
+    /**
+     * Constructs a new deserialization exception with the specified detail message.
+     */
+    public DeserializationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new deserialization exception with the specified cause.
+     */
+    public DeserializationException(Exception cause) {
+        super(cause);
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
new file mode 100644
index 0000000..e3b2bfe
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
@@ -0,0 +1,518 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.bindings.Struct.DataHeader;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed.
+ * It also keeps track of the associated handles, and the offset of the current data section.
+ */
+public class Encoder {
+
+    /**
+     * Container class for all state that must be shared between the main encoder and any used sub
+     * encoder.
+     */
+    private static class EncoderState {
+
+        /**
+         * The core used to encode interfaces.
+         */
+        public final Core core;
+
+        /**
+         * The ByteBuffer to which the message will be encoded.
+         */
+        public ByteBuffer byteBuffer;
+
+        /**
+         * The list of encountered handles.
+         */
+        public final List<Handle> handles = new ArrayList<Handle>();
+
+        /**
+         * The current absolute position for the next data section.
+         */
+        public int dataEnd;
+
+        /**
+         * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+         *            being encoded contains interfaces, can be |null| otherwise.
+         * @param bufferSize A hint on the size of the message. Used to build the initial byte
+         *            buffer.
+         */
+        private EncoderState(Core core, int bufferSize) {
+            assert bufferSize % BindingsHelper.ALIGNMENT == 0;
+            this.core = core;
+            byteBuffer = ByteBuffer.allocateDirect(
+                    bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE);
+            byteBuffer.order(ByteOrder.nativeOrder());
+            dataEnd = 0;
+        }
+
+        /**
+         * Claim the given amount of memory at the end of the buffer, resizing it if needed.
+         */
+        public void claimMemory(int size) {
+            dataEnd += size;
+            growIfNeeded();
+        }
+
+        /**
+         * Grow the associated ByteBuffer if needed.
+         */
+        private void growIfNeeded() {
+            if (byteBuffer.capacity() >= dataEnd) {
+                return;
+            }
+            int targetSize = byteBuffer.capacity() * 2;
+            while (targetSize < dataEnd) {
+                targetSize *= 2;
+            }
+            ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize);
+            newBuffer.order(ByteOrder.nativeOrder());
+            byteBuffer.position(0);
+            byteBuffer.limit(byteBuffer.capacity());
+            newBuffer.put(byteBuffer);
+            byteBuffer = newBuffer;
+        }
+    }
+
+    /**
+     * Default initial size of the data buffer. This must be a multiple of 8 bytes.
+     */
+    private static final int INITIAL_BUFFER_SIZE = 1024;
+
+    /**
+     * Base offset in the byte buffer for writing.
+     */
+    private int mBaseOffset;
+
+    /**
+     * The encoder state shared by the main encoder and all its sub-encoder.
+     */
+    private final EncoderState mEncoderState;
+
+    /**
+     * Returns the result message.
+     */
+    public Message getMessage() {
+        mEncoderState.byteBuffer.position(0);
+        mEncoderState.byteBuffer.limit(mEncoderState.dataEnd);
+        return new Message(mEncoderState.byteBuffer, mEncoderState.handles);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+     *            being encoded contains interfaces, can be |null| otherwise.
+     * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer.
+     */
+    public Encoder(Core core, int sizeHint) {
+        this(new EncoderState(core, sizeHint));
+    }
+
+    /**
+     * Private constructor for sub-encoders.
+     */
+    private Encoder(EncoderState bufferInformation) {
+        mEncoderState = bufferInformation;
+        mBaseOffset = bufferInformation.dataEnd;
+    }
+
+    /**
+     * Returns a new encoder that will append to the current buffer.
+     */
+    public Encoder getEncoderAtDataOffset(DataHeader dataHeader) {
+        Encoder result = new Encoder(mEncoderState);
+        result.encode(dataHeader);
+        return result;
+    }
+
+    /**
+     * Encode a {@link DataHeader} and claim the amount of memory required for the data section
+     * (resizing the buffer if required).
+     */
+    public void encode(DataHeader s) {
+        mEncoderState.claimMemory(BindingsHelper.align(s.size));
+        encode(s.size, DataHeader.SIZE_OFFSET);
+        encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET);
+    }
+
+    /**
+     * Encode a byte at the given offset.
+     */
+    public void encode(byte v, int offset) {
+        mEncoderState.byteBuffer.put(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode a boolean at the given offset.
+     */
+    public void encode(boolean v, int offset, int bit) {
+        if (v) {
+            byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset);
+            encodedValue |= 1 << bit;
+            mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue);
+        }
+    }
+
+    /**
+     * Encode a short at the given offset.
+     */
+    public void encode(short v, int offset) {
+        mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode an int at the given offset.
+     */
+    public void encode(int v, int offset) {
+        mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode a float at the given offset.
+     */
+    public void encode(float v, int offset) {
+        mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode a long at the given offset.
+     */
+    public void encode(long v, int offset) {
+        mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode a double at the given offset.
+     */
+    public void encode(double v, int offset) {
+        mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v);
+    }
+
+    /**
+     * Encode a {@link Struct} at the given offset.
+     */
+    public void encode(Struct v, int offset, boolean nullable) {
+        if (v == null) {
+            encodeNullPointer(offset, nullable);
+            return;
+        }
+        encodePointerToNextUnclaimedData(offset);
+        v.encode(this);
+    }
+
+    /**
+     * Encodes a String.
+     */
+    public void encode(String v, int offset, boolean nullable) {
+        if (v == null) {
+            encodeNullPointer(offset, nullable);
+            return;
+        }
+        final int arrayNullability = nullable ?
+                BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE;
+        encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability,
+                BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+    }
+
+    /**
+     * Encodes a {@link Handle}.
+     */
+    public void encode(Handle v, int offset, boolean nullable) {
+        if (v == null || !v.isValid()) {
+            encodeInvalidHandle(offset, nullable);
+        } else {
+            encode(mEncoderState.handles.size(), offset);
+            mEncoderState.handles.add(v);
+        }
+    }
+
+    /**
+     * Encode an {@link Interface}.
+     */
+    public <T extends Interface> void encode(T v, int offset, boolean nullable,
+            Interface.Manager<T, ?> manager) {
+        if (v == null) {
+            encodeInvalidHandle(offset, nullable);
+            return;
+        }
+        if (mEncoderState.core == null) {
+            throw new UnsupportedOperationException(
+                    "The encoder has been created without a Core. It can't encode an interface.");
+        }
+        // If the instance is a proxy, pass the proxy's handle instead of creating a new stub.
+        if (v instanceof Interface.AbstractProxy) {
+            Interface.AbstractProxy proxy = (Interface.AbstractProxy) v;
+            if (proxy.getMessageReceiver() instanceof HandleOwner) {
+                encode(((HandleOwner<?>) proxy.getMessageReceiver()).passHandle(), offset,
+                        nullable);
+                return;
+            }
+            // If the proxy is not over a message pipe, the default case applies.
+        }
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                mEncoderState.core.createMessagePipe(null);
+        manager.bind(v, handles.first);
+        encode(handles.second, offset, nullable);
+    }
+
+    /**
+     * Encode an {@link InterfaceRequest}.
+     */
+    public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) {
+        if (v == null) {
+            encodeInvalidHandle(offset, nullable);
+            return;
+        }
+        if (mEncoderState.core == null) {
+            throw new UnsupportedOperationException(
+                    "The encoder has been created without a Core. It can't encode an interface.");
+        }
+        encode(v.passHandle(), offset, nullable);
+    }
+
+    /**
+     * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length.
+     */
+    public Encoder encodePointerArray(int length, int offset, int expectedLength) {
+        return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength);
+    }
+
+    /**
+     * Encodes an array of booleans.
+     */
+    public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+                expectedLength != v.length) {
+            throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+        }
+        byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT];
+        for (int i = 0; i < bytes.length; ++i) {
+            for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+                int booleanIndex = BindingsHelper.ALIGNMENT * i + j;
+                if (booleanIndex < v.length && v[booleanIndex]) {
+                    bytes[i] |= (1 << j);
+                }
+            }
+        }
+        encodeByteArray(bytes, v.length, offset);
+    }
+
+    /**
+     * Encodes an array of bytes.
+     */
+    public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+                expectedLength != v.length) {
+            throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+        }
+        encodeByteArray(v, v.length, offset);
+    }
+
+    /**
+     * Encodes an array of shorts.
+     */
+    public void encode(short[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        encoderForArray(2, v.length, offset, expectedLength).append(v);
+    }
+
+    /**
+     * Encodes an array of ints.
+     */
+    public void encode(int[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        encoderForArray(4, v.length, offset, expectedLength).append(v);
+    }
+
+    /**
+     * Encodes an array of floats.
+     */
+    public void encode(float[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        encoderForArray(4, v.length, offset, expectedLength).append(v);
+    }
+
+    /**
+     * Encodes an array of longs.
+     */
+    public void encode(long[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        encoderForArray(8, v.length, offset, expectedLength).append(v);
+    }
+
+    /**
+     * Encodes an array of doubles.
+     */
+    public void encode(double[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        encoderForArray(8, v.length, offset, expectedLength).append(v);
+    }
+
+    /**
+     * Encodes an array of {@link Handle}.
+     */
+    public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        Encoder e = encoderForArray(
+                BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+        for (int i = 0; i < v.length; ++i) {
+            e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+    }
+
+    /**
+     * Encodes an array of {@link Interface}.
+     */
+    public <T extends Interface> void encode(T[] v, int offset, int arrayNullability,
+            int expectedLength, Interface.Manager<T, ?> manager) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        Encoder e = encoderForArray(
+                BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+        for (int i = 0; i < v.length; ++i) {
+            e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability), manager);
+        }
+    }
+
+    /**
+     * Encodes an array of {@link InterfaceRequest}.
+     */
+    public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset,
+            int arrayNullability, int expectedLength) {
+        if (v == null) {
+            encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+            return;
+        }
+        Encoder e = encoderForArray(
+                BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+        for (int i = 0; i < v.length; ++i) {
+            e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+    }
+
+    /**
+     * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception
+     * otherwise.
+     */
+    public void encodeNullPointer(int offset, boolean nullable) {
+        if (!nullable) {
+            throw new SerializationException(
+                    "Trying to encode a null pointer for a non-nullable type.");
+        }
+        mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0);
+    }
+
+    /**
+     * Encodes an invalid handle iff the object is nullable, raises an exception otherwise.
+     */
+    public void encodeInvalidHandle(int offset, boolean nullable) {
+        if (!nullable) {
+            throw new SerializationException(
+                    "Trying to encode an invalid handle for a non-nullable type.");
+        }
+        mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1);
+    }
+
+    private void encodePointerToNextUnclaimedData(int offset) {
+        encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
+    }
+
+    private Encoder encoderForArray(
+            int elementSizeInByte, int length, int offset, int expectedLength) {
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+                expectedLength != length) {
+            throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+        }
+        return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset);
+    }
+
+    private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) {
+        encodePointerToNextUnclaimedData(offset);
+        return getEncoderAtDataOffset(
+                new DataHeader(DataHeader.HEADER_SIZE + byteSize, length));
+    }
+
+    private void encodeByteArray(byte[] bytes, int length, int offset) {
+        encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
+    }
+
+    private void append(byte[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.put(v);
+    }
+
+    private void append(short[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.asShortBuffer().put(v);
+    }
+
+    private void append(int[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.asIntBuffer().put(v);
+    }
+
+    private void append(float[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.asFloatBuffer().put(v);
+    }
+
+    private void append(double[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.asDoubleBuffer().put(v);
+    }
+
+    private void append(long[] v) {
+        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+        mEncoderState.byteBuffer.asLongBuffer().put(v);
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
new file mode 100644
index 0000000..d0d5c3b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -0,0 +1,184 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A factory which provides per-thread executors, which enable execution on the thread from which
+ * they were obtained.
+ */
+class ExecutorFactory {
+
+    /**
+     * A null buffer which is used to send messages without any data on the PipedExecutor's
+     * signaling handles.
+     */
+    private static final ByteBuffer NOTIFY_BUFFER = null;
+
+    /**
+     * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling.
+     * The executor will wait asynchronously on one end of a {@link MessagePipeHandle} on the thread
+     * on which it was created. Other threads can call execute with a {@link Runnable}, and the
+     * executor will queue the {@link Runnable} and write a message on the other end of the handle.
+     * This will wake up the executor which is waiting on the handle, which will then dequeue the
+     * {@link Runnable} and execute it on the original thread.
+     */
+    private static class PipedExecutor implements Executor, Callback {
+
+        /**
+         * The handle which is written to. Access to this object must be protected with |mLock|.
+         */
+        private final MessagePipeHandle mWriteHandle;
+        /**
+         * The handle which is read from.
+         */
+        private final MessagePipeHandle mReadHandle;
+        /**
+         * The list of actions left to be run. Access to this object must be protected with |mLock|.
+         */
+        private final List<Runnable> mPendingActions;
+        /**
+         * Lock protecting access to |mWriteHandle| and |mPendingActions|.
+         */
+        private final Object mLock;
+        /**
+         * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|.
+         */
+        private final AsyncWaiter mWaiter;
+
+        /**
+         * Constructor.
+         */
+        public PipedExecutor(Core core) {
+            mWaiter = core.getDefaultAsyncWaiter();
+            assert mWaiter != null;
+            mLock = new Object();
+            Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
+                    new MessagePipeHandle.CreateOptions());
+            mReadHandle = handles.first;
+            mWriteHandle = handles.second;
+            mPendingActions = new ArrayList<Runnable>();
+            asyncWait();
+        }
+
+        /**
+         * Asynchronously wait for the next command to arrive. This should only be called on the
+         * executor thread.
+         */
+        private void asyncWait() {
+            mWaiter.asyncWait(mReadHandle, Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE,
+                    this);
+        }
+
+        /**
+         * @see Callback#onResult(int)
+         */
+        @Override
+        public void onResult(int result) {
+            if (result == MojoResult.OK && readNotifyBufferMessage()) {
+                runNextAction();
+            } else {
+                close();
+            }
+        }
+
+        /**
+         * @see Callback#onError(MojoException)
+         */
+        @Override
+        public void onError(MojoException exception) {
+            close();
+        }
+
+        /**
+         * Close the handles. Should only be called on the executor thread.
+         */
+        private void close() {
+            synchronized (mLock) {
+                mWriteHandle.close();
+                mPendingActions.clear();
+            }
+            mReadHandle.close();
+        }
+
+        /**
+         * Read the next message on |mReadHandle|, and return |true| if successful, |false|
+         * otherwise.
+         */
+        private boolean readNotifyBufferMessage() {
+            try {
+                ReadMessageResult readMessageResult = mReadHandle.readMessage(NOTIFY_BUFFER, 0,
+                        MessagePipeHandle.ReadFlags.NONE);
+                if (readMessageResult.getMojoResult() == MojoResult.OK) {
+                    asyncWait();
+                    return true;
+                }
+            } catch (MojoException e) {
+                // Will be closed by the fall back at the end of this method.
+            }
+            return false;
+        }
+
+        /**
+         * Run the next action in the |mPendingActions| queue.
+         */
+        private void runNextAction() {
+            Runnable toRun = null;
+            synchronized (mWriteHandle) {
+                toRun = mPendingActions.remove(0);
+            }
+            toRun.run();
+        }
+
+        /**
+         * Execute the given |command| in the executor thread. This can be called on any thread.
+         *
+         * @see Executor#execute(Runnable)
+         */
+        @Override
+        public void execute(Runnable command) {
+            // Accessing the write handle must be protected by the lock, because it can be closed
+            // from the executor's thread.
+            synchronized (mLock) {
+                if (!mWriteHandle.isValid()) {
+                    throw new IllegalStateException(
+                            "Trying to execute an action on a closed executor.");
+                }
+                mPendingActions.add(command);
+                mWriteHandle.writeMessage(NOTIFY_BUFFER, null, MessagePipeHandle.WriteFlags.NONE);
+            }
+        }
+    }
+
+    /**
+     * Keep one executor per executor thread.
+     */
+    private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>();
+
+    /**
+     * Returns an {@link Executor} that will run all of its actions in the current thread.
+     */
+    public static Executor getExecutorForCurrentThread(Core core) {
+        Executor executor = EXECUTORS.get();
+        if (executor == null) {
+            executor = new PipedExecutor(core);
+            EXECUTORS.set(executor);
+        }
+        return executor;
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
new file mode 100644
index 0000000..60fc33b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Handle;
+
+import java.io.Closeable;
+
+/**
+ * Describes a class that owns a handle.
+ *
+ * @param <H> The type of the owned handle.
+ */
+public interface HandleOwner<H extends Handle> extends Closeable {
+
+    /**
+     * Pass the handle owned by this class.
+     */
+    public H passHandle();
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close();
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
new file mode 100644
index 0000000..c3b26a2
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -0,0 +1,257 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.Pair;
+
+import java.io.Closeable;
+
+/**
+ * Base class for mojo generated interfaces.
+ */
+public interface Interface extends ConnectionErrorHandler, Closeable {
+
+    /**
+     * The close method is called when the connection to the interface is closed.
+     *
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close();
+
+    /**
+     * A proxy to a mojo interface. This is base class for all generated proxies. It implements the
+     * Interface and each time a method is called, the parameters are serialized and sent to the
+     * {@link MessageReceiverWithResponder}, along with the response callback if needed.
+     */
+    public interface Proxy extends Interface {
+
+        /**
+         * Set the {@link ConnectionErrorHandler} that will be notified of errors.
+         */
+        public void setErrorHandler(ConnectionErrorHandler errorHandler);
+
+    }
+
+    /**
+     * Base implementation of {@link Proxy}.
+     */
+    abstract class AbstractProxy implements Proxy {
+
+        /**
+         * The {@link Core} implementation to use.
+         */
+        private final Core mCore;
+
+        /**
+         * The {@link MessageReceiverWithResponder} that will receive a serialized message for each
+         * method call.
+         */
+        private final MessageReceiverWithResponder mMessageReceiver;
+
+        /**
+         * The {@link ConnectionErrorHandler} that will be notified of errors.
+         */
+        private ConnectionErrorHandler mErrorHandler = null;
+
+        /**
+         * Constructor.
+         *
+         * @param core the Core implementation used to create pipes and access the async waiter.
+         * @param messageReceiver the message receiver to send message to.
+         */
+        protected AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
+            this.mCore = core;
+            this.mMessageReceiver = messageReceiver;
+        }
+
+        /**
+         * Returns the message receiver to send message to.
+         */
+        protected MessageReceiverWithResponder getMessageReceiver() {
+            return mMessageReceiver;
+        }
+
+        /**
+         * Returns the Core implementation.
+         */
+        protected Core getCore() {
+            return mCore;
+        }
+
+        /**
+         * @see Proxy#setErrorHandler(ConnectionErrorHandler)
+         */
+        @Override
+        public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+            this.mErrorHandler = errorHandler;
+        }
+
+        /**
+         * @see ConnectionErrorHandler#onConnectionError(MojoException)
+         */
+        @Override
+        public void onConnectionError(MojoException e) {
+            if (mErrorHandler != null) {
+                mErrorHandler.onConnectionError(e);
+            }
+        }
+
+        /**
+         * @see Closeable#close()
+         */
+        @Override
+        public void close() {
+            mMessageReceiver.close();
+        }
+    }
+
+    /**
+     * Base implementation of Stub. Stubs are message receivers that deserialize the payload and
+     * call the appropriate method in the implementation. If the method returns result, the stub
+     * serializes the response and sends it back.
+     *
+     * @param <I> the type of the interface to delegate calls to.
+     */
+    abstract class Stub<I extends Interface> implements MessageReceiverWithResponder {
+
+        /**
+         * The {@link Core} implementation to use.
+         */
+        private final Core mCore;
+
+        /**
+         * The implementation to delegate calls to.
+         */
+        private final I mImpl;
+
+        /**
+         * Constructor.
+         *
+         * @param core the {@link Core} implementation to use.
+         * @param impl the implementation to delegate calls to.
+         */
+        public Stub(Core core, I impl) {
+            mCore = core;
+            mImpl = impl;
+        }
+
+        /**
+         * Returns the Core implementation.
+         */
+        protected Core getCore() {
+            return mCore;
+        }
+
+        /**
+         * Returns the implementation to delegate calls to.
+         */
+        protected I getImpl() {
+            return mImpl;
+        }
+
+        /**
+         * @see org.chromium.mojo.bindings.MessageReceiver#close()
+         */
+        @Override
+        public void close() {
+            mImpl.close();
+        }
+
+    }
+
+    /**
+     * The |Manager| object enables building of proxies and stubs for a given interface.
+     *
+     * @param <I> the type of the interface the manager can handle.
+     * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I.
+     */
+    abstract class Manager<I extends Interface, P extends Proxy> {
+
+        /**
+         * Returns the name of the interface. This is an opaque (but human readable) identifier used
+         * by the service provider to identify services.
+         */
+        public abstract String getName();
+
+        /**
+         * Binds the given implementation to the handle.
+         */
+        public void bind(I impl, MessagePipeHandle handle) {
+            // The router (and by consequence the handle) is intentionally leaked. It will close
+            // itself when the connected handle is closed and the proxy receives the connection
+            // error.
+            Router router = new RouterImpl(handle);
+            bind(handle.getCore(), impl, router);
+            router.start();
+        }
+
+        /**
+         * Binds the given implementation to the InterfaceRequest.
+         */
+        public final void bind(I impl, InterfaceRequest<I> request) {
+            bind(impl, request.passHandle());
+        }
+
+        /**
+         * Returns a Proxy that will send messages to the given |handle|. This implies that the
+         * other end of the handle must be binded to an implementation of the interface.
+         */
+        public final P attachProxy(MessagePipeHandle handle) {
+            RouterImpl router = new RouterImpl(handle);
+            P proxy = attachProxy(handle.getCore(), router);
+            DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
+            handlers.addConnectionErrorHandler(proxy);
+            router.setErrorHandler(handlers);
+            router.start();
+            return proxy;
+        }
+
+        /**
+         * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where
+         * the first element is a proxy, and the second element is the request. The proxy can be
+         * used immediately.
+         */
+        public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core) {
+            Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+            P proxy = attachProxy(handles.first);
+            return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
+        }
+
+        /**
+         * Binds the implementation to the given |router|.
+         */
+        final void bind(Core core, I impl, Router router) {
+            router.setErrorHandler(impl);
+            router.setIncomingMessageReceiver(buildStub(core, impl));
+        }
+
+        /**
+         * Returns a Proxy that will send messages to the given |router|.
+         */
+        final P attachProxy(Core core, Router router) {
+            return buildProxy(core, new AutoCloseableRouter(core, router));
+        }
+
+        /**
+         * Creates a new array of the given |size|.
+         */
+        protected abstract I[] buildArray(int size);
+
+        /**
+         * Constructs a Stub delegating to the given implementation.
+         */
+        protected abstract Stub<I> buildStub(Core core, I impl);
+
+        /**
+         * Constructs a Proxy forwarding the calls to the given message receiver.
+         */
+        protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver);
+
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
new file mode 100644
index 0000000..87835e0
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MessagePipeHandle;
+
+/**
+ * One end of the message pipe representing a request to create an implementation to be bound to it.
+ * The other end of the pipe is bound to a proxy, which can be used immediately, while the
+ * InterfaceRequest is being sent.
+ * <p>
+ * InterfaceRequest are built using |Interface.Manager|.
+ *
+ * @param <P> the type of the remote interface proxy.
+ */
+public class InterfaceRequest<P extends Interface> implements HandleOwner<MessagePipeHandle> {
+
+    /**
+     * The handle which will be sent and will be connected to the implementation.
+     */
+    private final MessagePipeHandle mHandle;
+
+    /**
+     * Constructor.
+     *
+     * @param handle the handle which will be sent and will be connected to the implementation.
+     */
+    InterfaceRequest(MessagePipeHandle handle) {
+        mHandle = handle;
+    }
+
+    /**
+     * @see HandleOwner#passHandle()
+     */
+    @Override
+    public MessagePipeHandle passHandle() {
+        return mHandle.pass();
+    }
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close() {
+        mHandle.close();
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java
new file mode 100644
index 0000000..f7d8afe
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+
+/**
+ * Base class for mojo generated interfaces that have a client.
+ *
+ * @param <CI> the type of the client interface.
+ */
+public interface InterfaceWithClient<CI extends Interface> extends Interface {
+
+    /**
+     * Proxy class for interfaces with a client.
+     */
+    interface Proxy<CI extends Interface> extends Interface.Proxy, InterfaceWithClient<CI> {
+    }
+
+    /**
+     * Base implementation of Proxy.
+     *
+     * @param <CI> the type of the client interface.
+     */
+    abstract class AbstractProxy<CI extends Interface> extends Interface.AbstractProxy
+            implements Proxy<CI> {
+
+        /**
+         * Constructor.
+         *
+         * @param core the Core implementation used to create pipes and access the async waiter.
+         * @param messageReceiver the message receiver to send message to.
+         */
+        public AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
+            super(core, messageReceiver);
+        }
+
+        /**
+         * @see InterfaceWithClient#setClient(Interface)
+         */
+        @Override
+        public void setClient(CI client) {
+            throw new UnsupportedOperationException(
+                    "Setting the client on a proxy is not supported");
+        }
+    }
+
+    /**
+     * Base manager implementation for interfaces that have a client.
+     *
+     * @param <I> the type of the interface the manager can handle.
+     * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I.
+     * @param <CI> the type of the client interface.
+     */
+    abstract class Manager<I extends InterfaceWithClient<CI>, P extends Proxy<CI>,
+            CI extends Interface> extends Interface.Manager<I, P> {
+
+        /**
+         * @see Interface.Manager#bind(Interface, MessagePipeHandle)
+         */
+        @Override
+        public final void bind(I impl, MessagePipeHandle handle) {
+            Router router = new RouterImpl(handle);
+            super.bind(handle.getCore(), impl, router);
+            @SuppressWarnings("unchecked")
+            CI client = (CI) getClientManager().attachProxy(handle.getCore(), router);
+            impl.setClient(client);
+            router.start();
+        }
+
+        /**
+         * Returns a Proxy that will send messages to the given |handle|. This implies that the
+         * other end of the handle must be connected to an implementation of the interface. |client|
+         * is the implementation of the client interface.
+         */
+        public P attachProxy(MessagePipeHandle handle, CI client) {
+            Router router = new RouterImpl(handle);
+            DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
+            handlers.addConnectionErrorHandler(client);
+            router.setErrorHandler(handlers);
+            getClientManager().bind(handle.getCore(), client, router);
+            P proxy = super.attachProxy(handle.getCore(), router);
+            handlers.addConnectionErrorHandler(proxy);
+            router.start();
+            return proxy;
+        }
+
+        /**
+         * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where
+         * the first element is a proxy, and the second element is the request. The proxy can be
+         * used immediately.
+         *
+         * @param client the implementation of the client interface.
+         */
+        public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core, CI client) {
+            Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+            P proxy = attachProxy(handles.first, client);
+            return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
+        }
+
+        /**
+         * Returns a manager for the client inetrafce.
+         */
+        protected abstract Interface.Manager<CI, ?> getClientManager();
+    }
+
+    /**
+     * Set the client implementation for this interface.
+     */
+    public void setClient(CI client);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
new file mode 100644
index 0000000..0d270cc
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A raw message to be sent/received from a {@link MessagePipeHandle}. Note that this can contain
+ * any data, not necessarily a Mojo message with a proper header. See also {@link ServiceMessage}.
+ */
+public class Message {
+
+    /**
+     * The data of the message.
+     */
+    private final ByteBuffer mBuffer;
+
+    /**
+     * The handles of the message.
+     */
+    private final List<? extends Handle> mHandle;
+
+    /**
+     * This message interpreted as a message for a mojo service with an appropriate header.
+     */
+    private ServiceMessage mWithHeader = null;
+
+    /**
+     * Constructor.
+     *
+     * @param buffer The buffer containing the bytes to send. This must be a direct buffer.
+     * @param handles The list of handles to send.
+     */
+    public Message(ByteBuffer buffer, List<? extends Handle> handles) {
+        assert buffer.isDirect();
+        mBuffer = buffer;
+        mHandle = handles;
+    }
+
+    /**
+     * The data of the message.
+     */
+    public ByteBuffer getData() {
+        return mBuffer;
+    }
+
+    /**
+     * The handles of the message.
+     */
+    public List<? extends Handle> getHandles() {
+        return mHandle;
+    }
+
+    /**
+     * Returns the message interpreted as a message for a mojo service.
+     */
+    public ServiceMessage asServiceMessage() {
+        if (mWithHeader == null) {
+            mWithHeader = new ServiceMessage(this);
+        }
+        return mWithHeader;
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
new file mode 100644
index 0000000..ad4e108
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
@@ -0,0 +1,254 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.bindings.Struct.DataHeader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Header information for a message.
+ */
+public class MessageHeader {
+
+    private static final int SIMPLE_MESSAGE_SIZE = 16;
+    private static final int SIMPLE_MESSAGE_NUM_FIELDS = 2;
+    private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO =
+            new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_NUM_FIELDS);
+
+    private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 24;
+    private static final int MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3;
+    private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO =
+            new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_NUM_FIELDS);
+
+    private static final int TYPE_OFFSET = 8;
+    private static final int FLAGS_OFFSET = 12;
+    private static final int REQUEST_ID_OFFSET = 16;
+
+    /**
+     * Flag for a header of a simple message.
+     */
+    public static final int NO_FLAG = 0;
+
+    /**
+     * Flag for a header of a message that expected a response.
+     */
+    public static final int MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0;
+
+    /**
+     * Flag for a header of a message that is a response.
+     */
+    public static final int MESSAGE_IS_RESPONSE_FLAG = 1 << 1;
+
+    private final DataHeader mDataHeader;
+    private final int mType;
+    private final int mFlags;
+    private long mRequestId;
+
+    /**
+     * Constructor for the header of a message which does not have a response.
+     */
+    public MessageHeader(int type) {
+        mDataHeader = SIMPLE_MESSAGE_STRUCT_INFO;
+        mType = type;
+        mFlags = 0;
+        mRequestId = 0;
+    }
+
+    /**
+     * Constructor for the header of a message which have a response or being itself a response.
+     */
+    public MessageHeader(int type, int flags, long requestId) {
+        assert mustHaveRequestId(flags);
+        mDataHeader = MESSAGE_WITH_REQUEST_ID_STRUCT_INFO;
+        mType = type;
+        mFlags = flags;
+        mRequestId = requestId;
+    }
+
+    /**
+     * Constructor, parsing the header from a message. Should only be used by {@link Message}
+     * itself.
+     */
+    MessageHeader(Message message) {
+        Decoder decoder = new Decoder(message);
+        mDataHeader = decoder.readDataHeader();
+        validateDataHeader(mDataHeader);
+        mType = decoder.readInt(TYPE_OFFSET);
+        mFlags = decoder.readInt(FLAGS_OFFSET);
+        if (mustHaveRequestId(mFlags)) {
+            if (mDataHeader.size < MESSAGE_WITH_REQUEST_ID_SIZE) {
+                throw new DeserializationException("Incorrect message size, expecting at least "
+                        + MESSAGE_WITH_REQUEST_ID_SIZE
+                        + " for a message with a request identifier, but got: " + mDataHeader.size);
+
+            }
+            mRequestId = decoder.readLong(REQUEST_ID_OFFSET);
+        } else {
+            mRequestId = 0;
+        }
+    }
+
+    /**
+     * Returns the size in bytes of the serialization of the header.
+     */
+    public int getSize() {
+        return mDataHeader.size;
+    }
+
+    /**
+     * Returns the type of the message.
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the flags associated to the message.
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Returns if the message has the given flag.
+     */
+    public boolean hasFlag(int flag) {
+        return (mFlags & flag) == flag;
+    }
+
+    /**
+     * Returns if the message has a request id.
+     */
+    public boolean hasRequestId() {
+        return mustHaveRequestId(mFlags);
+    }
+
+    /**
+     * Return the request id for the message. Must only be called if the message has a request id.
+     */
+    public long getRequestId() {
+        assert hasRequestId();
+        return mRequestId;
+    }
+
+    /**
+     * Encode the header.
+     */
+    public void encode(Encoder encoder) {
+        encoder.encode(mDataHeader);
+        encoder.encode(getType(), TYPE_OFFSET);
+        encoder.encode(getFlags(), FLAGS_OFFSET);
+        if (hasRequestId()) {
+            encoder.encode(getRequestId(), REQUEST_ID_OFFSET);
+        }
+    }
+
+    /**
+     * Returns true if the header has the expected flags. Only considers flags this class knows
+     * about in order to allow this class to work with future version of the header format.
+     */
+    public boolean validateHeader(int expectedFlags) {
+        int knownFlags = getFlags() & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG);
+        return knownFlags == expectedFlags;
+    }
+
+    /**
+     * Returns true if the header has the expected type and flags. Only consider flags this class
+     * knows about in order to allow this class to work with future version of the header format.
+     */
+    public boolean validateHeader(int expectedType, int expectedFlags) {
+        return getType() == expectedType && validateHeader(expectedFlags);
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mDataHeader == null) ? 0 : mDataHeader.hashCode());
+        result = prime * result + mFlags;
+        result = prime * result + (int) (mRequestId ^ (mRequestId >>> 32));
+        result = prime * result + mType;
+        return result;
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object object) {
+        if (object == this)
+            return true;
+        if (object == null)
+            return false;
+        if (getClass() != object.getClass())
+            return false;
+
+        MessageHeader other = (MessageHeader) object;
+        if (mDataHeader == null) {
+            if (other.mDataHeader != null) {
+                return false;
+            }
+        } else {
+            if (!mDataHeader.equals(other.mDataHeader)) {
+                return false;
+            }
+        }
+
+        return (mFlags == other.mFlags &&
+                mRequestId == other.mRequestId &&
+                mType == other.mType);
+    }
+
+    /**
+     * Set the request id on the message contained in the given buffer.
+     */
+    void setRequestId(ByteBuffer buffer, long requestId) {
+        assert mustHaveRequestId(buffer.getInt(FLAGS_OFFSET));
+        buffer.putLong(REQUEST_ID_OFFSET, requestId);
+        mRequestId = requestId;
+    }
+
+    /**
+     * Returns whether a message with the given flags must have a request Id.
+     */
+    private static boolean mustHaveRequestId(int flags) {
+        return (flags & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG)) != 0;
+    }
+
+    /**
+     * Validate that the given {@link DataHeader} can be the data header of a message header.
+     */
+    private static void validateDataHeader(DataHeader dataHeader) {
+        if (dataHeader.numFields < SIMPLE_MESSAGE_NUM_FIELDS) {
+            throw new DeserializationException(
+                    "Incorrect number of fields, expecting at least " + SIMPLE_MESSAGE_NUM_FIELDS
+                    + ", but got: " + dataHeader.numFields);
+        }
+        if (dataHeader.size < SIMPLE_MESSAGE_SIZE) {
+            throw new DeserializationException(
+                    "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE
+                    + ", but got: " + dataHeader.size);
+        }
+        if (dataHeader.numFields == SIMPLE_MESSAGE_NUM_FIELDS
+                && dataHeader.size != SIMPLE_MESSAGE_SIZE) {
+            throw new DeserializationException(
+                    "Incorrect message size for a message with " + SIMPLE_MESSAGE_NUM_FIELDS
+                    + " fields, expecting " + SIMPLE_MESSAGE_SIZE + ", but got: "
+                    + dataHeader.size);
+        }
+        if (dataHeader.numFields == MESSAGE_WITH_REQUEST_ID_NUM_FIELDS
+                && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) {
+            throw new DeserializationException(
+                    "Incorrect message size for a message with "
+                    + MESSAGE_WITH_REQUEST_ID_NUM_FIELDS + " fields, expecting "
+                    + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size);
+        }
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
new file mode 100644
index 0000000..4223297
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.io.Closeable;
+
+/**
+ * A class which implements this interface can receive {@link Message} objects.
+ */
+public interface MessageReceiver extends Closeable {
+
+    /**
+     * Receive a {@link Message}. The {@link MessageReceiver} is allowed to mutate the message.
+     * Returns |true| if the message has been handled, |false| otherwise.
+     */
+    boolean accept(Message message);
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close();
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
new file mode 100644
index 0000000..e24a558
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * A {@link MessageReceiver} that can also handle the handle the response message generated from the
+ * given message.
+ */
+public interface MessageReceiverWithResponder extends MessageReceiver {
+
+    /**
+     * A variant on {@link #accept(Message)} that registers a {@link MessageReceiver}
+     * (known as the responder) to handle the response message generated from the given message. The
+     * responder's {@link #accept(Message)} method may be called as part of the call to
+     * {@link #acceptWithResponder(Message, MessageReceiver)}, or some time after its
+     * return.
+     */
+    boolean acceptWithResponder(Message message, MessageReceiver responder);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
new file mode 100644
index 0000000..ba19ae5
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MessagePipeHandle;
+
+/**
+ * A {@link Router} will handle mojo message and forward those to a {@link Connector}. It deals with
+ * parsing of headers and adding of request ids in order to be able to match a response to a
+ * request.
+ */
+public interface Router extends MessageReceiverWithResponder, HandleOwner<MessagePipeHandle> {
+
+    /**
+     * Start listening for incoming messages.
+     */
+    public void start();
+
+    /**
+     * Set the {@link MessageReceiverWithResponder} that will deserialize and use the message
+     * received from the pipe.
+     */
+    public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver);
+
+    /**
+     * Set the handle that will be notified of errors on the message pipe.
+     */
+    public void setErrorHandler(ConnectionErrorHandler errorHandler);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
new file mode 100644
index 0000000..5b27d7e
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of {@link Router}.
+ */
+public class RouterImpl implements Router {
+
+    /**
+     * {@link MessageReceiver} used as the {@link Connector} callback.
+     */
+    private class ResponderThunk implements MessageReceiver {
+
+        /**
+         * @see MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            return handleIncomingMessage(message);
+        }
+
+        /**
+         * @see org.chromium.mojo.bindings.MessageReceiver#close()
+         */
+        @Override
+        public void close() {
+            handleConnectorClose();
+        }
+
+    }
+
+    /**
+     * The {@link Connector} which is connected to the handle.
+     */
+    private final Connector mConnector;
+
+    /**
+     * The {@link MessageReceiverWithResponder} that will consume the messages received from the
+     * pipe.
+     */
+    private MessageReceiverWithResponder mIncomingMessageReceiver;
+
+    /**
+     * The next id to use for a request id which needs a response. It is auto-incremented.
+     */
+    private long mNextRequestId = 1;
+
+    /**
+     * The map from request ids to {@link MessageReceiver} of request currently in flight.
+     */
+    private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>();
+
+    /**
+     * Constructor that will use the default {@link AsyncWaiter}.
+     *
+     * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+     */
+    public RouterImpl(MessagePipeHandle messagePipeHandle) {
+        this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+     * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the
+     *            handle.
+     */
+    public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+        mConnector = new Connector(messagePipeHandle, asyncWaiter);
+        mConnector.setIncomingMessageReceiver(new ResponderThunk());
+    }
+
+    /**
+     * @see org.chromium.mojo.bindings.Router#start()
+     */
+    @Override
+    public void start() {
+        mConnector.start();
+    }
+
+    /**
+     * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+     */
+    @Override
+    public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+        this.mIncomingMessageReceiver = incomingMessageReceiver;
+    }
+
+    /**
+     * @see MessageReceiver#accept(Message)
+     */
+    @Override
+    public boolean accept(Message message) {
+        // A message without responder is directly forwarded to the connector.
+        return mConnector.accept(message);
+    }
+
+    /**
+     * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+     */
+    @Override
+    public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+        // The message must have a header.
+        ServiceMessage messageWithHeader = message.asServiceMessage();
+        // Checking the message expects a response.
+        assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
+
+        // Compute a request id for being able to route the response.
+        long requestId = mNextRequestId++;
+        // Reserve 0 in case we want it to convey special meaning in the future.
+        if (requestId == 0) {
+            requestId = mNextRequestId++;
+        }
+        if (mResponders.containsKey(requestId)) {
+            throw new IllegalStateException("Unable to find a new request identifier.");
+        }
+        messageWithHeader.setRequestId(requestId);
+        if (!mConnector.accept(messageWithHeader)) {
+            return false;
+        }
+        // Only keep the responder is the message has been accepted.
+        mResponders.put(requestId, responder);
+        return true;
+    }
+
+    /**
+     * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+     */
+    @Override
+    public MessagePipeHandle passHandle() {
+        return mConnector.passHandle();
+    }
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close() {
+        mConnector.close();
+    }
+
+    /**
+     * @see Router#setErrorHandler(ConnectionErrorHandler)
+     */
+    @Override
+    public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+        mConnector.setErrorHandler(errorHandler);
+    }
+
+    /**
+     * Receive a message from the connector. Returns |true| if the message has been handled.
+     */
+    private boolean handleIncomingMessage(Message message) {
+        MessageHeader header = message.asServiceMessage().getHeader();
+        if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) {
+            if (mIncomingMessageReceiver != null) {
+                return mIncomingMessageReceiver.acceptWithResponder(message, this);
+            }
+            // If we receive a request expecting a response when the client is not
+            // listening, then we have no choice but to tear down the pipe.
+            close();
+            return false;
+        } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
+            long requestId = header.getRequestId();
+            MessageReceiver responder = mResponders.get(requestId);
+            if (responder == null) {
+                return false;
+            }
+            mResponders.remove(requestId);
+            return responder.accept(message);
+        } else {
+            if (mIncomingMessageReceiver != null) {
+                return mIncomingMessageReceiver.accept(message);
+            }
+            // OK to drop the message.
+        }
+        return false;
+    }
+
+    private void handleConnectorClose() {
+        if (mIncomingMessageReceiver != null) {
+            mIncomingMessageReceiver.close();
+        }
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
new file mode 100644
index 0000000..d4f5502
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Error that can be thrown when serializing a mojo message.
+ */
+public class SerializationException extends RuntimeException {
+
+    /**
+     * Constructs a new serialization exception with the specified detail message.
+     */
+    public SerializationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new serialization exception with the specified cause.
+     */
+    public SerializationException(Exception cause) {
+        super(cause);
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
new file mode 100644
index 0000000..ac62bf6
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Represents a {@link Message} which contains a {@link MessageHeader}. Deals with parsing the
+ * {@link MessageHeader} for a message.
+ */
+public class ServiceMessage extends Message {
+
+    private final MessageHeader mHeader;
+    private Message mPayload;
+
+    /**
+     * Reinterpret the given |message| as a message with the given |header|. The |message| must
+     * contain the |header| as the start of its raw data.
+     */
+    public ServiceMessage(Message baseMessage, MessageHeader header) {
+        super(baseMessage.getData(), baseMessage.getHandles());
+        assert header.equals(new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+        this.mHeader = header;
+    }
+
+    /**
+     * Reinterpret the given |message| as a message with a header. The |message| must contain a
+     * header as the start of it's raw data, which will be parsed by this constructor.
+     */
+    ServiceMessage(Message baseMessage) {
+        this(baseMessage, new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+    }
+
+    /**
+     * @see Message#asServiceMessage()
+     */
+    @Override
+    public ServiceMessage asServiceMessage() {
+        return this;
+    }
+
+    /**
+     * Returns the header of the given message. This will throw a {@link DeserializationException}
+     * if the start of the message is not a valid header.
+     */
+    public MessageHeader getHeader() {
+        return mHeader;
+    }
+
+    /**
+     * Returns the payload of the message.
+     */
+    public Message getPayload() {
+        if (mPayload == null) {
+            ByteBuffer truncatedBuffer =
+                    ((ByteBuffer) getData().position(getHeader().getSize())).slice();
+            truncatedBuffer.order(ByteOrder.nativeOrder());
+            mPayload = new Message(truncatedBuffer, getHandles());
+        }
+        return mPayload;
+    }
+
+    /**
+     * Set the request identifier on the message.
+     */
+    void setRequestId(long requestId) {
+        mHeader.setRequestId(getData(), requestId);
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
new file mode 100644
index 0000000..118c991
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.io.Closeable;
+
+/**
+ * An implementation of closeable that doesn't do anything.
+ */
+public class SideEffectFreeCloseable implements Closeable {
+
+    /**
+     * @see java.io.Closeable#close()
+     */
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
new file mode 100644
index 0000000..017d0ef
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium 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.bindings;
+
+import org.chromium.mojo.system.Core;
+
+/**
+ * Base class for all mojo structs.
+ */
+public abstract class Struct {
+
+    /**
+     * The header for a mojo complex element.
+     */
+    public static final class DataHeader {
+
+        /**
+         * The size of a serialized header, in bytes.
+         */
+        public static final int HEADER_SIZE = 8;
+
+        /**
+         * The offset of the size field.
+         */
+        public static final int SIZE_OFFSET = 0;
+
+        /**
+         * The offset of the number of fields field.
+         */
+        public static final int NUM_FIELDS_OFFSET = 4;
+
+        public final int size;
+        public final int numFields;
+
+        /**
+         * Constructor.
+         */
+        public DataHeader(int size, int numFields) {
+            super();
+            this.size = size;
+            this.numFields = numFields;
+        }
+
+        /**
+         * @see Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + numFields;
+            result = prime * result + size;
+            return result;
+        }
+
+        /**
+         * @see Object#equals(Object)
+         */
+        @Override
+        public boolean equals(Object object) {
+            if (object == this)
+                return true;
+            if (object == null)
+                return false;
+            if (getClass() != object.getClass())
+                return false;
+
+            DataHeader other = (DataHeader) object;
+            return (numFields == other.numFields &&
+                    size == other.size);
+        }
+    }
+
+    /**
+     * The base size of the struct.
+     */
+    protected final int mEncodedBaseSize;
+
+    /**
+     * Constructor.
+     */
+    protected Struct(int encodedBaseSize) {
+        this.mEncodedBaseSize = encodedBaseSize;
+    }
+
+    /**
+     * Use the given encoder to serialized this struct.
+     */
+    protected abstract void encode(Encoder encoder);
+
+    /**
+     * Returns the serialization of the struct. This method can close Handles.
+     *
+     * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+     *            being encoded contains interfaces, can be |null| otherwise.
+     */
+    public Message serialize(Core core) {
+        Encoder encoder = new Encoder(core, mEncodedBaseSize);
+        encode(encoder);
+        return encoder.getMessage();
+    }
+
+    /**
+     * Returns the serialization of the struct prepended with the given header.
+     *
+     * @param header the header to prepend to the returned message.
+     * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+     *            being encoded contains interfaces, can be |null| otherwise.
+     */
+    public ServiceMessage serializeWithHeader(Core core, MessageHeader header) {
+        Encoder encoder = new Encoder(core, mEncodedBaseSize + header.getSize());
+        header.encode(encoder);
+        encode(encoder);
+        return new ServiceMessage(encoder.getMessage(), header);
+    }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
new file mode 100644
index 0000000..474de4b
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+
+/**
+ * A class which implements the {@link AsyncWaiter} allows asynchronously waiting on a background
+ * thread.
+ */
+public interface AsyncWaiter {
+
+    /**
+     * Allows cancellation of an asyncWait operation.
+     */
+    interface Cancellable {
+        /**
+         * Cancels an asyncWait operation. Has no effect if the operation has already been canceled
+         * or the callback has already been called.
+         * <p>
+         * Must be called from the same thread as {@link AsyncWaiter#asyncWait} was called from.
+         */
+        void cancel();
+    }
+
+    /**
+     * Callback passed to {@link AsyncWaiter#asyncWait}.
+     */
+    public interface Callback {
+        /**
+         * Called when the handle is ready.
+         */
+        public void onResult(int result);
+
+        /**
+         * Called when an error occurred while waiting.
+         */
+        public void onError(MojoException exception);
+    }
+
+    /**
+     * Asynchronously call wait on a background thread. The given {@link Callback} will be notified
+     * of the result of the wait on the same thread as asyncWait was called.
+     *
+     * @return a {@link Cancellable} object that can be used to cancel waiting. The cancellable
+     *         should only be used on the current thread, and becomes invalid once the callback has
+     *         been notified.
+     */
+    Cancellable asyncWait(Handle handle, HandleSignals signals, long deadline, Callback callback);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
new file mode 100644
index 0000000..d6c8e7d
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium 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.system;
+
+import java.util.List;
+
+/**
+ * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h|
+ * for the underlying api.
+ */
+public interface Core {
+
+    /**
+     * Used to indicate an infinite deadline (timeout).
+     */
+    public static final long DEADLINE_INFINITE = -1;
+
+    /**
+     * Signals for the wait operations on handles.
+     */
+    public static class HandleSignals extends Flags<HandleSignals> {
+        /**
+         * Constructor.
+         *
+         * @param signals the serialized signals.
+         */
+        private HandleSignals(int signals) {
+            super(signals);
+        }
+
+        private static final int FLAG_NONE = 0;
+        private static final int FLAG_READABLE = 1 << 0;
+        private static final int FLAG_WRITABLE = 1 << 1;
+
+        /**
+         * Immutable signals.
+         */
+        public static final HandleSignals NONE = HandleSignals.none().immutable();
+        public static final HandleSignals READABLE =
+                HandleSignals.none().setReadable(true).immutable();
+        public static final HandleSignals WRITABLE =
+                HandleSignals.none().setWritable(true).immutable();
+
+        /**
+         * Change the readable bit of this signal.
+         *
+         * @param readable the new value of the readable bit.
+         * @return this.
+         */
+        public HandleSignals setReadable(boolean readable) {
+            return setFlag(FLAG_READABLE, readable);
+        }
+
+        /**
+         * Change the writable bit of this signal.
+         *
+         * @param writable the new value of the writable bit.
+         * @return this.
+         */
+        public HandleSignals setWritable(boolean writable) {
+            return setFlag(FLAG_WRITABLE, writable);
+        }
+
+        /**
+         * @return a signal with no bit set.
+         */
+        public static HandleSignals none() {
+            return new HandleSignals(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * @return a platform-dependent monotonically increasing tick count representing "right now."
+     */
+    public long getTimeTicksNow();
+
+    /**
+     * Waits on the given |handle| until the state indicated by |signals| is satisfied or until
+     * |deadline| has passed.
+     *
+     * @return |MojoResult.OK| if some signal in |signals| was satisfied (or is already satisfied).
+     *         <p>
+     *         |MojoResult.DEADLINE_EXCEEDED| if the deadline has passed without any of the signals
+     *         being satisfied.
+     *         <p>
+     *         |MojoResult.CANCELLED| if |handle| is closed concurrently by another thread.
+     *         <p>
+     *         |MojoResult.FAILED_PRECONDITION| if it is or becomes impossible that any flag in
+     *         |signals| will ever be satisfied (for example, if the other endpoint is close).
+     */
+    public int wait(Handle handle, HandleSignals signals, long deadline);
+
+    /**
+     * Result for the |waitMany| method.
+     */
+    public static class WaitManyResult {
+
+        /**
+         * See |wait| for the different possible values.
+         */
+        private int mMojoResult;
+        /**
+         * If |mojoResult| is |MojoResult.OK|, |handleIndex| is the index of the handle for which
+         * some flag was satisfied (or is already satisfied). If |mojoResult| is
+         * |MojoResult.CANCELLED| or |MojoResult.FAILED_PRECONDITION|, |handleIndex| is the index of
+         * the handle for which the issue occurred.
+         */
+        private int mHandleIndex;
+
+        /**
+         * @return the mojoResult
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * @return the handleIndex
+         */
+        public int getHandleIndex() {
+            return mHandleIndex;
+        }
+
+        /**
+         * @param handleIndex the handleIndex to set
+         */
+        public void setHandleIndex(int handleIndex) {
+            mHandleIndex = handleIndex;
+        }
+    }
+
+    /**
+     * Waits on handle in |handles| for at least one of them to satisfy the associated
+     * |HandleSignals|, or until |deadline| has passed.
+     *
+     * @returns a |WaitManyResult|.
+     */
+    public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline);
+
+    /**
+     * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e.,
+     * messages), with the given options. Messages can contain plain data and/or Mojo handles.
+     *
+     * @return the set of handles for the two endpoints (ports) of the message pipe.
+     */
+    public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
+            MessagePipeHandle.CreateOptions options);
+
+    /**
+     * Creates a data pipe, which is a unidirectional communication channel for unframed data, with
+     * the given options. Data is unframed, but must come as (multiples of) discrete elements, of
+     * the size given in |options|. See |DataPipe.CreateOptions| for a description of the different
+     * options available for data pipes. |options| may be set to null for a data pipe with the
+     * default options (which will have an element size of one byte and have some system-dependent
+     * capacity).
+     *
+     * @return the set of handles for the two endpoints of the data pipe.
+     */
+    public Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> createDataPipe(
+            DataPipe.CreateOptions options);
+
+    /**
+     * Creates a buffer that can be shared between applications (by duplicating the handle -- see
+     * |SharedBufferHandle.duplicate()| -- and passing it over a message pipe). To access the
+     * buffer, one must call |SharedBufferHandle.map|.
+     *
+     * @return the new |SharedBufferHandle|.
+     */
+    public SharedBufferHandle createSharedBuffer(SharedBufferHandle.CreateOptions options,
+            long numBytes);
+
+    /**
+     * Acquires a handle from the native side. The handle will be owned by the returned object and
+     * must not be closed outside of it.
+     *
+     * @return a new {@link UntypedHandle} representing the native handle.
+     */
+    public UntypedHandle acquireNativeHandle(int handle);
+
+    /**
+     * Returns a default implementation of {@link AsyncWaiter}.
+     */
+    public AsyncWaiter getDefaultAsyncWaiter();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
new file mode 100644
index 0000000..4cd74e1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
@@ -0,0 +1,335 @@
+// Copyright 2014 The Chromium 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.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface for data pipes. A data pipe is a unidirectional communication channel for unframed
+ * data. Data is unframed, but must come as (multiples of) discrete elements, of the size given at
+ * creation time.
+ */
+public interface DataPipe {
+
+    /**
+     * Flags for the data pipe creation operation.
+     */
+    public static class CreateFlags extends Flags<CreateFlags> {
+        private static final int FLAG_NONE = 0;
+        private static final int FLAG_MAY_DISCARD = 1 << 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        protected CreateFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * Change the may-discard bit of this flag. This indicates that the data pipe may discard
+         * data for whatever reason; best-effort delivery. In particular, if the capacity is
+         * reached, old data may be discard to make room for new data.
+         *
+         * @param mayDiscard the new value of the may-discard bit.
+         * @return this.
+         */
+        public CreateFlags setMayDiscard(boolean mayDiscard) {
+            return setFlag(FLAG_MAY_DISCARD, mayDiscard);
+        }
+
+        /**
+         * @return flags with no bit set.
+         */
+        public static CreateFlags none() {
+            return new CreateFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Used to specify creation parameters for a data pipe to |Core.createDataPipe()|.
+     */
+    public static class CreateOptions {
+
+        /**
+         * Used to specify different modes of operation, see |DataPipe.CreateFlags|.
+         */
+        private CreateFlags mFlags = CreateFlags.none();
+        /**
+         * The size of an element, in bytes. All transactions and buffers will consist of an
+         * integral number of elements. Must be nonzero.
+         */
+        private int mElementNumBytes;
+        /**
+         * The capacity of the data pipe, in number of bytes; must be a multiple of
+         * |element_num_bytes|. The data pipe will always be able to queue AT LEAST this much data.
+         * Set to zero to opt for a system-dependent automatically-calculated capacity (which will
+         * always be at least one element).
+         */
+        private int mCapacityNumBytes;
+
+        /**
+         * @return the flags
+         */
+        public CreateFlags getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * @return the elementNumBytes
+         */
+        public int getElementNumBytes() {
+            return mElementNumBytes;
+        }
+
+        /**
+         * @param elementNumBytes the elementNumBytes to set
+         */
+        public void setElementNumBytes(int elementNumBytes) {
+            mElementNumBytes = elementNumBytes;
+        }
+
+        /**
+         * @return the capacityNumBytes
+         */
+        public int getCapacityNumBytes() {
+            return mCapacityNumBytes;
+        }
+
+        /**
+         * @param capacityNumBytes the capacityNumBytes to set
+         */
+        public void setCapacityNumBytes(int capacityNumBytes) {
+            mCapacityNumBytes = capacityNumBytes;
+        }
+
+    }
+
+    /**
+     * Flags for the write operations on MessagePipeHandle .
+     */
+    public static class WriteFlags extends Flags<WriteFlags> {
+        private static final int FLAG_NONE = 0;
+        private static final int FLAG_ALL_OR_NONE = 1 << 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        private WriteFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * Change the all-or-none bit of those flags. If set, write either all the elements
+         * requested or none of them.
+         *
+         * @param allOrNone the new value of all-or-none bit.
+         * @return this.
+         */
+        public WriteFlags setAllOrNone(boolean allOrNone) {
+            return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+        }
+
+        /**
+         * @return a flag with no bit set.
+         */
+        public static WriteFlags none() {
+            return new WriteFlags(FLAG_NONE);
+        }
+    }
+
+    /**
+     * Flags for the read operations on MessagePipeHandle.
+     */
+    public static class ReadFlags extends Flags<ReadFlags> {
+        private static final int FLAG_NONE = 0;
+        private static final int FLAG_ALL_OR_NONE = 1 << 0;
+        private static final int FLAG_QUERY = 1 << 2;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flag.
+         */
+        private ReadFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * Change the all-or-none bit of this flag. If set, read (or discard) either the requested
+         * number of elements or none.
+         *
+         * @param allOrNone the new value of the all-or-none bit.
+         * @return this.
+         */
+        public ReadFlags setAllOrNone(boolean allOrNone) {
+            return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+        }
+
+        /**
+         * Change the query bit of this flag. If set query the number of elements available to read.
+         * Mutually exclusive with |dicard| and |allOrNone| is ignored if this flag is set.
+         *
+         * @param query the new value of the query bit.
+         * @return this.
+         */
+        public ReadFlags query(boolean query) {
+            return setFlag(FLAG_QUERY, query);
+        }
+
+        /**
+         * @return a flag with no bit set.
+         */
+        public static ReadFlags none() {
+            return new ReadFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Handle for the producer part of a data pipe.
+     */
+    public static interface ProducerHandle extends Handle {
+
+        /**
+         * @see org.chromium.mojo.system.Handle#pass()
+         */
+        @Override
+        public ProducerHandle pass();
+
+        /**
+         * Writes the given data to the data pipe producer. |elements| points to data; the buffer
+         * must be a direct ByteBuffer and the limit should be a multiple of the data pipe's element
+         * size. If |allOrNone| is set in |flags|, either all the data will be written or none is.
+         * <p>
+         * On success, returns the amount of data that was actually written.
+         * <p>
+         * Note: If the data pipe has the "may discard" option flag (specified on creation), this
+         * will discard as much data as required to write the given data, starting with the earliest
+         * written data that has not been consumed. However, even with "may discard", if the buffer
+         * limit is greater than the data pipe's capacity (and |allOrNone| is not set), this will
+         * write the maximum amount possible (namely, the data pipe's capacity) and return that
+         * amount. It will *not* discard data from |elements|.
+         *
+         * @return number of written bytes.
+         */
+        public int writeData(ByteBuffer elements, WriteFlags flags);
+
+        /**
+         * Begins a two-phase write to the data pipe producer . On success, returns a |ByteBuffer|
+         * to which the caller can write. If flags has |allOrNone| set, then the buffer capacity
+         * will be at least as large as |numBytes|, which must also be a multiple of the element
+         * size (if |allOrNone| is not set, |numBytes| is ignored and the caller must check the
+         * capacity of the buffer).
+         * <p>
+         * During a two-phase write, this handle is *not* writable. E.g., if another thread tries to
+         * write to it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread can
+         * then wait for this handle to become writable again.
+         * <p>
+         * Once the caller has finished writing data to the buffer, it should call |endWriteData()|
+         * to specify the amount written and to complete the two-phase write.
+         * <p>
+         * Note: If the data pipe has the "may discard" option flag (specified on creation) and
+         * |flags| has |allOrNone| set, this may discard some data.
+         *
+         * @return The buffer to write to.
+         */
+        public ByteBuffer beginWriteData(int numBytes, WriteFlags flags);
+
+        /**
+         * Ends a two-phase write to the data pipe producer that was begun by a call to
+         * |beginWriteData()| on the same handle. |numBytesWritten| should indicate the amount of
+         * data actually written; it must be less than or equal to the capacity of the buffer
+         * returned by |beginWriteData()| and must be a multiple of the element size. The buffer
+         * returned from |beginWriteData()| must have been filled with exactly |numBytesWritten|
+         * bytes of data.
+         * <p>
+         * On failure, the two-phase write (if any) is ended (so the handle may become writable
+         * again, if there's space available) but no data written to the buffer is "put into" the
+         * data pipe.
+         */
+        public void endWriteData(int numBytesWritten);
+    }
+
+    /**
+     * Handle for the consumer part of a data pipe.
+     */
+    public static interface ConsumerHandle extends Handle {
+
+        /**
+         * @see org.chromium.mojo.system.Handle#pass()
+         */
+        @Override
+        public ConsumerHandle pass();
+
+       /**
+         * Discards data on the data pie consumer. This method discards up to |numBytes| (which
+         * again be a multiple of the element size) bytes of data, returning the amount actually
+         * discarded. if |flags| has |allOrNone|, it will either discard exactly |numBytes| bytes of
+         * data or none. In this case, |query| must not be set.
+         */
+        public int discardData(int numBytes, ReadFlags flags);
+
+        /**
+         * Reads data from the data pipe consumer. May also be used to query the amount of data
+         * available. If |flags| has not |query| set, this tries to read up to |elements| capacity
+         * (which must be a multiple of the data pipe's element size) bytes of data to |elements|
+         * and returns the amount actually read. |elements| must be a direct ByteBuffer. If flags
+         * has |allOrNone| set, it will either read exactly |elements| capacity bytes of data or
+         * none.
+         * <p>
+         * If flags has |query| set, it queries the amount of data available, returning the number
+         * of bytes available. In this case |allOrNone| is ignored, as are |elements|.
+         */
+        public int readData(ByteBuffer elements, ReadFlags flags);
+
+        /**
+         * Begins a two-phase read from the data pipe consumer. On success, returns a |ByteBuffer|
+         * from which the caller can read up to its limit bytes of data. If flags has |allOrNone|
+         * set, then the limit will be at least as large as |numBytes|, which must also be a
+         * multiple of the element size (if |allOrNone| is not set, |numBytes| is ignored). |flags|
+         * must not have |query| set.
+         * <p>
+         * During a two-phase read, this handle is *not* readable. E.g., if another thread tries to
+         * read from it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread
+         * can then wait for this handle to become readable again.
+         * <p>
+         * Once the caller has finished reading data from the buffer, it should call |endReadData()|
+         * to specify the amount read and to complete the two-phase read.
+         */
+        public ByteBuffer beginReadData(int numBytes, ReadFlags flags);
+
+        /**
+         * Ends a two-phase read from the data pipe consumer that was begun by a call to
+         * |beginReadData()| on the same handle. |numBytesRead| should indicate the amount of data
+         * actually read; it must be less than or equal to the limit of the buffer returned by
+         * |beginReadData()| and must be a multiple of the element size.
+         * <p>
+         * On failure, the two-phase read (if any) is ended (so the handle may become readable
+         * again) but no data is "removed" from the data pipe.
+         */
+        public void endReadData(int numBytesRead);
+    }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
new file mode 100644
index 0000000..dd2b00a
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+/**
+ * Base class for bit field used as flags.
+ *
+ * @param <F> the type of the flags.
+ */
+public abstract class Flags<F extends Flags<F>> {
+    private int mFlags;
+    private boolean mImmutable;
+
+    /**
+     * Dedicated constructor.
+     *
+     * @param flags initial value of the flag.
+     */
+    protected Flags(int flags) {
+        mImmutable = false;
+        mFlags = flags;
+    }
+
+    /**
+     * @return the computed flag.
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Change the given bit of this flag.
+     *
+     * @param value the new value of given bit.
+     * @return this.
+     */
+    protected F setFlag(int flag, boolean value) {
+        if (mImmutable) {
+            throw new UnsupportedOperationException("Flags is immutable.");
+        }
+        if (value) {
+            mFlags |= flag;
+        } else {
+            mFlags &= ~flag;
+        }
+        @SuppressWarnings("unchecked")
+        F f = (F) this;
+        return f;
+    }
+
+    /**
+     * Makes this flag immutable. This is a non-reversable operation.
+     */
+    protected F immutable() {
+        mImmutable = true;
+        @SuppressWarnings("unchecked")
+        F f = (F) this;
+        return f;
+    }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
new file mode 100644
index 0000000..907e2b9
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium 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.system;
+
+import java.io.Closeable;
+
+/**
+ * A generic mojo handle.
+ */
+public interface Handle extends Closeable {
+
+    /**
+     * Closes the given |handle|.
+     * <p>
+     * Concurrent operations on |handle| may succeed (or fail as usual) if they happen before the
+     * close, be cancelled with result |MojoResult.CANCELLED| if they properly overlap (this is
+     * likely the case with |wait()|, etc.), or fail with |MojoResult.INVALID_ARGUMENT| if they
+     * happen after.
+     */
+    @Override
+    public void close();
+
+    /**
+     * @see Core#wait(Handle, Core.HandleSignals, long)
+     */
+    public int wait(Core.HandleSignals signals, long deadline);
+
+    /**
+     * @return whether the handle is valid. A handle is valid until it has been explicitly closed or
+     *         send through a message pipe via |MessagePipeHandle.writeMessage|.
+     */
+    public boolean isValid();
+
+    /**
+     * Converts this handle into an {@link UntypedHandle}, invalidating this handle.
+     */
+    public UntypedHandle toUntypedHandle();
+
+    /**
+     * Returns the {@link Core} implementation for this handle. Can be null if this handle is
+     * invalid.
+     */
+    public Core getCore();
+
+    /**
+     * Passes ownership of the handle from this handle to the newly created Handle object,
+     * invalidating this handle object in the process.
+     */
+    public Handle pass();
+
+    /**
+     * Releases the native handle backed by this {@link Handle}. The caller owns the handle and must
+     * close it.
+     */
+    public int releaseNativeHandle();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
new file mode 100644
index 0000000..12f0457
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -0,0 +1,219 @@
+// Copyright 2014 The Chromium 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.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A handle that will always be invalid.
+ */
+public class InvalidHandle implements UntypedHandle, MessagePipeHandle, ConsumerHandle,
+        ProducerHandle, SharedBufferHandle {
+
+    /**
+     * Instance singleton.
+     */
+    public static final InvalidHandle INSTANCE = new InvalidHandle();
+
+    /**
+     * Private constructor.
+     */
+    private InvalidHandle() {
+    }
+
+    /**
+     * @see Handle#close()
+     */
+    @Override
+    public void close() {
+        // Do nothing.
+    }
+
+    /**
+     * @see Handle#wait(Core.HandleSignals, long)
+     */
+    @Override
+    public int wait(HandleSignals signals, long deadline) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see Handle#isValid()
+     */
+    @Override
+    public boolean isValid() {
+        return false;
+    }
+
+    /**
+     * @see Handle#getCore()
+     */
+    @Override
+    public Core getCore() {
+        return null;
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#pass()
+     */
+    @Override
+    public InvalidHandle pass() {
+        return this;
+    }
+
+    /**
+     * @see Handle#toUntypedHandle()
+     */
+    @Override
+    public UntypedHandle toUntypedHandle() {
+        return this;
+    }
+
+    /**
+     * @see Handle#releaseNativeHandle()
+     */
+    @Override
+    public int releaseNativeHandle() {
+        return -1;
+    }
+
+    /**
+     * @see UntypedHandle#toMessagePipeHandle()
+     */
+    @Override
+    public MessagePipeHandle toMessagePipeHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toDataPipeConsumerHandle()
+     */
+    @Override
+    public ConsumerHandle toDataPipeConsumerHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toDataPipeProducerHandle()
+     */
+    @Override
+    public ProducerHandle toDataPipeProducerHandle() {
+        return this;
+    }
+
+    /**
+     * @see UntypedHandle#toSharedBufferHandle()
+     */
+    @Override
+    public SharedBufferHandle toSharedBufferHandle() {
+        return this;
+    }
+
+    /**
+     * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions)
+     */
+    @Override
+    public SharedBufferHandle duplicate(DuplicateOptions options) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags)
+     */
+    @Override
+    public ByteBuffer map(long offset, long numBytes, MapFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see SharedBufferHandle#unmap(java.nio.ByteBuffer)
+     */
+    @Override
+    public void unmap(ByteBuffer buffer) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags)
+     */
+    @Override
+    public int writeData(ByteBuffer elements, DataPipe.WriteFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
+     */
+    @Override
+    public ByteBuffer beginWriteData(int numBytes,
+            DataPipe.WriteFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ProducerHandle#endWriteData(int)
+     */
+    @Override
+    public void endWriteData(int numBytesWritten) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+     */
+    @Override
+    public int discardData(int numBytes, DataPipe.ReadFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags)
+     */
+    @Override
+    public int readData(ByteBuffer elements, DataPipe.ReadFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+     */
+    @Override
+    public ByteBuffer beginReadData(int numBytes,
+            DataPipe.ReadFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see DataPipe.ConsumerHandle#endReadData(int)
+     */
+    @Override
+    public void endReadData(int numBytesRead) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List,
+     *      MessagePipeHandle.WriteFlags)
+     */
+    @Override
+    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+    /**
+     * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags)
+     */
+    @Override
+    public ReadMessageResult readMessage(ByteBuffer bytes, int maxNumberOfHandles,
+            ReadFlags flags) {
+        throw new MojoException(MojoResult.INVALID_ARGUMENT);
+    }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
new file mode 100644
index 0000000..60ac682
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
@@ -0,0 +1,242 @@
+// Copyright 2014 The Chromium 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.system;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Message pipes are bidirectional communication channel for framed data (i.e., messages). Messages
+ * can contain plain data and/or Mojo handles.
+ */
+public interface MessagePipeHandle extends Handle {
+
+    /**
+     * Flags for the message pipe creation operation.
+     */
+    public static class CreateFlags extends Flags<CreateFlags> {
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        protected CreateFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * @return flags with no bit set.
+         */
+        public static CreateFlags none() {
+            return new CreateFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Used to specify creation parameters for a message pipe to |Core#createMessagePipe()|.
+     */
+    public static class CreateOptions {
+        private CreateFlags mFlags = CreateFlags.NONE;
+
+        /**
+         * @return the flags
+         */
+        public CreateFlags getFlags() {
+            return mFlags;
+        }
+
+    }
+
+    /**
+     * Flags for the write operations on MessagePipeHandle .
+     */
+    public static class WriteFlags extends Flags<WriteFlags> {
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Immutable flag with no bit set.
+         */
+        public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flag.
+         */
+        private WriteFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * @return a flag with no bit set.
+         */
+        public static WriteFlags none() {
+            return new WriteFlags(FLAG_NONE);
+        }
+    }
+
+    /**
+     * Flags for the read operations on MessagePipeHandle.
+     */
+    public static class ReadFlags extends Flags<ReadFlags> {
+        private static final int FLAG_NONE = 0;
+        private static final int FLAG_MAY_DISCARD = 1 << 0;
+
+        /**
+         * Immutable flag with no bit set.
+         */
+        public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flag.
+         */
+        private ReadFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * Change the may-discard bit of this flag. If set, if the message is unable to be read for
+         * whatever reason (e.g., the caller-supplied buffer is too small), discard the message
+         * (i.e., simply dequeue it).
+         *
+         * @param mayDiscard the new value of the may-discard bit.
+         * @return this.
+         */
+        public ReadFlags setMayDiscard(boolean mayDiscard) {
+            return setFlag(FLAG_MAY_DISCARD, mayDiscard);
+        }
+
+        /**
+         * @return a flag with no bit set.
+         */
+        public static ReadFlags none() {
+            return new ReadFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Result of the |readMessage| method.
+     */
+    public static class ReadMessageResult {
+        /**
+         * The MojoResult value of the read call.
+         */
+        private int mMojoResult;
+        /**
+         * If a message was read, the size in bytes of the message, otherwise the size in bytes of
+         * the next message.
+         */
+        private int mMessageSize;
+        /**
+         * If a message was read, the number of handles contained in the message, otherwise the
+         * number of handles contained in the next message.
+         */
+        private int mHandlesCount;
+        /**
+         * If a message was read, the handles contained in the message, undefined otherwise.
+         */
+        private List<UntypedHandle> mHandles;
+
+        /**
+         * @return the mojoResult
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * @return the messageSize
+         */
+        public int getMessageSize() {
+            return mMessageSize;
+        }
+
+        /**
+         * @param messageSize the messageSize to set
+         */
+        public void setMessageSize(int messageSize) {
+            mMessageSize = messageSize;
+        }
+
+        /**
+         * @return the handlesCount
+         */
+        public int getHandlesCount() {
+            return mHandlesCount;
+        }
+
+        /**
+         * @param handlesCount the handlesCount to set
+         */
+        public void setHandlesCount(int handlesCount) {
+            mHandlesCount = handlesCount;
+        }
+
+        /**
+         * @return the handles
+         */
+        public List<UntypedHandle> getHandles() {
+            return mHandles;
+        }
+
+        /**
+         * @param handles the handles to set
+         */
+        public void setHandles(List<UntypedHandle> handles) {
+            mHandles = handles;
+        }
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#pass()
+     */
+    @Override
+    public MessagePipeHandle pass();
+
+    /**
+     * Writes a message to the message pipe endpoint, with message data specified by |bytes| and
+     * attached handles specified by |handles|, and options specified by |flags|. If there is no
+     * message data, |bytes| may be null, otherwise it must be a direct ByteBuffer. If there are no
+     * attached handles, |handles| may be null.
+     * <p>
+     * If handles are attached, on success the handles will no longer be valid (the receiver will
+     * receive equivalent, but logically different, handles). Handles to be sent should not be in
+     * simultaneous use (e.g., on another thread).
+     */
+    void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags);
+
+    /**
+     * Reads a message from the message pipe endpoint; also usable to query the size of the next
+     * message or discard the next message. |bytes| indicate the buffer/buffer size to receive the
+     * message data (if any) and |maxNumberOfHandles| indicate the maximum handle count to receive
+     * the attached handles (if any). |bytes| is optional. If null, |maxNumberOfHandles| must be
+     * zero, and the return value will indicate the size of the next message. If non-null, it must
+     * be a direct ByteBuffer and the return value will indicate if the message was read or not. If
+     * the message was read its content will be in |bytes|, and the attached handles will be in the
+     * return value. Partial reads are NEVER done. Either a full read is done and |wasMessageRead|
+     * will be true, or the read is NOT done and |wasMessageRead| will be false (if |mayDiscard| was
+     * set, the message is also discarded in this case).
+     */
+    ReadMessageResult readMessage(ByteBuffer bytes, int maxNumberOfHandles,
+            ReadFlags flags);
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
new file mode 100644
index 0000000..e06f647
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+/**
+ * Exception for the core mojo API.
+ */
+public class MojoException extends RuntimeException {
+
+    private final int mCode;
+
+    /**
+     * Constructor.
+     */
+    public MojoException(int code) {
+        mCode = code;
+    }
+
+    /**
+     * The mojo result code associated with this exception. See {@link MojoResult} for possible
+     * values.
+     */
+    public int getMojoResult() {
+        return mCode;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "MojoResult(" + mCode + "): " + MojoResult.describe(mCode);
+    }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
new file mode 100644
index 0000000..a829c83
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium 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.system;
+
+/**
+ * The different mojo result codes.
+ */
+public final class MojoResult {
+    public static final int OK = 0;
+    public static final int CANCELLED = -1;
+    public static final int UNKNOWN = -2;
+    public static final int INVALID_ARGUMENT = -3;
+    public static final int DEADLINE_EXCEEDED = -4;
+    public static final int NOT_FOUND = -5;
+    public static final int ALREADY_EXISTS = -6;
+    public static final int PERMISSION_DENIED = -7;
+    public static final int RESOURCE_EXHAUSTED = -8;
+    public static final int FAILED_PRECONDITION = -9;
+    public static final int ABORTED = -10;
+    public static final int OUT_OF_RANGE = -11;
+    public static final int UNIMPLEMENTED = -12;
+    public static final int INTERNAL = -13;
+    public static final int UNAVAILABLE = -14;
+    public static final int DATA_LOSS = -15;
+    public static final int BUSY = -16;
+    public static final int SHOULD_WAIT = -17;
+
+    /**
+     * never instantiate.
+     */
+    private MojoResult() {
+    }
+
+    /**
+     * Describes the given result code.
+     */
+    public static String describe(int mCode) {
+        switch (mCode) {
+            case OK:
+                return "OK";
+            case CANCELLED:
+                return "CANCELLED";
+            case UNKNOWN:
+                return "UNKNOWN";
+            case INVALID_ARGUMENT:
+                return "INVALID_ARGUMENT";
+            case DEADLINE_EXCEEDED:
+                return "DEADLINE_EXCEEDED";
+            case NOT_FOUND:
+                return "NOT_FOUND";
+            case ALREADY_EXISTS:
+                return "ALREADY_EXISTS";
+            case PERMISSION_DENIED:
+                return "PERMISSION_DENIED";
+            case RESOURCE_EXHAUSTED:
+                return "RESOURCE_EXHAUSTED";
+            case FAILED_PRECONDITION:
+                return "FAILED_PRECONDITION";
+            case ABORTED:
+                return "ABORTED";
+            case OUT_OF_RANGE:
+                return "OUT_OF_RANGE";
+            case UNIMPLEMENTED:
+                return "UNIMPLEMENTED";
+            case INTERNAL:
+                return "INTERNAL";
+            case UNAVAILABLE:
+                return "UNAVAILABLE";
+            case DATA_LOSS:
+                return "DATA_LOSS";
+            case BUSY:
+                return "BUSY";
+            case SHOULD_WAIT:
+                return "SHOULD_WAIT";
+            default:
+                return "UNKNOWN";
+        }
+
+    }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
new file mode 100644
index 0000000..2ead020
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+
+/**
+ * A pair of object.
+ *
+ * @param <F> Type of the first element.
+ * @param <S> Type of the second element.
+ */
+public class Pair<F, S> {
+
+    public final F first;
+    public final S second;
+
+    /**
+     * Dedicated constructor.
+     *
+     * @param first the first element of the pair.
+     * @param second the second element of the pair.
+     */
+    public Pair(F first, S second) {
+        this.first = first;
+        this.second = second;
+    }
+
+    /**
+     * equals() that handles null values.
+     */
+    private boolean equals(Object o1, Object o2) {
+        return o1 == null ? o2 == null : o1.equals(o2);
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof Pair)) {
+            return false;
+        }
+        Pair<?, ?> p = (Pair<?, ?>) o;
+        return equals(first, p.first) && equals(second, p.second);
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+    }
+
+    /**
+     * Helper method for creating a pair.
+     *
+     * @param a the first element of the pair.
+     * @param b the second element of the pair.
+     * @return the pair containing a and b.
+     */
+    public static <A, B> Pair<A, B> create(A a, B b) {
+        return new Pair<A, B>(a, b);
+    }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
new file mode 100644
index 0000000..df317d1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that can be shared between applications.
+ */
+public interface SharedBufferHandle extends Handle {
+
+    /**
+     * Flags for the shared buffer creation operation.
+     */
+    public static class CreateFlags extends Flags<CreateFlags> {
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        protected CreateFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * @return flags with no bit set.
+         */
+        public static CreateFlags none() {
+            return new CreateFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Used to specify creation parameters for a shared buffer to |Core#createSharedBuffer()|.
+     */
+    public static class CreateOptions {
+        private CreateFlags mFlags = CreateFlags.NONE;
+
+        /**
+         * @return the flags
+         */
+        public CreateFlags getFlags() {
+            return mFlags;
+        }
+
+    }
+
+    /**
+     * Flags for the shared buffer duplication operation.
+     */
+    public static class DuplicateFlags extends Flags<DuplicateFlags> {
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final DuplicateFlags NONE = DuplicateFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        protected DuplicateFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * @return flags with no bit set.
+         */
+        public static DuplicateFlags none() {
+            return new DuplicateFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Used to specify parameters in duplicating access to a shared buffer to
+     * |SharedBufferHandle#duplicate|
+     */
+    public static class DuplicateOptions {
+        private DuplicateFlags mFlags = DuplicateFlags.NONE;
+
+        /**
+         * @return the flags
+         */
+        public DuplicateFlags getFlags() {
+            return mFlags;
+        }
+
+    }
+
+    /**
+     * Flags for the shared buffer map operation.
+     */
+    public static class MapFlags extends Flags<MapFlags> {
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Immutable flag with not bit set.
+         */
+        public static final MapFlags NONE = MapFlags.none().immutable();
+
+        /**
+         * Dedicated constructor.
+         *
+         * @param flags initial value of the flags.
+         */
+        protected MapFlags(int flags) {
+            super(flags);
+        }
+
+        /**
+         * @return flags with no bit set.
+         */
+        public static MapFlags none() {
+            return new MapFlags(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * @see org.chromium.mojo.system.Handle#pass()
+     */
+    @Override
+    public SharedBufferHandle pass();
+
+    /**
+     * Duplicates the handle. This creates another handle (returned on success), which can then be
+     * sent to another application over a message pipe, while retaining access to this handle (and
+     * any mappings that it may have).
+     */
+    public SharedBufferHandle duplicate(DuplicateOptions options);
+
+    /**
+     * Map the part (at offset |offset| of length |numBytes|) of the buffer given by this handle
+     * into memory. |offset + numBytes| must be less than or equal to the size of the buffer. On
+     * success, the returned buffer points to memory with the requested part of the buffer. A single
+     * buffer handle may have multiple active mappings (possibly depending on the buffer type). The
+     * permissions (e.g., writable or executable) of the returned memory may depend on the
+     * properties of the buffer and properties attached to the buffer handle as well as |flags|.
+     */
+    public ByteBuffer map(long offset, long numBytes, MapFlags flags);
+
+    /**
+     * Unmap a buffer pointer that was mapped by |map()|.
+     */
+    public void unmap(ByteBuffer buffer);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
new file mode 100644
index 0000000..199b0a1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+/**
+ * A mojo handle of unknown type. This handle can be typed by using one of its methods, which will
+ * return a handle of the requested type and invalidate this object. No validation is made when the
+ * conversion operation is called.
+ */
+public interface UntypedHandle extends Handle {
+
+    /**
+     * @see org.chromium.mojo.system.Handle#pass()
+     */
+    @Override
+    public UntypedHandle pass();
+
+    /**
+     * Returns the underlying handle, as a {@link MessagePipeHandle}, invalidating this
+     * representation.
+     */
+    public MessagePipeHandle toMessagePipeHandle();
+
+    /**
+     * Returns the underlying handle, as a {@link ConsumerHandle}, invalidating this representation.
+     */
+    public ConsumerHandle toDataPipeConsumerHandle();
+
+    /**
+     * Returns the underlying handle, as a {@link ProducerHandle}, invalidating this representation.
+     */
+    public ProducerHandle toDataPipeProducerHandle();
+
+    /**
+     * Returns the underlying handle, as a {@link SharedBufferHandle}, invalidating this
+     * representation.
+     */
+    public SharedBufferHandle toSharedBufferHandle();
+
+}
diff --git a/mojo/public/js/bindings/BUILD.gn b/mojo/public/js/bindings/BUILD.gn
new file mode 100644
index 0000000..0f305e9
--- /dev/null
+++ b/mojo/public/js/bindings/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("bindings") {
+  sources = [
+    "constants.cc",
+    "constants.h",
+  ]
+}
diff --git a/mojo/public/js/bindings/buffer.js b/mojo/public/js/bindings/buffer.js
new file mode 100644
index 0000000..d74fef6
--- /dev/null
+++ b/mojo/public/js/bindings/buffer.js
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/buffer", function() {
+
+  var kHostIsLittleEndian = (function () {
+    var endianArrayBuffer = new ArrayBuffer(2);
+    var endianUint8Array = new Uint8Array(endianArrayBuffer);
+    var endianUint16Array = new Uint16Array(endianArrayBuffer);
+    endianUint16Array[0] = 1;
+    return endianUint8Array[0] == 1;
+  })();
+
+  var kHighWordMultiplier = 0x100000000;
+
+  function Buffer(sizeOrArrayBuffer) {
+    if (sizeOrArrayBuffer instanceof ArrayBuffer)
+      this.arrayBuffer = sizeOrArrayBuffer;
+    else
+      this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
+
+    this.dataView = new DataView(this.arrayBuffer);
+    this.next = 0;
+  }
+
+  Object.defineProperty(Buffer.prototype, "byteLength", {
+    get: function() { return this.arrayBuffer.byteLength; }
+  });
+
+  Buffer.prototype.alloc = function(size) {
+    var pointer = this.next;
+    this.next += size;
+    if (this.next > this.byteLength) {
+      var newSize = (1.5 * (this.byteLength + size)) | 0;
+      this.grow(newSize);
+    }
+    return pointer;
+  };
+
+  function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
+    (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
+  }
+
+  Buffer.prototype.grow = function(size) {
+    var newArrayBuffer = new ArrayBuffer(size);
+    copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
+    this.arrayBuffer = newArrayBuffer;
+    this.dataView = new DataView(this.arrayBuffer);
+  };
+
+  Buffer.prototype.trim = function() {
+    this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
+    this.dataView = new DataView(this.arrayBuffer);
+  };
+
+  Buffer.prototype.getUint8 = function(offset) {
+    return this.dataView.getUint8(offset);
+  }
+  Buffer.prototype.getUint16 = function(offset) {
+    return this.dataView.getUint16(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getUint32 = function(offset) {
+    return this.dataView.getUint32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getUint64 = function(offset) {
+    var lo, hi;
+    if (kHostIsLittleEndian) {
+      lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      hi = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    } else {
+      hi = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    }
+    return lo + hi * kHighWordMultiplier;
+  }
+
+  Buffer.prototype.getInt8 = function(offset) {
+    return this.dataView.getInt8(offset);
+  }
+  Buffer.prototype.getInt16 = function(offset) {
+    return this.dataView.getInt16(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getInt32 = function(offset) {
+    return this.dataView.getInt32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getInt64 = function(offset) {
+    var lo, hi;
+    if (kHostIsLittleEndian) {
+      lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      hi = this.dataView.getInt32(offset + 4, kHostIsLittleEndian);
+    } else {
+      hi = this.dataView.getInt32(offset, kHostIsLittleEndian);
+      lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    }
+    return lo + hi * kHighWordMultiplier;
+  }
+
+  Buffer.prototype.getFloat32 = function(offset) {
+    return this.dataView.getFloat32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getFloat64 = function(offset) {
+    return this.dataView.getFloat64(offset, kHostIsLittleEndian);
+  }
+
+  Buffer.prototype.setUint8 = function(offset, value) {
+    this.dataView.setUint8(offset, value);
+  }
+  Buffer.prototype.setUint16 = function(offset, value) {
+    this.dataView.setUint16(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setUint32 = function(offset, value) {
+    this.dataView.setUint32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setUint64 = function(offset, value) {
+    var hi = (value / kHighWordMultiplier) | 0;
+    if (kHostIsLittleEndian) {
+      this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+    } else {
+      this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+    }
+  }
+
+  Buffer.prototype.setInt8 = function(offset, value) {
+    this.dataView.setInt8(offset, value);
+  }
+  Buffer.prototype.setInt16 = function(offset, value) {
+    this.dataView.setInt16(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setInt32 = function(offset, value) {
+    this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setInt64 = function(offset, value) {
+    var hi = Math.floor(value / kHighWordMultiplier);
+    if (kHostIsLittleEndian) {
+      this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+    } else {
+      this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+    }
+  }
+
+  Buffer.prototype.setFloat32 = function(offset, value) {
+    this.dataView.setFloat32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setFloat64 = function(offset, value) {
+    this.dataView.setFloat64(offset, value, kHostIsLittleEndian);
+  }
+
+  var exports = {};
+  exports.Buffer = Buffer;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/codec.js b/mojo/public/js/bindings/codec.js
new file mode 100644
index 0000000..623cca7
--- /dev/null
+++ b/mojo/public/js/bindings/codec.js
@@ -0,0 +1,739 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/codec", [
+  "mojo/public/js/bindings/unicode",
+  "mojo/public/js/bindings/buffer"
+  ], function(unicode, buffer) {
+
+  var kErrorUnsigned = "Passing negative value to unsigned";
+
+  // Memory -------------------------------------------------------------------
+
+  var kAlignment = 8;
+
+  function align(size) {
+    return size + (kAlignment - (size % kAlignment)) % kAlignment;
+  }
+
+  function isAligned(offset) {
+    return offset >= 0 && (offset % kAlignment) === 0;
+  }
+
+  // Constants ----------------------------------------------------------------
+
+  var kArrayHeaderSize = 8;
+  var kStructHeaderSize = 8;
+  var kMessageHeaderSize = 16;
+  var kMessageWithRequestIDHeaderSize = 24;
+
+  var kStructHeaderNumBytesOffset = 0;
+  var kStructHeaderNumFieldsOffset = 4;
+
+  var kEncodedInvalidHandleValue = 0xFFFFFFFF;
+
+  // Decoder ------------------------------------------------------------------
+
+  function Decoder(buffer, handles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Decoder.prototype.skip = function(offset) {
+    this.next += offset;
+  };
+
+  Decoder.prototype.readInt8 = function() {
+    var result = this.buffer.getInt8(this.next);
+    this.next += 1;
+    return result;
+  };
+
+  Decoder.prototype.readUint8 = function() {
+    var result = this.buffer.getUint8(this.next);
+    this.next += 1;
+    return result;
+  };
+
+  Decoder.prototype.readInt16 = function() {
+    var result = this.buffer.getInt16(this.next);
+    this.next += 2;
+    return result;
+  };
+
+  Decoder.prototype.readUint16 = function() {
+    var result = this.buffer.getUint16(this.next);
+    this.next += 2;
+    return result;
+  };
+
+  Decoder.prototype.readInt32 = function() {
+    var result = this.buffer.getInt32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readUint32 = function() {
+    var result = this.buffer.getUint32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readInt64 = function() {
+    var result = this.buffer.getInt64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.readUint64 = function() {
+    var result = this.buffer.getUint64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.readFloat = function() {
+    var result = this.buffer.getFloat32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readDouble = function() {
+    var result = this.buffer.getFloat64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.decodePointer = function() {
+    // TODO(abarth): To correctly decode a pointer, we need to know the real
+    // base address of the array buffer.
+    var offsetPointer = this.next;
+    var offset = this.readUint64();
+    if (!offset)
+      return 0;
+    return offsetPointer + offset;
+  };
+
+  Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
+    return new Decoder(this.buffer, this.handles, pointer);
+  };
+
+  Decoder.prototype.decodeHandle = function() {
+    return this.handles[this.readUint32()];
+  };
+
+  Decoder.prototype.decodeString = function() {
+    var numberOfBytes = this.readUint32();
+    var numberOfElements = this.readUint32();
+    var base = this.next;
+    this.next += numberOfElements;
+    return unicode.decodeUtf8String(
+        new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
+  };
+
+  Decoder.prototype.decodeArray = function(cls) {
+    var numberOfBytes = this.readUint32();
+    var numberOfElements = this.readUint32();
+    var val = new Array(numberOfElements);
+    if (cls === PackedBool) {
+      var byte;
+      for (var i = 0; i < numberOfElements; ++i) {
+        if (i % 8 === 0)
+          byte = this.readUint8();
+        val[i] = (byte & (1 << i % 8)) ? true : false;
+      }
+    } else {
+      for (var i = 0; i < numberOfElements; ++i) {
+        val[i] = cls.decode(this);
+      }
+    }
+    return val;
+  };
+
+  Decoder.prototype.decodeStruct = function(cls) {
+    return cls.decode(this);
+  };
+
+  Decoder.prototype.decodeStructPointer = function(cls) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return cls.decode(this.decodeAndCreateDecoder(pointer));
+  };
+
+  Decoder.prototype.decodeArrayPointer = function(cls) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
+  };
+
+  Decoder.prototype.decodeStringPointer = function() {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.decodeAndCreateDecoder(pointer).decodeString();
+  };
+
+  // Encoder ------------------------------------------------------------------
+
+  function Encoder(buffer, handles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Encoder.prototype.skip = function(offset) {
+    this.next += offset;
+  };
+
+  Encoder.prototype.writeInt8 = function(val) {
+    this.buffer.setInt8(this.next, val);
+    this.next += 1;
+  };
+
+  Encoder.prototype.writeUint8 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint8(this.next, val);
+    this.next += 1;
+  };
+
+  Encoder.prototype.writeInt16 = function(val) {
+    this.buffer.setInt16(this.next, val);
+    this.next += 2;
+  };
+
+  Encoder.prototype.writeUint16 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint16(this.next, val);
+    this.next += 2;
+  };
+
+  Encoder.prototype.writeInt32 = function(val) {
+    this.buffer.setInt32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeUint32 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeInt64 = function(val) {
+    this.buffer.setInt64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.writeUint64 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.writeFloat = function(val) {
+    this.buffer.setFloat32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeDouble = function(val) {
+    this.buffer.setFloat64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.encodePointer = function(pointer) {
+    if (!pointer)
+      return this.writeUint64(0);
+    // TODO(abarth): To correctly encode a pointer, we need to know the real
+    // base address of the array buffer.
+    var offset = pointer - this.next;
+    this.writeUint64(offset);
+  };
+
+  Encoder.prototype.createAndEncodeEncoder = function(size) {
+    var pointer = this.buffer.alloc(align(size));
+    this.encodePointer(pointer);
+    return new Encoder(this.buffer, this.handles, pointer);
+  };
+
+  Encoder.prototype.encodeHandle = function(handle) {
+    this.handles.push(handle);
+    this.writeUint32(this.handles.length - 1);
+  };
+
+  Encoder.prototype.encodeString = function(val) {
+    var base = this.next + kArrayHeaderSize;
+    var numberOfElements = unicode.encodeUtf8String(
+        val, new Uint8Array(this.buffer.arrayBuffer, base));
+    var numberOfBytes = kArrayHeaderSize + numberOfElements;
+    this.writeUint32(numberOfBytes);
+    this.writeUint32(numberOfElements);
+    this.next += numberOfElements;
+  };
+
+  Encoder.prototype.encodeArray =
+      function(cls, val, numberOfElements, encodedSize) {
+    if (numberOfElements === undefined)
+      numberOfElements = val.length;
+    if (encodedSize === undefined)
+      encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+
+    this.writeUint32(encodedSize);
+    this.writeUint32(numberOfElements);
+
+    if (cls === PackedBool) {
+      var byte = 0;
+      for (i = 0; i < numberOfElements; ++i) {
+        if (val[i])
+          byte |= (1 << i % 8);
+        if (i % 8 === 7 || i == numberOfElements - 1) {
+          Uint8.encode(this, byte);
+          byte = 0;
+        }
+      }
+    } else {
+      for (var i = 0; i < numberOfElements; ++i)
+        cls.encode(this, val[i]);
+    }
+  };
+
+  Encoder.prototype.encodeStruct = function(cls, val) {
+    return cls.encode(this, val);
+  };
+
+  Encoder.prototype.encodeStructPointer = function(cls, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    var encoder = this.createAndEncodeEncoder(cls.encodedSize);
+    cls.encode(encoder, val);
+  };
+
+  Encoder.prototype.encodeArrayPointer = function(cls, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    var numberOfElements = val.length;
+    var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
+        Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeArray(cls, val, numberOfElements, encodedSize);
+  };
+
+  Encoder.prototype.encodeStringPointer = function(val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeString(val);
+  };
+
+  // Message ------------------------------------------------------------------
+
+  var kMessageNameOffset = kStructHeaderSize;
+  var kMessageFlagsOffset = kMessageNameOffset + 4;
+  var kMessageRequestIDOffset = kMessageFlagsOffset + 4;
+
+  var kMessageExpectsResponse = 1 << 0;
+  var kMessageIsResponse      = 1 << 1;
+
+  function Message(buffer, handles) {
+    this.buffer = buffer;
+    this.handles = handles;
+  }
+
+  Message.prototype.getHeaderNumBytes = function() {
+    return this.buffer.getUint32(kStructHeaderNumBytesOffset);
+  };
+
+  Message.prototype.getHeaderNumFields = function() {
+    return this.buffer.getUint32(kStructHeaderNumFieldsOffset);
+  };
+
+  Message.prototype.getName = function() {
+    return this.buffer.getUint32(kMessageNameOffset);
+  };
+
+  Message.prototype.getFlags = function() {
+    return this.buffer.getUint32(kMessageFlagsOffset);
+  };
+
+  Message.prototype.isResponse = function() {
+    return (this.getFlags() & kMessageIsResponse) != 0;
+  };
+
+  Message.prototype.expectsResponse = function() {
+    return (this.getFlags() & kMessageExpectsResponse) != 0;
+  };
+
+  Message.prototype.setRequestID = function(requestID) {
+    // TODO(darin): Verify that space was reserved for this field!
+    this.buffer.setUint64(kMessageRequestIDOffset, requestID);
+  };
+
+
+  // MessageBuilder -----------------------------------------------------------
+
+  function MessageBuilder(messageName, payloadSize) {
+    // Currently, we don't compute the payload size correctly ahead of time.
+    // Instead, we resize the buffer at the end.
+    var numberOfBytes = kMessageHeaderSize + payloadSize;
+    this.buffer = new buffer.Buffer(numberOfBytes);
+    this.handles = [];
+    var encoder = this.createEncoder(kMessageHeaderSize);
+    encoder.writeUint32(kMessageHeaderSize);
+    encoder.writeUint32(2);  // num_fields.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(0);  // flags.
+  }
+
+  MessageBuilder.prototype.createEncoder = function(size) {
+    var pointer = this.buffer.alloc(size);
+    return new Encoder(this.buffer, this.handles, pointer);
+  };
+
+  MessageBuilder.prototype.encodeStruct = function(cls, val) {
+    cls.encode(this.createEncoder(cls.encodedSize), val);
+  };
+
+  MessageBuilder.prototype.finish = function() {
+    // TODO(abarth): Rather than resizing the buffer at the end, we could
+    // compute the size we need ahead of time, like we do in C++.
+    this.buffer.trim();
+    var message = new Message(this.buffer, this.handles);
+    this.buffer = null;
+    this.handles = null;
+    this.encoder = null;
+    return message;
+  };
+
+  // MessageWithRequestIDBuilder -----------------------------------------------
+
+  function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
+                                       requestID) {
+    // Currently, we don't compute the payload size correctly ahead of time.
+    // Instead, we resize the buffer at the end.
+    var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
+    this.buffer = new buffer.Buffer(numberOfBytes);
+    this.handles = [];
+    var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
+    encoder.writeUint32(kMessageWithRequestIDHeaderSize);
+    encoder.writeUint32(3);  // num_fields.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(flags);
+    encoder.writeUint64(requestID);
+  }
+
+  MessageWithRequestIDBuilder.prototype =
+      Object.create(MessageBuilder.prototype);
+
+  MessageWithRequestIDBuilder.prototype.constructor =
+      MessageWithRequestIDBuilder;
+
+  // MessageReader ------------------------------------------------------------
+
+  function MessageReader(message) {
+    this.decoder = new Decoder(message.buffer, message.handles, 0);
+    var messageHeaderSize = this.decoder.readUint32();
+    this.payloadSize = message.buffer.byteLength - messageHeaderSize;
+    var numFields = this.decoder.readUint32();
+    this.messageName = this.decoder.readUint32();
+    this.flags = this.decoder.readUint32();
+    if (numFields >= 3)
+      this.requestID = this.decoder.readUint64();
+    this.decoder.skip(messageHeaderSize - this.decoder.next);
+  }
+
+  MessageReader.prototype.decodeStruct = function(cls) {
+    return cls.decode(this.decoder);
+  };
+
+  // Built-in types -----------------------------------------------------------
+
+  // This type is only used with ArrayOf(PackedBool).
+  function PackedBool() {
+  }
+
+  function Int8() {
+  }
+
+  Int8.encodedSize = 1;
+
+  Int8.decode = function(decoder) {
+    return decoder.readInt8();
+  };
+
+  Int8.encode = function(encoder, val) {
+    encoder.writeInt8(val);
+  };
+
+  Uint8.encode = function(encoder, val) {
+    encoder.writeUint8(val);
+  };
+
+  function Uint8() {
+  }
+
+  Uint8.encodedSize = 1;
+
+  Uint8.decode = function(decoder) {
+    return decoder.readUint8();
+  };
+
+  Uint8.encode = function(encoder, val) {
+    encoder.writeUint8(val);
+  };
+
+  function Int16() {
+  }
+
+  Int16.encodedSize = 2;
+
+  Int16.decode = function(decoder) {
+    return decoder.readInt16();
+  };
+
+  Int16.encode = function(encoder, val) {
+    encoder.writeInt16(val);
+  };
+
+  function Uint16() {
+  }
+
+  Uint16.encodedSize = 2;
+
+  Uint16.decode = function(decoder) {
+    return decoder.readUint16();
+  };
+
+  Uint16.encode = function(encoder, val) {
+    encoder.writeUint16(val);
+  };
+
+  function Int32() {
+  }
+
+  Int32.encodedSize = 4;
+
+  Int32.decode = function(decoder) {
+    return decoder.readInt32();
+  };
+
+  Int32.encode = function(encoder, val) {
+    encoder.writeInt32(val);
+  };
+
+  function Uint32() {
+  }
+
+  Uint32.encodedSize = 4;
+
+  Uint32.decode = function(decoder) {
+    return decoder.readUint32();
+  };
+
+  Uint32.encode = function(encoder, val) {
+    encoder.writeUint32(val);
+  };
+
+  function Int64() {
+  }
+
+  Int64.encodedSize = 8;
+
+  Int64.decode = function(decoder) {
+    return decoder.readInt64();
+  };
+
+  Int64.encode = function(encoder, val) {
+    encoder.writeInt64(val);
+  };
+
+  function Uint64() {
+  }
+
+  Uint64.encodedSize = 8;
+
+  Uint64.decode = function(decoder) {
+    return decoder.readUint64();
+  };
+
+  Uint64.encode = function(encoder, val) {
+    encoder.writeUint64(val);
+  };
+
+  function String() {
+  };
+
+  String.encodedSize = 8;
+
+  String.decode = function(decoder) {
+    return decoder.decodeStringPointer();
+  };
+
+  String.encode = function(encoder, val) {
+    encoder.encodeStringPointer(val);
+  };
+
+  function NullableString() {
+  }
+
+  NullableString.encodedSize = String.encodedSize;
+
+  NullableString.decode = String.decode;
+
+  NullableString.encode = String.encode;
+
+  function Float() {
+  }
+
+  Float.encodedSize = 4;
+
+  Float.decode = function(decoder) {
+    return decoder.readFloat();
+  };
+
+  Float.encode = function(encoder, val) {
+    encoder.writeFloat(val);
+  };
+
+  function Double() {
+  }
+
+  Double.encodedSize = 8;
+
+  Double.decode = function(decoder) {
+    return decoder.readDouble();
+  };
+
+  Double.encode = function(encoder, val) {
+    encoder.writeDouble(val);
+  };
+
+  function PointerTo(cls) {
+    this.cls = cls;
+  }
+
+  PointerTo.prototype.encodedSize = 8;
+
+  PointerTo.prototype.decode = function(decoder) {
+    var pointer = decoder.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
+  };
+
+  PointerTo.prototype.encode = function(encoder, val) {
+    if (!val) {
+      encoder.encodePointer(val);
+      return;
+    }
+    var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
+    this.cls.encode(objectEncoder, val);
+  };
+
+  function NullablePointerTo(cls) {
+    PointerTo.call(this, cls);
+  }
+
+  NullablePointerTo.prototype = Object.create(PointerTo.prototype);
+
+  function ArrayOf(cls) {
+    this.cls = cls;
+  }
+
+  ArrayOf.prototype.encodedSize = 8;
+
+  ArrayOf.prototype.decode = function(decoder) {
+    return decoder.decodeArrayPointer(this.cls);
+  };
+
+  ArrayOf.prototype.encode = function(encoder, val) {
+    encoder.encodeArrayPointer(this.cls, val);
+  };
+
+  function NullableArrayOf(cls) {
+    ArrayOf.call(this, cls);
+  }
+
+  NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
+
+  function Handle() {
+  }
+
+  Handle.encodedSize = 4;
+
+  Handle.decode = function(decoder) {
+    return decoder.decodeHandle();
+  };
+
+  Handle.encode = function(encoder, val) {
+    encoder.encodeHandle(val);
+  };
+
+  function NullableHandle() {
+  }
+
+  NullableHandle.encodedSize = Handle.encodedSize;
+
+  NullableHandle.decode = Handle.decode;
+
+  NullableHandle.encode = Handle.encode;
+
+  var exports = {};
+  exports.align = align;
+  exports.isAligned = isAligned;
+  exports.Message = Message;
+  exports.MessageBuilder = MessageBuilder;
+  exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
+  exports.MessageReader = MessageReader;
+  exports.kArrayHeaderSize = kArrayHeaderSize;
+  exports.kStructHeaderSize = kStructHeaderSize;
+  exports.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
+  exports.kMessageHeaderSize = kMessageHeaderSize;
+  exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize;
+  exports.kMessageExpectsResponse = kMessageExpectsResponse;
+  exports.kMessageIsResponse = kMessageIsResponse;
+  exports.Int8 = Int8;
+  exports.Uint8 = Uint8;
+  exports.Int16 = Int16;
+  exports.Uint16 = Uint16;
+  exports.Int32 = Int32;
+  exports.Uint32 = Uint32;
+  exports.Int64 = Int64;
+  exports.Uint64 = Uint64;
+  exports.Float = Float;
+  exports.Double = Double;
+  exports.String = String;
+  exports.NullableString = NullableString;
+  exports.PointerTo = PointerTo;
+  exports.NullablePointerTo = NullablePointerTo;
+  exports.ArrayOf = ArrayOf;
+  exports.NullableArrayOf = NullableArrayOf;
+  exports.PackedBool = PackedBool;
+  exports.Handle = Handle;
+  exports.NullableHandle = NullableHandle;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/codec_unittests.js b/mojo/public/js/bindings/codec_unittests.js
new file mode 100644
index 0000000..0276db2
--- /dev/null
+++ b/mojo/public/js/bindings/codec_unittests.js
@@ -0,0 +1,258 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+    "gin/test/expect",
+    "mojo/public/js/bindings/codec",
+    "mojo/public/interfaces/bindings/tests/rect.mojom",
+    "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+    "mojo/public/interfaces/bindings/tests/test_structs.mojom",
+  ], function(expect, codec, rect, sample, structs) {
+  testBar();
+  testFoo();
+  testNamedRegion();
+  testTypes();
+  testAlign();
+  testUtf8();
+  this.result = "PASS";
+
+  function testBar() {
+    var bar = new sample.Bar();
+    bar.alpha = 1;
+    bar.beta = 2;
+    bar.gamma = 3;
+    bar.type = 0x08070605;
+    bar.extraProperty = "banana";
+
+    var messageName = 42;
+    var payloadSize = sample.Bar.encodedSize;
+
+    var builder = new codec.MessageBuilder(messageName, payloadSize);
+    builder.encodeStruct(sample.Bar, bar);
+
+    var message = builder.finish();
+
+    var expectedMemory = new Uint8Array([
+      16, 0, 0, 0,
+       2, 0, 0, 0,
+      42, 0, 0, 0,
+       0, 0, 0, 0,
+
+      16, 0, 0, 0,
+       4, 0, 0, 0,
+
+       1, 2, 3, 0,
+       5, 6, 7, 8,
+    ]);
+
+    var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+    expect(actualMemory).toEqual(expectedMemory);
+
+    var reader = new codec.MessageReader(message);
+
+    expect(reader.payloadSize).toBe(payloadSize);
+    expect(reader.messageName).toBe(messageName);
+
+    var bar2 = reader.decodeStruct(sample.Bar);
+
+    expect(bar2.alpha).toBe(bar.alpha);
+    expect(bar2.beta).toBe(bar.beta);
+    expect(bar2.gamma).toBe(bar.gamma);
+    expect("extraProperty" in bar2).toBeFalsy();
+  }
+
+  function testFoo() {
+    var foo = new sample.Foo();
+    foo.x = 0x212B4D5;
+    foo.y = 0x16E93;
+    foo.a = 1;
+    foo.b = 0;
+    foo.c = 3; // This will get truncated to one bit.
+    foo.bar = new sample.Bar();
+    foo.bar.alpha = 91;
+    foo.bar.beta = 82;
+    foo.bar.gamma = 73;
+    foo.data = [
+      4, 5, 6, 7, 8,
+    ];
+    foo.extra_bars = [
+      new sample.Bar(), new sample.Bar(), new sample.Bar(),
+    ];
+    for (var i = 0; i < foo.extra_bars.length; ++i) {
+      foo.extra_bars[i].alpha = 1 * i;
+      foo.extra_bars[i].beta = 2 * i;
+      foo.extra_bars[i].gamma = 3 * i;
+    }
+    foo.name = "I am a banana";
+    // This is supposed to be a handle, but we fake it with an integer.
+    foo.source = 23423782;
+    foo.array_of_array_of_bools = [
+      [true], [false, true]
+    ];
+    foo.array_of_bools = [
+      true, false, true, false, true, false, true, true
+    ];
+
+
+    var messageName = 31;
+    var payloadSize = 304;
+
+    var builder = new codec.MessageBuilder(messageName, payloadSize);
+    builder.encodeStruct(sample.Foo, foo);
+
+    var message = builder.finish();
+
+    var expectedMemory = new Uint8Array([
+      /*  0: */   16,    0,    0,    0,    2,    0,    0,    0,
+      /*  8: */   31,    0,    0,    0,    0,    0,    0,    0,
+      /* 16: */   96,    0,    0,    0,   15,    0,    0,    0,
+      /* 24: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01,    0,
+      /* 32: */    5,    0,    0,    0,    0,    0,    0,    0,
+      /* 40: */   72,    0,    0,    0,    0,    0,    0,    0,
+    ]);
+    // TODO(abarth): Test more of the message's raw memory.
+    var actualMemory = new Uint8Array(message.buffer.arrayBuffer,
+                                      0, expectedMemory.length);
+    expect(actualMemory).toEqual(expectedMemory);
+
+    var expectedHandles = [
+      23423782,
+    ];
+
+    expect(message.handles).toEqual(expectedHandles);
+
+    var reader = new codec.MessageReader(message);
+
+    expect(reader.payloadSize).toBe(payloadSize);
+    expect(reader.messageName).toBe(messageName);
+
+    var foo2 = reader.decodeStruct(sample.Foo);
+
+    expect(foo2.x).toBe(foo.x);
+    expect(foo2.y).toBe(foo.y);
+
+    expect(foo2.a).toBe(foo.a & 1 ? true : false);
+    expect(foo2.b).toBe(foo.b & 1 ? true : false);
+    expect(foo2.c).toBe(foo.c & 1 ? true : false);
+
+    expect(foo2.bar).toEqual(foo.bar);
+    expect(foo2.data).toEqual(foo.data);
+
+    expect(foo2.extra_bars).toEqual(foo.extra_bars);
+    expect(foo2.name).toBe(foo.name);
+    expect(foo2.source).toEqual(foo.source);
+
+    expect(foo2.array_of_bools).toEqual(foo.array_of_bools);
+  }
+
+  function createRect(x, y, width, height) {
+    var r = new rect.Rect();
+    r.x = x;
+    r.y = y;
+    r.width = width;
+    r.height = height;
+    return r;
+  }
+
+  // Verify that the references to the imported Rect type in test_structs.mojom
+  // are generated correctly.
+  function testNamedRegion() {
+    var r = new structs.NamedRegion();
+    r.name = "rectangle";
+    r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40));
+
+    var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize);
+    builder.encodeStruct(structs.NamedRegion, r);
+    var reader = new codec.MessageReader(builder.finish());
+    var result = reader.decodeStruct(structs.NamedRegion);
+
+    expect(result.name).toEqual("rectangle");
+    expect(result.rects[0]).toEqual(createRect(1, 2, 3, 4));
+    expect(result.rects[1]).toEqual(createRect(10, 20, 30, 40));
+  }
+
+  function testTypes() {
+    function encodeDecode(cls, input, expectedResult, encodedSize) {
+      var messageName = 42;
+      var payloadSize = encodedSize || cls.encodedSize;
+
+      var builder = new codec.MessageBuilder(messageName, payloadSize);
+      builder.encodeStruct(cls, input)
+      var message = builder.finish();
+
+      var reader = new codec.MessageReader(message);
+      expect(reader.payloadSize).toBe(payloadSize);
+      expect(reader.messageName).toBe(messageName);
+      var result = reader.decodeStruct(cls);
+      expect(result).toEqual(expectedResult);
+    }
+    encodeDecode(codec.String, "banana", "banana", 24);
+    encodeDecode(codec.NullableString, null, null, 8);
+    encodeDecode(codec.Int8, -1, -1);
+    encodeDecode(codec.Int8, 0xff, -1);
+    encodeDecode(codec.Int16, -1, -1);
+    encodeDecode(codec.Int16, 0xff, 0xff);
+    encodeDecode(codec.Int16, 0xffff, -1);
+    encodeDecode(codec.Int32, -1, -1);
+    encodeDecode(codec.Int32, 0xffff, 0xffff);
+    encodeDecode(codec.Int32, 0xffffffff, -1);
+    encodeDecode(codec.Float, 1.0, 1.0);
+    encodeDecode(codec.Double, 1.0, 1.0);
+  }
+
+  function testAlign() {
+    var aligned = [
+      0, // 0
+      8, // 1
+      8, // 2
+      8, // 3
+      8, // 4
+      8, // 5
+      8, // 6
+      8, // 7
+      8, // 8
+      16, // 9
+      16, // 10
+      16, // 11
+      16, // 12
+      16, // 13
+      16, // 14
+      16, // 15
+      16, // 16
+      24, // 17
+      24, // 18
+      24, // 19
+      24, // 20
+    ];
+    for (var i = 0; i < aligned.length; ++i)
+      expect(codec.align(i)).toBe(aligned[i]);
+  }
+
+  function testUtf8() {
+    var str = "B\u03ba\u1f79";  // some UCS-2 codepoints
+    var messageName = 42;
+    var payloadSize = 24;
+
+    var builder = new codec.MessageBuilder(messageName, payloadSize);
+    var encoder = builder.createEncoder(8);
+    encoder.encodeStringPointer(str);
+    var message = builder.finish();
+    var expectedMemory = new Uint8Array([
+      /*  0: */   16,    0,    0,    0,    2,    0,    0,    0,
+      /*  8: */   42,    0,    0,    0,    0,    0,    0,    0,
+      /* 16: */    8,    0,    0,    0,    0,    0,    0,    0,
+      /* 24: */   14,    0,    0,    0,    6,    0,    0,    0,
+      /* 32: */ 0x42, 0xCE, 0xBA, 0xE1, 0xBD, 0xB9,    0,    0,
+    ]);
+    var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+    expect(actualMemory.length).toEqual(expectedMemory.length);
+    expect(actualMemory).toEqual(expectedMemory);
+
+    var reader = new codec.MessageReader(message);
+    expect(reader.payloadSize).toBe(payloadSize);
+    expect(reader.messageName).toBe(messageName);
+    var str2 = reader.decoder.decodeStringPointer();
+    expect(str2).toEqual(str);
+  }
+});
diff --git a/mojo/public/js/bindings/connection.js b/mojo/public/js/bindings/connection.js
new file mode 100644
index 0000000..31cf2aa
--- /dev/null
+++ b/mojo/public/js/bindings/connection.js
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/connection", [
+  "mojo/public/js/bindings/connector",
+  "mojo/public/js/bindings/router",
+], function(connector, router) {
+
+  function Connection(
+      handle, localFactory, remoteFactory, routerFactory, connectorFactory) {
+    if (routerFactory === undefined)
+      routerFactory = router.Router;
+    this.router_ = new routerFactory(handle, connectorFactory);
+    this.remote = new remoteFactory(this.router_);
+    this.local = new localFactory(this.remote);
+    this.router_.setIncomingReceiver(this.local);
+
+    var validateRequest = localFactory.prototype.validator;
+    var validateResponse = remoteFactory.prototype.validator;
+    var payloadValidators = [];
+    if (validateRequest)
+      payloadValidators.push(validateRequest);
+    if (validateResponse)
+      payloadValidators.push(validateResponse);
+    this.router_.setPayloadValidators(payloadValidators);
+  }
+
+  Connection.prototype.close = function() {
+    this.router_.close();
+    this.router_ = null;
+    this.local = null;
+    this.remote = null;
+  };
+
+  Connection.prototype.encounteredError = function() {
+    return this.router_.encounteredError();
+  };
+
+  // The TestConnection subclass is only intended to be used in unit tests.
+
+  function TestConnection(handle, localFactory, remoteFactory) {
+    Connection.call(this,
+                    handle,
+                    localFactory,
+                    remoteFactory,
+                    router.TestRouter,
+                    connector.TestConnector);
+  }
+
+  TestConnection.prototype = Object.create(Connection.prototype);
+
+  var exports = {};
+  exports.Connection = Connection;
+  exports.TestConnection = TestConnection;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/connector.js b/mojo/public/js/bindings/connector.js
new file mode 100644
index 0000000..495896d
--- /dev/null
+++ b/mojo/public/js/bindings/connector.js
@@ -0,0 +1,127 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/connector", [
+  "mojo/public/js/bindings/buffer",
+  "mojo/public/js/bindings/codec",
+  "mojo/public/js/bindings/core",
+  "mojo/public/js/bindings/support",
+], function(buffer, codec, core, support) {
+
+  function Connector(handle) {
+    this.handle_ = handle;
+    this.dropWrites_ = false;
+    this.error_ = false;
+    this.incomingReceiver_ = null;
+    this.readWaitCookie_ = null;
+    this.errorHandler_ = null;
+
+    this.waitToReadMore_();
+  }
+
+  Connector.prototype.close = function() {
+    if (this.readWaitCookie_) {
+      support.cancelWait(this.readWaitCookie_);
+      this.readWaitCookie_ = null;
+    }
+    if (this.handle_ != null) {
+      core.close(this.handle_);
+      this.handle_ = null;
+    }
+  };
+
+  Connector.prototype.accept = function(message) {
+    if (this.error_)
+      return false;
+
+    if (this.dropWrites_)
+      return true;
+
+    var result = core.writeMessage(this.handle_,
+                                   new Uint8Array(message.buffer.arrayBuffer),
+                                   message.handles,
+                                   core.WRITE_MESSAGE_FLAG_NONE);
+    switch (result) {
+      case core.RESULT_OK:
+        // The handles were successfully transferred, so we don't own them
+        // anymore.
+        message.handles = [];
+        break;
+      case core.RESULT_FAILED_PRECONDITION:
+        // There's no point in continuing to write to this pipe since the other
+        // end is gone. Avoid writing any future messages. Hide write failures
+        // from the caller since we'd like them to continue consuming any
+        // backlog of incoming messages before regarding the message pipe as
+        // closed.
+        this.dropWrites_ = true;
+        break;
+      default:
+        // This particular write was rejected, presumably because of bad input.
+        // The pipe is not necessarily in a bad state.
+        return false;
+    }
+    return true;
+  };
+
+  Connector.prototype.setIncomingReceiver = function(receiver) {
+    this.incomingReceiver_ = receiver;
+  };
+
+  Connector.prototype.setErrorHandler = function(handler) {
+    this.errorHandler_ = handler;
+  };
+
+  Connector.prototype.encounteredError = function() {
+    return this.error_;
+  };
+
+  Connector.prototype.waitToReadMore_ = function() {
+    this.readWaitCookie_ = support.asyncWait(this.handle_,
+                                             core.HANDLE_SIGNAL_READABLE,
+                                             this.readMore_.bind(this));
+  };
+
+  Connector.prototype.readMore_ = function(result) {
+    for (;;) {
+      var read = core.readMessage(this.handle_,
+                                  core.READ_MESSAGE_FLAG_NONE);
+      if (read.result == core.RESULT_SHOULD_WAIT) {
+        this.waitToReadMore_();
+        return;
+      }
+      if (read.result != core.RESULT_OK) {
+        this.error_ = true;
+        if (this.errorHandler_)
+          this.errorHandler_.onError(read.result);
+        return;
+      }
+      var messageBuffer = new buffer.Buffer(read.buffer);
+      var message = new codec.Message(messageBuffer, read.handles);
+      if (this.incomingReceiver_) {
+          this.incomingReceiver_.accept(message);
+      }
+    }
+  };
+
+  // The TestConnector subclass is only intended to be used in unit tests. It
+  // enables delivering a message to the pipe's handle without an async wait.
+
+  function TestConnector(handle) {
+    Connector.call(this, handle);
+  }
+
+  TestConnector.prototype = Object.create(Connector.prototype);
+
+  TestConnector.prototype.waitToReadMore_ = function() {
+  };
+
+  TestConnector.prototype.deliverMessage = function() {
+    this.readMore_(core.RESULT_OK);
+  }
+
+  var exports = {};
+  exports.Connector = Connector;
+  exports.TestConnector = TestConnector;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/constants.cc b/mojo/public/js/bindings/constants.cc
new file mode 100644
index 0000000..547279d
--- /dev/null
+++ b/mojo/public/js/bindings/constants.cc
@@ -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.
+
+#include "mojo/public/js/bindings/constants.h"
+
+namespace mojo {
+
+const char kBufferModuleName[] = "mojo/public/js/bindings/buffer";
+const char kCodecModuleName[] = "mojo/public/js/bindings/codec";
+const char kConnectionModuleName[] = "mojo/public/js/bindings/connection";
+const char kConnectorModuleName[] = "mojo/public/js/bindings/connector";
+const char kUnicodeModuleName[] = "mojo/public/js/bindings/unicode";
+const char kRouterModuleName[] = "mojo/public/js/bindings/router";
+const char kValidatorModuleName[] = "mojo/public/js/bindings/validator";
+
+}  // namespace mojo
diff --git a/mojo/public/js/bindings/constants.h b/mojo/public/js/bindings/constants.h
new file mode 100644
index 0000000..9927c8e
--- /dev/null
+++ b/mojo/public/js/bindings/constants.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+#define MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+
+namespace mojo {
+
+// JavaScript module names:
+extern const char kBufferModuleName[];
+extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
+extern const char kConnectorModuleName[];
+extern const char kUnicodeModuleName[];
+extern const char kRouterModuleName[];
+extern const char kValidatorModuleName[];
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
diff --git a/mojo/public/js/bindings/core.js b/mojo/public/js/bindings/core.js
new file mode 100644
index 0000000..95d49a5
--- /dev/null
+++ b/mojo/public/js/bindings/core.js
@@ -0,0 +1,229 @@
+// Copyright 2014 The Chromium 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/public/js/bindings/core"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+//
+// This module provides the JavaScript bindings for mojo/public/c/system/core.h.
+// Refer to that file for more detailed documentation for equivalent methods.
+
+while (1);
+
+/**
+ * MojoHandle: An opaque handles to a Mojo object (e.g. a message pipe).
+ */
+var kInvalidHandle;
+
+/**
+ * MojoResult {number}: Result codes for Mojo operations.
+ * See core.h for more information.
+ */
+var RESULT_OK;
+var RESULT_CANCELLED;
+var RESULT_UNKNOWN;
+var RESULT_INVALID_ARGUMENT;
+var RESULT_DEADLINE_EXCEEDED;
+var RESULT_NOT_FOUND;
+var RESULT_ALREADY_EXISTS;
+var RESULT_PERMISSION_DENIED;
+var RESULT_RESOURCE_EXHAUSTED;
+var RESULT_FAILED_PRECONDITION;
+var RESULT_ABORTED;
+var RESULT_OUT_OF_RANGE;
+var RESULT_UNIMPLEMENTED;
+var RESULT_INTERNAL;
+var RESULT_UNAVAILABLE;
+var RESULT_DATA_LOSS;
+var RESULT_BUSY;
+var RESULT_SHOULD_WAIT;
+
+/**
+ * MojoDeadline {number}: Used to specify deadlines (timeouts), in microseconds.
+ * See core.h for more information.
+ */
+var DEADLINE_INDEFINITE;
+
+/**
+ * MojoHandleSignals: Used to specify signals that can be waited on for a handle
+ *(and which can be triggered), e.g., the ability to read or write to
+ * the handle.
+ * See core.h for more information.
+ */
+var HANDLE_SIGNAL_NONE;
+var HANDLE_SIGNAL_READABLE;
+var HANDLE_SIGNAL_WRITABLE;
+
+/**
+ * MojoCreateDataMessageOptions: Used to specify creation parameters for a data
+ * pipe to |createDataMessage()|.
+ * See core.h for more information.
+ */
+dictionary MojoCreateDataMessageOptions {
+  MojoCreateDataMessageOptionsFlags flags;  // See below.
+};
+
+// MojoCreateDataMessageOptionsFlags
+var CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE;
+
+/*
+ * MojoWriteMessageFlags: Used to specify different modes to |writeMessage()|.
+ * See core.h for more information.
+ */
+var WRITE_MESSAGE_FLAG_NONE;
+
+/**
+ * MojoReadMessageFlags: Used to specify different modes to |readMessage()|.
+ * See core.h for more information.
+ */
+var READ_MESSAGE_FLAG_NONE;
+var READ_MESSAGE_FLAG_MAY_DISCARD;
+
+/**
+ * MojoCreateDataPipeOptions: Used to specify creation parameters for a data
+ * pipe to |createDataPipe()|.
+ * See core.h for more information.
+ */
+dictionary MojoCreateDataPipeOptions {
+  MojoCreateDataPipeOptionsFlags flags;  // See below.
+  int32 elementNumBytes;  // The size of an element, in bytes.
+  int32 capacityNumBytes;  // The capacity of the data pipe, in bytes.
+};
+
+// MojoCreateDataPipeOptionsFlags
+var CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+var CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD;
+
+/*
+ * MojoWriteDataFlags: Used to specify different modes to |writeData()|.
+ * See core.h for more information.
+ */
+var WRITE_DATA_FLAG_NONE;
+var WRITE_DATA_FLAG_ALL_OR_NONE;
+
+/**
+ * MojoReadDataFlags: Used to specify different modes to |readData()|.
+ * See core.h for more information.
+ */
+var READ_DATA_FLAG_NONE;
+var READ_DATA_FLAG_ALL_OR_NONE;
+var READ_DATA_FLAG_DISCARD;
+var READ_DATA_FLAG_QUERY;
+
+/**
+ * Closes the given |handle|. See MojoClose for more info.
+ * @param {MojoHandle} Handle to close.
+ * @return {MojoResult} Result code.
+ */
+function close(handle) { [native code] }
+
+/**
+ * Waits on the given handle until a signal indicated by |signals| is
+ * satisfied or until |deadline| is passed. See MojoWait for more information.
+ *
+ * @param {MojoHandle} handle Handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function wait(handle, signals, deadline) { [native code] }
+
+/**
+ * Waits on |handles[0]|, ..., |handles[handles.length-1]| for at least one of
+ * them to satisfy the state indicated by |flags[0]|, ...,
+ * |flags[handles.length-1]|, respectively, or until |deadline| has passed.
+ * See MojoWaitMany for more information.
+ *
+ * @param {Array.MojoHandle} handles Handles to wait on.
+ * @param {Array.MojoHandleSignals} signals Specifies the condition to wait for,
+ *   for each corresponding handle. Must be the same length as |handles|.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function waitMany(handles, signals, deadline) { [native code] }
+
+/**
+ * Creates a message pipe. This function always succeeds.
+ * See MojoCreateMessagePipe for more information on message pipes.
+ *
+ * @param {MojoCreateMessagePipeOptions} optionsDict Options to control the
+ * message pipe parameters. May be null.
+ * @return {MessagePipe} An object of the form {
+ *     handle0,
+ *     handle1,
+ *   }
+ *   where |handle0| and |handle1| are MojoHandles to each end of the channel.
+ */
+function createMessagePipe(optionsDict) { [native code] }
+
+/**
+ * Writes a message to the message pipe endpoint given by |handle|. See
+ * MojoWriteMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to write to.
+ * @param {ArrayBufferView} buffer The message data. May be empty.
+ * @param {Array.MojoHandle} handlesArray Any handles to attach. Handles are
+ *   transferred on success and will no longer be valid. May be empty.
+ * @param {MojoWriteMessageFlags} flags Flags.
+ * @return {MojoResult} Result code.
+ */
+function writeMessage(handle, buffer, handlesArray, flags) { [native code] }
+
+/**
+ * Reads a message from the message pipe endpoint given by |handle|. See
+ * MojoReadMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to read from.
+ * @param {MojoReadMessageFlags} flags Flags.
+ * @return {object} An object of the form {
+ *     result,  // |RESULT_OK| on success, error code otherwise.
+ *     buffer,  // An ArrayBufferView of the message data (only on success).
+ *     handles  // An array of MojoHandles transferred, if any.
+ *   }
+ */
+function readMessage(handle, flags) { [native code] }
+
+/**
+ * Creates a data pipe, which is a unidirectional communication channel for
+ * unframed data, with the given options. See MojoCreateDataPipe for more
+ * more information, including return codes.
+ *
+ * @param {MojoCreateDataPipeOptions} optionsDict Options to control the data
+ *   pipe parameters. May be null.
+ * @return {object} An object of the form {
+ *     result,  // |RESULT_OK| on success, error code otherwise.
+ *     producerHandle,  // MojoHandle to use with writeData (only on success).
+ *     consumerHandle,  // MojoHandle to use with readData (only on success).
+ *   }
+ */
+function createDataPipe(optionsDict) { [native code] }
+
+/**
+ * Writes the given data to the data pipe producer given by |handle|. See
+ * MojoWriteData for more information, including return codes.
+ *
+ * @param {MojoHandle} handle A producerHandle returned by createDataPipe.
+ * @param {ArrayBufferView} buffer The data to write.
+ * @param {MojoWriteDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ *     result,  // |RESULT_OK| on success, error code otherwise.
+ *     numBytes,  // The number of bytes written.
+ *   }
+ */
+function writeData(handle, buffer, flags) { [native code] }
+
+/**
+ * Reads data from the data pipe consumer given by |handle|. May also
+ * be used to discard data. See MojoReadData for more information, including
+ * return codes.
+ *
+ * @param {MojoHandle} handle A consumerHandle returned by createDataPipe.
+ * @param {MojoReadDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ *     result,  // |RESULT_OK| on success, error code otherwise.
+ *     buffer,  // An ArrayBufferView of the data read (only on success).
+ *   }
+ */
+function readData(handle, flags) { [native code] }
diff --git a/mojo/public/js/bindings/core_unittests.js b/mojo/public/js/bindings/core_unittests.js
new file mode 100644
index 0000000..b115cc0
--- /dev/null
+++ b/mojo/public/js/bindings/core_unittests.js
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+    "gin/test/expect",
+    "mojo/public/js/bindings/core",
+    "gc",
+  ], function(expect, core, gc) {
+  runWithMessagePipe(testNop);
+  runWithMessagePipe(testReadAndWriteMessage);
+  runWithMessagePipeWithOptions(testNop);
+  runWithMessagePipeWithOptions(testReadAndWriteMessage);
+  runWithDataPipe(testNop);
+  runWithDataPipe(testReadAndWriteDataPipe);
+  runWithDataPipeWithOptions(testNop);
+  runWithDataPipeWithOptions(testReadAndWriteDataPipe);
+  gc.collectGarbage();  // should not crash
+  this.result = "PASS";
+
+  function runWithMessagePipe(test) {
+    var pipe = core.createMessagePipe();
+    expect(pipe.result).toBe(core.RESULT_OK);
+
+    test(pipe);
+
+    expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+    expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+  }
+
+  function runWithMessagePipeWithOptions(test) {
+    var pipe = core.createMessagePipe({
+        flags: core.CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+    });
+    expect(pipe.result).toBe(core.RESULT_OK);
+
+    test(pipe);
+
+    expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+    expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+  }
+
+  function runWithDataPipe(test) {
+    var pipe = core.createDataPipe();
+    expect(pipe.result).toBe(core.RESULT_OK);
+
+    test(pipe);
+
+    expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK);
+    expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK);
+  }
+
+  function runWithDataPipeWithOptions(test) {
+    var pipe = core.createDataPipe({
+        flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+        elementNumBytes: 1,
+        capacityNumBytes: 64
+        });
+    expect(pipe.result).toBe(core.RESULT_OK);
+
+    test(pipe);
+
+    expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK);
+    expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK);
+  }
+
+  function testNop(pipe) {
+  }
+
+  function testReadAndWriteMessage(pipe) {
+    var senderData = new Uint8Array(42);
+    for (var i = 0; i < senderData.length; ++i) {
+      senderData[i] = i * i;
+    }
+
+    var result = core.writeMessage(
+      pipe.handle0, senderData, [],
+      core.WRITE_MESSAGE_FLAG_NONE);
+
+    expect(result).toBe(core.RESULT_OK);
+
+    var read = core.readMessage(
+      pipe.handle1, core.READ_MESSAGE_FLAG_NONE);
+
+    expect(read.result).toBe(core.RESULT_OK);
+    expect(read.buffer.byteLength).toBe(42);
+    expect(read.handles.length).toBe(0);
+
+    var memory = new Uint8Array(read.buffer);
+    for (var i = 0; i < memory.length; ++i)
+      expect(memory[i]).toBe((i * i) & 0xFF);
+  }
+
+  function testReadAndWriteDataPipe(pipe) {
+    var senderData = new Uint8Array(42);
+    for (var i = 0; i < senderData.length; ++i) {
+      senderData[i] = i * i;
+    }
+
+    var write = core.writeData(
+      pipe.producerHandle, senderData,
+      core.WRITE_DATA_FLAG_ALL_OR_NONE);
+
+    expect(write.result).toBe(core.RESULT_OK);
+    expect(write.numBytes).toBe(42);
+
+    var read = core.readData(
+      pipe.consumerHandle, core.READ_DATA_FLAG_ALL_OR_NONE);
+
+    expect(read.result).toBe(core.RESULT_OK);
+    expect(read.buffer.byteLength).toBe(42);
+
+    var memory = new Uint8Array(read.buffer);
+    for (var i = 0; i < memory.length; ++i)
+      expect(memory[i]).toBe((i * i) & 0xFF);
+  }
+
+});
diff --git a/mojo/public/js/bindings/router.js b/mojo/public/js/bindings/router.js
new file mode 100644
index 0000000..3682392
--- /dev/null
+++ b/mojo/public/js/bindings/router.js
@@ -0,0 +1,135 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/router", [
+  "mojo/public/js/bindings/codec",
+  "mojo/public/js/bindings/connector",
+  "mojo/public/js/bindings/validator",
+], function(codec, connector, validator) {
+
+  function Router(handle, connectorFactory) {
+    if (connectorFactory === undefined)
+      connectorFactory = connector.Connector;
+    this.connector_ = new connectorFactory(handle);
+    this.incomingReceiver_ = null;
+    this.nextRequestID_ = 0;
+    this.responders_ = {};
+    this.payloadValidators_ = [];
+
+    this.connector_.setIncomingReceiver({
+        accept: this.handleIncomingMessage_.bind(this),
+    });
+    this.connector_.setErrorHandler({
+        onError: this.handleConnectionError_.bind(this),
+    });
+  }
+
+  Router.prototype.close = function() {
+    this.responders_ = {};  // Drop any responders.
+    this.connector_.close();
+  };
+
+  Router.prototype.accept = function(message) {
+    this.connector_.accept(message);
+  };
+
+  Router.prototype.reject = function(message) {
+    // TODO(mpcomplete): no way to trasmit errors over a Connection.
+  };
+
+  Router.prototype.acceptWithResponder = function(message, responder) {
+    // Reserve 0 in case we want it to convey special meaning in the future.
+    var requestID = this.nextRequestID_++;
+    if (requestID == 0)
+      requestID = this.nextRequestID_++;
+
+    message.setRequestID(requestID);
+    var result = this.connector_.accept(message);
+
+    this.responders_[requestID] = responder;
+
+    // TODO(mpcomplete): accept should return a Promise too, maybe?
+    if (result)
+      return Promise.resolve();
+    return Promise.reject(Error("Connection error"));
+  };
+
+  Router.prototype.setIncomingReceiver = function(receiver) {
+    this.incomingReceiver_ = receiver;
+  };
+
+  Router.prototype.setPayloadValidators = function(payloadValidators) {
+    this.payloadValidators_ = payloadValidators;
+  };
+
+  Router.prototype.encounteredError = function() {
+    return this.connector_.encounteredError();
+  };
+
+  Router.prototype.handleIncomingMessage_ = function(message) {
+    var noError = validator.validationError.NONE;
+    var messageValidator = new validator.Validator(message);
+    var err = messageValidator.validateMessageHeader();
+    for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
+      err = this.payloadValidators_[i](messageValidator);
+
+    if (err == noError)
+      this.handleValidIncomingMessage_(message);
+    else
+      this.handleInvalidIncomingMessage_(message, err);
+  };
+
+  Router.prototype.handleValidIncomingMessage_ = function(message) {
+    if (message.expectsResponse()) {
+      if (this.incomingReceiver_) {
+        this.incomingReceiver_.acceptWithResponder(message, this);
+      } else {
+        // If we receive a request expecting a response when the client is not
+        // listening, then we have no choice but to tear down the pipe.
+        this.close();
+      }
+    } else if (message.isResponse()) {
+      var reader = new codec.MessageReader(message);
+      var requestID = reader.requestID;
+      var responder = this.responders_[requestID];
+      delete this.responders_[requestID];
+      responder.accept(message);
+    } else {
+      if (this.incomingReceiver_)
+        this.incomingReceiver_.accept(message);
+    }
+  }
+
+  Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
+    this.close();
+  }
+
+  Router.prototype.handleConnectionError_ = function(result) {
+    for (var each in this.responders_)
+      this.responders_[each].reject(result);
+    this.close();
+  };
+
+  // The TestRouter subclass is only intended to be used in unit tests.
+  // It defeats valid message handling and delgates invalid message handling.
+
+  function TestRouter(handle, connectorFactory) {
+    Router.call(this, handle, connectorFactory);
+  }
+
+  TestRouter.prototype = Object.create(Router.prototype);
+
+  TestRouter.prototype.handleValidIncomingMessage_ = function() {
+  };
+
+  TestRouter.prototype.handleInvalidIncomingMessage_ =
+      function(message, error) {
+        this.validationErrorHandler(error);
+      };
+
+  var exports = {};
+  exports.Router = Router;
+  exports.TestRouter = TestRouter;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/struct_unittests.js b/mojo/public/js/bindings/struct_unittests.js
new file mode 100644
index 0000000..b9948a9
--- /dev/null
+++ b/mojo/public/js/bindings/struct_unittests.js
@@ -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.
+
+define([
+    "gin/test/expect",
+    "mojo/public/interfaces/bindings/tests/rect.mojom",
+    "mojo/public/interfaces/bindings/tests/test_structs.mojom"
+], function(expect,
+            rect,
+            testStructs) {
+
+  function testConstructors() {
+    var r = new rect.Rect();
+    expect(r).toEqual(new rect.Rect({x:0, y:0, width:0, height:0}));
+    expect(r).toEqual(new rect.Rect({foo:100, bar:200}));
+
+    r.x = 10;
+    r.y = 20;
+    r.width = 30;
+    r.height = 40;
+    var rp = new testStructs.RectPair({first: r, second: r});
+    expect(rp.first).toEqual(r);
+    expect(rp.second).toEqual(r);
+
+    expect(new testStructs.RectPair({second: r}).first).toBeNull();
+
+    var nr = new testStructs.NamedRegion();
+    expect(nr.name).toBeNull();
+    expect(nr.rects).toBeNull();
+    expect(nr).toEqual(new testStructs.NamedRegion({}));
+
+    nr.name = "foo";
+    nr.rects = [r, r, r];
+    expect(nr).toEqual(new testStructs.NamedRegion({
+      name: "foo",
+      rects: [r, r, r],
+    }));
+
+    var e = new testStructs.EmptyStruct();
+    expect(e).toEqual(new testStructs.EmptyStruct({foo:123}));
+  }
+
+  function testNoDefaultFieldValues() {
+    var s = new testStructs.NoDefaultFieldValues();
+    expect(s.f0).toEqual(false);
+
+    // f1 - f10, number type fields
+    for (var i = 1; i <= 10; i++)
+      expect(s["f" + i]).toEqual(0);
+
+    // f11,12 strings, f13-22 handles, f23-f26 arrays, f27,28 structs
+    for (var i = 11; i <= 28; i++)
+      expect(s["f" + i]).toBeNull();
+  }
+
+  function testDefaultFieldValues() {
+    var s = new testStructs.DefaultFieldValues();
+    expect(s.f0).toEqual(true);
+
+    // f1 - f12, number type fields
+    for (var i = 1; i <= 12; i++)
+      expect(s["f" + i]).toEqual(100);
+
+    // f13,14 "foo"
+    for (var i = 13; i <= 14; i++)
+      expect(s["f" + i]).toEqual("foo");
+
+    // f15,16 a default instance of Rect
+    var r = new rect.Rect();
+    expect(s.f15).toEqual(r);
+    expect(s.f16).toEqual(r);
+  }
+
+  testConstructors();
+  testNoDefaultFieldValues();
+  testDefaultFieldValues();
+  this.result = "PASS";
+});
\ No newline at end of file
diff --git a/mojo/public/js/bindings/support.js b/mojo/public/js/bindings/support.js
new file mode 100644
index 0000000..58df6bd
--- /dev/null
+++ b/mojo/public/js/bindings/support.js
@@ -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.
+
+// Module "mojo/public/js/bindings/support"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+
+while (1);
+
+/*
+ * Waits on the given handle until the state indicated by |signals| is
+ * satisfied.
+ *
+ * @param {MojoHandle} handle The handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {function (mojoResult)} callback Called with the result the wait is
+ * complete. See MojoWait for possible result codes.
+ *
+ * @return {MojoWaitId} A waitId that can be passed to cancelWait to cancel the
+ * wait.
+ */
+function asyncWait(handle, signals, callback) { [native code] }
+
+/*
+ * Cancels the asyncWait operation specified by the given |waitId|.
+ * @param {MojoWaitId} waitId The waitId returned by asyncWait.
+ */
+function cancelWait(waitId) { [native code] }
diff --git a/mojo/public/js/bindings/tests/validation_test_input_parser.js b/mojo/public/js/bindings/tests/validation_test_input_parser.js
new file mode 100644
index 0000000..98b1c19
--- /dev/null
+++ b/mojo/public/js/bindings/tests/validation_test_input_parser.js
@@ -0,0 +1,299 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Support for parsing binary sequences encoded as readable strings
+// or ".data" files. The input format is described here:
+// mojo/public/cpp/bindings/tests/validation_test_input_parser.h
+
+define([
+    "mojo/public/js/bindings/buffer"
+  ], function(buffer) {
+
+  // Files and Lines represent the raw text from an input string
+  // or ".data" file.
+
+  function InputError(message, line) {
+    this.message = message;
+    this.line = line;
+  }
+
+  InputError.prototype.toString = function() {
+    var s = 'Error: ' + this.message;
+    if (this.line)
+      s += ', at line ' +
+           (this.line.number + 1) + ': "' + this.line.contents + '"';
+    return s;
+  }
+
+  function File(contents) {
+    this.contents = contents;
+    this.index = 0;
+    this.lineNumber = 0;
+  }
+
+  File.prototype.endReached = function() {
+    return this.index >= this.contents.length;
+  }
+
+  File.prototype.nextLine = function() {
+    if (this.endReached())
+      return null;
+    var start = this.index;
+    var end = this.contents.indexOf('\n', start);
+    if (end == -1)
+      end = this.contents.length;
+    this.index = end + 1;
+    return new Line(this.contents.substring(start, end), this.lineNumber++);
+  }
+
+  function Line(contents, number) {
+    var i = contents.indexOf('//');
+    var s = (i == -1) ? contents.trim() : contents.substring(0, i).trim();
+    this.contents = contents;
+    this.items = (s.length > 0) ? s.split(/\s+/) : [];
+    this.index = 0;
+    this.number = number;
+  }
+
+  Line.prototype.endReached = function() {
+    return this.index >= this.items.length;
+  }
+
+  var ITEM_TYPE_SIZES = {
+    u1: 1, u2: 2, u4: 4, u8: 8, s1: 1, s2: 2, s4: 4, s8: 8, b: 1, f: 4, d: 8,
+    dist4: 4, dist8: 8, anchr: 0, handles: 0
+  };
+
+  function isValidItemType(type) {
+    return ITEM_TYPE_SIZES[type] !== undefined;
+  }
+
+  Line.prototype.nextItem = function() {
+    if (this.endReached())
+      return null;
+
+    var itemString = this.items[this.index++];
+    var type = 'u1';
+    var value = itemString;
+
+    if (itemString.charAt(0) == '[') {
+      var i = itemString.indexOf(']');
+      if (i != -1 && i + 1 < itemString.length) {
+        type = itemString.substring(1, i);
+        value = itemString.substring(i + 1);
+      } else {
+        throw new InputError('invalid item', this);
+      }
+    }
+    if (!isValidItemType(type))
+      throw new InputError('invalid item type', this);
+
+    return new Item(this, type, value);
+  }
+
+  // The text for each whitespace delimited binary data "item" is represented
+  // by an Item.
+
+  function Item(line, type, value) {
+    this.line = line;
+    this.type = type;
+    this.value = value;
+    this.size = ITEM_TYPE_SIZES[type];
+  }
+
+  Item.prototype.isFloat = function() {
+    return this.type == 'f' || this.type == 'd';
+  }
+
+  Item.prototype.isInteger = function() {
+    return ['u1', 'u2', 'u4', 'u8',
+            's1', 's2', 's4', 's8'].indexOf(this.type) != -1;
+  }
+
+  Item.prototype.isNumber = function() {
+    return this.isFloat() || this.isInteger();
+  }
+
+  Item.prototype.isByte = function() {
+    return this.type == 'b';
+  }
+
+  Item.prototype.isDistance = function() {
+    return this.type == 'dist4' || this.type == 'dist8';
+  }
+
+  Item.prototype.isAnchor = function() {
+    return this.type == 'anchr';
+  }
+
+  Item.prototype.isHandles = function() {
+    return this.type == 'handles';
+  }
+
+  // A TestMessage represents the complete binary message loaded from an input
+  // string or ".data" file. The parseTestMessage() function below constructs
+  // a TestMessage from a File.
+
+  function TestMessage(byteLength) {
+    this.index = 0;
+    this.buffer = new buffer.Buffer(byteLength);
+    this.distances = {};
+    this.handleCount = 0;
+  }
+
+  function checkItemNumberValue(item, n, min, max) {
+    if (n < min || n > max)
+      throw new InputError('invalid item value', item.line);
+  }
+
+  TestMessage.prototype.addNumber = function(item) {
+    var n = item.isInteger() ? parseInt(item.value) : parseFloat(item.value);
+    if (Number.isNaN(n))
+      throw new InputError("can't parse item value", item.line);
+
+    switch(item.type) {
+      case 'u1':
+        checkItemNumberValue(item, n, 0, 0xFF);
+        this.buffer.setUint8(this.index, n);
+        break;
+      case 'u2':
+        checkItemNumberValue(item, n, 0, 0xFFFF);
+        this.buffer.setUint16(this.index, n);
+        break;
+      case 'u4':
+        checkItemNumberValue(item, n, 0, 0xFFFFFFFF);
+        this.buffer.setUint32(this.index, n);
+        break;
+      case 'u8':
+        checkItemNumberValue(item, n, 0, Number.MAX_SAFE_INTEGER);
+        this.buffer.setUint64(this.index, n);
+        break;
+      case 's1':
+        checkItemNumberValue(item, n, -128, 127);
+        this.buffer.setInt8(this.index, n);
+        break;
+      case 's2':
+        checkItemNumberValue(item, n, -32768, 32767);
+        this.buffer.setInt16(this.index, n);
+        break;
+      case 's4':
+        checkItemNumberValue(item, n, -2147483648, 2147483647);
+        this.buffer.setInt32(this.index, n);
+        break;
+      case 's8':
+        checkItemNumberValue(item, n,
+                             Number.MIN_SAFE_INTEGER,
+                             Number.MAX_SAFE_INTEGER);
+        this.buffer.setInt64(this.index, n);
+        break;
+      case 'f':
+        this.buffer.setFloat32(this.index, n);
+        break;
+      case 'd':
+        this.buffer.setFloat64(this.index, n);
+        break;
+
+      default:
+        throw new InputError('unrecognized item type', item.line);
+      }
+  }
+
+  TestMessage.prototype.addByte = function(item) {
+    if (!/^[01]{8}$/.test(item.value))
+      throw new InputError('invalid byte item value', item.line);
+    function b(i) {
+      return (item.value.charAt(7 - i) == '1') ? 1 << i : 0;
+    }
+    var n = b(0) | b(1) | b(2) | b(3) | b(4) | b(5) | b(6) | b(7);
+    this.buffer.setUint8(this.index, n);
+  }
+
+  TestMessage.prototype.addDistance = function(item) {
+    if (this.distances[item.value])
+      throw new InputError('duplicate distance item', item.line);
+    this.distances[item.value] = {index: this.index, item: item};
+  }
+
+  TestMessage.prototype.addAnchor = function(item) {
+    var dist = this.distances[item.value];
+    if (!dist)
+      throw new InputError('unmatched anchor item', item.line);
+    delete this.distances[item.value];
+
+    var n = this.index - dist.index;
+    // TODO(hansmuller): validate n
+
+    if (dist.item.type == 'dist4')
+      this.buffer.setUint32(dist.index, n);
+    else if (dist.item.type == 'dist8')
+      this.buffer.setUint64(dist.index, n);
+    else
+      throw new InputError('unrecognzed distance item type', dist.item.line);
+  }
+
+  TestMessage.prototype.addHandles = function(item) {
+    this.handleCount = parseInt(item.value);
+    if (Number.isNaN(this.handleCount))
+      throw new InputError("can't parse handleCount", item.line);
+  }
+
+  TestMessage.prototype.addItem = function(item) {
+    if (item.isNumber())
+      this.addNumber(item);
+    else if (item.isByte())
+      this.addByte(item);
+    else if (item.isDistance())
+      this.addDistance(item);
+    else if (item.isAnchor())
+      this.addAnchor(item);
+    else if (item.isHandles())
+      this.addHandles(item);
+    else
+      throw new InputError('unrecognized item type', item.line);
+
+    this.index += item.size;
+  }
+
+  TestMessage.prototype.unanchoredDistances = function() {
+    var names = null;
+    for (var name in this.distances) {
+      if (this.distances.hasOwnProperty(name))
+        names = (names === null) ? name : names + ' ' + name;
+    }
+    return names;
+  }
+
+  function parseTestMessage(text) {
+    var file = new File(text);
+    var items = [];
+    var messageLength = 0;
+    while(!file.endReached()) {
+      var line = file.nextLine();
+      while (!line.endReached()) {
+        var item = line.nextItem();
+        if (item.isHandles() && items.length > 0)
+          throw new InputError('handles item is not first');
+        messageLength += item.size;
+        items.push(item);
+      }
+    }
+
+    var msg = new TestMessage(messageLength);
+    for (var i = 0; i < items.length; i++)
+      msg.addItem(items[i]);
+
+    if (messageLength != msg.index)
+      throw new InputError('failed to compute message length');
+    var names = msg.unanchoredDistances();
+    if (names)
+      throw new InputError('no anchors for ' + names, 0);
+
+    return msg;
+  }
+
+  var exports = {};
+  exports.parseTestMessage = parseTestMessage;
+  exports.InputError = InputError;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/unicode.js b/mojo/public/js/bindings/unicode.js
new file mode 100644
index 0000000..ba0f00f
--- /dev/null
+++ b/mojo/public/js/bindings/unicode.js
@@ -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.
+
+/**
+ * Defines functions for translating between JavaScript strings and UTF8 strings
+ * stored in ArrayBuffers. There is much room for optimization in this code if
+ * it proves necessary.
+ */
+define("mojo/public/js/bindings/unicode", function() {
+  /**
+   * Decodes the UTF8 string from the given buffer.
+   * @param {ArrayBufferView} buffer The buffer containing UTF8 string data.
+   * @return {string} The corresponding JavaScript string.
+   */
+  function decodeUtf8String(buffer) {
+    return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer)));
+  }
+
+  /**
+   * Encodes the given JavaScript string into UTF8.
+   * @param {string} str The string to encode.
+   * @param {ArrayBufferView} outputBuffer The buffer to contain the result.
+   * Should be pre-allocated to hold enough space. Use |utf8Length| to determine
+   * how much space is required.
+   * @return {number} The number of bytes written to |outputBuffer|.
+   */
+  function encodeUtf8String(str, outputBuffer) {
+    var utf8String = unescape(encodeURIComponent(str));
+    if (outputBuffer.length < utf8String.length)
+      throw new Error("Buffer too small for encodeUtf8String");
+    for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++)
+      outputBuffer[i] = utf8String.charCodeAt(i);
+    return i;
+  }
+
+  /**
+   * Returns the number of bytes that a UTF8 encoding of the JavaScript string
+   * |str| would occupy.
+   */
+  function utf8Length(str) {
+    var utf8String = unescape(encodeURIComponent(str));
+    return utf8String.length;
+  }
+
+  var exports = {};
+  exports.decodeUtf8String = decodeUtf8String;
+  exports.encodeUtf8String = encodeUtf8String;
+  exports.utf8Length = utf8Length;
+  return exports;
+});
diff --git a/mojo/public/js/bindings/validation_unittests.js b/mojo/public/js/bindings/validation_unittests.js
new file mode 100644
index 0000000..73fd9c7
--- /dev/null
+++ b/mojo/public/js/bindings/validation_unittests.js
@@ -0,0 +1,302 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+    "console",
+    "file",
+    "gin/test/expect",
+    "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
+    "mojo/public/js/bindings/buffer",
+    "mojo/public/js/bindings/codec",
+    "mojo/public/js/bindings/connection",
+    "mojo/public/js/bindings/connector",
+    "mojo/public/js/bindings/core",
+    "mojo/public/js/bindings/tests/validation_test_input_parser",
+    "mojo/public/js/bindings/router",
+    "mojo/public/js/bindings/validator",
+], function(console,
+            file,
+            expect,
+            testInterface,
+            buffer,
+            codec,
+            connection,
+            connector,
+            core,
+            parser,
+            router,
+            validator) {
+
+  var noError = validator.validationError.NONE;
+
+  function checkTestMessageParser() {
+    function TestMessageParserFailure(message, input) {
+      this.message = message;
+      this.input = input;
+    }
+
+    TestMessageParserFailure.prototype.toString = function() {
+      return 'Error: ' + this.message + ' for "' + this.input + '"';
+    }
+
+    function checkData(data, expectedData, input) {
+      if (data.byteLength != expectedData.byteLength) {
+        var s = "message length (" + data.byteLength + ") doesn't match " +
+            "expected length: " + expectedData.byteLength;
+        throw new TestMessageParserFailure(s, input);
+      }
+
+      for (var i = 0; i < data.byteLength; i++) {
+        if (data.getUint8(i) != expectedData.getUint8(i)) {
+          var s = 'message data mismatch at byte offset ' + i;
+          throw new TestMessageParserFailure(s, input);
+        }
+      }
+    }
+
+    function testFloatItems() {
+      var input = '[f]+.3e9 [d]-10.03';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(12);
+      expectedData.setFloat32(0, +.3e9);
+      expectedData.setFloat64(4, -10.03);
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testUnsignedIntegerItems() {
+      var input = '[u1]0x10// hello world !! \n\r  \t [u2]65535 \n' +
+          '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(17);
+      expectedData.setUint8(0, 0x10);
+      expectedData.setUint16(1, 65535);
+      expectedData.setUint32(3, 65536);
+      expectedData.setUint64(7, 0xFFFFFFFFFFFFF);
+      expectedData.setUint8(15, 0);
+      expectedData.setUint8(16, 0xff);
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testSignedIntegerItems() {
+      var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(15);
+      expectedData.setInt64(0, -0x800);
+      expectedData.setInt8(8, -128);
+      expectedData.setInt16(9, 0);
+      expectedData.setInt32(11, -40);
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testByteItems() {
+      var input = '[b]00001011 [b]10000000  // hello world\n [b]00000000';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(3);
+      expectedData.setUint8(0, 11);
+      expectedData.setUint8(1, 128);
+      expectedData.setUint8(2, 0);
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testAnchors() {
+      var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(14);
+      expectedData.setUint32(0, 14);
+      expectedData.setUint8(4, 0);
+      expectedData.setUint64(5, 9);
+      expectedData.setUint8(13, 0);
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testHandles() {
+      var input = '// This message has handles! \n[handles]50 [u8]2';
+      var msg = parser.parseTestMessage(input);
+      var expectedData = new buffer.Buffer(8);
+      expectedData.setUint64(0, 2);
+
+      if (msg.handleCount != 50) {
+        var s = 'wrong handle count (' + msg.handleCount + ')';
+        throw new TestMessageParserFailure(s, input);
+      }
+      checkData(msg.buffer, expectedData, input);
+    }
+
+    function testEmptyInput() {
+      var msg = parser.parseTestMessage('');
+      if (msg.buffer.byteLength != 0)
+        throw new TestMessageParserFailure('expected empty message', '');
+    }
+
+    function testBlankInput() {
+      var input = '    \t  // hello world \n\r \t// the answer is 42   ';
+      var msg = parser.parseTestMessage(input);
+      if (msg.buffer.byteLength != 0)
+        throw new TestMessageParserFailure('expected empty message', input);
+    }
+
+    function testInvalidInput() {
+      function parserShouldFail(input) {
+        try {
+          parser.parseTestMessage(input);
+        } catch (e) {
+          if (e instanceof parser.InputError)
+            return;
+          throw new TestMessageParserFailure(
+            'unexpected exception ' + e.toString(), input);
+        }
+        throw new TestMessageParserFailure("didn't detect invalid input", file);
+      }
+
+      ['/ hello world',
+       '[u1]x',
+       '[u2]-1000',
+       '[u1]0x100',
+       '[s2]-0x8001',
+       '[b]1',
+       '[b]1111111k',
+       '[dist4]unmatched',
+       '[anchr]hello [dist8]hello',
+       '[dist4]a [dist4]a [anchr]a',
+       // '[dist4]a [anchr]a [dist4]a [anchr]a',
+       '0 [handles]50'
+      ].forEach(parserShouldFail);
+    }
+
+    try {
+      testFloatItems();
+      testUnsignedIntegerItems();
+      testSignedIntegerItems();
+      testByteItems();
+      testInvalidInput();
+      testEmptyInput();
+      testBlankInput();
+      testHandles();
+      testAnchors();
+    } catch (e) {
+      return e.toString();
+    }
+    return null;
+  }
+
+  function getMessageTestFiles(key) {
+    var sourceRoot = file.getSourceRootDirectory();
+    expect(sourceRoot).not.toBeNull();
+
+    var testDir = sourceRoot +
+      "/mojo/public/interfaces/bindings/tests/data/validation/";
+    var testFiles = file.getFilesInDirectory(testDir);
+    expect(testFiles).not.toBeNull();
+    expect(testFiles.length).toBeGreaterThan(0);
+
+    // The matching ".data" pathnames with the extension removed.
+    return testFiles.filter(function(s) {
+      return s.substr(-5) == ".data";
+    }).map(function(s) {
+      return testDir + s.slice(0, -5);
+    }).filter(function(s) {
+      return s.indexOf(key) != -1;
+    });
+  }
+
+  function readTestMessage(filename) {
+    var contents = file.readFileToString(filename + ".data");
+    expect(contents).not.toBeNull();
+    return parser.parseTestMessage(contents);
+  }
+
+  function readTestExpected(filename) {
+    var contents = file.readFileToString(filename + ".expected");
+    expect(contents).not.toBeNull();
+    return contents.trim();
+  }
+
+  function checkValidationResult(testFile, err) {
+    var actualResult = (err === noError) ? "PASS" : err;
+    var expectedResult = readTestExpected(testFile);
+    if (actualResult != expectedResult)
+      console.log("[Test message validation failed: " + testFile + " ]");
+    expect(actualResult).toEqual(expectedResult);
+  }
+
+  function testMessageValidation(key, filters) {
+    var testFiles = getMessageTestFiles(key);
+    expect(testFiles.length).toBeGreaterThan(0);
+
+    for (var i = 0; i < testFiles.length; i++) {
+      // TODO(hansmuller): Temporarily skipping array pointer overflow tests.
+      if (testFiles[i].indexOf("overflow") != -1) {
+        console.log("[Skipping " + testFiles[i] + "]");
+        continue;
+      }
+
+      var testMessage = readTestMessage(testFiles[i]);
+      var handles = new Array(testMessage.handleCount);
+      var message = new codec.Message(testMessage.buffer, handles);
+      var messageValidator = new validator.Validator(message);
+
+      var err = messageValidator.validateMessageHeader();
+      for (var j = 0; err === noError && j < filters.length; ++j)
+        err = filters[j](messageValidator);
+
+      checkValidationResult(testFiles[i], err);
+    }
+  }
+
+  function testConformanceMessageValidation() {
+    testMessageValidation("conformance_", [
+        testInterface.ConformanceTestInterfaceStub.prototype.validator]);
+  }
+
+  function testNotImplementedMessageValidation() {
+    testMessageValidation("not_implemented_", [
+        testInterface.ConformanceTestInterfaceStub.prototype.validator]);
+  }
+
+  function testIntegratedMessageValidation() {
+    var testFiles = getMessageTestFiles("integration_");
+    expect(testFiles.length).toBeGreaterThan(0);
+
+    for (var i = 0; i < testFiles.length; i++) {
+      // TODO(hansmuller): Temporarily skipping array pointer overflow tests.
+      if (testFiles[i].indexOf("overflow") != -1) {
+        console.log("[Skipping " + testFiles[i] + "]");
+        continue;
+      }
+
+      var testMessage = readTestMessage(testFiles[i]);
+      var handles = new Array(testMessage.handleCount);
+      var testMessagePipe = new core.createMessagePipe();
+      expect(testMessagePipe.result).toBe(core.RESULT_OK);
+
+      var writeMessageValue = core.writeMessage(
+          testMessagePipe.handle0,
+          new Uint8Array(testMessage.buffer.arrayBuffer),
+          new Array(testMessage.handleCount),
+          core.WRITE_MESSAGE_FLAG_NONE);
+      expect(writeMessageValue).toBe(core.RESULT_OK);
+
+      var testConnection = new connection.TestConnection(
+          testMessagePipe.handle1,
+          testInterface.IntegrationTestInterface1Stub,
+          testInterface.IntegrationTestInterface2Proxy);
+
+      var validationError = noError;
+      testConnection.router_.validationErrorHandler = function(err) {
+        validationError = err;
+      }
+
+      testConnection.router_.connector_.deliverMessage();
+      checkValidationResult(testFiles[i], validationError);
+
+      testConnection.close();
+      expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
+    }
+  }
+
+  expect(checkTestMessageParser()).toBeNull();
+  testConformanceMessageValidation();
+  testIntegratedMessageValidation();
+  this.result = "PASS";
+});
diff --git a/mojo/public/js/bindings/validator.js b/mojo/public/js/bindings/validator.js
new file mode 100644
index 0000000..de75656
--- /dev/null
+++ b/mojo/public/js/bindings/validator.js
@@ -0,0 +1,291 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/bindings/validator", [
+  "mojo/public/js/bindings/codec",
+], function(codec) {
+
+  var validationError = {
+    NONE: 'VALIDATION_ERROR_NONE',
+    MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
+    ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
+    UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
+    UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
+    ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
+    UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
+    ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
+    UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
+    MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
+        'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION',
+    MESSAGE_HEADER_MISSING_REQUEST_ID:
+        'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID'
+  };
+
+  var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
+
+  function isStringClass(cls) {
+    return cls === codec.String || cls === codec.NullableString;
+  }
+
+  function isHandleClass(cls) {
+    return cls === codec.Handle || cls === codec.NullableHandle;
+  }
+
+  function isNullable(type) {
+    return type === codec.NullableString || type === codec.NullableHandle ||
+        type instanceof codec.NullableArrayOf ||
+        type instanceof codec.NullablePointerTo;
+  }
+
+  function Validator(message) {
+    this.message = message;
+    this.offset = 0;
+    this.handleIndex = 0;
+  }
+
+  Object.defineProperty(Validator.prototype, "offsetLimit", {
+    get: function() { return this.message.buffer.byteLength; }
+  });
+
+  Object.defineProperty(Validator.prototype, "handleIndexLimit", {
+    get: function() { return this.message.handles.length; }
+  });
+
+  // True if we can safely allocate a block of bytes from start to
+  // to start + numBytes.
+  Validator.prototype.isValidRange = function(start, numBytes) {
+    // Only positive JavaScript integers that are less than 2^53
+    // (Number.MAX_SAFE_INTEGER) can be represented exactly.
+    if (start < this.offset || numBytes <= 0 ||
+        !Number.isSafeInteger(start) ||
+        !Number.isSafeInteger(numBytes))
+      return false;
+
+    var newOffset = start + numBytes;
+    if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
+      return false;
+
+    return true;
+  }
+
+  Validator.prototype.claimRange = function(start, numBytes) {
+    if (this.isValidRange(start, numBytes)) {
+      this.offset = start + numBytes;
+      return true;
+    }
+    return false;
+  }
+
+  Validator.prototype.claimHandle = function(index) {
+    if (index === codec.kEncodedInvalidHandleValue)
+      return true;
+
+    if (index < this.handleIndex || index >= this.handleIndexLimit)
+      return false;
+
+    // This is safe because handle indices are uint32.
+    this.handleIndex = index + 1;
+    return true;
+  }
+
+  Validator.prototype.validateHandle = function(offset, nullable) {
+    var index = this.message.buffer.getUint32(offset);
+
+    if (index === codec.kEncodedInvalidHandleValue)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
+
+    if (!this.claimHandle(index))
+      return validationError.ILLEGAL_HANDLE;
+    return validationError.NONE;
+  }
+
+  Validator.prototype.validateStructHeader =
+      function(offset, minNumBytes, minNumFields) {
+    if (!codec.isAligned(offset))
+      return validationError.MISALIGNED_OBJECT;
+
+    if (!this.isValidRange(offset, codec.kStructHeaderSize))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    var numBytes = this.message.buffer.getUint32(offset);
+    var numFields = this.message.buffer.getUint32(offset + 4);
+
+    if (numBytes < minNumBytes || numFields < minNumFields)
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+
+    if (!this.claimRange(offset, numBytes))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    return validationError.NONE;
+  }
+
+  Validator.prototype.validateMessageHeader = function() {
+    var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 2);
+    if (err != validationError.NONE)
+      return err;
+
+    var numBytes = this.message.getHeaderNumBytes();
+    var numFields = this.message.getHeaderNumFields();
+
+    var validNumFieldsAndNumBytes =
+        (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
+        (numFields == 3 &&
+         numBytes == codec.kMessageWithRequestIDHeaderSize) ||
+        (numFields > 3 &&
+         numBytes >= codec.kMessageWithRequestIDHeaderSize);
+    if (!validNumFieldsAndNumBytes)
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+
+    var expectsResponse = this.message.expectsResponse();
+    var isResponse = this.message.isResponse();
+
+    if (numFields == 2 && (expectsResponse || isResponse))
+      return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
+
+    if (isResponse && expectsResponse)
+      return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
+
+    return validationError.NONE;
+  }
+
+  // Returns the message.buffer relative offset this pointer "points to",
+  // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
+  // pointer's value is not valid.
+  Validator.prototype.decodePointer = function(offset) {
+    var pointerValue = this.message.buffer.getUint64(offset);
+    if (pointerValue === 0)
+      return NULL_MOJO_POINTER;
+    var bufferOffset = offset + pointerValue;
+    return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
+  }
+
+  Validator.prototype.validateArrayPointer = function(
+      offset, elementSize, expectedElementCount, elementType, nullable) {
+    var arrayOffset = this.decodePointer(offset);
+    if (arrayOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (arrayOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    return this.validateArray(
+        arrayOffset, elementSize, expectedElementCount, elementType);
+  }
+
+  Validator.prototype.validateStructPointer = function(
+        offset, structClass, nullable) {
+    var structOffset = this.decodePointer(offset);
+    if (structOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (structOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    return structClass.validate(this, structOffset);
+  }
+
+  Validator.prototype.validateStringPointer = function(offset, nullable) {
+    return this.validateArrayPointer(
+        offset, codec.Uint8.encodedSize, 0, codec.Uint8, nullable);
+  }
+
+  // Similar to Array_Data<T>::Validate()
+  // mojo/public/cpp/bindings/lib/array_internal.h
+
+  Validator.prototype.validateArray =
+      function (offset, elementSize, expectedElementCount, elementType) {
+    if (!codec.isAligned(offset))
+      return validationError.MISALIGNED_OBJECT;
+
+    if (!this.isValidRange(offset, codec.kArrayHeaderSize))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    var numBytes = this.message.buffer.getUint32(offset);
+    var numElements = this.message.buffer.getUint32(offset + 4);
+
+    // Note: this computation is "safe" because elementSize <= 8 and
+    // numElements is a uint32.
+    var elementsTotalSize = (elementType === codec.PackedBool) ?
+        Math.ceil(numElements / 8) : (elementSize * numElements);
+
+    if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
+      return validationError.UNEXPECTED_ARRAY_HEADER;
+
+    if (expectedElementCount != 0 && numElements != expectedElementCount)
+      return validationError.UNEXPECTED_ARRAY_HEADER;
+
+    if (!this.claimRange(offset, numBytes))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    // Validate the array's elements if they are pointers or handles.
+
+    var elementsOffset = offset + codec.kArrayHeaderSize;
+    var nullable = isNullable(elementType);
+
+    if (isHandleClass(elementType))
+      return this.validateHandleElements(elementsOffset, numElements, nullable);
+    if (isStringClass(elementType))
+      return this.validateArrayElements(
+          elementsOffset, numElements, codec.Uint8, nullable)
+    if (elementType instanceof codec.PointerTo)
+      return this.validateStructElements(
+          elementsOffset, numElements, elementType.cls, nullable);
+    if (elementType instanceof codec.ArrayOf)
+      return this.validateArrayElements(
+          elementsOffset, numElements, elementType.cls, nullable);
+
+    return validationError.NONE;
+  }
+
+  // Note: the |offset + i * elementSize| computation in the validateFooElements
+  // methods below is "safe" because elementSize <= 8, offset and
+  // numElements are uint32, and 0 <= i < numElements.
+
+  Validator.prototype.validateHandleElements =
+      function(offset, numElements, nullable) {
+    var elementSize = codec.Handle.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateHandle(elementOffset, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  }
+
+  // The elementClass parameter is the element type of the element arrays.
+  Validator.prototype.validateArrayElements =
+      function(offset, numElements, elementClass, nullable) {
+    var elementSize = codec.PointerTo.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateArrayPointer(
+          elementOffset, elementClass.encodedSize, 0, elementClass, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  }
+
+  Validator.prototype.validateStructElements =
+      function(offset, numElements, structClass, nullable) {
+    var elementSize = codec.PointerTo.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err =
+          this.validateStructPointer(elementOffset, structClass, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  }
+
+  var exports = {};
+  exports.validationError = validationError;
+  exports.Validator = Validator;
+  return exports;
+});
diff --git a/mojo/public/platform/native/BUILD.gn b/mojo/public/platform/native/BUILD.gn
new file mode 100644
index 0000000..a3e720b
--- /dev/null
+++ b/mojo/public/platform/native/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("system_thunks") {
+  visibility = [
+    "//mojo/public/c/system:for_component",
+    "//mojo/public/c/system:for_shared_library",
+  ]
+
+  sources = [
+    "system_thunks.h",
+    "system_thunks.cc",
+  ]
+  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
+  deps = [ "//mojo/public/c/system" ]
+
+  # The GYP target analogous to this one builds this code into a
+  # static library.  When building for Android, both the GYP and GN
+  # builds add --exclude-libs=ALL globally, which means that all
+  # symbols in static libraries are excluded from export.  That's a
+  # problem, as code outside this target needs to be able to call
+  # MojoSetSystemThunks(). Therefore, the GYP target needs to specifiy
+  # that all dependent targets remove that link flag. Since GN uses a
+  # source_set here, this flag change is not needed.
+}
+
+# GYP version: mojo/mojo_public.gypi:mojo_gles2
+source_set("gles2_thunks") {
+  visibility = [ "//mojo/public/gles2:for_shared_library" ]
+
+  sources = [
+    "gles2_thunks.cc",
+    "gles2_thunks.h",
+    "gles2_impl_thunks.cc",
+    "gles2_impl_thunks.h",
+    "gles2_impl_chromium_texture_mailbox_thunks.cc",
+    "gles2_impl_chromium_texture_mailbox_thunks.h",
+    "gles2_impl_chromium_sync_point_thunks.cc",
+    "gles2_impl_chromium_sync_point_thunks.h",
+  ]
+
+  defines = [
+    "MOJO_GLES2_IMPLEMENTATION",
+  ]
+
+  configs += [ "//third_party/khronos:khronos_headers" ]
+
+  deps = [
+    "//mojo/public/c/gles2",
+    "//mojo/public/c/environment",
+    "//mojo/public/c/system",
+  ]
+
+  if (is_mac) {
+    # TODO(GYP): Make it a run-path dependent library.
+    # 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+  }
+}
diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc
new file mode 100644
index 0000000..0a47f33
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc
@@ -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.
+
+#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumSyncPointThunks g_impl_chromium_sync_point_thunks =
+    {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType gl##Function PARAMETERS {                             \
+    assert(g_impl_chromium_sync_point_thunks.Function);            \
+    return g_impl_chromium_sync_point_thunks.Function ARGUMENTS;   \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumSyncPointThunks(
+    const MojoGLES2ImplChromiumSyncPointThunks*
+        gles2_impl_chromium_sync_point_thunks) {
+  if (gles2_impl_chromium_sync_point_thunks->size >=
+      sizeof(g_impl_chromium_sync_point_thunks))
+    g_impl_chromium_sync_point_thunks = *gles2_impl_chromium_sync_point_thunks;
+  return sizeof(g_impl_chromium_sync_point_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h
new file mode 100644
index 0000000..af6817e
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_sync_point.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_sync_point extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumSyncPointThunks {
+  size_t size;  // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumSyncPointThunks
+MojoMakeGLES2ImplChromiumSyncPointThunks() {
+  MojoGLES2ImplChromiumSyncPointThunks gles2_impl_chromium_sync_point_thunks = {
+      sizeof(MojoGLES2ImplChromiumSyncPointThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+  };
+
+  return gles2_impl_chromium_sync_point_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_sync_point_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumSyncPointThunksFn)(
+    const MojoGLES2ImplChromiumSyncPointThunks* thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc
new file mode 100644
index 0000000..db2bc81
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumTextureMailboxThunks
+    g_impl_chromium_texture_mailbox_thunks = {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS)    \
+  ReturnType gl##Function PARAMETERS {                                \
+    assert(g_impl_chromium_texture_mailbox_thunks.Function);          \
+    return g_impl_chromium_texture_mailbox_thunks.Function ARGUMENTS; \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumTextureMailboxThunks(
+    const MojoGLES2ImplChromiumTextureMailboxThunks*
+        gles2_impl_chromium_texture_mailbox_thunks) {
+  if (gles2_impl_chromium_texture_mailbox_thunks->size >=
+      sizeof(g_impl_chromium_texture_mailbox_thunks))
+    g_impl_chromium_texture_mailbox_thunks =
+        *gles2_impl_chromium_texture_mailbox_thunks;
+  return sizeof(g_impl_chromium_texture_mailbox_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h
new file mode 100644
index 0000000..839e441
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_texture_mailbox.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_texture_mailbox extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumTextureMailboxThunks {
+  size_t size;  // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumTextureMailboxThunks
+MojoMakeGLES2ImplChromiumTextureMailboxThunks() {
+  MojoGLES2ImplChromiumTextureMailboxThunks
+      gles2_impl_chromium_texture_mailbox_thunks = {
+          sizeof(MojoGLES2ImplChromiumTextureMailboxThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+      };
+
+  return gles2_impl_chromium_texture_mailbox_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_texture_mailbox_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumTextureMailboxThunksFn)(
+    const MojoGLES2ImplChromiumTextureMailboxThunks* thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_impl_thunks.cc b/mojo/public/platform/native/gles2_impl_thunks.cc
new file mode 100644
index 0000000..66dbc39
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_thunks.cc
@@ -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.
+
+#include "mojo/public/platform/native/gles2_impl_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplThunks g_impl_thunks = {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType gl##Function PARAMETERS {                             \
+    assert(g_impl_thunks.Function);                                \
+    return g_impl_thunks.Function ARGUMENTS;                       \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t
+MojoSetGLES2ImplThunks(const MojoGLES2ImplThunks* gles2_impl_thunks) {
+  if (gles2_impl_thunks->size >= sizeof(g_impl_thunks))
+    g_impl_thunks = *gles2_impl_thunks;
+  return sizeof(g_impl_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_thunks.h b/mojo/public/platform/native/gles2_impl_thunks.h
new file mode 100644
index 0000000..04174fa
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_thunks.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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+
+// Like MojoGLES2ControlThunks, but specifies the frozen GLES2 API. Separated
+// out as MojoGLES2ControlThunks may be modified and added to, but this
+// interface is frozen.
+#pragma pack(push, 8)
+struct MojoGLES2ImplThunks {
+  size_t size;  // Should be set to sizeof(MojoGLES2ImplThunks).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplThunks MojoMakeGLES2ImplThunks() {
+  MojoGLES2ImplThunks gles2_impl_thunks = {
+      sizeof(MojoGLES2ImplThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+  };
+
+  return gles2_impl_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetGLES2ImplThunksFn mojo_set_gles2_impl_thunks_fn =
+//     reinterpret_cast<MojoSetGLES2ImplThunksFn>(
+//         app_library.GetFunctionPointer("MojoSetGLES2ImplThunks"));
+// The expected size of |gles2_impl_thunks| is returned.
+// The contents of |gles2_impl_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplThunksFn)(
+    const MojoGLES2ImplThunks* gles2_impl_thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_thunks.cc b/mojo/public/platform/native/gles2_thunks.cc
new file mode 100644
index 0000000..365fac9
--- /dev/null
+++ b/mojo/public/platform/native/gles2_thunks.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 "mojo/public/platform/native/gles2_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+
+static MojoGLES2ControlThunks g_control_thunks = {0};
+
+MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle,
+                                        MojoGLES2ContextLost lost_callback,
+                                        void* closure,
+                                        const MojoAsyncWaiter* async_waiter) {
+  assert(g_control_thunks.GLES2CreateContext);
+  return g_control_thunks.GLES2CreateContext(
+      handle, lost_callback, closure, async_waiter);
+}
+
+void MojoGLES2DestroyContext(MojoGLES2Context context) {
+  assert(g_control_thunks.GLES2DestroyContext);
+  g_control_thunks.GLES2DestroyContext(context);
+}
+
+void MojoGLES2MakeCurrent(MojoGLES2Context context) {
+  assert(g_control_thunks.GLES2MakeCurrent);
+  g_control_thunks.GLES2MakeCurrent(context);
+}
+
+void MojoGLES2SwapBuffers() {
+  assert(g_control_thunks.GLES2SwapBuffers);
+  g_control_thunks.GLES2SwapBuffers();
+}
+
+void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) {
+  assert(g_control_thunks.GLES2GetGLES2Interface);
+  return g_control_thunks.GLES2GetGLES2Interface(context);
+}
+
+void* MojoGLES2GetContextSupport(MojoGLES2Context context) {
+  assert(g_control_thunks.GLES2GetContextSupport);
+  return g_control_thunks.GLES2GetContextSupport(context);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ControlThunks(
+    const MojoGLES2ControlThunks* gles2_control_thunks) {
+  if (gles2_control_thunks->size >= sizeof(g_control_thunks))
+    g_control_thunks = *gles2_control_thunks;
+  return sizeof(g_control_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/gles2_thunks.h b/mojo/public/platform/native/gles2_thunks.h
new file mode 100644
index 0000000..4718ab3
--- /dev/null
+++ b/mojo/public/platform/native/gles2_thunks.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_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+
+// Structure used to bind the interface which manipulates GLES2 surfaces to a
+// DSO to theose of the embedder.
+//
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoGLES2ControlThunks {
+  size_t size;  // Should be set to sizeof(MojoGLES2ControlThunks).
+
+  MojoGLES2Context (*GLES2CreateContext)(MojoHandle handle,
+                                         MojoGLES2ContextLost lost_callback,
+                                         void* closure,
+                                         const MojoAsyncWaiter* async_waiter);
+  void (*GLES2DestroyContext)(MojoGLES2Context context);
+  void (*GLES2MakeCurrent)(MojoGLES2Context context);
+  void (*GLES2SwapBuffers)();
+
+  // TODO(piman): We shouldn't have to leak these 2 interfaces, especially in a
+  // type-unsafe way.
+  void* (*GLES2GetGLES2Interface)(MojoGLES2Context context);
+  void* (*GLES2GetContextSupport)(MojoGLES2Context context);
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder. Returns an object initialized to
+// contain pointers to each of the embedder's MojoGLES2ControlThunks functions.
+inline MojoGLES2ControlThunks MojoMakeGLES2ControlThunks() {
+  MojoGLES2ControlThunks gles2_control_thunks = {
+    sizeof(MojoGLES2ControlThunks),
+    MojoGLES2CreateContext,
+    MojoGLES2DestroyContext,
+    MojoGLES2MakeCurrent,
+    MojoGLES2SwapBuffers,
+    MojoGLES2GetGLES2Interface,
+    MojoGLES2GetContextSupport
+  };
+
+  return gles2_control_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetGLES2ControlThunksFn mojo_set_gles2_control_thunks_fn =
+//     reinterpret_cast<MojoSetGLES2ControlThunksFn>(
+//         app_library.GetFunctionPointer("MojoSetGLES2ControlThunks"));
+// The expected size of |gles2_control_thunks| is returned.
+// The contents of |gles2_control_thunks| are copied.
+typedef size_t (*MojoSetGLES2ControlThunksFn)(
+    const MojoGLES2ControlThunks* gles2_control_thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_
diff --git a/mojo/public/platform/native/system_thunks.cc b/mojo/public/platform/native/system_thunks.cc
new file mode 100644
index 0000000..fed6db9
--- /dev/null
+++ b/mojo/public/platform/native/system_thunks.cc
@@ -0,0 +1,164 @@
+// 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/public/platform/native/system_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+
+static MojoSystemThunks g_thunks = {0};
+
+MojoTimeTicks MojoGetTimeTicksNow() {
+  assert(g_thunks.GetTimeTicksNow);
+  return g_thunks.GetTimeTicksNow();
+}
+
+MojoResult MojoClose(MojoHandle handle) {
+  assert(g_thunks.Close);
+  return g_thunks.Close(handle);
+}
+
+MojoResult MojoWait(MojoHandle handle,
+                    MojoHandleSignals signals,
+                    MojoDeadline deadline) {
+  assert(g_thunks.Wait);
+  return g_thunks.Wait(handle, signals, deadline);
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+                        const MojoHandleSignals* signals,
+                        uint32_t num_handles,
+                        MojoDeadline deadline) {
+  assert(g_thunks.WaitMany);
+  return g_thunks.WaitMany(handles, signals, num_handles, deadline);
+}
+
+MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+                                 MojoHandle* message_pipe_handle0,
+                                 MojoHandle* message_pipe_handle1) {
+  assert(g_thunks.CreateMessagePipe);
+  return g_thunks.CreateMessagePipe(options, message_pipe_handle0,
+                                    message_pipe_handle1);
+}
+
+MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+                            const void* bytes,
+                            uint32_t num_bytes,
+                            const MojoHandle* handles,
+                            uint32_t num_handles,
+                            MojoWriteMessageFlags flags) {
+  assert(g_thunks.WriteMessage);
+  return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles,
+                               num_handles, flags);
+}
+
+MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+                           void* bytes,
+                           uint32_t* num_bytes,
+                           MojoHandle* handles,
+                           uint32_t* num_handles,
+                           MojoReadMessageFlags flags) {
+  assert(g_thunks.ReadMessage);
+  return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles,
+                              num_handles, flags);
+}
+
+MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
+                              MojoHandle* data_pipe_producer_handle,
+                              MojoHandle* data_pipe_consumer_handle) {
+  assert(g_thunks.CreateDataPipe);
+  return g_thunks.CreateDataPipe(options, data_pipe_producer_handle,
+                                 data_pipe_consumer_handle);
+}
+
+MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
+                         const void* elements,
+                         uint32_t* num_elements,
+                         MojoWriteDataFlags flags) {
+  assert(g_thunks.WriteData);
+  return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements,
+                            flags);
+}
+
+MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+                              void** buffer,
+                              uint32_t* buffer_num_elements,
+                              MojoWriteDataFlags flags) {
+  assert(g_thunks.BeginWriteData);
+  return g_thunks.BeginWriteData(data_pipe_producer_handle, buffer,
+                                 buffer_num_elements, flags);
+}
+
+MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+                            uint32_t num_elements_written) {
+  assert(g_thunks.EndWriteData);
+  return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+                        void* elements,
+                        uint32_t* num_elements,
+                        MojoReadDataFlags flags) {
+  assert(g_thunks.ReadData);
+  return g_thunks.ReadData(data_pipe_consumer_handle, elements, num_elements,
+                           flags);
+}
+
+MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+                             const void** buffer,
+                             uint32_t* buffer_num_elements,
+                             MojoReadDataFlags flags) {
+  assert(g_thunks.BeginReadData);
+  return g_thunks.BeginReadData(data_pipe_consumer_handle, buffer,
+                                buffer_num_elements, flags);
+}
+
+MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+                           uint32_t num_elements_read) {
+  assert(g_thunks.EndReadData);
+  return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBuffer(
+    const struct MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  assert(g_thunks.CreateSharedBuffer);
+  return g_thunks.CreateSharedBuffer(options, num_bytes, shared_buffer_handle);
+}
+
+MojoResult MojoDuplicateBufferHandle(
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  assert(g_thunks.DuplicateBufferHandle);
+  return g_thunks.DuplicateBufferHandle(buffer_handle, options,
+                                        new_buffer_handle);
+}
+
+MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+                         uint64_t offset,
+                         uint64_t num_bytes,
+                         void** buffer,
+                         MojoMapBufferFlags flags) {
+  assert(g_thunks.MapBuffer);
+  return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, buffer, flags);
+}
+
+MojoResult MojoUnmapBuffer(void* buffer) {
+  assert(g_thunks.UnmapBuffer);
+  return g_thunks.UnmapBuffer(buffer);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetSystemThunks(
+    const MojoSystemThunks* system_thunks) {
+  if (system_thunks->size >= sizeof(g_thunks))
+    g_thunks = *system_thunks;
+  return sizeof(g_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h
new file mode 100644
index 0000000..d53485c
--- /dev/null
+++ b/mojo/public/platform/native/system_thunks.h
@@ -0,0 +1,145 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/system/core.h"
+
+// The embedder needs to bind the basic Mojo Core functions of a DSO to those of
+// the embedder when loading a DSO that is dependent on mojo_system.
+// The typical usage would look like:
+// base::ScopedNativeLibrary app_library(
+//     base::LoadNativeLibrary(app_path_, &error));
+// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*);
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+//     reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+//         "MojoSetSystemThunks"));
+// MojoSystemThunks system_thunks = MojoMakeSystemThunks();
+// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks);
+// if (expected_size > sizeof(MojoSystemThunks)) {
+//   LOG(ERROR)
+//       << "Invalid DSO. Expected MojoSystemThunks size: "
+//       << expected_size;
+//   break;
+// }
+
+// Structure used to bind the basic Mojo Core functions of a DSO to those of
+// the embedder.
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoSystemThunks {
+  size_t size;  // Should be set to sizeof(MojoSystemThunks).
+  MojoTimeTicks (*GetTimeTicksNow)();
+  MojoResult (*Close)(MojoHandle handle);
+  MojoResult (*Wait)(MojoHandle handle,
+                     MojoHandleSignals signals,
+                     MojoDeadline deadline);
+  MojoResult (*WaitMany)(const MojoHandle* handles,
+                         const MojoHandleSignals* signals,
+                         uint32_t num_handles,
+                         MojoDeadline deadline);
+  MojoResult (*CreateMessagePipe)(
+      const struct MojoCreateMessagePipeOptions* options,
+      MojoHandle* message_pipe_handle0,
+      MojoHandle* message_pipe_handle1);
+  MojoResult (*WriteMessage)(MojoHandle message_pipe_handle,
+                             const void* bytes,
+                             uint32_t num_bytes,
+                             const MojoHandle* handles,
+                             uint32_t num_handles,
+                             MojoWriteMessageFlags flags);
+  MojoResult (*ReadMessage)(MojoHandle message_pipe_handle,
+                            void* bytes,
+                            uint32_t* num_bytes,
+                            MojoHandle* handles,
+                            uint32_t* num_handles,
+                            MojoReadMessageFlags flags);
+  MojoResult (*CreateDataPipe)(const struct MojoCreateDataPipeOptions* options,
+                               MojoHandle* data_pipe_producer_handle,
+                               MojoHandle* data_pipe_consumer_handle);
+  MojoResult (*WriteData)(MojoHandle data_pipe_producer_handle,
+                          const void* elements,
+                          uint32_t* num_elements,
+                          MojoWriteDataFlags flags);
+  MojoResult (*BeginWriteData)(MojoHandle data_pipe_producer_handle,
+                               void** buffer,
+                               uint32_t* buffer_num_elements,
+                               MojoWriteDataFlags flags);
+  MojoResult (*EndWriteData)(MojoHandle data_pipe_producer_handle,
+                             uint32_t num_elements_written);
+  MojoResult (*ReadData)(MojoHandle data_pipe_consumer_handle,
+                         void* elements,
+                         uint32_t* num_elements,
+                         MojoReadDataFlags flags);
+  MojoResult (*BeginReadData)(MojoHandle data_pipe_consumer_handle,
+                              const void** buffer,
+                              uint32_t* buffer_num_elements,
+                              MojoReadDataFlags flags);
+  MojoResult (*EndReadData)(MojoHandle data_pipe_consumer_handle,
+                            uint32_t num_elements_read);
+  MojoResult (*CreateSharedBuffer)(
+      const struct MojoCreateSharedBufferOptions* options,
+      uint64_t num_bytes,
+      MojoHandle* shared_buffer_handle);
+  MojoResult (*DuplicateBufferHandle)(
+      MojoHandle buffer_handle,
+      const struct MojoDuplicateBufferHandleOptions* options,
+      MojoHandle* new_buffer_handle);
+  MojoResult (*MapBuffer)(MojoHandle buffer_handle,
+                          uint64_t offset,
+                          uint64_t num_bytes,
+                          void** buffer,
+                          MojoMapBufferFlags flags);
+  MojoResult (*UnmapBuffer)(void* buffer);
+};
+#pragma pack(pop)
+
+
+#ifdef __cplusplus
+// Intended to be called from the embedder. Returns a |MojoCore| initialized
+// to contain pointers to each of the embedder's MojoCore functions.
+inline MojoSystemThunks MojoMakeSystemThunks() {
+  MojoSystemThunks system_thunks = {
+    sizeof(MojoSystemThunks),
+    MojoGetTimeTicksNow,
+    MojoClose,
+    MojoWait,
+    MojoWaitMany,
+    MojoCreateMessagePipe,
+    MojoWriteMessage,
+    MojoReadMessage,
+    MojoCreateDataPipe,
+    MojoWriteData,
+    MojoBeginWriteData,
+    MojoEndWriteData,
+    MojoReadData,
+    MojoBeginReadData,
+    MojoEndReadData,
+    MojoCreateSharedBuffer,
+    MojoDuplicateBufferHandle,
+    MojoMapBuffer,
+    MojoUnmapBuffer
+  };
+  return system_thunks;
+}
+#endif
+
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+//     reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+//         "MojoSetSystemThunks"));
+// The expected size of |system_thunks} is returned.
+// The contents of |system_thunks| are copied.
+typedef size_t (*MojoSetSystemThunksFn)(
+    const struct MojoSystemThunks* system_thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
diff --git a/mojo/public/platform/native/thunk_export.h b/mojo/public/platform/native/thunk_export.h
new file mode 100644
index 0000000..d118fc5
--- /dev/null
+++ b/mojo/public/platform/native/thunk_export.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_
+#define MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_
+
+// Call this function by looking inside the resulting shared object and
+// grabbing the symbol manually.
+//
+// Always export this api.
+#if defined(WIN32)
+#define THUNK_EXPORT __declspec(dllexport)
+#else
+#define THUNK_EXPORT __attribute__((visibility("default")))
+#endif
+
+#endif // MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_
diff --git a/mojo/public/python/BUILD.gn b/mojo/public/python/BUILD.gn
new file mode 100644
index 0000000..66f132f
--- /dev/null
+++ b/mojo/public/python/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.
+
+import("//third_party/cython/rules.gni")
+
+group("python") {
+  deps = [
+    ":base",
+    ":bindings",
+    ":system",
+  ]
+}
+
+# GYP version: mojo.gyp:mojo_python_system
+python_binary_module("system") {
+  python_base_module = "mojo"
+  sources = [
+    "mojo/c_core.pxd",
+    "mojo/c_environment.pxd",
+    "mojo/system.pyx",
+  ]
+  additional_sources = [
+    "src/python_system_helper.cc",
+    "src/python_system_helper.h",
+  ]
+  deps = [
+    "//mojo/public/c/environment",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/environment:standalone",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/cpp/bindings:callback",
+    ":base",
+  ]
+}
+
+copy("base") {
+  sources = [
+    "mojo/__init__.py",
+  ]
+  outputs = [
+    "$root_out_dir/python/mojo/{{source_file_part}}",
+  ]
+}
+
+# GYP version: mojo.gyp:mojo_python_bindings
+copy("bindings") {
+  sources = [
+    "mojo/bindings/__init__.py",
+    "mojo/bindings/descriptor.py",
+    "mojo/bindings/messaging.py",
+    "mojo/bindings/promise.py",
+    "mojo/bindings/reflection.py",
+    "mojo/bindings/serialization.py",
+  ]
+  outputs = [
+    "$root_out_dir/python/mojo/bindings/{{source_file_part}}",
+  ]
+  deps = [
+    ":base",
+    ":system",
+  ]
+}
diff --git a/mojo/public/python/mojo/__init__.py b/mojo/public/python/mojo/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/mojo/public/python/mojo/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/mojo/public/python/mojo/bindings/__init__.py b/mojo/public/python/mojo/bindings/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/mojo/public/python/mojo/bindings/descriptor.py b/mojo/public/python/mojo/bindings/descriptor.py
new file mode 100644
index 0000000..d56789a
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/descriptor.py
@@ -0,0 +1,555 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+The descriptors used to define generated elements of the mojo python bindings.
+"""
+
+import array
+import itertools
+import struct
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+import mojo.system
+
+
+class Type(object):
+  """Describes the type of a struct field or a method parameter,"""
+
+  def Convert(self, value): # pylint: disable=R0201
+    """
+    Convert the given value into its canonical representation, raising an
+    exception if the value cannot be converted.
+    """
+    return value
+
+  def GetDefaultValue(self, value):
+    """
+    Returns the default value for this type associated with the given value.
+    This method must be able to correcly handle value being None.
+    """
+    return self.Convert(value)
+
+
+class SerializableType(Type):
+  """Describe a type that can be serialized by itself."""
+
+  def __init__(self, typecode):
+    Type.__init__(self)
+    self.typecode = typecode
+    self.byte_size = struct.calcsize('=%s' % self.GetTypeCode())
+
+  def GetTypeCode(self):
+    """
+    Returns the type code (as defined by the struct module) used to encode
+    this type.
+    """
+    return self.typecode
+
+  def GetByteSize(self):
+    """
+    Returns the size of the encoding of this type.
+    """
+    return self.byte_size
+
+  def Serialize(self, value, data_offset, data, handle_offset):
+    """
+    Serialize a value of this type.
+
+    Args:
+      value: the value to serialize.
+      data_offset: the offset to the end of the data bytearray. Used to encode
+                   pointers.
+      data: the bytearray to append additional data to.
+      handle_offset: the offset to use to encode handles.
+
+    Returns a a tuple where the first element is the value to encode, and the
+    second is the array of handles to add to the message.
+    """
+    raise NotImplementedError()
+
+  def Deserialize(self, value, data, handles):
+    """
+    Deserialize a value of this type.
+
+    Args:
+      value: the base value for this type. This is always a numeric type, and
+             corresponds to the first element in the tuple returned by
+             Serialize.
+      data: the bytearray to retrieve additional data from.
+      handles: the array of handles contained in the message to deserialize.
+
+    Returns the deserialized value.
+    """
+    raise NotImplementedError()
+
+
+class BooleanType(Type):
+  """Type object for booleans"""
+
+  def Convert(self, value):
+    return bool(value)
+
+
+class NumericType(SerializableType):
+  """Base Type object for all numeric types"""
+
+  def GetDefaultValue(self, value):
+    if value is None:
+      return self.Convert(0)
+    return self.Convert(value)
+
+  def Serialize(self, value, data_offset, data, handle_offset):
+    return (value, [])
+
+  def Deserialize(self, value, data, handles):
+    return value
+
+
+class IntegerType(NumericType):
+  """Type object for integer types."""
+
+  def __init__(self, typecode):
+    NumericType.__init__(self, typecode)
+    size = 8 * self.byte_size
+    signed = typecode.islower()
+    if signed:
+      self._min_value = -(1 << (size - 1))
+      self._max_value = (1 << (size - 1)) - 1
+    else:
+      self._min_value = 0
+      self._max_value = (1 << size) - 1
+
+  def Convert(self, value):
+    if value is None:
+      raise TypeError('None is not an integer.')
+    if not isinstance(value, (int, long)):
+      raise TypeError('%r is not an integer type' % value)
+    if value < self._min_value or value > self._max_value:
+      raise OverflowError('%r is not in the range [%d, %d]' %
+                          (value, self._min_value, self._max_value))
+    return value
+
+
+class FloatType(NumericType):
+  """Type object for floating point number types."""
+
+  def Convert(self, value):
+    if value is None:
+      raise TypeError('None is not an floating point number.')
+    if not isinstance(value, (int, long, float)):
+      raise TypeError('%r is not a numeric type' % value)
+    return float(value)
+
+
+class PointerType(SerializableType):
+  """Base Type object for pointers."""
+
+  def __init__(self, nullable=False):
+    SerializableType.__init__(self, 'Q')
+    self.nullable = nullable
+
+  def Serialize(self, value, data_offset, data, handle_offset):
+    if value is None and not self.nullable:
+      raise serialization.SerializationException(
+          'Trying to serialize null for non nullable type.')
+    if value is None:
+      return (0, [])
+    return self.SerializePointer(value, data_offset, data, handle_offset)
+
+  def Deserialize(self, value, data, handles):
+    if value == 0:
+      if not self.nullable:
+        raise serialization.DeserializationException(
+            'Trying to deserialize null for non nullable type.')
+      return None
+    pointed_data = buffer(data, value)
+    (size, nb_elements) = serialization.HEADER_STRUCT.unpack_from(pointed_data)
+    return self.DeserializePointer(size, nb_elements, pointed_data, handles)
+
+  def SerializePointer(self, value, data_offset, data, handle_offset):
+    """Serialize the not null value."""
+    raise NotImplementedError()
+
+  def DeserializePointer(self, size, nb_elements, data, handles):
+    raise NotImplementedError()
+
+
+class StringType(PointerType):
+  """
+  Type object for strings.
+
+  Strings are represented as unicode, and the conversion is done using the
+  default encoding if a string instance is used.
+  """
+
+  def __init__(self, nullable=False):
+    PointerType.__init__(self, nullable)
+    self._array_type = NativeArrayType('B', nullable)
+
+  def Convert(self, value):
+    if value is None or isinstance(value, unicode):
+      return value
+    if isinstance(value, str):
+      return unicode(value)
+    raise TypeError('%r is not a string' % value)
+
+  def SerializePointer(self, value, data_offset, data, handle_offset):
+    string_array = array.array('b')
+    string_array.fromstring(value.encode('utf8'))
+    return self._array_type.SerializeArray(
+        string_array, data_offset, data, handle_offset)
+
+  def DeserializePointer(self, size, nb_elements, data, handles):
+    string_array = self._array_type.DeserializeArray(
+        size, nb_elements, data, handles)
+    return unicode(string_array.tostring(), 'utf8')
+
+
+class HandleType(SerializableType):
+  """Type object for handles."""
+
+  def __init__(self, nullable=False):
+    SerializableType.__init__(self, 'i')
+    self.nullable = nullable
+
+  def Convert(self, value):
+    if value is None:
+      return mojo.system.Handle()
+    if not isinstance(value, mojo.system.Handle):
+      raise TypeError('%r is not a handle' % value)
+    return value
+
+  def Serialize(self, value, data_offset, data, handle_offset):
+    if not value.IsValid() and not self.nullable:
+      raise serialization.SerializationException(
+          'Trying to serialize null for non nullable type.')
+    if not value.IsValid():
+      return (-1, [])
+    return (handle_offset, [value])
+
+  def Deserialize(self, value, data, handles):
+    if value == -1:
+      if not self.nullable:
+        raise serialization.DeserializationException(
+            'Trying to deserialize null for non nullable type.')
+      return mojo.system.Handle()
+    # TODO(qsr) validate handle order
+    return handles[value]
+
+
+class BaseArrayType(PointerType):
+  """Abstract Type object for arrays."""
+
+  def __init__(self, nullable=False, length=0):
+    PointerType.__init__(self, nullable)
+    self.length = length
+
+  def SerializePointer(self, value, data_offset, data, handle_offset):
+    if self.length != 0 and len(value) != self.length:
+      raise serialization.SerializationException('Incorrect array size')
+    return self.SerializeArray(value, data_offset, data, handle_offset)
+
+  def SerializeArray(self, value, data_offset, data, handle_offset):
+    """Serialize the not null array."""
+    raise NotImplementedError()
+
+  def DeserializePointer(self, size, nb_elements, data, handles):
+    if self.length != 0 and size != self.length:
+      raise serialization.DeserializationException('Incorrect array size')
+    return self.DeserializeArray(size, nb_elements, data, handles)
+
+  def DeserializeArray(self, size, nb_elements, data, handles):
+    raise NotImplementedError()
+
+
+class BooleanArrayType(BaseArrayType):
+
+  def __init__(self, nullable=False, length=0):
+    BaseArrayType.__init__(self, nullable, length)
+    self._array_type = NativeArrayType('B', nullable)
+
+  def Convert(self, value):
+    if value is None:
+      return value
+    return [TYPE_BOOL.Convert(x) for x in value]
+
+  def SerializeArray(self, value, data_offset, data, handle_offset):
+    groups = [value[i:i+8] for i in range(0, len(value), 8)]
+    converted = array.array('B', [_ConvertBooleansToByte(x) for x in groups])
+    return _SerializeNativeArray(converted, data_offset, data, len(value))
+
+  def DeserializeArray(self, size, nb_elements, data, handles):
+    converted = self._array_type.DeserializeArray(
+        size, nb_elements, data, handles)
+    elements = list(itertools.islice(
+        itertools.chain.from_iterable(
+            [_ConvertByteToBooleans(x, 8) for x in converted]),
+        0,
+        nb_elements))
+    return elements
+
+
+class GenericArrayType(BaseArrayType):
+  """Type object for arrays of pointers."""
+
+  def __init__(self, sub_type, nullable=False, length=0):
+    BaseArrayType.__init__(self, nullable, length)
+    assert isinstance(sub_type, SerializableType)
+    self.sub_type = sub_type
+
+  def Convert(self, value):
+    if value is None:
+      return value
+    return [self.sub_type.Convert(x) for x in value]
+
+  def SerializeArray(self, value, data_offset, data, handle_offset):
+    size = (serialization.HEADER_STRUCT.size +
+            self.sub_type.GetByteSize() * len(value))
+    data_end = len(data)
+    position = len(data) + serialization.HEADER_STRUCT.size
+    data.extend(bytearray(size +
+                          serialization.NeededPaddingForAlignment(size)))
+    returned_handles = []
+    to_pack = []
+    for item in value:
+      (new_data, new_handles) = self.sub_type.Serialize(
+          item,
+          len(data) - position,
+          data,
+          handle_offset + len(returned_handles))
+      to_pack.append(new_data)
+      returned_handles.extend(new_handles)
+      position = position + self.sub_type.GetByteSize()
+    serialization.HEADER_STRUCT.pack_into(data, data_end, size, len(value))
+    struct.pack_into('%d%s' % (len(value), self.sub_type.GetTypeCode()),
+                     data,
+                     data_end + serialization.HEADER_STRUCT.size,
+                     *to_pack)
+    return (data_offset, returned_handles)
+
+  def DeserializeArray(self, size, nb_elements, data, handles):
+    values = struct.unpack_from(
+        '%d%s' % (nb_elements, self.sub_type.GetTypeCode()),
+        buffer(data, serialization.HEADER_STRUCT.size))
+    result = []
+    position = serialization.HEADER_STRUCT.size
+    for value in values:
+      result.append(
+          self.sub_type.Deserialize(value, buffer(data, position), handles))
+      position += self.sub_type.GetByteSize()
+    return result
+
+
+class NativeArrayType(BaseArrayType):
+  """Type object for arrays of native types."""
+
+  def __init__(self, typecode, nullable=False, length=0):
+    BaseArrayType.__init__(self, nullable, length)
+    self.array_typecode = typecode
+
+  def Convert(self, value):
+    if value is None:
+      return value
+    if (isinstance(value, array.array) and
+        value.array_typecode == self.array_typecode):
+      return value
+    return array.array(self.array_typecode, value)
+
+  def SerializeArray(self, value, data_offset, data, handle_offset):
+    return _SerializeNativeArray(value, data_offset, data, len(value))
+
+  def DeserializeArray(self, size, nb_elements, data, handles):
+    result = array.array(self.array_typecode)
+    result.fromstring(buffer(data,
+                             serialization.HEADER_STRUCT.size,
+                             size - serialization.HEADER_STRUCT.size))
+    return result
+
+
+class StructType(PointerType):
+  """Type object for structs."""
+
+  def __init__(self, struct_type, nullable=False):
+    PointerType.__init__(self)
+    self.struct_type = struct_type
+    self.nullable = nullable
+
+  def Convert(self, value):
+    if value is None or isinstance(value, self.struct_type):
+      return value
+    raise TypeError('%r is not an instance of %r' % (value, self.struct_type))
+
+  def GetDefaultValue(self, value):
+    if value:
+      return self.struct_type()
+    return None
+
+  def SerializePointer(self, value, data_offset, data, handle_offset):
+    (new_data, new_handles) = value.Serialize(handle_offset)
+    data.extend(new_data)
+    return (data_offset, new_handles)
+
+  def DeserializePointer(self, size, nb_elements, data, handles):
+    return self.struct_type.Deserialize(data, handles)
+
+
+class NoneType(SerializableType):
+  """Placeholder type, used temporarily until all mojo types are handled."""
+
+  def __init__(self):
+    SerializableType.__init__(self, 'B')
+
+  def Convert(self, value):
+    return None
+
+  def Serialize(self, value, data_offset, data, handle_offset):
+    return (0, [])
+
+  def Deserialize(self, value, data, handles):
+    return None
+
+
+TYPE_NONE = NoneType()
+
+TYPE_BOOL = BooleanType()
+
+TYPE_INT8 = IntegerType('b')
+TYPE_INT16 = IntegerType('h')
+TYPE_INT32 = IntegerType('i')
+TYPE_INT64 = IntegerType('q')
+
+TYPE_UINT8 = IntegerType('B')
+TYPE_UINT16 = IntegerType('H')
+TYPE_UINT32 = IntegerType('I')
+TYPE_UINT64 = IntegerType('Q')
+
+TYPE_FLOAT = FloatType('f')
+TYPE_DOUBLE = FloatType('d')
+
+TYPE_STRING = StringType()
+TYPE_NULLABLE_STRING = StringType(True)
+
+TYPE_HANDLE = HandleType()
+TYPE_NULLABLE_HANDLE = HandleType(True)
+
+
+class FieldDescriptor(object):
+  """Describes a field in a generated struct."""
+
+  def __init__(self, name, field_type, index, version, default_value=None):
+    self.name = name
+    self.field_type = field_type
+    self.version = version
+    self.index = index
+    self._default_value = default_value
+
+  def GetDefaultValue(self):
+    return self.field_type.GetDefaultValue(self._default_value)
+
+
+class FieldGroup(object):
+  """
+  Describe a list of field in the generated struct that must be
+  serialized/deserialized together.
+  """
+  def __init__(self, descriptors):
+    self.descriptors = descriptors
+
+  def GetDescriptors(self):
+    return self.descriptors
+
+  def GetTypeCode(self):
+    raise NotImplementedError()
+
+  def GetByteSize(self):
+    raise NotImplementedError()
+
+  def GetVersion(self):
+    raise NotImplementedError()
+
+  def Serialize(self, obj, data_offset, data, handle_offset):
+    raise NotImplementedError()
+
+  def Deserialize(self, value, data, handles):
+    raise NotImplementedError()
+
+
+class SingleFieldGroup(FieldGroup, FieldDescriptor):
+  """A FieldGroup that contains a single FieldDescriptor."""
+
+  def __init__(self, name, field_type, index, version, default_value=None):
+    FieldDescriptor.__init__(
+        self, name, field_type, index, version, default_value)
+    FieldGroup.__init__(self, [self])
+
+  def GetTypeCode(self):
+    return self.field_type.GetTypeCode()
+
+  def GetByteSize(self):
+    return self.field_type.GetByteSize()
+
+  def GetVersion(self):
+    return self.version
+
+  def Serialize(self, obj, data_offset, data, handle_offset):
+    value = getattr(obj, self.name)
+    return self.field_type.Serialize(value, data_offset, data, handle_offset)
+
+  def Deserialize(self, value, data, handles):
+    entity = self.field_type.Deserialize(value, data, handles)
+    return { self.name: entity }
+
+
+class BooleanGroup(FieldGroup):
+  """A FieldGroup to pack booleans."""
+  def __init__(self, descriptors):
+    FieldGroup.__init__(self, descriptors)
+    self.version = min([descriptor.version  for descriptor in descriptors])
+
+  def GetTypeCode(self):
+    return 'B'
+
+  def GetByteSize(self):
+    return 1
+
+  def GetVersion(self):
+    return self.version
+
+  def Serialize(self, obj, data_offset, data, handle_offset):
+    value = _ConvertBooleansToByte(
+        [getattr(obj, field.name) for field in self.GetDescriptors()])
+    return (value, [])
+
+  def Deserialize(self, value, data, handles):
+    values =  itertools.izip_longest([x.name for x in self.descriptors],
+                                      _ConvertByteToBooleans(value),
+                                     fillvalue=False)
+    return dict(values)
+
+
+def _SerializeNativeArray(value, data_offset, data, length):
+  data_size = len(data)
+  data.extend(bytearray(serialization.HEADER_STRUCT.size))
+  data.extend(buffer(value))
+  data_length = len(data) - data_size
+  data.extend(bytearray(serialization.NeededPaddingForAlignment(data_length)))
+  serialization.HEADER_STRUCT.pack_into(data, data_size, data_length, length)
+  return (data_offset, [])
+
+
+def _ConvertBooleansToByte(booleans):
+  """Pack a list of booleans into an integer."""
+  return reduce(lambda x, y: x * 2 + y, reversed(booleans), 0)
+
+
+def _ConvertByteToBooleans(value, min_size=0):
+  "Unpack an integer into a list of booleans."""
+  res = []
+  while value:
+    res.append(bool(value&1))
+    value = value / 2
+  res.extend([False] * (min_size - len(res)))
+  return res
diff --git a/mojo/public/python/mojo/bindings/messaging.py b/mojo/public/python/mojo/bindings/messaging.py
new file mode 100644
index 0000000..956f5b3
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/messaging.py
@@ -0,0 +1,387 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utility classes to handle sending and receiving messages."""
+
+
+import struct
+import weakref
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+import mojo.system as system
+
+
+# The flag values for a message header.
+NO_FLAG = 0
+MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0
+MESSAGE_IS_RESPONSE_FLAG = 1 << 1
+
+
+class MessageHeader(object):
+  """The header of a mojo message."""
+
+  _SIMPLE_MESSAGE_NUM_FIELDS = 2
+  _SIMPLE_MESSAGE_STRUCT = struct.Struct("=IIII")
+
+  _REQUEST_ID_STRUCT = struct.Struct("=Q")
+  _REQUEST_ID_OFFSET = _SIMPLE_MESSAGE_STRUCT.size
+
+  _MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3
+  _MESSAGE_WITH_REQUEST_ID_SIZE = (
+      _SIMPLE_MESSAGE_STRUCT.size + _REQUEST_ID_STRUCT.size)
+
+  def __init__(self, message_type, flags, request_id=0, data=None):
+    self._message_type = message_type
+    self._flags = flags
+    self._request_id = request_id
+    self._data = data
+
+  @classmethod
+  def Deserialize(cls, data):
+    buf = buffer(data)
+    if len(data) < cls._SIMPLE_MESSAGE_STRUCT.size:
+      raise serialization.DeserializationException('Header is too short.')
+    (size, version, message_type, flags) = (
+        cls._SIMPLE_MESSAGE_STRUCT.unpack_from(buf))
+    if (version < cls._SIMPLE_MESSAGE_NUM_FIELDS):
+      raise serialization.DeserializationException('Incorrect version.')
+    request_id = 0
+    if _HasRequestId(flags):
+      if version < cls._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS:
+        raise serialization.DeserializationException('Incorrect version.')
+      if (size < cls._MESSAGE_WITH_REQUEST_ID_SIZE or
+          len(data) < cls._MESSAGE_WITH_REQUEST_ID_SIZE):
+        raise serialization.DeserializationException('Header is too short.')
+      (request_id, ) = cls._REQUEST_ID_STRUCT.unpack_from(
+          buf, cls._REQUEST_ID_OFFSET)
+    return MessageHeader(message_type, flags, request_id, data)
+
+  @property
+  def message_type(self):
+    return self._message_type
+
+  # pylint: disable=E0202
+  @property
+  def request_id(self):
+    assert self.has_request_id
+    return self._request_id
+
+  # pylint: disable=E0202
+  @request_id.setter
+  def request_id(self, request_id):
+    assert self.has_request_id
+    self._request_id = request_id
+    self._REQUEST_ID_STRUCT.pack_into(self._data, self._REQUEST_ID_OFFSET,
+                                      request_id)
+
+  @property
+  def has_request_id(self):
+    return _HasRequestId(self._flags)
+
+  @property
+  def expects_response(self):
+    return self._HasFlag(MESSAGE_EXPECTS_RESPONSE_FLAG)
+
+  @property
+  def is_response(self):
+    return self._HasFlag(MESSAGE_IS_RESPONSE_FLAG)
+
+  @property
+  def size(self):
+    if self.has_request_id:
+      return self._MESSAGE_WITH_REQUEST_ID_SIZE
+    return self._SIMPLE_MESSAGE_STRUCT.size
+
+  def Serialize(self):
+    if not self._data:
+      self._data = bytearray(self.size)
+      version = self._SIMPLE_MESSAGE_NUM_FIELDS
+      size = self._SIMPLE_MESSAGE_STRUCT.size
+      if self.has_request_id:
+        version = self._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS
+        size = self._MESSAGE_WITH_REQUEST_ID_SIZE
+      self._SIMPLE_MESSAGE_STRUCT.pack_into(self._data, 0, size, version,
+                                            self._message_type, self._flags)
+      if self.has_request_id:
+        self._REQUEST_ID_STRUCT.pack_into(self._data, self._REQUEST_ID_OFFSET,
+                                          self._request_id)
+    return self._data
+
+  def _HasFlag(self, flag):
+    return self._flags & flag != 0
+
+
+class Message(object):
+  """A message for a message pipe. This contains data and handles."""
+
+  def __init__(self, data=None, handles=None):
+    self.data = data
+    self.handles = handles
+    self._header = None
+    self._payload = None
+
+  @property
+  def header(self):
+    if self._header is None:
+      self._header = MessageHeader.Deserialize(self.data)
+    return self._header
+
+  @property
+  def payload(self):
+    if self._payload is None:
+      self._payload = Message(self.data[self.header.size:], self.handles)
+    return self._payload
+
+  def SetRequestId(self, request_id):
+    header = self.header
+    header.request_id = request_id
+    (data, _) = header.Serialize()
+    self.data[:header.Size] = data[:header.Size]
+
+
+class MessageReceiver(object):
+  """A class which implements this interface can receive Message objects."""
+
+  def Accept(self, message):
+    """
+    Receive a Message. The MessageReceiver is allowed to mutate the message.
+
+    Args:
+      message: the received message.
+
+    Returns:
+      True if the message has been handled, False otherwise.
+    """
+    raise NotImplementedError()
+
+
+class MessageReceiverWithResponder(MessageReceiver):
+  """
+  A MessageReceiver that can also handle the response message generated from the
+  given message.
+  """
+
+  def AcceptWithResponder(self, message, responder):
+    """
+    A variant on Accept that registers a MessageReceiver (known as the
+    responder) to handle the response message generated from the given message.
+    The responder's Accept method may be called as part of the call to
+    AcceptWithResponder, or some time after its return.
+
+    Args:
+      message: the received message.
+      responder: the responder that will receive the response.
+
+    Returns:
+      True if the message has been handled, False otherwise.
+    """
+    raise NotImplementedError()
+
+
+class ConnectionErrorHandler(object):
+  """
+  A ConnectionErrorHandler is notified of an error happening while using the
+  bindings over message pipes.
+  """
+
+  def OnError(self, result):
+    raise NotImplementedError()
+
+
+class Connector(MessageReceiver):
+  """
+  A Connector owns a message pipe and will send any received messages to the
+  registered MessageReceiver. It also acts as a MessageReceiver and will send
+  any message through the handle.
+
+  The method Start must be called before the Connector will start listening to
+  incoming messages.
+  """
+
+  def __init__(self, handle):
+    MessageReceiver.__init__(self)
+    self._handle = handle
+    self._cancellable = None
+    self._incoming_message_receiver = None
+    self._error_handler = None
+
+  def __del__(self):
+    if self._cancellable:
+      self._cancellable()
+
+  def SetIncomingMessageReceiver(self, message_receiver):
+    """
+    Set the MessageReceiver that will receive message from the owned message
+    pipe.
+    """
+    self._incoming_message_receiver = message_receiver
+
+  def SetErrorHandler(self, error_handler):
+    """
+    Set the ConnectionErrorHandler that will be notified of errors on the owned
+    message pipe.
+    """
+    self._error_handler = error_handler
+
+  def Start(self):
+    assert not self._cancellable
+    self._RegisterAsyncWaiterForRead()
+
+  def Accept(self, message):
+    result = self._handle.WriteMessage(message.data, message.handles)
+    return result == system.RESULT_OK
+
+  def Close(self):
+    if self._cancellable:
+      self._cancellable()
+      self._cancellable = None
+    self._handle.Close()
+
+  def _OnAsyncWaiterResult(self, result):
+    self._cancellable = None
+    if result == system.RESULT_OK:
+      self._ReadOutstandingMessages()
+    else:
+      self._OnError(result)
+
+  def _OnError(self, result):
+    assert not self._cancellable
+    if self._error_handler:
+      self._error_handler.OnError(result)
+
+  def _RegisterAsyncWaiterForRead(self) :
+    assert not self._cancellable
+    self._cancellable = self._handle.AsyncWait(
+        system.HANDLE_SIGNAL_READABLE,
+        system.DEADLINE_INDEFINITE,
+        _WeakCallback(self._OnAsyncWaiterResult))
+
+  def _ReadOutstandingMessages(self):
+    result = system.RESULT_OK
+    while result == system.RESULT_OK:
+      result = _ReadAndDispatchMessage(self._handle,
+                                       self._incoming_message_receiver)
+    if result == system.RESULT_SHOULD_WAIT:
+      self._RegisterAsyncWaiterForRead()
+      return
+    self._OnError(result)
+
+
+class Router(MessageReceiverWithResponder):
+  """
+  A Router will handle mojo message and forward those to a Connector. It deals
+  with parsing of headers and adding of request ids in order to be able to match
+  a response to a request.
+  """
+
+  def __init__(self, handle):
+    MessageReceiverWithResponder.__init__(self)
+    self._incoming_message_receiver = None
+    self._next_request_id = 1
+    self._responders = {}
+    self._connector = Connector(handle)
+    self._connector.SetIncomingMessageReceiver(
+        ForwardingMessageReceiver(self._HandleIncomingMessage))
+
+  def Start(self):
+    self._connector.Start()
+
+  def SetIncomingMessageReceiver(self, message_receiver):
+    """
+    Set the MessageReceiver that will receive message from the owned message
+    pipe.
+    """
+    self._incoming_message_receiver = message_receiver
+
+  def SetErrorHandler(self, error_handler):
+    """
+    Set the ConnectionErrorHandler that will be notified of errors on the owned
+    message pipe.
+    """
+    self._connector.SetErrorHandler(error_handler)
+
+  def Accept(self, message):
+    # A message without responder is directly forwarded to the connector.
+    return self._connector.Accept(message)
+
+  def AcceptWithResponder(self, message, responder):
+    # The message must have a header.
+    header = message.header
+    assert header.expects_response
+    request_id = self.NextRequestId()
+    header.request_id = request_id
+    if not self._connector.Accept(message):
+      return False
+    self._responders[request_id] = responder
+    return True
+
+  def Close(self):
+    self._connector.Close()
+
+  def _HandleIncomingMessage(self, message):
+    header = message.header
+    if header.expects_response:
+      if self._incoming_message_receiver:
+        return self._incoming_message_receiver.AcceptWithResponder(
+            message, self)
+      # If we receive a request expecting a response when the client is not
+      # listening, then we have no choice but to tear down the pipe.
+      self.Close()
+      return False
+    if header.is_response:
+      request_id = header.request_id
+      responder = self._responders.pop(request_id, None)
+      if responder is None:
+        return False
+      return responder.Accept(message)
+    if self._incoming_message_receiver:
+      return self._incoming_message_receiver.Accept(message)
+    # Ok to drop the message
+    return False
+
+  def NextRequestId(self):
+    request_id = self._next_request_id
+    while request_id == 0 or request_id in self._responders:
+      request_id = (request_id + 1) % (1 << 64)
+    self._next_request_id = (request_id + 1) % (1 << 64)
+    return request_id
+
+class ForwardingMessageReceiver(MessageReceiver):
+  """A MessageReceiver that forward calls to |Accept| to a callable."""
+
+  def __init__(self, callback):
+    MessageReceiver.__init__(self)
+    self._callback = callback
+
+  def Accept(self, message):
+    return self._callback(message)
+
+
+def _WeakCallback(callback):
+  func = callback.im_func
+  self = callback.im_self
+  if not self:
+    return callback
+  weak_self = weakref.ref(self)
+  def Callback(*args, **kwargs):
+    self = weak_self()
+    if self:
+      return func(self, *args, **kwargs)
+  return Callback
+
+
+def _ReadAndDispatchMessage(handle, message_receiver):
+  (result, _, sizes) = handle.ReadMessage()
+  if result == system.RESULT_OK and message_receiver:
+    message_receiver.Accept(Message(bytearray(), []))
+  if result != system.RESULT_RESOURCE_EXHAUSTED:
+    return result
+  (result, data, _) = handle.ReadMessage(bytearray(sizes[0]))
+  if result == system.RESULT_OK and message_receiver:
+    message_receiver.Accept(Message(data[0], data[1]))
+  return result
+
+def _HasRequestId(flags):
+  return flags & (MESSAGE_EXPECTS_RESPONSE_FLAG|MESSAGE_IS_RESPONSE_FLAG) != 0
diff --git a/mojo/public/python/mojo/bindings/promise.py b/mojo/public/python/mojo/bindings/promise.py
new file mode 100644
index 0000000..c5d7d7c
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/promise.py
@@ -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.
+
+"""
+Promise used by the python bindings.
+
+The API is following the ECMAScript 6 API for promises.
+"""
+
+
+class Promise(object):
+  """The promise object."""
+
+  STATE_PENDING = 0
+  STATE_FULLFILLED = 1
+  STATE_REJECTED = 2
+  STATE_BOUND = 3
+
+  def __init__(self, generator_function):
+    """
+    Constructor.
+
+    Args:
+      generator_function: A function taking 2 arguments: resolve and reject.
+      When |resolve| is called, the promise is fullfilled with the given value.
+      When |reject| is called, the promise is rejected with the given value.
+      A promise can only be resolved or rejected once, all following calls will
+      have no effect.
+    """
+    self._onCatched = []
+    self._onFulfilled = []
+    self._onRejected = []
+    self._state = Promise.STATE_PENDING
+    self._result = None
+    if generator_function:
+      generator_function(self._Resolve, self._Reject)
+
+  @staticmethod
+  def Resolve(value):
+    """
+    If value is a promise, make a promise that have the same behavior as value,
+    otherwise make a promise that fulfills to value.
+    """
+    if isinstance(value, Promise):
+      return value
+    return Promise(lambda x, y: x(value))
+
+  @staticmethod
+  def Reject(reason):
+    "Make a promise that rejects to reason."""
+    return Promise(lambda x, y: y(reason))
+
+  @staticmethod
+  def All(*iterable):
+    """
+    Make a promise that fulfills when every item in the array fulfills, and
+    rejects if (and when) any item rejects. Each array item is passed to
+    Promise.resolve, so the array can be a mixture of promise-like objects and
+    other objects. The fulfillment value is an array (in order) of fulfillment
+    values. The rejection value is the first rejection value.
+    """
+    def GeneratorFunction(resolve, reject):
+      state = {
+        'rejected': False,
+        'nb_resolved': 0,
+      }
+      promises = [Promise.Resolve(x) for x in iterable]
+      results = [None for x in promises]
+      def OnFullfilled(i):
+        def OnFullfilled(res):
+          if state['rejected']:
+            return
+          results[i] = res
+          state['nb_resolved'] = state['nb_resolved'] + 1
+          if state['nb_resolved'] == len(results):
+            resolve(results)
+        return OnFullfilled
+      def OnRejected(reason):
+        if state['rejected']:
+          return
+        state['rejected'] = True
+        reject(reason)
+
+      for (i, promise) in enumerate(promises):
+        promise.Then(OnFullfilled(i), OnRejected)
+    return Promise(GeneratorFunction)
+
+  @staticmethod
+  def Race(*iterable):
+    """
+    Make a Promise that fulfills as soon as any item fulfills, or rejects as
+    soon as any item rejects, whichever happens first.
+    """
+    def GeneratorFunction(resolve, reject):
+      state = {
+        'ended': False
+      }
+      def OnEvent(callback):
+        def OnEvent(res):
+          if state['ended']:
+            return
+          state['ended'] = True
+          callback(res)
+        return OnEvent
+      for promise in [Promise.Resolve(x) for x in iterable]:
+        promise.Then(OnEvent(resolve), OnEvent(reject))
+    return Promise(GeneratorFunction)
+
+  @property
+  def state(self):
+    if isinstance(self._result, Promise):
+      return self._result.state
+    return self._state
+
+  def Then(self, onFullfilled=None, onRejected=None):
+    """
+    onFulfilled is called when/if this promise resolves. onRejected is called
+    when/if this promise rejects. Both are optional, if either/both are omitted
+    the next onFulfilled/onRejected in the chain is called. Both callbacks have
+    a single parameter, the fulfillment value or rejection reason. |Then|
+    returns a new promise equivalent to the value you return from
+    onFulfilled/onRejected after being passed through Resolve. If an
+    error is thrown in the callback, the returned promise rejects with that
+    error.
+    """
+    if isinstance(self._result, Promise):
+      return self._result.Then(onFullfilled, onRejected)
+    def GeneratorFunction(resolve, reject):
+      if self._state == Promise.STATE_PENDING:
+        self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled))
+        self._onRejected.append(_Delegate(reject, reject, onRejected))
+      if self._state == self.STATE_FULLFILLED:
+        _Delegate(resolve, reject, onFullfilled)(self._result)
+      if self._state == self.STATE_REJECTED:
+        recover = reject
+        if onRejected:
+          recover = resolve
+        _Delegate(recover, reject, onRejected)(self._result)
+    return Promise(GeneratorFunction)
+
+  def Catch(self, onCatched):
+    """Equivalent to |Then(None, onCatched)|"""
+    return self.Then(None, onCatched)
+
+  def _Resolve(self, value):
+    if self.state != Promise.STATE_PENDING:
+      return
+    self._result = value
+    if isinstance(value, Promise):
+      self._state = Promise.STATE_BOUND
+      self._result.Then(_IterateAction(self._onFulfilled),
+                        _IterateAction(self._onRejected))
+      return
+    self._state = Promise.STATE_FULLFILLED
+    for f in self._onFulfilled:
+      f(value)
+    self._onFulfilled = None
+    self._onRejected = None
+
+  def _Reject(self, reason):
+    if self.state != Promise.STATE_PENDING:
+      return
+    self._result = reason
+    self._state = Promise.STATE_REJECTED
+    for f in self._onRejected:
+      f(reason)
+    self._onFulfilled = None
+    self._onRejected = None
+
+
+def _IterateAction(iterable):
+  def _Run(x):
+    for f in iterable:
+      f(x)
+  return _Run
+
+
+def _Delegate(resolve, reject, action):
+  def _Run(x):
+    try:
+      if action:
+        resolve(action(x))
+      else:
+        resolve(x)
+    except Exception as e:
+      reject(e)
+  return _Run
diff --git a/mojo/public/python/mojo/bindings/reflection.py b/mojo/public/python/mojo/bindings/reflection.py
new file mode 100644
index 0000000..c682e5e
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/reflection.py
@@ -0,0 +1,163 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""The metaclasses used by the mojo python bindings."""
+
+import itertools
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+
+
+class MojoEnumType(type):
+  """Meta class for enumerations.
+
+  Usage:
+    class MyEnum(object):
+      __metaclass__ = MojoEnumType
+      VALUES = [
+        ('A', 0),
+        'B',
+        ('C', 5),
+      ]
+
+      This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5.
+  """
+
+  def __new__(mcs, name, bases, dictionary):
+    dictionary['__slots__'] = ()
+    dictionary['__new__'] = None
+    for value in dictionary.pop('VALUES', []):
+      if not isinstance(value, tuple):
+        raise ValueError('incorrect value: %r' % value)
+      key, enum_value = value
+      if isinstance(key, str) and isinstance(enum_value, int):
+        dictionary[key] = enum_value
+      else:
+        raise ValueError('incorrect value: %r' % value)
+    return type.__new__(mcs, name, bases, dictionary)
+
+  def __setattr__(mcs, key, value):
+    raise AttributeError, 'can\'t set attribute'
+
+  def __delattr__(mcs, key):
+    raise AttributeError, 'can\'t delete attribute'
+
+
+class MojoStructType(type):
+  """Meta class for structs.
+
+  Usage:
+    class MyStruct(object):
+      __metaclass__ = MojoStructType
+      DESCRIPTOR = {
+        'constants': {
+          'C1': 1,
+          'C2': 2,
+        },
+        'enums': {
+          'ENUM1': [
+            ('V1', 1),
+            'V2',
+          ],
+          'ENUM2': [
+            ('V1', 1),
+            'V2',
+          ],
+        },
+        'fields': [
+           FieldDescriptor('x', _descriptor.TYPE_INT32, 0),
+        ],
+      }
+
+      This will define an struct, with:
+      - 2 constants 'C1' and 'C2';
+      - 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and
+        'V2';
+      - 1 int32 field named 'x'.
+  """
+
+  def __new__(mcs, name, bases, dictionary):
+    dictionary['__slots__'] = ('_fields')
+    descriptor = dictionary.pop('DESCRIPTOR', {})
+
+    # Add constants
+    dictionary.update(descriptor.get('constants', {}))
+
+    # Add enums
+    enums = descriptor.get('enums', {})
+    for key in enums:
+      dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] })
+
+    # Add fields
+    groups = descriptor.get('fields', [])
+
+    fields = list(
+        itertools.chain.from_iterable([group.descriptors for group in groups]))
+    fields.sort(key=lambda f: f.index)
+    for field in fields:
+      dictionary[field.name] = _BuildProperty(field)
+
+    # Add init
+    dictionary['__init__'] = _StructInit(fields)
+
+    # Add serialization method
+    serialization_object = serialization.Serialization(groups)
+    def Serialize(self, handle_offset=0):
+      return serialization_object.Serialize(self, handle_offset)
+    dictionary['Serialize'] = Serialize
+
+    def Deserialize(cls, data, handles):
+      result = cls.__new__(cls)
+      fields = {}
+      serialization_object.Deserialize(fields, data, handles)
+      result._fields = fields
+      return result
+    dictionary['Deserialize'] = classmethod(Deserialize)
+
+    return type.__new__(mcs, name, bases, dictionary)
+
+  # Prevent adding new attributes, or mutating constants.
+  def __setattr__(mcs, key, value):
+    raise AttributeError, 'can\'t set attribute'
+
+  # Prevent deleting constants.
+  def __delattr__(mcs, key):
+    raise AttributeError, 'can\'t delete attribute'
+
+
+def _StructInit(fields):
+  def _Init(self, *args, **kwargs):
+    if len(args) + len(kwargs) > len(fields):
+      raise TypeError('__init__() takes %d argument (%d given)' %
+                      (len(fields), len(args) + len(kwargs)))
+    self._fields = {}
+    for f, a in zip(fields, args):
+      self.__setattr__(f.name, a)
+    remaining_fields = set(x.name for x in fields[len(args):])
+    for name in kwargs:
+      if not name in remaining_fields:
+        if name in (x.name for x in fields[:len(args)]):
+          raise TypeError(
+              '__init__() got multiple values for keyword argument %r' % name)
+        raise TypeError('__init__() got an unexpected keyword argument %r' %
+                        name)
+      self.__setattr__(name, kwargs[name])
+  return _Init
+
+
+def _BuildProperty(field):
+  """Build the property for the given field."""
+
+  # pylint: disable=W0212
+  def Get(self):
+    if field.name not in self._fields:
+      self._fields[field.name] = field.GetDefaultValue()
+    return self._fields[field.name]
+
+  # pylint: disable=W0212
+  def Set(self, value):
+    self._fields[field.name] = field.field_type.Convert(value)
+
+  return property(Get, Set)
diff --git a/mojo/public/python/mojo/bindings/serialization.py b/mojo/public/python/mojo/bindings/serialization.py
new file mode 100644
index 0000000..9a4b6a9
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/serialization.py
@@ -0,0 +1,126 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utility classes for serialization"""
+
+import struct
+
+
+# Format of a header for a struct or an array.
+HEADER_STRUCT = struct.Struct("=II")
+
+
+class SerializationException(Exception):
+  """Error when strying to serialize a struct."""
+  pass
+
+
+class DeserializationException(Exception):
+  """Error when strying to deserialize a struct."""
+  pass
+
+
+class Serialization(object):
+  """
+  Helper class to serialize/deserialize a struct.
+  """
+  def __init__(self, groups):
+    self.version = _GetVersion(groups)
+    self._groups = groups
+    main_struct = _GetStruct(groups)
+    self.size = HEADER_STRUCT.size + main_struct.size
+    self._struct_per_version = {
+        self.version: main_struct,
+    }
+    self._groups_per_version = {
+        self.version: groups,
+    }
+
+  def _GetMainStruct(self):
+    return self._GetStruct(self.version)
+
+  def _GetGroups(self, version):
+    # If asking for a version greater than the last known.
+    version = min(version, self.version)
+    if version not in self._groups_per_version:
+      self._groups_per_version[version] = _FilterGroups(self._groups, version)
+    return self._groups_per_version[version]
+
+  def _GetStruct(self, version):
+    # If asking for a version greater than the last known.
+    version = min(version, self.version)
+    if version not in self._struct_per_version:
+      self._struct_per_version[version] = _GetStruct(self._GetGroups(version))
+    return self._struct_per_version[version]
+
+  def Serialize(self, obj, handle_offset):
+    """
+    Serialize the given obj. handle_offset is the the first value to use when
+    encoding handles.
+    """
+    handles = []
+    data = bytearray(self.size)
+    HEADER_STRUCT.pack_into(data, 0, self.size, self.version)
+    position = HEADER_STRUCT.size
+    to_pack = []
+    for group in self._groups:
+      position = position + NeededPaddingForAlignment(position,
+                                                      group.GetByteSize())
+      (entry, new_handles) = group.Serialize(
+          obj,
+          len(data) - position,
+          data,
+          handle_offset + len(handles))
+      to_pack.append(entry)
+      handles.extend(new_handles)
+      position = position + group.GetByteSize()
+    self._GetMainStruct().pack_into(data, HEADER_STRUCT.size, *to_pack)
+    return (data, handles)
+
+  def Deserialize(self, fields, data, handles):
+    if not isinstance(data, buffer):
+      data = buffer(data)
+    (_, version) = HEADER_STRUCT.unpack_from(data)
+    version_struct = self._GetStruct(version)
+    entitities = version_struct.unpack_from(data, HEADER_STRUCT.size)
+    filtered_groups = self._GetGroups(version)
+    position = HEADER_STRUCT.size
+    for (group, value) in zip(filtered_groups, entitities):
+      position = position + NeededPaddingForAlignment(position,
+                                                      group.GetByteSize())
+      fields.update(group.Deserialize(value, buffer(data, position), handles))
+      position += group.GetByteSize()
+
+
+def NeededPaddingForAlignment(value, alignment=8):
+  """Returns the padding necessary to align value with the given alignment."""
+  if value % alignment:
+    return alignment - (value % alignment)
+  return 0
+
+
+def _GetVersion(groups):
+  return sum([len(x.descriptors) for x in groups])
+
+
+def _FilterGroups(groups, version):
+  return [group for group in groups if group.GetVersion() < version]
+
+
+def _GetStruct(groups):
+  index = 0
+  codes = [ '=' ]
+  for group in groups:
+    code = group.GetTypeCode()
+    size = group.GetByteSize()
+    needed_padding = NeededPaddingForAlignment(index, size)
+    if needed_padding:
+      codes.append('x' * needed_padding)
+      index = index + needed_padding
+    codes.append(code)
+    index = index + size
+  alignment_needed = NeededPaddingForAlignment(index)
+  if alignment_needed:
+    codes.append('x' * alignment_needed)
+  return struct.Struct(''.join(codes))
diff --git a/mojo/public/python/mojo/c_core.pxd b/mojo/public/python/mojo/c_core.pxd
new file mode 100644
index 0000000..80b8487
--- /dev/null
+++ b/mojo/public/python/mojo/c_core.pxd
@@ -0,0 +1,201 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# distutils: language = c++
+
+from cpython.buffer cimport PyBUF_CONTIG
+from cpython.buffer cimport PyBUF_CONTIG_RO
+from cpython.buffer cimport Py_buffer
+from cpython.buffer cimport PyBuffer_FillInfo
+from cpython.buffer cimport PyBuffer_Release
+from cpython.buffer cimport PyObject_GetBuffer
+from cpython.mem cimport PyMem_Malloc, PyMem_Free
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t
+
+cdef extern from "third_party/cython/python_export.h":
+  pass
+
+cdef extern from "mojo/public/platform/native/system_thunks.h" nogil:
+  cdef struct MojoSystemThunks:
+    pass
+
+cdef extern size_t MojoSetSystemThunks(const MojoSystemThunks* system_thunks)
+
+cdef extern from "mojo/public/c/system/core.h" nogil:
+  # types.h
+  ctypedef int64_t MojoTimeTicks
+
+  ctypedef uint32_t MojoHandle
+  const MojoHandle MOJO_HANDLE_INVALID
+
+  ctypedef int32_t MojoResult
+  const MojoResult MOJO_RESULT_OK
+  const MojoResult MOJO_RESULT_CANCELLED
+  const MojoResult MOJO_RESULT_UNKNOWN
+  const MojoResult MOJO_RESULT_INVALID_ARGUMENT
+  const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED
+  const MojoResult MOJO_RESULT_NOT_FOUND
+  const MojoResult MOJO_RESULT_ALREADY_EXISTS
+  const MojoResult MOJO_RESULT_PERMISSION_DENIED
+  const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED
+  const MojoResult MOJO_RESULT_FAILED_PRECONDITION
+  const MojoResult MOJO_RESULT_ABORTED
+  const MojoResult MOJO_RESULT_OUT_OF_RANGE
+  const MojoResult MOJO_RESULT_UNIMPLEMENTED
+  const MojoResult MOJO_RESULT_INTERNAL
+  const MojoResult MOJO_RESULT_UNAVAILABLE
+  const MojoResult MOJO_RESULT_DATA_LOSS
+  const MojoResult MOJO_RESULT_BUSY
+  const MojoResult MOJO_RESULT_SHOULD_WAIT
+
+  ctypedef uint64_t MojoDeadline
+  const MojoDeadline MOJO_DEADLINE_INDEFINITE
+
+  ctypedef uint32_t MojoHandleSignals
+  const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE
+  const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE
+  const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE
+
+  # functions.h
+  MojoTimeTicks MojoGetTimeTicksNow()
+  MojoResult MojoClose(MojoHandle handle)
+  MojoResult MojoWait(MojoHandle handle,
+                      MojoHandleSignals signals,
+                      MojoDeadline deadline)
+  MojoResult MojoWaitMany(const MojoHandle* handles,
+                          const MojoHandleSignals* signals,
+                          uint32_t num_handles,
+                          MojoDeadline deadline)
+
+  # message_pipe.h
+  ctypedef uint32_t MojoCreateMessagePipeOptionsFlags
+  const MojoCreateMessagePipeOptionsFlags MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+
+  ctypedef uint32_t MojoWriteMessageFlags
+  const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE
+
+  ctypedef uint32_t MojoReadMessageFlags
+  const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE
+  const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
+
+  cdef struct MojoCreateMessagePipeOptions:
+    uint32_t struct_size
+    MojoCreateMessagePipeOptionsFlags flags
+
+  MojoResult MojoCreateMessagePipe(
+      const MojoCreateMessagePipeOptions* options,
+      MojoHandle* message_pipe_handle0,
+      MojoHandle* message_pipe_handle1)
+
+  MojoResult MojoWriteMessage(
+      MojoHandle message_pipe_handle,
+      const void* bytes,
+      uint32_t num_bytes,
+      const MojoHandle* handles,
+      uint32_t num_handles,
+      MojoWriteMessageFlags flags)
+
+  MojoResult MojoReadMessage(
+      MojoHandle message_pipe_handle,
+      void* bytes,
+      uint32_t* num_bytes,
+      MojoHandle* handles,
+      uint32_t* num_handles,
+      MojoReadMessageFlags flags)
+
+  # data_pipe.h
+  ctypedef uint32_t MojoCreateDataPipeOptionsFlags
+  const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
+  const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD
+
+  cdef struct MojoCreateDataPipeOptions:
+    uint32_t struct_size
+    MojoCreateDataPipeOptionsFlags flags
+    uint32_t element_num_bytes
+    uint32_t capacity_num_bytes
+
+  ctypedef uint32_t MojoWriteDataFlags
+
+  const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE
+  const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+
+  ctypedef uint32_t MojoReadDataFlags
+
+  const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE
+  const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE
+  const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD
+  const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY
+
+  MojoResult MojoCreateDataPipe(
+      const MojoCreateDataPipeOptions* options,
+      MojoHandle* data_pipe_producer_handle,
+      MojoHandle* data_pipe_consumer_handle)
+
+  MojoResult MojoWriteData(
+      MojoHandle data_pipe_producer_handle,
+      const void* elements,
+      uint32_t* num_bytes,
+      MojoWriteDataFlags flags)
+
+  MojoResult MojoBeginWriteData(
+      MojoHandle data_pipe_producer_handle,
+      void** buffer,
+      uint32_t* buffer_num_bytes,
+      MojoWriteDataFlags flags)
+
+  MojoResult MojoEndWriteData(
+      MojoHandle data_pipe_producer_handle,
+      uint32_t num_bytes_written)
+
+  MojoResult MojoReadData(
+      MojoHandle data_pipe_consumer_handle,
+      void* elements,
+      uint32_t* num_bytes,
+      MojoReadDataFlags flags)
+
+  MojoResult MojoBeginReadData(
+      MojoHandle data_pipe_consumer_handle,
+      const void** buffer,
+      uint32_t* buffer_num_bytes,
+      MojoReadDataFlags flags)
+
+  MojoResult MojoEndReadData(
+      MojoHandle data_pipe_consumer_handle,
+      uint32_t num_bytes_read)
+
+  # buffer.h
+  ctypedef uint32_t MojoCreateSharedBufferOptionsFlags
+  const MojoCreateSharedBufferOptionsFlags MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+
+  cdef struct MojoCreateSharedBufferOptions:
+    uint32_t struct_size
+    MojoCreateSharedBufferOptionsFlags flags
+
+  ctypedef uint32_t MojoDuplicateBufferHandleOptionsFlags
+  const MojoDuplicateBufferHandleOptionsFlags MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+
+  cdef struct MojoDuplicateBufferHandleOptions:
+    uint32_t struct_size
+    MojoDuplicateBufferHandleOptionsFlags flags
+
+  ctypedef uint32_t MojoMapBufferFlags
+  const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE
+
+  MojoResult MojoCreateSharedBuffer(
+      const MojoCreateSharedBufferOptions* options,
+      uint64_t num_bytes,
+      MojoHandle* shared_buffer_handle)
+
+  MojoResult MojoDuplicateBufferHandle(
+      MojoHandle buffer_handle,
+      const MojoDuplicateBufferHandleOptions* options,
+      MojoHandle* new_buffer_handle)
+
+  MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+                           uint64_t offset,
+                           uint64_t num_bytes,
+                           void** buffer,
+                           MojoMapBufferFlags flags)
+
+  MojoResult MojoUnmapBuffer(void* buffer)
diff --git a/mojo/public/python/mojo/c_environment.pxd b/mojo/public/python/mojo/c_environment.pxd
new file mode 100644
index 0000000..13a0934
--- /dev/null
+++ b/mojo/public/python/mojo/c_environment.pxd
@@ -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.
+
+# distutils: language = c++
+
+from libc.stdint cimport int64_t, intptr_t, uint32_t, uint64_t
+
+
+cdef extern from "mojo/public/c/system/core.h" nogil:
+  ctypedef uint32_t MojoHandle
+  ctypedef uint64_t MojoDeadline
+  ctypedef uint32_t MojoHandleSignals
+
+
+cdef extern from "mojo/public/cpp/bindings/callback.h" nogil:
+  cdef cppclass CClosure "mojo::Callback<void()>":
+    CClosure()
+
+
+cdef extern from "mojo/public/c/environment/async_waiter.h"  nogil:
+  ctypedef intptr_t MojoAsyncWaitID
+
+
+cdef extern from "mojo/public/python/src/python_system_helper.h" \
+    namespace "mojo::python" nogil:
+  cdef CClosure BuildClosure(object)
+  cdef cppclass PythonAsyncWaiter "mojo::python::PythonAsyncWaiter":
+    PythonAsyncWaiter()
+    MojoAsyncWaitID AsyncWait(MojoHandle,
+                              MojoHandleSignals,
+                              MojoDeadline,
+                              object)
+    void CancelWait(MojoAsyncWaitID)
+
+
+cdef extern from "mojo/public/cpp/utility/run_loop.h" nogil:
+  cdef cppclass CRunLoop "mojo::RunLoop":
+    CRunLoop()
+    void Run() except *
+    void RunUntilIdle() except *
+    void Quit()
+    void PostDelayedTask(CClosure&, int64_t)
+
+
+cdef extern from "mojo/public/cpp/environment/environment.h" nogil:
+  cdef cppclass CEnvironment "mojo::Environment":
+    CEnvironment()
diff --git a/mojo/public/python/mojo/system.pyx b/mojo/public/python/mojo/system.pyx
new file mode 100644
index 0000000..4768247
--- /dev/null
+++ b/mojo/public/python/mojo/system.pyx
@@ -0,0 +1,741 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# distutils language = c++
+
+cimport c_core
+cimport c_environment
+
+
+from cpython.buffer cimport PyBUF_CONTIG
+from cpython.buffer cimport PyBUF_CONTIG_RO
+from cpython.buffer cimport Py_buffer
+from cpython.buffer cimport PyBuffer_FillInfo
+from cpython.buffer cimport PyBuffer_Release
+from cpython.buffer cimport PyObject_GetBuffer
+from cpython.mem cimport PyMem_Malloc, PyMem_Free
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t
+
+def SetSystemThunks(system_thunks_as_object):
+  """Bind the basic Mojo Core functions.
+
+  This should only be used by the embedder.
+  """
+  cdef const c_core.MojoSystemThunks* system_thunks = (
+      <const c_core.MojoSystemThunks*><uintptr_t>system_thunks_as_object)
+  c_core.MojoSetSystemThunks(system_thunks)
+
+HANDLE_INVALID = c_core.MOJO_HANDLE_INVALID
+RESULT_OK = c_core.MOJO_RESULT_OK
+RESULT_CANCELLED = c_core.MOJO_RESULT_CANCELLED
+RESULT_UNKNOWN = c_core.MOJO_RESULT_UNKNOWN
+RESULT_INVALID_ARGUMENT = c_core.MOJO_RESULT_INVALID_ARGUMENT
+RESULT_DEADLINE_EXCEEDED = c_core.MOJO_RESULT_DEADLINE_EXCEEDED
+RESULT_NOT_FOUND = c_core.MOJO_RESULT_NOT_FOUND
+RESULT_ALREADY_EXISTS = c_core.MOJO_RESULT_ALREADY_EXISTS
+RESULT_PERMISSION_DENIED = c_core.MOJO_RESULT_PERMISSION_DENIED
+RESULT_RESOURCE_EXHAUSTED = c_core.MOJO_RESULT_RESOURCE_EXHAUSTED
+RESULT_FAILED_PRECONDITION = c_core.MOJO_RESULT_FAILED_PRECONDITION
+RESULT_ABORTED = c_core.MOJO_RESULT_ABORTED
+RESULT_OUT_OF_RANGE = c_core.MOJO_RESULT_OUT_OF_RANGE
+RESULT_UNIMPLEMENTED = c_core.MOJO_RESULT_UNIMPLEMENTED
+RESULT_INTERNAL = c_core.MOJO_RESULT_INTERNAL
+RESULT_UNAVAILABLE = c_core.MOJO_RESULT_UNAVAILABLE
+RESULT_DATA_LOSS = c_core.MOJO_RESULT_DATA_LOSS
+RESULT_BUSY = c_core.MOJO_RESULT_BUSY
+RESULT_SHOULD_WAIT = c_core.MOJO_RESULT_SHOULD_WAIT
+DEADLINE_INDEFINITE = c_core.MOJO_DEADLINE_INDEFINITE
+HANDLE_SIGNAL_NONE = c_core.MOJO_HANDLE_SIGNAL_NONE
+HANDLE_SIGNAL_READABLE = c_core.MOJO_HANDLE_SIGNAL_READABLE
+HANDLE_SIGNAL_WRITABLE = c_core.MOJO_HANDLE_SIGNAL_WRITABLE
+WRITE_MESSAGE_FLAG_NONE = c_core.MOJO_WRITE_MESSAGE_FLAG_NONE
+READ_MESSAGE_FLAG_NONE = c_core.MOJO_READ_MESSAGE_FLAG_NONE
+READ_MESSAGE_FLAG_MAY_DISCARD = c_core.MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
+WRITE_DATA_FLAG_NONE = c_core.MOJO_WRITE_DATA_FLAG_NONE
+WRITE_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+READ_DATA_FLAG_NONE = c_core.MOJO_READ_DATA_FLAG_NONE
+READ_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE
+READ_DATA_FLAG_DISCARD = c_core.MOJO_READ_DATA_FLAG_DISCARD
+READ_DATA_FLAG_QUERY = c_core.MOJO_READ_DATA_FLAG_QUERY
+MAP_BUFFER_FLAG_NONE = c_core.MOJO_MAP_BUFFER_FLAG_NONE
+
+def GetTimeTicksNow():
+  """Monotonically increasing tick count representing "right now."
+
+  See mojo/public/c/system/functions.h
+  """
+  return c_core.MojoGetTimeTicksNow()
+
+cdef class _ScopedMemory:
+  """Allocate memory at creation, and deallocate it at destruction."""
+  cdef void* memory
+  def __init__(self, size):
+    self.memory = PyMem_Malloc(size)
+
+  def __dealloc__(self):
+    PyMem_Free(self.memory)
+
+cdef class _ScopedBuffer:
+  """Retrieve pointer to a buffer a creation, and release it at destruction.
+  """
+  cdef Py_buffer _buf
+  cdef void* buf
+  cdef Py_ssize_t len
+
+  def __init__(self, obj, flags=PyBUF_CONTIG_RO):
+    if obj:
+      if PyObject_GetBuffer(obj, &self._buf, flags) < 0:
+        raise TypeError('Unable to read buffer.')
+      self.buf = self._buf.buf
+      self.len = self._buf.len
+    else:
+      self.buf = NULL
+      self.len = 0
+
+  def __dealloc__(self):
+    if self.buf:
+      PyBuffer_Release(&self._buf)
+
+def _SliceBuffer(buffer, size):
+  """Slice the given buffer, reducing it to the given size.
+
+  Return None if None is passed in.
+  """
+  if not buffer:
+    return buffer
+  return buffer[:size]
+
+cdef class _NativeMemoryView(object):
+  """Create a python buffer wrapping the given memory.
+
+  Will also retain the given handle until this object is deallocated.
+  """
+  cdef void* _memory
+  cdef uint32_t _size
+  cdef char _read_only
+  cdef char _wrapped
+  cdef object _handle
+
+  def __init__(self, handle):
+    self._handle = handle
+
+  def __cinit__(self):
+    self._memory = NULL
+    self._size = 0
+    self._read_only = True
+    self._wrapped = False
+
+  cdef Wrap(self,
+            const void* memory,
+            uint32_t size,
+            read_only=True):
+    """Makes this buffer wraps the given memory.
+
+    Must be called before using this buffer, and must only be called once.
+    """
+    assert not self._wrapped
+    self._wrapped = True
+    self._memory = <void*>memory
+    self._size = size
+    self._read_only = read_only
+
+  # buffer interface (PEP 3118)
+  def __getbuffer__(self, Py_buffer *view, int flags):
+    assert self._wrapped
+    if view == NULL:
+      return
+    PyBuffer_FillInfo(view,
+                      self,
+                      self._memory,
+                      self._size,
+                      self._read_only,
+                      flags)
+
+  def __releasebuffer__(self, Py_buffer *view):
+    assert self._wrapped
+    pass
+
+  # legacy buffer interface
+  def __getsegcount__(self, Py_ssize_t *sizes):
+    assert self._wrapped
+    if sizes != NULL:
+      sizes[0] = self._size
+    return 1
+
+  def __getreadbuffer__(self, Py_ssize_t index, void **data):
+    assert self._wrapped
+    if index != 0:
+      raise SystemError('Index out of bounds: %d' % index)
+    data[0] = self._memory
+    return self._size
+
+  def __getwritebuffer__(self, Py_ssize_t index, void **data):
+    assert self._wrapped
+    if index != 0:
+      raise SystemError('Index out of bounds: %d' % index)
+    if self._read_only:
+      raise TypeError('Buffer is read-only.')
+    data[0] = self._memory
+    return self._size
+
+class MojoException(Exception):
+  """Exception wrapping a mojo result error code."""
+
+  def __init__(self, mojo_result):
+    self.mojo_result = mojo_result
+
+def WaitMany(handles_and_signals, deadline):
+  """Waits on a list of handles.
+
+  Args:
+    handles_and_signals: list of tuples of handle and signal.
+
+  See mojo/public/c/system/functions.h
+  """
+  cdef uint32_t length = len(handles_and_signals)
+  cdef _ScopedMemory handles_alloc = _ScopedMemory(
+      sizeof(c_core.MojoHandle) * length)
+  cdef _ScopedMemory signals_alloc = _ScopedMemory(
+      sizeof(c_core.MojoHandleSignals) * length)
+  cdef c_core.MojoHandle* handles = <c_core.MojoHandle*>handles_alloc.memory
+  cdef c_core.MojoHandleSignals* signals = (
+      <c_core.MojoHandleSignals*>signals_alloc.memory)
+  cdef int index = 0
+  for (h, s) in handles_and_signals:
+    handles[index] = (<Handle?>h)._mojo_handle
+    signals[index] = s
+    index += 1
+  cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK
+  cdef c_core.MojoDeadline cdeadline = deadline
+  with nogil:
+    result = c_core.MojoWaitMany(handles, signals, length, cdeadline)
+  return result
+
+cdef class DataPipeTwoPhaseBuffer(object):
+  """Return value for two phases read and write.
+
+  The buffer field contains the python buffer where data can be read or written.
+  When done with the buffer, the |end| method must be called with the number of
+  bytes read or written.
+  """
+
+  cdef object _buffer
+  cdef Handle _handle
+  cdef char _read
+
+  def __init__(self, handle, buffer, read=True):
+    self._buffer = buffer
+    self._handle = handle
+    self._read = read
+
+  def End(self, num_bytes):
+    self._buffer = None
+    cdef c_core.MojoResult result
+    if self._read:
+      result = c_core.MojoEndReadData(self._handle._mojo_handle, num_bytes)
+    else:
+      result = c_core.MojoEndWriteData(self._handle._mojo_handle, num_bytes)
+    self._handle = None
+    return result
+
+  @property
+  def buffer(self):
+    return self._buffer
+
+  def __dealloc__(self):
+    assert not self._buffer
+
+cdef class MappedBuffer(object):
+  """Return value for the |map| operation on shared buffer handles.
+
+  The buffer field contains the python buffer where data can be read or written.
+  When done with the buffer, the |unmap| method must be called.
+  """
+
+  cdef object _buffer
+  cdef object _handle
+  cdef object _cleanup
+
+  def __init__(self, handle, buffer, cleanup):
+    self._buffer = buffer
+    self._handle = handle
+    self._cleanup = cleanup
+
+  def UnMap(self):
+    self._buffer = None
+    cdef c_core.MojoResult result = self._cleanup()
+    self._cleanup = None
+    self._handle = None
+    return result
+
+  @property
+  def buffer(self):
+    return self._buffer
+
+  def __dealloc__(self):
+    if self._buffer:
+      self.UnMap()
+
+cdef class Handle(object):
+  """A mojo object."""
+
+  cdef c_core.MojoHandle _mojo_handle
+
+  def __init__(self, mojo_handle=c_core.MOJO_HANDLE_INVALID):
+    self._mojo_handle = mojo_handle
+
+  def _Invalidate(self):
+    """Invalidate the current handle.
+
+    The close operation is not called. It is the responsability of the caller to
+    ensure that the handle is not leaked.
+    """
+    self._mojo_handle = c_core.MOJO_HANDLE_INVALID
+
+  def IsValid(self):
+    """Returns whether this handle is valid."""
+    return self._mojo_handle != c_core.MOJO_HANDLE_INVALID
+
+  def Close(self):
+    """Closes this handle.
+
+    See mojo/public/c/system/functions.h
+    """
+    cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK
+    if self.IsValid():
+      result = c_core.MojoClose(self._mojo_handle)
+      self._Invalidate()
+    return result
+
+  def __dealloc__(self):
+    self.Close()
+
+  def Wait(self, signals, deadline):
+    """Waits on the given handle.
+
+    See mojo/public/c/system/functions.h
+    """
+    cdef c_core.MojoHandle handle = self._mojo_handle
+    cdef c_core.MojoHandleSignals csignals = signals
+    cdef c_core.MojoDeadline cdeadline = deadline
+    cdef c_core.MojoResult result
+    with nogil:
+      result = c_core.MojoWait(handle, csignals, cdeadline)
+    return result
+
+  def AsyncWait(self, signals, deadline, callback):
+    cdef c_core.MojoHandle handle = self._mojo_handle
+    cdef c_core.MojoHandleSignals csignals = signals
+    cdef c_core.MojoDeadline cdeadline = deadline
+    cdef c_environment.MojoAsyncWaitID wait_id = _ASYNC_WAITER.AsyncWait(
+        handle,
+        csignals,
+        cdeadline,
+        callback)
+    def cancel():
+      _ASYNC_WAITER.CancelWait(wait_id)
+    return cancel
+
+  def WriteMessage(self,
+                    buffer=None,
+                    handles=None,
+                    flags=WRITE_MESSAGE_FLAG_NONE):
+    """Writes a message to the message pipe.
+
+    This method can only be used on a handle obtained from |MessagePipe()|.
+
+    See mojo/public/c/system/message_pipe.h
+    """
+    cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+    cdef uint32_t input_buffer_length = buffer_as_buffer.len
+    cdef c_core.MojoHandle* input_handles = NULL
+    cdef uint32_t input_handles_length = 0
+    cdef _ScopedMemory handles_alloc = None
+    if handles:
+      input_handles_length = len(handles)
+      handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) *
+                                    input_handles_length)
+      input_handles = <c_core.MojoHandle*>handles_alloc.memory
+      for i in xrange(input_handles_length):
+        input_handles[i] = (<Handle?>handles[i])._mojo_handle
+    cdef c_core.MojoResult res = c_core.MojoWriteMessage(self._mojo_handle,
+                                                         buffer_as_buffer.buf,
+                                                         input_buffer_length,
+                                                         input_handles,
+                                                         input_handles_length,
+                                                         flags)
+    if res == c_core.MOJO_RESULT_OK and handles:
+      # Handles have been transferred. Let's invalidate those.
+      for handle in handles:
+        handle._Invalidate()
+    return res
+
+  def ReadMessage(self,
+                   buffer=None,
+                   max_number_of_handles=0,
+                   flags=READ_MESSAGE_FLAG_NONE):
+    """Reads a message from the message pipe.
+
+    This method can only be used on a handle obtained from |MessagePipe()|.
+
+    This method returns a triplet of value (code, data, sizes):
+    - if code is RESULT_OK, sizes will be None, and data will be a pair of
+      (buffer, handles) where buffer is a view of the input buffer with the read
+      data, and handles is a list of received handles.
+    - if code is RESULT_RESOURCE_EXHAUSTED, data will be None and sizes will be
+      a pair of (buffer_size, handles_size) where buffer_size is the size of the
+      next message data and handles_size is the number of handles in the next
+      message.
+    - if code is any other value, data and sizes will be None.
+
+    See mojo/public/c/system/message_pipe.h
+    """
+    cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer, PyBUF_CONTIG)
+    cdef uint32_t input_buffer_length = buffer_as_buffer.len
+    cdef c_core.MojoHandle* input_handles = NULL
+    cdef uint32_t input_handles_length = 0
+    cdef _ScopedMemory handles_alloc = None
+    if max_number_of_handles > 0:
+      input_handles_length = max_number_of_handles
+      handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) *
+                                    input_handles_length)
+      input_handles = <c_core.MojoHandle*>handles_alloc.memory
+    cdef res = c_core.MojoReadMessage(self._mojo_handle,
+                                      buffer_as_buffer.buf,
+                                      &input_buffer_length,
+                                      input_handles,
+                                      &input_handles_length,
+                                      flags)
+    if res == c_core.MOJO_RESULT_RESOURCE_EXHAUSTED:
+      return (res, None, (input_buffer_length, input_handles_length))
+    if res == c_core.MOJO_RESULT_OK:
+      returned_handles = [Handle(input_handles[i])
+                          for i in xrange(input_handles_length)]
+      return (res,
+              (_SliceBuffer(buffer, input_buffer_length), returned_handles),
+              None)
+    return (res, None, None)
+
+  def WriteData(self, buffer=None, flags=WRITE_DATA_FLAG_NONE):
+    """
+    Writes the given data to the data pipe producer.
+
+    This method can only be used on a producer handle obtained from
+    |DataPipe()|.
+
+    This method returns a tuple (code, num_bytes).
+    - If code is RESULT_OK, num_bytes is the number of written bytes.
+    - Otherwise, num_bytes is None.
+
+    See mojo/public/c/system/data_pipe.h
+    """
+    cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+    cdef uint32_t input_buffer_length = buffer_as_buffer.len
+    cdef c_core.MojoResult res = c_core.MojoWriteData(self._mojo_handle,
+                                                      buffer_as_buffer.buf,
+                                                      &input_buffer_length,
+                                                      flags)
+    if res == c_core.MOJO_RESULT_OK:
+      return (res, input_buffer_length)
+    return (res, None)
+
+  def BeginWriteData(self,
+                       min_size=None,
+                       flags=WRITE_DATA_FLAG_NONE):
+    """
+    Begins a two-phase write to the data pipe producer.
+
+    This method can only be used on a producer handle obtained from
+    |DataPipe()|.
+
+    This method returns a tuple (code, two_phase_buffer).
+    - If code is RESULT_OK, two_phase_buffer is a writable
+      DataPipeTwoPhaseBuffer
+    - Otherwise, two_phase_buffer is None.
+
+    See mojo/public/c/system/data_pipe.h
+    """
+    cdef void* out_buffer
+    cdef uint32_t out_size = 0
+    if min_size:
+      flags |= c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+      out_size = min_size
+    cdef c_core.MojoResult res = c_core.MojoBeginWriteData(self._mojo_handle,
+                                                           &out_buffer,
+                                                           &out_size,
+                                                           flags)
+    if res != c_core.MOJO_RESULT_OK:
+      return (res, None)
+    cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+    view_buffer.Wrap(out_buffer, out_size, read_only=False)
+    return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), False))
+
+  def ReadData(self, buffer=None, flags=READ_DATA_FLAG_NONE):
+    """Reads data from the data pipe consumer.
+
+    This method can only be used on a consumer handle obtained from
+    |DataPipe()|.
+
+    This method returns a tuple (code, buffer)
+    - if code is RESULT_OK, buffer will be a view of the input buffer with the
+      read data.
+    - otherwise, buffer will be None.
+
+    See mojo/public/c/system/data_pipe.h
+    """
+    cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+    cdef uint32_t input_buffer_length = buffer_as_buffer.len
+    cdef c_core.MojoResult res = c_core.MojoReadData(self._mojo_handle,
+                                                     buffer_as_buffer.buf,
+                                                     &input_buffer_length,
+                                                     flags)
+    if res == c_core.MOJO_RESULT_OK:
+      return (res, _SliceBuffer(buffer, input_buffer_length))
+    return (res, None)
+
+  def QueryData(self, flags=READ_DATA_FLAG_NONE):
+    """Queries the amount of data available on the data pipe consumer.
+
+    This method can only be used on a consumer handle obtained from
+    |DataPipe()|.
+
+    This method returns a tuple (code, num_bytes)
+    - if code is RESULT_OK, num_bytes will be the number of bytes available on
+      the data pipe consumer.
+    - otherwise, num_bytes will be None.
+
+    See mojo/public/c/system/data_pipe.h
+    """
+    cdef uint32_t num_bytes = 0
+    cdef c_core.MojoResult res = c_core.MojoReadData(
+        self._mojo_handle,
+        NULL,
+        &num_bytes,
+        flags|c_core.MOJO_READ_DATA_FLAG_QUERY)
+    return (res, num_bytes)
+
+  def BeginReadData(self, min_size=None, flags=READ_DATA_FLAG_NONE):
+    """
+    Begins a two-phase read to the data pipe consumer.
+
+    This method can only be used on a consumer handle obtained from
+    |DataPipe()|.
+
+    This method returns a tuple (code, two_phase_buffer).
+    - If code is RESULT_OK, two_phase_buffer is a readable
+      DataPipeTwoPhaseBuffer
+    - Otherwise, two_phase_buffer is None.
+
+    See mojo/public/c/system/data_pipe.h
+    """
+    cdef const void* out_buffer
+    cdef uint32_t out_size = 0
+    if min_size:
+      flags |= c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE
+      out_size = min_size
+    cdef c_core.MojoResult res = c_core.MojoBeginReadData(self._mojo_handle,
+                                                          &out_buffer,
+                                                          &out_size,
+                                                          flags)
+    if res != c_core.MOJO_RESULT_OK:
+      return (res, None)
+    cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+    view_buffer.Wrap(out_buffer, out_size, read_only=True)
+    return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), True))
+
+  def Duplicate(self, options=None):
+    """Duplicate the shared buffer handle.
+
+    This method can only be used on a handle obtained from
+    |CreateSharedBuffer()| or |Duplicate()|.
+
+    See mojo/public/c/system/buffer.h
+    """
+    cdef c_core.MojoDuplicateBufferHandleOptions coptions
+    cdef c_core.MojoDuplicateBufferHandleOptions* coptions_ptr = NULL
+    cdef c_core.MojoHandle cnew_handle = c_core.MOJO_HANDLE_INVALID
+    if options:
+      coptions.struct_size = sizeof(c_core.MojoDuplicateBufferHandleOptions)
+      coptions.flags = options.flags
+      coptions_ptr = &coptions
+    cdef c_core.MojoResult result = c_core.MojoDuplicateBufferHandle(
+        self._mojo_handle, coptions_ptr, &cnew_handle)
+    new_handle = Handle(cnew_handle)
+    if result != c_core.MOJO_RESULT_OK:
+      raise MojoException(result)
+    return new_handle
+
+  def Map(self, offset, num_bytes, flags=MAP_BUFFER_FLAG_NONE):
+    """Maps the part (at offset |offset| of length |num_bytes|) of the buffer.
+
+    This method can only be used on a handle obtained from
+    |CreateSharedBuffer()| or |Duplicate()|.
+
+    This method returns a tuple (code, mapped_buffer).
+    - If code is RESULT_OK, mapped_buffer is a readable/writable
+      MappedBuffer
+    - Otherwise, mapped_buffer is None.
+
+    See mojo/public/c/system/buffer.h
+    """
+    cdef void* buffer
+    res = c_core.MojoMapBuffer(self._mojo_handle,
+                               offset,
+                               num_bytes,
+                               &buffer,
+                               flags)
+    if res != c_core.MOJO_RESULT_OK:
+      return (res, None)
+    cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+    view_buffer.Wrap(buffer, num_bytes, read_only=False)
+    return (res, MappedBuffer(self,
+                              memoryview(view_buffer),
+                              lambda: c_core.MojoUnmapBuffer(buffer)))
+
+class CreateMessagePipeOptions(object):
+  """Options for creating a message pipe.
+
+  See mojo/public/c/system/message_pipe.h
+  """
+  FLAG_NONE = c_core.MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+
+  def __init__(self):
+    self.flags = CreateMessagePipeOptions.FLAG_NONE
+
+class MessagePipe(object):
+  """Creates a message pipe.
+
+  The two ends of the message pipe are accessible with the members handle0 and
+  handle1.
+
+  See mojo/public/c/system/message_pipe.h
+  """
+  def __init__(self, options=None):
+    cdef c_core.MojoCreateMessagePipeOptions coptions
+    cdef c_core.MojoCreateMessagePipeOptions* coptions_ptr = NULL
+    cdef c_core.MojoHandle chandle0 = c_core.MOJO_HANDLE_INVALID
+    cdef c_core.MojoHandle chandle1 = c_core.MOJO_HANDLE_INVALID
+    if options:
+      coptions.struct_size = sizeof(c_core.MojoCreateMessagePipeOptions)
+      coptions.flags = options.flags
+      coptions_ptr = &coptions
+    cdef c_core.MojoResult result = c_core.MojoCreateMessagePipe(coptions_ptr,
+                                                                 &chandle0,
+                                                                 &chandle1)
+    self.handle0 = Handle(chandle0)
+    self.handle1 = Handle(chandle1)
+    if result != c_core.MOJO_RESULT_OK:
+      raise c_core.MojoException(result)
+
+
+class CreateDataPipeOptions(object):
+  """Options for creating a data pipe.
+
+  See mojo/public/c/system/data_pipe.h
+  """
+  FLAG_NONE = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
+  FLAG_MAY_DISCARD = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD
+
+  def __init__(self):
+    self.flags = CreateDataPipeOptions.FLAG_NONE
+    self.element_num_bytes = 1
+    self.capacity_num_bytes = 0
+
+class DataPipe(object):
+  """Creates a data pipe.
+
+  The producer end of the data pipe is accessible with the member
+  producer_handle and the consumer end of the data pipe is accessible with the
+  member cconsumer_handle.
+
+  See mojo/public/c/system/data_pipe.h
+  """
+  def __init__(self, options=None):
+    cdef c_core.MojoCreateDataPipeOptions coptions
+    cdef c_core.MojoCreateDataPipeOptions* coptions_ptr = NULL
+    cdef c_core.MojoHandle cproducer_handle = c_core.MOJO_HANDLE_INVALID
+    cdef c_core.MojoHandle cconsumer_handle = c_core.MOJO_HANDLE_INVALID
+    if options:
+      coptions.struct_size = sizeof(c_core.MojoCreateDataPipeOptions)
+      coptions.flags = options.flags
+      coptions.element_num_bytes = options.element_num_bytes
+      coptions.capacity_num_bytes = options.capacity_num_bytes
+      coptions_ptr = &coptions
+    cdef c_core.MojoResult result = c_core.MojoCreateDataPipe(coptions_ptr,
+                                                              &cproducer_handle,
+                                                              &cconsumer_handle)
+    self.producer_handle = Handle(cproducer_handle)
+    self.consumer_handle = Handle(cconsumer_handle)
+    if result != c_core.MOJO_RESULT_OK:
+      raise MojoException(result)
+
+class CreateSharedBufferOptions(object):
+  """Options for creating a shared buffer.
+
+  See mojo/public/c/system/buffer.h
+  """
+  FLAG_NONE = c_core.MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+
+  def __init__(self):
+    self.flags = CreateSharedBufferOptions.FLAG_NONE
+
+def CreateSharedBuffer(num_bytes, options=None):
+  """Creates a buffer of size |num_bytes| bytes that can be shared.
+
+  See mojo/public/c/system/buffer.h
+  """
+  cdef c_core.MojoCreateSharedBufferOptions coptions
+  cdef c_core.MojoCreateSharedBufferOptions* coptions_ptr = NULL
+  cdef c_core.MojoHandle chandle = c_core.MOJO_HANDLE_INVALID
+  if options:
+    coptions.struct_size = sizeof(c_core.MojoCreateSharedBufferOptions)
+    coptions.flags = options.flags
+    coptions_ptr = &coptions
+  cdef c_core.MojoResult result = c_core.MojoCreateSharedBuffer(coptions_ptr,
+                                                                num_bytes,
+                                                                &chandle)
+  handle = Handle(chandle)
+  if result != c_core.MOJO_RESULT_OK:
+    raise MojoException(result)
+  return handle
+
+class DuplicateSharedBufferOptions(object):
+  """Options for duplicating a shared buffer.
+
+  See mojo/public/c/system/buffer.h
+  """
+  FLAG_NONE = c_core.MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+
+  def __init__(self):
+    self.flags = DuplicateSharedBufferOptions.FLAG_NONE
+
+
+cdef class RunLoop(object):
+  """RunLoop to use when using asynchronous operations on handles."""
+
+  cdef c_environment.CRunLoop c_run_loop
+
+  def Run(self):
+    """Run the runloop until Quit is called."""
+    self.c_run_loop.Run()
+
+  def RunUntilIdle(self):
+    """Run the runloop until Quit is called or no operation is waiting."""
+    self.c_run_loop.RunUntilIdle()
+
+  def Quit(self):
+    """Quit the runloop."""
+    self.c_run_loop.Quit()
+
+  def PostDelayedTask(self, runnable, delay=0):
+    """
+    Post a task on the runloop. This must be called from the thread owning the
+    runloop.
+    """
+    cdef c_environment.CClosure closure = c_environment.BuildClosure(runnable)
+    self.c_run_loop.PostDelayedTask(closure, delay)
+
+
+cdef c_environment.CEnvironment* _ENVIRONMENT = new c_environment.CEnvironment()
+cdef c_environment.PythonAsyncWaiter* _ASYNC_WAITER = new c_environment.PythonAsyncWaiter()
diff --git a/mojo/public/python/src/python_system_helper.cc b/mojo/public/python/src/python_system_helper.cc
new file mode 100644
index 0000000..f42e671
--- /dev/null
+++ b/mojo/public/python/src/python_system_helper.cc
@@ -0,0 +1,171 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/python/src/python_system_helper.h"
+
+#include "Python.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace {
+
+class ScopedGIL {
+ public:
+  ScopedGIL() { state_ = PyGILState_Ensure(); }
+
+  ~ScopedGIL() { PyGILState_Release(state_); }
+
+ private:
+  PyGILState_STATE state_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedGIL);
+};
+
+class PythonClosure : public mojo::Closure::Runnable {
+ public:
+  PythonClosure(PyObject* callable) : callable_(callable) {
+    MOJO_DCHECK(callable);
+    Py_XINCREF(callable);
+  }
+
+  virtual ~PythonClosure() {
+    ScopedGIL acquire_gil;
+    Py_DECREF(callable_);
+  }
+
+  virtual void Run() const override {
+    ScopedGIL acquire_gil;
+    PyObject* empty_tuple = PyTuple_New(0);
+    if (!empty_tuple) {
+      mojo::RunLoop::current()->Quit();
+      return;
+    }
+
+    PyObject* result = PyObject_CallObject(callable_, empty_tuple);
+    Py_DECREF(empty_tuple);
+    if (result) {
+      Py_DECREF(result);
+    } else {
+      mojo::RunLoop::current()->Quit();
+      return;
+    }
+  }
+
+ private:
+  PyObject* callable_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(PythonClosure);
+};
+
+void AsyncCallbackForwarder(void* closure, MojoResult result) {
+  mojo::Callback<void(MojoResult)>* callback =
+      static_cast<mojo::Callback<void(MojoResult)>*>(closure);
+  // callback will be deleted when it is run.
+  callback->Run(result);
+}
+
+}  // namespace
+
+namespace mojo {
+namespace python {
+
+class PythonAsyncWaiter::AsyncWaiterRunnable
+    : public mojo::Callback<void(MojoResult)>::Runnable {
+ public:
+  AsyncWaiterRunnable(PyObject* callable, CallbackMap* callbacks)
+      : wait_id_(0), callable_(callable), callbacks_(callbacks) {
+    MOJO_DCHECK(callable);
+    MOJO_DCHECK(callbacks_);
+    Py_XINCREF(callable);
+  }
+
+  virtual ~AsyncWaiterRunnable() {
+    ScopedGIL acquire_gil;
+    Py_DECREF(callable_);
+  }
+
+  void set_wait_id(int wait_id) { wait_id_ = wait_id; }
+
+  virtual void Run(MojoResult mojo_result) const override {
+    MOJO_DCHECK(wait_id_);
+
+    // Remove to reference to this object from PythonAsyncWaiter and ensure this
+    // object will be destroyed when this method exits.
+    MOJO_DCHECK(callbacks_->find(wait_id_) != callbacks_->end());
+    internal::SharedPtr<mojo::Callback<void(MojoResult)> > self =
+        (*callbacks_)[wait_id_];
+    callbacks_->erase(wait_id_);
+
+    ScopedGIL acquire_gil;
+    PyObject* args_tuple = Py_BuildValue("(i)", mojo_result);
+    if (!args_tuple) {
+      mojo::RunLoop::current()->Quit();
+      return;
+    }
+
+    PyObject* result = PyObject_CallObject(callable_, args_tuple);
+    Py_DECREF(args_tuple);
+    if (result) {
+      Py_DECREF(result);
+    } else {
+      mojo::RunLoop::current()->Quit();
+      return;
+    }
+  }
+
+ private:
+  MojoAsyncWaitID wait_id_;
+  PyObject* callable_;
+  CallbackMap* callbacks_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterRunnable);
+};
+
+Closure BuildClosure(PyObject* callable) {
+  if (!PyCallable_Check(callable))
+    return Closure();
+
+  return Closure(
+      static_cast<mojo::Closure::Runnable*>(new PythonClosure(callable)));
+}
+
+PythonAsyncWaiter::PythonAsyncWaiter() {
+  async_waiter_ = Environment::GetDefaultAsyncWaiter();
+}
+
+PythonAsyncWaiter::~PythonAsyncWaiter() {
+  for (CallbackMap::const_iterator it = callbacks_.begin();
+       it != callbacks_.end();
+       ++it) {
+    async_waiter_->CancelWait(it->first);
+  }
+}
+
+MojoAsyncWaitID PythonAsyncWaiter::AsyncWait(MojoHandle handle,
+                                             MojoHandleSignals signals,
+                                             MojoDeadline deadline,
+                                             PyObject* callable) {
+  AsyncWaiterRunnable* runner = new AsyncWaiterRunnable(callable, &callbacks_);
+  internal::SharedPtr<mojo::Callback<void(MojoResult)> > callback(
+      new mojo::Callback<void(MojoResult)>(
+          static_cast<mojo::Callback<void(MojoResult)>::Runnable*>(runner)));
+  MojoAsyncWaitID wait_id = async_waiter_->AsyncWait(
+      handle, signals, deadline, &AsyncCallbackForwarder, callback.get());
+  callbacks_[wait_id] = callback;
+  runner->set_wait_id(wait_id);
+  return wait_id;
+}
+
+void PythonAsyncWaiter::CancelWait(MojoAsyncWaitID wait_id) {
+  if (callbacks_.find(wait_id) != callbacks_.end()) {
+    async_waiter_->CancelWait(wait_id);
+    callbacks_.erase(wait_id);
+  }
+}
+
+}  // namespace python
+}  // namespace mojo
diff --git a/mojo/public/python/src/python_system_helper.h b/mojo/public/python/src/python_system_helper.h
new file mode 100644
index 0000000..e0d83ca
--- /dev/null
+++ b/mojo/public/python/src/python_system_helper.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_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
+#define MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
+
+// Python must be the first include, as it defines preprocessor variable without
+// checking if they already exist.
+#include <Python.h>
+
+#include <map>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+
+namespace mojo {
+namespace python {
+
+// Create a mojo::Closure from a callable python object.
+mojo::Closure BuildClosure(PyObject* callable);
+
+class PythonAsyncWaiter {
+ public:
+  PythonAsyncWaiter();
+  ~PythonAsyncWaiter();
+  MojoAsyncWaitID AsyncWait(MojoHandle handle,
+                            MojoHandleSignals signals,
+                            MojoDeadline deadline,
+                            PyObject* callable);
+
+  void CancelWait(MojoAsyncWaitID wait_id);
+
+ private:
+  class AsyncWaiterRunnable;
+
+  typedef std::map<MojoAsyncWaitID,
+                   internal::SharedPtr<mojo::Callback<void(MojoResult)> > >
+      CallbackMap;
+
+  CallbackMap callbacks_;
+  const MojoAsyncWaiter* async_waiter_;
+};
+
+}  // namespace python
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
diff --git a/mojo/public/tests/DEPS b/mojo/public/tests/DEPS
new file mode 100644
index 0000000..0669117
--- /dev/null
+++ b/mojo/public/tests/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo",
+]
diff --git a/mojo/public/tests/test_support_private.cc b/mojo/public/tests/test_support_private.cc
new file mode 100644
index 0000000..fa5ead4
--- /dev/null
+++ b/mojo/public/tests/test_support_private.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/public/tests/test_support_private.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static mojo::test::TestSupport* g_test_support = NULL;
+
+extern "C" {
+
+void MojoTestSupportLogPerfResult(const char* test_name,
+                                  double value,
+                                  const char* units) {
+  if (g_test_support)
+    g_test_support->LogPerfResult(test_name, value, units);
+  else
+    printf("[no test runner]\t%s\t%g\t%s\n", test_name, value, units);
+}
+
+FILE* MojoTestSupportOpenSourceRootRelativeFile(const char* relative_path) {
+  if (g_test_support)
+    return g_test_support->OpenSourceRootRelativeFile(relative_path);
+  printf("[no test runner]\n");
+  return NULL;
+}
+
+char** MojoTestSupportEnumerateSourceRootRelativeDirectory(
+    const char* relative_path) {
+  if (g_test_support)
+    return g_test_support->EnumerateSourceRootRelativeDirectory(relative_path);
+
+  printf("[no test runner]\n");
+
+  // Return empty list:
+  char** rv = static_cast<char**>(calloc(1, sizeof(char*)));
+  rv[0] = NULL;
+  return rv;
+}
+
+}  // extern "C"
+
+namespace mojo {
+namespace test {
+
+TestSupport::~TestSupport() {
+}
+
+// static
+void TestSupport::Init(TestSupport* test_support) {
+  assert(!g_test_support);
+  g_test_support = test_support;
+}
+
+// static
+TestSupport* TestSupport::Get() {
+  return g_test_support;
+}
+
+// static
+void TestSupport::Reset() {
+  g_test_support = NULL;
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/tests/test_support_private.h b/mojo/public/tests/test_support_private.h
new file mode 100644
index 0000000..29983ca
--- /dev/null
+++ b/mojo/public/tests/test_support_private.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_
+#define MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_
+
+#include <stdio.h>
+
+#include "mojo/public/c/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+// Implementors of the test support APIs can use this interface to install their
+// implementation into the mojo_test_support dynamic library.
+class MOJO_TEST_SUPPORT_EXPORT TestSupport {
+ public:
+  virtual ~TestSupport();
+
+  static void Init(TestSupport* test_support);
+  static TestSupport* Get();
+  static void Reset();
+
+  virtual void LogPerfResult(const char* test_name,
+                             double value,
+                             const char* units) = 0;
+  virtual FILE* OpenSourceRootRelativeFile(const char* relative_path) = 0;
+  virtual char** EnumerateSourceRootRelativeDirectory(
+      const char* relative_path) = 0;
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
new file mode 100644
index 0000000..86c85d7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
@@ -0,0 +1,9 @@
+enum {{enum.name}} {
+{%- for field in enum.fields %}
+{%-    if field.value %}
+  {{enum.name|to_all_caps}}_{{field.name}} = {{field.value|expression_to_text}},
+{%-    else %}
+  {{enum.name|to_all_caps}}_{{field.name}},
+{%-    endif %}
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
new file mode 100644
index 0000000..30d20d8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -0,0 +1,49 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy;
+class {{interface.name}}Stub;
+
+class {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+class {{interface.name}}ResponseValidator;
+{%- endif %}
+{% if interface.client %}
+class {{interface.client}};
+{% endif %}
+
+class {{interface.name}} {
+ public:
+  static const char* Name_;
+
+  typedef {{interface.name}}Proxy Proxy_;
+  typedef {{interface.name}}Stub Stub_;
+
+  typedef {{interface.name}}RequestValidator RequestValidator_;
+{%- if interface|has_callbacks %}
+  typedef {{interface.name}}ResponseValidator ResponseValidator_;
+{%- else %}
+  typedef mojo::PassThroughFilter ResponseValidator_;
+{%- endif %}
+{% if interface.client %}
+  typedef {{interface.client}} Client;
+{% else %}
+  typedef mojo::NoInterface Client;
+{% endif %}
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- for enum in interface.enums %}
+{%    macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+  {{enum_def()|indent(2)}}
+{%- endfor %}
+
+{#--- Methods #}
+  virtual ~{{interface.name}}() {}
+
+{%- for method in interface.methods %}
+  virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0;
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
new file mode 100644
index 0000000..bb5b777
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -0,0 +1,334 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+{%- set class_name = interface.name %}
+{%- set proxy_name = interface.name ~ "Proxy" %}
+{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+
+{%- macro alloc_params(parameters) %}
+{%-   for param in parameters %}
+{%-     if param.kind|is_object_kind  %}
+{{param.kind|cpp_result_type}} p{{loop.index}};
+Deserialize_(params->{{param.name}}.ptr, &p{{loop.index}});
+{%      endif -%}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro pass_params(parameters) %}
+{%-   for param in parameters %}
+{%-     if param.kind|is_string_kind -%}
+p{{loop.index}}
+{%-     elif param.kind|is_object_kind -%}
+p{{loop.index}}.Pass()
+{%-     elif param.kind|is_interface_kind -%}
+mojo::MakeProxy<{{param.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}})))
+{%-     elif param.kind|is_interface_request_kind -%}
+mojo::MakeRequest<{{param.kind.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}})))
+{%-     elif param.kind|is_any_handle_kind -%}
+mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}}))
+{%-     elif param.kind|is_enum_kind -%}
+static_cast<{{param.kind|cpp_wrapper_type}}>(params->{{param.name}})
+{%-     else -%}
+params->{{param.name}}
+{%-     endif -%}
+{%-     if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro compute_payload_size(params_name, parameters) -%}
+  size_t payload_size =
+      mojo::internal::Align(sizeof({{params_name}}));
+{#--- Computes #}
+{%-   for param in parameters %}
+{%-     if param.kind|is_object_kind %}
+  payload_size += GetSerializedSize_(in_{{param.name}});
+{%-     endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro build_message(params_name, parameters, params_description) -%}
+  {# TODO(yzshen): Consider refactoring to share code with
+     struct_serialization_definition.tmpl #}
+  {{params_name}}* params =
+      {{params_name}}::New(builder.buffer());
+{#--- Sets #}
+{%   for param in parameters %}
+{%-     if param.kind|is_object_kind %}
+{%-       if param.kind|is_any_array_kind %}
+  mojo::SerializeArray_<{{param.kind|get_array_validate_params|indent(24)}}>(
+      mojo::internal::Forward(in_{{param.name}}), builder.buffer(), &params->{{param.name}}.ptr);
+{%-       else %}
+  Serialize_(mojo::internal::Forward(in_{{param.name}}), builder.buffer(), &params->{{param.name}}.ptr);
+{%-       endif %}
+{%-       if not param.kind|is_nullable_kind %}
+  MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+      !params->{{param.name}}.ptr,
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+      "null {{param.name}} argument in {{params_description}}");
+{%-       endif %}
+{%-     elif param.kind|is_any_handle_kind %}
+{%-       if param.kind|is_interface_kind or
+             param.kind|is_interface_request_kind %}
+  // Delegate handle.
+  params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release();
+{%-       else %}
+  params->{{param.name}} = in_{{param.name}}.release();
+{%-       endif %}
+{%-       if not param.kind|is_nullable_kind %}
+  MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+      !params->{{param.name}}.is_valid(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+      "invalid {{param.name}} argument in {{params_description}}");
+{%-       endif %}
+{%-     else %}
+  params->{{param.name}} = in_{{param.name}};
+{%-     endif %}
+{%-   endfor %}
+  mojo::Message message;
+  params->EncodePointersAndHandles(message.mutable_handles());
+  builder.Finish(&message);
+{%- endmacro %}
+
+{#--- Begin #}
+const char* {{class_name}}::Name_ = "{{namespace_as_string}}::{{class_name}}";
+{#--- Constants #}
+{%  for constant in interface.constants %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant|constant_value}};
+{%- endfor %}
+
+{#--- ForwardToCallback definition #}
+{%- for method in interface.methods -%}
+{%-   if method.response_parameters != None %}
+class {{class_name}}_{{method.name}}_ForwardToCallback
+    : public mojo::MessageReceiver {
+ public:
+  {{class_name}}_{{method.name}}_ForwardToCallback(
+      const {{interface_macros.declare_callback(method)}}& callback)
+      : callback_(callback) {
+  }
+  virtual bool Accept(mojo::Message* message) override;
+ private:
+  {{interface_macros.declare_callback(method)}} callback_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback);
+};
+bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept(
+    mojo::Message* message) {
+  internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
+      reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
+          message->mutable_payload());
+
+  params->DecodePointersAndHandles(message->mutable_handles());
+  {{alloc_params(method.response_parameters)|indent(2)}}
+  callback_.Run({{pass_params(method.response_parameters)}});
+  return true;
+}
+{%-   endif %}
+{%- endfor %}
+
+{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
+    : receiver_(receiver) {
+}
+
+{#--- Proxy definitions #}
+
+{%- for method in interface.methods %}
+{%-   set message_name =
+          "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%-   set params_name =
+          "internal::%s_%s_Params_Data"|format(interface.name, method.name) %}
+{%-   set params_description =
+          "%s.%s request"|format(interface.name, method.name) %}
+void {{proxy_name}}::{{method.name}}(
+    {{interface_macros.declare_request_params("in_", method)}}) {
+  {{compute_payload_size(params_name, method.parameters)}}
+
+{%- if method.response_parameters != None %}
+  mojo::internal::RequestMessageBuilder builder({{message_name}}, payload_size);
+{%- else %}
+  mojo::internal::MessageBuilder builder({{message_name}}, payload_size);
+{%- endif %}
+
+  {{build_message(params_name, method.parameters, params_description)}}
+
+{%- if method.response_parameters != None %}
+  mojo::MessageReceiver* responder =
+      new {{class_name}}_{{method.name}}_ForwardToCallback(callback);
+  if (!receiver_->AcceptWithResponder(&message, responder))
+    delete responder;
+{%- else %}
+  bool ok MOJO_ALLOW_UNUSED = receiver_->Accept(&message);
+  // This return value may be ignored as !ok implies the Connector has
+  // encountered an error, which will be visible through other means.
+{%- endif %}
+}
+{%- endfor %}
+
+{#--- ProxyToResponder definition #}
+{%- for method in interface.methods -%}
+{%-   if method.response_parameters != None %}
+{%-     set message_name =
+            "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%-     set params_name =
+            "internal::%s_%s_ResponseParams_Data"|format(interface.name, method.name) %}
+{%-     set params_description =
+            "%s.%s response"|format(interface.name, method.name) %}
+class {{class_name}}_{{method.name}}_ProxyToResponder
+    : public {{interface_macros.declare_callback(method)}}::Runnable {
+ public:
+  virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+    delete responder_;
+  }
+
+  {{class_name}}_{{method.name}}_ProxyToResponder(
+      uint64_t request_id,
+      mojo::MessageReceiver* responder)
+      : request_id_(request_id),
+        responder_(responder) {
+  }
+
+  virtual void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override;
+
+ private:
+  uint64_t request_id_;
+  mutable mojo::MessageReceiver* responder_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
+};
+void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
+    {{interface_macros.declare_params("in_", method.response_parameters)}}) const {
+  {{compute_payload_size(params_name, method.response_parameters)}}
+  mojo::internal::ResponseMessageBuilder builder(
+      {{message_name}}, payload_size, request_id_);
+  {{build_message(params_name, method.response_parameters, params_description)}}
+  bool ok MOJO_ALLOW_UNUSED = responder_->Accept(&message);
+  // TODO(darin): !ok returned here indicates a malformed message, and that may
+  // be good reason to close the connection. However, we don't have a way to do
+  // that from here. We should add a way.
+  delete responder_;
+  responder_ = nullptr;
+}
+{%-   endif -%}
+{%- endfor %}
+
+{{class_name}}Stub::{{class_name}}Stub()
+    : sink_(nullptr) {
+}
+
+{#--- Stub definition #}
+
+bool {{class_name}}Stub::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+  switch (message->header()->name) {
+{%-   for method in interface.methods %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+{%-     if method.response_parameters == None %}
+      internal::{{class_name}}_{{method.name}}_Params_Data* params =
+          reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+              message->mutable_payload());
+
+      params->DecodePointersAndHandles(message->mutable_handles());
+      {{alloc_params(method.parameters)|indent(6)}}
+      sink_->{{method.name}}({{pass_params(method.parameters)}});
+      return true;
+{%-     else %}
+      break;
+{%-     endif %}
+    }
+{%-   endfor %}
+  }
+{%- endif %}
+  return false;
+}
+
+bool {{class_name}}Stub::AcceptWithResponder(
+    mojo::Message* message, mojo::MessageReceiver* responder) {
+{%- if interface.methods %}
+  switch (message->header()->name) {
+{%-   for method in interface.methods %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+{%-     if method.response_parameters != None %}
+      internal::{{class_name}}_{{method.name}}_Params_Data* params =
+          reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+              message->mutable_payload());
+
+      params->DecodePointersAndHandles(message->mutable_handles());
+      {{interface_macros.declare_callback(method)}}::Runnable* runnable =
+          new {{class_name}}_{{method.name}}_ProxyToResponder(
+              message->request_id(), responder);
+      {{interface_macros.declare_callback(method)}} callback(runnable);
+      {{alloc_params(method.parameters)|indent(6)}}
+      sink_->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
+      return true;
+{%-     else %}
+      break;
+{%-     endif %}
+    }
+{%-   endfor %}
+  }
+{%- endif %}
+  return false;
+}
+
+{#--- Request validator definitions #}
+
+{{class_name}}RequestValidator::{{class_name}}RequestValidator(
+    mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+  switch (message->header()->name) {
+{%-   for method in interface.methods %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+{%-     if method.response_parameters != None %}
+      if (!message->has_flag(mojo::internal::kMessageExpectsResponse))
+        break;
+{%-     else %}
+      if (message->has_flag(mojo::internal::kMessageExpectsResponse) ||
+          message->has_flag(mojo::internal::kMessageIsResponse)) {
+        break;
+      }
+{%-     endif %}
+      mojo::internal::BoundsChecker bounds_checker(
+          message->payload(), message->payload_num_bytes(),
+          message->handles()->size());
+      if (!internal::{{class_name}}_{{method.name}}_Params_Data::Validate(
+              message->payload(), &bounds_checker)) {
+        return false;
+      }
+      break;
+    }
+{%-   endfor %}
+  }
+{%- endif %}
+
+  return sink_->Accept(message);
+}
+
+{#--- Response validator definitions #}
+{% if interface|has_callbacks %}
+{{class_name}}ResponseValidator::{{class_name}}ResponseValidator(
+    mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+  switch (message->header()->name) {
+{%-   for method in interface.methods if method.response_parameters != None %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+      if (!message->has_flag(mojo::internal::kMessageIsResponse))
+        break;
+      mojo::internal::BoundsChecker bounds_checker(
+          message->payload(), message->payload_num_bytes(),
+          message->handles()->size());
+      if (!internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate(
+              message->payload(), &bounds_checker)) {
+        return false;
+      }
+      break;
+    }
+{%-   endfor %}
+  }
+{%- endif %}
+
+  return sink_->Accept(message);
+}
+{%- endif -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
new file mode 100644
index 0000000..fbefce2
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -0,0 +1,23 @@
+{%- macro declare_params(prefix, parameters) %}
+{%-   for param in parameters -%}
+{{param.kind|cpp_const_wrapper_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro declare_callback(method) -%}
+mojo::Callback<void(
+{%-   for param in method.response_parameters -%}
+{{param.kind|cpp_result_type}}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor -%}
+)>
+{%- endmacro -%}
+
+{%- macro declare_request_params(prefix, method) -%}
+{{declare_params(prefix, method.parameters)}}
+{%-   if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif %}
+const {{declare_callback(method)}}& callback
+{%-   endif -%}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
new file mode 100644
index 0000000..6b8e7c5
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -0,0 +1,14 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy : public {{interface.name}} {
+ public:
+  explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
+
+{%- for method in interface.methods %}
+  virtual void {{method.name}}(
+      {{interface_macros.declare_request_params("", method)}}
+  ) override;
+{%- endfor %}
+
+ private:
+  mojo::MessageReceiverWithResponder* receiver_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
new file mode 100644
index 0000000..2239b69
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}RequestValidator : public mojo::MessageFilter {
+ public:
+  explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = nullptr);
+
+  virtual bool Accept(mojo::Message* message) override;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
new file mode 100644
index 0000000..801603d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}ResponseValidator : public mojo::MessageFilter {
+ public:
+  explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = nullptr);
+
+  virtual bool Accept(mojo::Message* message) override;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
new file mode 100644
index 0000000..afc6504
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -0,0 +1,13 @@
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponder {
+ public:
+  {{interface.name}}Stub();
+  void set_sink({{interface.name}}* sink) { sink_ = sink; }
+  {{interface.name}}* sink() { return sink_; }
+
+  virtual bool Accept(mojo::Message* message) override;
+  virtual bool AcceptWithResponder(mojo::Message* message,
+                                   mojo::MessageReceiver* responder) override;
+
+ private:
+  {{interface.name}}* sink_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
new file mode 100644
index 0000000..f0cf33b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -0,0 +1,49 @@
+// 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.
+
+{%- set header_guard = "%s_INTERNAL_H_"|
+        format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+
+{%- for import in imports %}
+#include "{{import.module.path}}-internal.h"
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class BoundsChecker;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Wrapper forward declarations #}
+{%  for struct in structs %}
+class {{struct.name}};
+{%- endfor %}
+
+namespace internal {
+
+#pragma pack(push, 1)
+
+{#--- Class declarations #}
+{%  for struct in structs %}
+{%    include "struct_declaration.tmpl" %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+}  // namespace internal
+{%- for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%- endfor %}
+
+#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
new file mode 100644
index 0000000..8f5e812
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -0,0 +1,94 @@
+// 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.
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+#elif defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4056)
+#pragma warning(disable:4756)
+#endif
+
+#include "{{module.path}}.h"
+
+#include <math.h>
+
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{%  for constant in module.constants %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%- endfor %}
+
+namespace internal {
+namespace {
+
+#pragma pack(push, 1)
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%-   for method in interface.methods %}
+{%-     set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+const uint32_t {{method_name}} = {{method.ordinal}};
+{%      set struct = method|struct_from_method %}
+{%-     include "params_definition.tmpl" %}
+{%-     if method.response_parameters != None %}
+{%-       set struct = method|response_struct_from_method %}
+{%-       include "params_definition.tmpl" %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+}  // namespace
+
+{#--- Struct definitions #}
+{%  for struct in structs %}
+{%-   include "struct_definition.tmpl" %}
+{%- endfor %}
+
+}  // namespace internal
+
+{#--- Struct Constants #}
+{%- for struct in structs %}
+{%     for constant in struct.constants %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant|constant_value}};
+{%-   endfor %}
+{%- endfor %}
+
+{#--- Struct builder definitions #}
+{%- for struct in structs %}
+{%-   include "wrapper_class_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%-   include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers #}
+{%- for struct in structs %}
+{%-   include "struct_serialization_definition.tmpl" %}
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%- endfor %}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning(pop)
+#endif
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
new file mode 100644
index 0000000..4e21d47
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -0,0 +1,108 @@
+// 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.
+
+{%- set header_guard = "%s_H_"|
+        format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/no_interface.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "{{module.path}}-internal.h"
+{%- for import in imports %}
+#include "{{import.module.path}}.h"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{%  for constant in module.constants %}
+extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{%  for enum in enums %}
+{%    include "enum_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Forward Declarations -#}
+{%  for interface in interfaces %}
+class {{interface.name}};
+typedef mojo::InterfacePtr<{{interface.name}}> {{interface.name}}Ptr;
+{%  endfor %}
+
+{#--- Struct Forward Declarations -#}
+{%  for struct in structs %}
+class {{struct.name}};
+{%    if struct|should_inline %}
+typedef mojo::InlinedStructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{%    else %}
+typedef mojo::StructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{%    endif %}
+{%  endfor %}
+
+{#--- NOTE: Non-inlined structs may have pointers to inlined structs, so we  #}
+{#---       need to fully define inlined structs ahead of the others.        #}
+
+{#--- Inlined structs #}
+{%  for struct in structs %}
+{%    if struct|should_inline %}
+{%      include "wrapper_class_declaration.tmpl" %}
+{%    endif %}
+{%- endfor %}
+
+{#--- Non-inlined structs #}
+{%  for struct in structs %}
+{%    if not struct|should_inline %}
+{%      include "wrapper_class_declaration.tmpl" %}
+{%    endif %}
+{%- endfor %}
+
+{#--- Interfaces -#}
+{%  for interface in interfaces %}
+{%    include "interface_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Proxies -#}
+{%  for interface in interfaces %}
+{%    include "interface_proxy_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Stubs -#}
+{%  for interface in interfaces %}
+{%    include "interface_stub_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Request Validators -#}
+{%  for interface in interfaces %}
+{%    include "interface_request_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Response Validators -#}
+{%  for interface in interfaces if interface|has_callbacks %}
+{%    include "interface_response_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers -#}
+{%  if structs %}
+{%    for struct in structs %}
+{%      include "struct_serialization_declaration.tmpl" %}
+{%-   endfor %}
+{%- endif %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%- endfor %}
+
+#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
new file mode 100644
index 0000000..0b11047
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
@@ -0,0 +1,33 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+class {{class_name}} {
+ public:
+  static {{class_name}}* New(mojo::internal::Buffer* buf) {
+    return new (buf->Allocate(sizeof({{class_name}})))
+        {{class_name}}();
+  }
+
+  static bool Validate(const void* data,
+                       mojo::internal::BoundsChecker* bounds_checker) {
+    {{ struct_macros.validate(struct, class_name)|indent(4) }}
+  }
+
+  mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+  void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+    {{ struct_macros.encodes(struct)|indent(4) }}
+  }
+
+  void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+    {{ struct_macros.decodes(struct)|indent(4) }}
+  }
+
+ private:
+  {{class_name}}() {
+    header_.num_bytes = sizeof(*this);
+    header_.num_fields = {{struct.packed.packed_fields|length}};
+  }
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+                    bad_sizeof_{{class_name}});
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
new file mode 100644
index 0000000..60a6a9e
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -0,0 +1,22 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" -%}
+
+class {{class_name}} {
+ public:
+  static {{class_name}}* New(mojo::internal::Buffer* buf);
+
+  static bool Validate(const void* data,
+                       mojo::internal::BoundsChecker* bounds_checker);
+
+  mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+  void EncodePointersAndHandles(std::vector<mojo::Handle>* handles);
+  void DecodePointersAndHandles(std::vector<mojo::Handle>* handles);
+
+ private:
+  {{class_name}}();
+  ~{{class_name}}();  // NOT IMPLEMENTED
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+                    bad_sizeof_{{class_name}});
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
new file mode 100644
index 0000000..461f158
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -0,0 +1,28 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+
+// static
+{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
+  return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
+}
+
+// static
+bool {{class_name}}::Validate(const void* data,
+                              mojo::internal::BoundsChecker* bounds_checker) {
+  {{ struct_macros.validate(struct, class_name)|indent(2) }}
+}
+
+{{class_name}}::{{class_name}}() {
+  header_.num_bytes = sizeof(*this);
+  header_.num_fields = {{struct.packed.packed_fields|length}};
+}
+
+void {{class_name}}::EncodePointersAndHandles(
+    std::vector<mojo::Handle>* handles) {
+  {{ struct_macros.encodes(struct)|indent(2) }}
+}
+
+void {{class_name}}::DecodePointersAndHandles(
+    std::vector<mojo::Handle>* handles) {
+  {{ struct_macros.decodes(struct)|indent(2) }}
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
new file mode 100644
index 0000000..0e51193
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -0,0 +1,115 @@
+{%- macro validate(struct, class_name) %}
+  if (!data)
+    return true;
+
+  if (!ValidateStructHeader(
+          data, sizeof({{class_name}}),
+          {{struct.packed.packed_fields|length}}, bounds_checker)) {
+    return false;
+  }
+
+  const {{class_name}}* MOJO_ALLOW_UNUSED object =
+      static_cast<const {{class_name}}*>(data);
+
+{%-   for packed_field in struct.packed.packed_fields %}
+{%-     set name = packed_field.field.name %}
+{%-     set kind = packed_field.field.kind %}
+{%-     if kind|is_object_kind %}
+{%-       set wrapper_type = kind|cpp_wrapper_type %}
+{%-       if not kind|is_nullable_kind %}
+  if (!object->{{name}}.offset) {
+    ReportValidationError(
+        mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+        "null {{name}} field in {{struct.name}} struct");
+    return false;
+  }
+{%-       endif %}
+  if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) {
+    ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER);
+    return false;
+  }
+{%-       if kind|is_any_array_kind or kind|is_string_kind %}
+  if (!{{wrapper_type}}::Data_::Validate<
+          {{kind|get_array_validate_params|indent(10)}}>(
+              mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
+              bounds_checker)) {
+{%-       else %}
+  if (!{{wrapper_type}}::Data_::Validate(
+          mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
+          bounds_checker)) {
+{%-       endif %}
+    return false;
+  }
+{%-     elif kind|is_any_handle_kind %}
+{%-       if not kind|is_nullable_kind %}
+  if (object->{{name}}.value() == mojo::internal::kEncodedInvalidHandleValue) {
+    ReportValidationError(
+        mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+        "invalid {{name}} field in {{struct.name}} struct");
+    return false;
+  }
+{%-       endif %}
+  if (!bounds_checker->ClaimHandle(object->{{name}})) {
+    ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE);
+    return false;
+  }
+{%-     endif %}
+{%-   endfor %}
+
+  return true;
+{%- endmacro %}
+
+{%- macro field_line(field) %}
+{%-   set type = field.kind|cpp_field_type %}
+{%-   set name = field.name -%}
+{%-   if field.kind.spec == 'b' -%}
+  uint8_t {{name}} : 1;
+{%-   elif field.kind|is_enum_kind -%}
+  int32_t {{name}};
+{%-   else -%}
+  {{type}} {{name}};
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro fields(struct) %}
+{%-   for packed_field in struct.packed.packed_fields %}
+  {{field_line(packed_field.field)}}
+{%-     if not loop.last %}
+{%-       set next_pf = struct.packed.packed_fields[loop.index0 + 1] %}
+{%-       set pad = next_pf.offset - (packed_field.offset + packed_field.size) %}
+{%-       if pad > 0 %}
+  uint8_t pad{{loop.index0}}_[{{pad}}];
+{%-       endif %}
+{%-     endif %}
+{%-   endfor -%}
+
+{%-   set num_fields = struct.packed.packed_fields|length %}
+{%-   if num_fields > 0 %}
+{%-     set last_field = struct.packed.packed_fields[num_fields - 1] %}
+{%-     set offset = last_field.offset + last_field.size %}
+{%-     set pad = offset|get_pad(8) -%}
+{%-     if pad > 0 %}
+  uint8_t padfinal_[{{pad}}];
+{%-     endif %}
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro encodes(struct) -%}
+{%-   for pf in struct.packed.packed_fields %}
+{%-     if pf.field.kind|is_object_kind %}
+mojo::internal::Encode(&{{pf.field.name}}, handles);
+{%-     elif pf.field.kind|is_any_handle_kind %}
+mojo::internal::EncodeHandle(&{{pf.field.name}}, handles);
+{%-     endif %}
+{%-   endfor %}
+{%- endmacro -%}
+
+{%- macro decodes(struct) -%}
+{%-   for pf in struct.packed.packed_fields %}
+{%-     if pf.field.kind|is_object_kind %}
+mojo::internal::Decode(&{{pf.field.name}}, handles);
+{%-     elif pf.field.kind|is_any_handle_kind %}
+mojo::internal::DecodeHandle(&{{pf.field.name}}, handles);
+{%-     endif %}
+{%-   endfor %}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
new file mode 100644
index 0000000..604be86
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -0,0 +1,5 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input);
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buffer,
+                internal::{{struct.name}}_Data** output);
+void Deserialize_(internal::{{struct.name}}_Data* input,
+                  {{struct.name}}Ptr* output);
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
new file mode 100644
index 0000000..09bf392
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -0,0 +1,75 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input) {
+  if (!input)
+    return 0;
+  size_t size = sizeof(internal::{{struct.name}}_Data);
+{%- for pf in struct.packed.packed_fields if pf.field.kind|is_object_kind %}
+  size += GetSerializedSize_(input->{{pf.field.name}});
+{%- endfor %}
+  return size;
+}
+
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf,
+                internal::{{struct.name}}_Data** output) {
+  if (input) {
+    internal::{{struct.name}}_Data* result =
+        internal::{{struct.name}}_Data::New(buf);
+{%- for pf in struct.packed.packed_fields %}
+{%-   if pf.field.kind|is_object_kind %}
+{%-     if pf.field.kind|is_any_array_kind %}
+    mojo::SerializeArray_<{{pf.field.kind|get_array_validate_params|indent(26)}}>(
+        mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr);
+{%-     else %}
+    Serialize_(mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr);
+{%-     endif %}
+{%-     if not pf.field.kind|is_nullable_kind %}
+    MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+        !result->{{pf.field.name}}.ptr,
+        mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+        "null {{pf.field.name}} field in {{struct.name}} struct");
+{%-     endif %}
+{%-   elif pf.field.kind|is_any_handle_kind %}
+{%-     if pf.field.kind|is_interface_kind %}
+    result->{{pf.field.name}} = input->{{pf.field.name}}.PassMessagePipe().release();
+{%-     else %}
+    result->{{pf.field.name}} = input->{{pf.field.name}}.release();
+{%-     endif %}
+{%-     if not pf.field.kind|is_nullable_kind %}
+    MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+        !result->{{pf.field.name}}.is_valid(),
+        mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+        "invalid {{pf.field.name}} field in {{struct.name}} struct");
+{%-     endif %}
+{%-   else %}
+    result->{{pf.field.name}} = input->{{pf.field.name}};
+{%-   endif %}
+{%- endfor %}
+    *output = result;
+  } else {
+    *output = nullptr;
+  }
+}
+
+void Deserialize_(internal::{{struct.name}}_Data* input,
+                  {{struct.name}}Ptr* output) {
+  if (input) {
+    {{struct.name}}Ptr result({{struct.name}}::New());
+{%- for pf in struct.packed.packed_fields %}
+{%-   if pf.field.kind|is_object_kind %}
+    Deserialize_(input->{{pf.field.name}}.ptr, &result->{{pf.field.name}});
+{%-   elif pf.field.kind|is_interface_kind %}
+    if (input->{{pf.field.name}}.is_valid())
+      result->{{pf.field.name}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&input->{{pf.field.name}})));
+{%-   elif pf.field.kind|is_any_handle_kind %}
+    result->{{pf.field.name}}.reset(mojo::internal::FetchAndReset(&input->{{pf.field.name}}));
+{%-   elif pf.field.kind|is_enum_kind %}
+    result->{{pf.field.name}} = static_cast<{{pf.field.kind|cpp_wrapper_type}}>(
+      input->{{pf.field.name}});
+{%-   else %}
+    result->{{pf.field.name}} = input->{{pf.field.name}};
+{%-   endif %}
+{%- endfor %}
+    *output = result.Pass();
+  } else {
+    output->reset();
+  }
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
new file mode 100644
index 0000000..21f2968
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -0,0 +1,34 @@
+
+class {{struct.name}} {
+ public:
+  typedef internal::{{struct.name}}_Data Data_;
+
+{#--- Constants #}
+{%- for constant in struct.constants %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{%    macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+  {{enum_def()|indent(2)}}
+{%- endfor %}
+  static {{struct.name}}Ptr New();
+
+  template <typename U>
+  static {{struct.name}}Ptr From(const U& u) {
+    return mojo::TypeConverter<{{struct.name}}Ptr, U>::Convert(u);
+  }
+
+  {{struct.name}}();
+  ~{{struct.name}}();
+{%  if struct|is_cloneable_kind %}
+  {{struct.name}}Ptr Clone() const;
+{%- endif %}
+
+{#--- Getters #}
+{%  for field in struct.fields %}
+{%-   set type = field.kind|cpp_wrapper_type %}
+{%-   set name = field.name %}
+  {{type}} {{name}};
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
new file mode 100644
index 0000000..42f7575
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -0,0 +1,28 @@
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+  {{struct.name}}Ptr rv;
+  mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+  return rv.Pass();
+}
+
+{{struct.name}}::{{struct.name}}()
+{%- for field in struct.fields %}
+    {% if loop.first %}:{% else %} {% endif %} {{field.name}}({{field|default_value}}){% if not loop.last %},{% endif %}
+{%- endfor %} {
+}
+
+{{struct.name}}::~{{struct.name}}() {
+}
+{%  if struct|is_cloneable_kind %}
+{{struct.name}}Ptr {{struct.name}}::Clone() const {
+  {{struct.name}}Ptr rv(New());
+{%-   for field in struct.fields %}
+{%-     if field.kind|is_struct_kind or field.kind|is_any_array_kind %}
+  rv->{{field.name}} = {{field.name}}.Clone();
+{%-     else %}
+  rv->{{field.name}} = {{field.name}};
+{%-     endif %}
+{%-   endfor %}
+  return rv.Pass();
+}
+{%  endif %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
new file mode 100644
index 0000000..db193e2
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
@@ -0,0 +1,3 @@
+{% macro constant_def(constant) %}
+public static final {{constant.kind|java_type}} {{constant|name}} = {{constant|constant_value}};
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
new file mode 100644
index 0000000..0a4e299
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
@@ -0,0 +1,12 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% include "header.java.tmpl" %}
+
+public final class {{main_entity}} {
+{%  for constant in constants %}
+
+    {{constant_def(constant)|indent(4)}}
+{%  endfor %}
+
+    private {{main_entity}}() {}
+
+}
diff --git a/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
new file mode 100644
index 0000000..7096a18
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
@@ -0,0 +1,4 @@
+{% from "enum_definition.tmpl" import enum_def %}
+{% include "header.java.tmpl" %}
+
+{{enum_def(enum, true)}}
diff --git a/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
new file mode 100644
index 0000000..a16c178
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
@@ -0,0 +1,21 @@
+{%- macro enum_value(enum, field, index) -%}
+{%- if field.value -%}
+(int) ({{field.value|expression_to_text('i32')}})
+{%- elif index == 0 -%}
+0
+{%- else -%}
+{{enum.fields[index - 1]|name}} + 1
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro enum_def(enum, top_level) -%}
+public {{ 'static ' if not top_level }}final class {{enum|name}} {
+
+{% for field in enum.fields %}
+    public static final int {{field|name}} = {{enum_value(enum, field, loop.index0)}};
+{% endfor %}
+
+    private {{enum|name}}() {}
+
+}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
new file mode 100644
index 0000000..ec6a88b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
@@ -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.
+
+// This file is autogenerated by:
+//     mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+//     {{module.path}}
+//
+
+package {{package}};
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl
new file mode 100644
index 0000000..10e5d7a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl
@@ -0,0 +1,4 @@
+{% from "interface_definition.tmpl" import interface_def %}
+{% include "header.java.tmpl" %}
+
+{{ interface_def(interface, client) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
new file mode 100644
index 0000000..941fec7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -0,0 +1,294 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+{% from "struct_definition.tmpl" import struct_def %}
+
+{%- macro declare_params(parameters, boxed=false) %}
+{%-   for param in parameters -%}
+{{param.kind|java_type(boxed)}} {{param|name}}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{% macro declare_request_params(method) %}
+{{declare_params(method.parameters)}}
+{%-   if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif %}
+{{method|interface_response_name}} callback
+{%-   endif -%}
+{% endmacro %}
+
+{%- macro declare_callback(method) -%}
+
+interface {{method|interface_response_name}} extends org.chromium.mojo.bindings.Callbacks.Callback{{method.response_parameters|length}}{% if method.response_parameters %}<
+{%-   for param in method.response_parameters -%}
+{{param.kind|java_type(True)}}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor -%}
+>{% endif %} { }
+{%- endmacro -%}
+
+{%- macro run_callback(variable, parameters) -%}
+{%-   if parameters -%}
+{%-     for param in parameters -%}
+{{variable}}.{{param|name}}
+{%-   if not loop.last %}, {% endif %}
+{%-     endfor -%}
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro super_class(client, with_generic=True) -%}
+{%-   if client -%}
+org.chromium.mojo.bindings.InterfaceWithClient{% if with_generic %}<{{client|java_type}}>{% endif %}
+{%-   else -%}
+org.chromium.mojo.bindings.Interface
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro flags_for_method(method, is_parameter) -%}
+{{flags(method.response_parameters, is_parameter)}}
+{%- endmacro -%}
+
+{%- macro flags(has_response_parameters, is_parameter) -%}
+{%-  if not has_response_parameters -%}
+org.chromium.mojo.bindings.MessageHeader.NO_FLAG
+{%-   elif is_parameter: -%}
+org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG
+{%-   else -%}
+org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro manager_class(interface, client, fully_qualified=False) -%}
+{% if fully_qualified %}{{super_class(client, False)}}.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy
+{%- if client -%}, {{client|java_type}}{%-  endif -%}
+>
+{%- endmacro -%}
+
+{%- macro manager_def(interface, client) -%}
+public static final {{manager_class(interface, client, True)}} MANAGER =
+        new {{manager_class(interface, client, True)}}() {
+
+    public String getName() {
+        return "{{namespace|replace(".","::")}}::{{interface.name}}";
+    }
+
+    public Proxy buildProxy(org.chromium.mojo.system.Core core,
+                            org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) {
+        return new Proxy(core, messageReceiver);
+    }
+
+    public Stub buildStub(org.chromium.mojo.system.Core core, {{interface|name}} impl) {
+        return new Stub(core, impl);
+    }
+
+    public {{interface|name}}[] buildArray(int size) {
+      return new {{interface|name}}[size];
+    }
+{%  if client %}
+
+    protected org.chromium.mojo.bindings.Interface.Manager<{{client|java_type}}, ?> getClientManager() {
+        return {{client|java_type}}.MANAGER;
+    }
+{%  endif %}
+};
+{%- endmacro -%}
+
+{%- macro accept_body(interface, with_response) -%}
+{% if (interface|has_method_with_response and with_response) or
+      (interface|has_method_without_response and not with_response) %}
+try {
+    org.chromium.mojo.bindings.ServiceMessage messageWithHeader =
+            message.asServiceMessage();
+    org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader();
+    if (!header.validateHeader({{flags(with_response, True)}})) {
+        return false;
+    }
+    switch(header.getType()) {
+{%   for method in interface.methods %}
+{%     if (with_response and method.response_parameters != None) or
+        (not with_response and method.response_parameters == None) %}
+{%       set request_struct = method|struct_from_method %}
+{%       if with_response %}
+{%         set response_struct = method|response_struct_from_method %}
+{%       endif %}
+        case {{method|method_ordinal_name}}: {
+{%       if method.parameters %}
+            {{request_struct|name}} data =
+                    {{request_struct|name}}.deserialize(messageWithHeader.getPayload());
+{%       else %}
+            {{request_struct|name}}.deserialize(messageWithHeader.getPayload());
+{%       endif %}
+            getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %});
+            return true;
+        }
+{%     endif %}
+{%   endfor %}
+        default:
+            return false;
+    }
+} catch (org.chromium.mojo.bindings.DeserializationException e) {
+    return false;
+}
+{% else %}
+return false;
+{% endif %}
+{%- endmacro -%}
+
+{% macro interface_def(interface, client) %}
+public interface {{interface|name}} extends {{super_class(client)}} {
+{%  for constant in interface.constants %}
+
+    {{constant_def(constant)|indent(4)}}
+{%  endfor %}
+{%  for enum in interface.enums %}
+
+    {{enum_def(enum, false)|indent(4)}}
+{% endfor %}
+
+    public interface Proxy extends {{interface|name}}, {{super_class(client, False)}}.Proxy{% if client %}<{{client|java_type}}>{% endif %} {
+    }
+
+    {{manager_class(interface, client)}} MANAGER = {{interface|name}}_Internal.MANAGER;
+{% for method in interface.methods %}
+
+    void {{method|name}}({{declare_request_params(method)}});
+{%   if method.response_parameters != None %}
+    {{declare_callback(method)|indent(4)}}
+{%   endif %}
+{% endfor %}
+}
+{% endmacro %}
+
+{% macro interface_internal_def(interface, client) %}
+class {{interface|name}}_Internal {
+
+    {{manager_def(interface, client)|indent(4)}}
+
+{% for method in interface.methods %}
+    private static final int {{method|method_ordinal_name}} = {{method.ordinal}};
+{% endfor %}
+
+    static final class Proxy extends {% if client %}org.chromium.mojo.bindings.InterfaceWithClient.AbstractProxy<{{client|java_type}}>{% else %}org.chromium.mojo.bindings.Interface.AbstractProxy{% endif %} implements {{interface|name}}.Proxy {
+
+        Proxy(org.chromium.mojo.system.Core core,
+              org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) {
+            super(core, messageReceiver);
+        }
+{% for method in interface.methods %}
+
+        @Override
+        public void {{method|name}}({{declare_request_params(method)}}) {
+{%   set request_struct = method|struct_from_method %}
+            {{request_struct|name}} _message = new {{request_struct|name}}();
+{%   for param in method.parameters %}
+            _message.{{param|name}} = {{param|name}};
+{%   endfor %}
+{%   if method.response_parameters != None %}
+            getMessageReceiver().acceptWithResponder(
+                    _message.serializeWithHeader(
+                            getCore(),
+                            new org.chromium.mojo.bindings.MessageHeader(
+                                    {{method|method_ordinal_name}},
+                                    {{flags_for_method(method, True)}},
+                                    0)),
+                    new {{method|response_struct_from_method|name}}ForwardToCallback(callback));
+{%   else %}
+            getMessageReceiver().accept(
+                    _message.serializeWithHeader(
+                            getCore(),
+                            new org.chromium.mojo.bindings.MessageHeader({{method|method_ordinal_name}})));
+{%   endif %}
+        }
+{% endfor %}
+
+    }
+
+    static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<{{interface|name}}> {
+
+        Stub(org.chromium.mojo.system.Core core, {{interface|name}} impl) {
+            super(core, impl);
+        }
+
+        @Override
+        public boolean accept(org.chromium.mojo.bindings.Message message) {
+            {{accept_body(interface, False)|indent(12)}}
+        }
+
+        @Override
+        public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) {
+            {{accept_body(interface, True)|indent(12)}}
+        }
+    }
+{% for method in interface.methods %}
+
+    {{ struct_def(method|struct_from_method, True)|indent(4) }}
+{%   if method.response_parameters != None %}
+{%   set response_struct = method|response_struct_from_method %}
+
+    {{ struct_def(response_struct, True)|indent(4) }}
+
+    static class {{response_struct|name}}ForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable
+            implements org.chromium.mojo.bindings.MessageReceiver {
+        private final {{interface|name}}.{{method|interface_response_name}} mCallback;
+
+        {{response_struct|name}}ForwardToCallback({{interface|name}}.{{method|interface_response_name}} callback) {
+            this.mCallback = callback;
+        }
+
+        @Override
+        public boolean accept(org.chromium.mojo.bindings.Message message) {
+            try {
+                org.chromium.mojo.bindings.ServiceMessage messageWithHeader =
+                        message.asServiceMessage();
+                org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader();
+                if (!header.validateHeader({{method|method_ordinal_name}},
+                                           {{flags_for_method(method, False)}})) {
+                    return false;
+                }
+{%   if method.response_parameters|length %}
+                {{response_struct|name}} response = {{response_struct|name}}.deserialize(messageWithHeader.getPayload());
+{%   endif %}
+                mCallback.call({{run_callback('response', method.response_parameters)}});
+                return true;
+            } catch (org.chromium.mojo.bindings.DeserializationException e) {
+                return false;
+            }
+        }
+    }
+
+    static class {{response_struct|name}}ProxyToResponder implements {{interface|name}}.{{method|interface_response_name}} {
+
+        private final org.chromium.mojo.system.Core mCore;
+        private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver;
+        private final long mRequestId;
+
+        {{response_struct|name}}ProxyToResponder(
+                org.chromium.mojo.system.Core core,
+                org.chromium.mojo.bindings.MessageReceiver messageReceiver,
+                long requestId) {
+            mCore = core;
+            mMessageReceiver = messageReceiver;
+            mRequestId = requestId;
+        }
+
+        @Override
+        public void call({{declare_params(method.response_parameters, true)}}) {
+            {{response_struct|name}} _response = new {{response_struct|name}}();
+{%   for param in method.response_parameters %}
+            _response.{{param|name}} = {{param|name}};
+{%   endfor %}
+            org.chromium.mojo.bindings.ServiceMessage _message =
+                    _response.serializeWithHeader(
+                            mCore,
+                            new org.chromium.mojo.bindings.MessageHeader(
+                                    {{method|method_ordinal_name}},
+                                    {{flags_for_method(method, False)}},
+                                    mRequestId));
+            mMessageReceiver.accept(_message);
+        }
+    }
+{%   endif %}
+{% endfor %}
+
+}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl
new file mode 100644
index 0000000..efb17f3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl
@@ -0,0 +1,4 @@
+{% from "interface_definition.tmpl" import interface_internal_def %}
+{% include "header.java.tmpl" %}
+
+{{ interface_internal_def(interface, client) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
new file mode 100644
index 0000000..232ec26
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
@@ -0,0 +1,4 @@
+{% from "struct_definition.tmpl" import struct_def %}
+{% include "header.java.tmpl" %}
+
+{{ struct_def(struct) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl
new file mode 100644
index 0000000..2fc4439
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl
@@ -0,0 +1,118 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+
+{%- macro array_expected_length(kind) -%}
+{%- if kind|is_fixed_array_kind -%}
+{{kind.length}}
+{%- else -%}
+org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+{%- endif -%}
+{%- endmacro -%}
+
+{% macro encode(variable, kind, offset, bit, level=0) %}
+{% if kind|is_pointer_array_kind %}
+{% set sub_kind = kind.kind %}
+if ({{variable}} == null) {
+    encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+} else {
+    org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.encodePointerArray({{variable}}.length, {{offset}}, {{array_expected_length(kind)}});
+    for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+        {{encode(variable~'[i'~level~']', sub_kind, 'DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE * i'~level, 0, level+1)|indent(8)}}
+    }
+}
+{% else %}
+encoder{{level}}.{{kind|encode_method(variable, offset, bit)}};
+{% endif %}
+{% endmacro %}
+
+{% macro decode(variable, kind, offset, bit, level=0) %}
+{% if kind|is_struct_kind or kind|is_pointer_array_kind %}
+org.chromium.mojo.bindings.Decoder decoder{{level+1}} = decoder{{level}}.readPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+{%   if kind|is_struct_kind %}
+{{variable}} = {{kind|java_type}}.decode(decoder{{level+1}});
+{%   else %}{# kind|is_pointer_array_kind #}
+if (decoder{{level+1}} == null) {
+    {{variable}} = null;
+} else {
+    DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{array_expected_length(kind)}});
+    {{variable}} = {{kind|new_array('si'~(level+1)~'.numFields')}};
+    for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.numFields; ++i{{level+1}}) {
+        {{decode(variable~'[i'~(level+1)~']', kind.kind, 'DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE * i'~(level+1), 0, level+1)|indent(8)}}
+    }
+}
+{%   endif %}
+{% else %}
+{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}};
+{% endif %}
+{% endmacro %}
+
+{% macro struct_def(struct, inner_class=False) %}
+{{'static' if inner_class else 'public'}} final class {{struct|name}} extends org.chromium.mojo.bindings.Struct {
+
+    private static final int STRUCT_SIZE = {{struct.packed|struct_size}};
+    private static final DataHeader DEFAULT_STRUCT_INFO = new DataHeader(STRUCT_SIZE, {{struct.packed.packed_fields|length}});
+{%  for constant in struct.constants %}
+
+    {{constant_def(constant)|indent(4)}}
+{%  endfor %}
+{%  for enum in struct.enums %}
+
+    {{enum_def(enum, false)|indent(4)}}
+{% endfor %}
+{% if struct.fields %}
+
+{%   for field in struct.fields %}
+    public {{field.kind|java_type}} {{field|name}};
+{%   endfor %}
+{% endif %}
+
+    public {{struct|name}}() {
+        super(STRUCT_SIZE);
+{% for field in struct.fields %}
+{%   if field.default %}
+        {{field|name}} = {{field|default_value}};
+{%   elif field.kind|is_handle %}
+        {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE;
+{%   endif %}
+{% endfor %}
+    }
+
+    public static {{struct|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+        return decode(new org.chromium.mojo.bindings.Decoder(message));
+    }
+
+    public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
+        if (decoder0 == null) {
+            return null;
+        }
+        {{struct|name}} result = new {{struct|name}}();
+{% if not struct.bytes %}
+        decoder0.readDataHeader();
+{% else %}
+        DataHeader mainDataHeader = decoder0.readDataHeader();
+{% endif %}
+{% for byte in struct.bytes %}
+{%   for packed_field in byte.packed_fields %}
+        if (mainDataHeader.numFields > {{packed_field.ordinal}}) {
+            {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}}
+        }
+{%   endfor %}
+{% endfor %}
+        return result;
+    }
+
+    @Override
+    protected final void encode(org.chromium.mojo.bindings.Encoder encoder) {
+{% if not struct.bytes %}
+        encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
+{% else %}
+        org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
+{% endif %}
+{% for byte in struct.bytes %}
+{%   for packed_field in byte.packed_fields %}
+        {{encode(packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(8)}}
+{%   endfor %}
+{% endfor %}
+    }
+}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
new file mode 100644
index 0000000..795116d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -0,0 +1,14 @@
+{%- macro enum_def(enum_name, enum, module) -%}
+  {{enum_name}} = {};
+
+{%- set prev_enum = 0 %}
+{%- for field in enum.fields %}
+{%-   if field.value %}
+  {{enum_name}}.{{field.name}} = {{field.value|expression_to_text}};
+{%-   elif loop.first %}
+  {{enum_name}}.{{field.name}} = 0;
+{%-   else %}
+  {{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1;
+{%-   endif %}
+{%- endfor %}
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
new file mode 100644
index 0000000..b41929c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -0,0 +1,178 @@
+{%- set namespace_as_string = namespace|replace(".","::") %}
+{%- for method in interface.methods %}
+  var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
+{%- endfor %}
+
+  function {{interface.name}}Proxy(receiver) {
+    this.receiver_ = receiver;
+  }
+
+  {{interface.name}}Proxy.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+{%- for method in interface.methods %}
+  {{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function(
+{%- for parameter in method.parameters -%}
+{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor -%}
+) {
+    var params = new {{interface.name}}_{{method.name}}_Params();
+{%- for parameter in method.parameters %}
+    params.{{parameter.name}} = {{parameter.name}};
+{%- endfor %}
+
+{%- if method.response_parameters == None %}
+    var builder = new codec.MessageBuilder(
+        k{{interface.name}}_{{method.name}}_Name,
+        codec.align({{interface.name}}_{{method.name}}_Params.encodedSize));
+    builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+{%- else %}
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageWithRequestIDBuilder(
+          k{{interface.name}}_{{method.name}}_Name,
+          codec.align({{interface.name}}_{{method.name}}_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptWithResponder(message, {
+          accept: function(message) {
+            var reader = new codec.MessageReader(message);
+            var responseParams =
+                reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+            resolve(responseParams);
+          },
+          reject: function(result) {
+            reject(Error("Connection error: " + result));
+          },
+      }).catch(reject);
+    }.bind(this));
+{%- endif %}
+  };
+{%- endfor %}
+
+  function {{interface.name}}Stub() {
+  }
+
+  {{interface.name}}Stub.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+  {{interface.name}}Stub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters == None %}
+    case k{{interface.name}}_{{method.name}}_Name:
+      var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+      this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor %});
+      return true;
+{%- endif %}
+{%- endfor %}
+    default:
+      return false;
+    }
+  };
+
+  {{interface.name}}Stub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters != None %}
+    case k{{interface.name}}_{{method.name}}_Name:
+      var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+      return this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif -%}
+{%- endfor %}).then(function(response) {
+        var responseParams =
+            new {{interface.name}}_{{method.name}}_ResponseParams();
+{%-     for parameter in method.response_parameters %}
+        responseParams.{{parameter.name}} = response.{{parameter.name}};
+{%-     endfor %}
+        var builder = new codec.MessageWithRequestIDBuilder(
+            k{{interface.name}}_{{method.name}}_Name,
+            codec.align({{interface.name}}_{{method.name}}_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct({{interface.name}}_{{method.name}}_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+{%- endif %}
+{%- endfor %}
+    default:
+      return Promise.reject(Error("Unhandled message: " + reader.messageName));
+    }
+  };
+
+{#--- Validation #}
+
+  function validate{{interface.name}}Request(messageValidator) {
+{%- if not(interface.methods) %}
+    return validator.validationError.NONE;
+{%- else %}
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+{%-   for method in interface.methods %}
+      case k{{interface.name}}_{{method.name}}_Name:
+{%-     if method.response_parameters == None %}
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = {{interface.name}}_{{method.name}}_Params;
+{%-     else %}
+        if (message.expectsResponse())
+          paramsClass = {{interface.name}}_{{method.name}}_Params;
+{%-     endif %}
+      break;
+{%-   endfor %}
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+{%- endif %}
+  }
+
+  function validate{{interface.name}}Response(messageValidator) {
+{%- if not(interface|has_callbacks) %}
+    return validator.validationError.NONE;
+{%- else %}
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+{%-   for method in interface.methods %}
+{%-     if method.response_parameters != None %}
+      case k{{interface.name}}_{{method.name}}_Name:
+        if (message.isResponse())
+          paramsClass = {{interface.name}}_{{method.name}}_ResponseParams;
+        break;
+{%-     endif %}
+{%-   endfor %}
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+{%- endif %}
+  }
+
+  {{interface.name}}Stub.prototype.validator = validate{{interface.name}}Request;
+{%- if interface|has_callbacks %}
+  {{interface.name}}Proxy.prototype.validator = validate{{interface.name}}Response;
+{%- else %}
+  {{interface.name}}Proxy.prototype.validator = null;
+{%- endif -%}
+
+{#--- Enums #}
+{%  from "enum_definition.tmpl" import enum_def -%}
+{%  for enum in interface.enums %}
+  {{enum_def("%sProxy.%s"|format(interface.name, enum.name), enum, module)}}
+  {{interface.name}}Stub.{{enum.name}} = {{interface.name}}Proxy.{{enum.name}};
+{%-  endfor %}
+
+{#--- Constants. #}
+{%  for constant in interface.constants %}
+  {{interface.name}}Proxy.{{constant.name}} = {{constant.value|expression_to_text}};
+  {{interface.name}}Stub.{{constant.name}} = {{interface.name}}Proxy.{{constant.name}};
+{%-  endfor %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
new file mode 100644
index 0000000..93fa537
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
@@ -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.
+
+define("{{module.path}}", [
+    "mojo/public/js/bindings/codec",
+    "mojo/public/js/bindings/validator",
+{%- for import in imports %}
+    "{{import.module.path}}",
+{%- endfor %}
+  ], function(codec, validator
+{%- for import in imports -%}
+    , {{import.unique_name}}
+{%- endfor -%}
+) {
+
+{#--- Constants #}
+{%  for constant in module.constants %}
+  var {{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{%- for enum in enums %}
+  var {{ enum_def(enum.name, enum, module) }}
+{%-  endfor %}
+
+{#--- Struct definitions #}
+{%  for struct in structs %}
+{%-   include "struct_definition.tmpl" %}
+{%- endfor -%}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces -%}
+{%-   include "interface_definition.tmpl" %}
+{%- endfor %}
+
+  var exports = {};
+{%-  for constant in module.constants %}
+  exports.{{constant.name}} = {{constant.name}};
+{%- endfor %}
+{%- for enum in enums %}
+  exports.{{enum.name}} = {{enum.name}};
+{%- endfor %}
+{%- for struct in structs if struct.exported %}
+  exports.{{struct.name}} = {{struct.name}};
+{%- endfor %}
+{%- for interface in interfaces %}
+  exports.{{interface.name}}Proxy = {{interface.name}}Proxy;
+  exports.{{interface.name}}Stub = {{interface.name}}Stub;
+{%- endfor %}
+  return exports;
+});
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
new file mode 100644
index 0000000..d77b28b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -0,0 +1,116 @@
+{#--- Begin #}
+  function {{struct.name}}(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{%  for enum in struct.enums %}
+  {{enum_def("%s.%s"|format(struct.name, enum.name), enum, module)}}
+{%-  endfor %}
+
+{#--- Constants #}
+{%  for constant in struct.constants %}
+  {{struct.name}}.{{constant.name}} = {{constant.value|expression_to_text}};
+{%-  endfor %}
+
+{#--- initDefaults() #}
+  {{struct.name}}.prototype.initDefaults_ = function() {
+{%- for packed_field in struct.packed.packed_fields %}
+    this.{{packed_field.field.name}} = {{packed_field.field|default_value}};
+{%- endfor %}
+  };
+
+{#--- initFields() #}
+  {{struct.name}}.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+{#--- Validation #}
+
+  {{struct.name}}.validate = function(messageValidator, offset) {
+    var err;
+{% macro check_err() -%}
+    if (err !== validator.validationError.NONE)
+        return err;
+{%- endmacro %}
+    err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.packed.packed_fields|length}});
+    {{check_err()}}
+
+{%- for packed_field in struct.packed.packed_fields %}
+{%-   set field_name = packed_field.field.name %}
+{%-   if packed_field.field|is_string_pointer_field %}
+    // validate {{struct.name}}.{{field_name}}
+    err = messageValidator.validateStringPointer({{packed_field|validate_string_params}})
+    {{check_err()}}
+{%-   elif packed_field.field|is_array_pointer_field %}
+    // validate {{struct.name}}.{{field_name}}
+    err = messageValidator.validateArrayPointer({{packed_field|validate_array_params}});
+    {{check_err()}}
+{%-   elif packed_field.field|is_struct_pointer_field %}
+    // validate {{struct.name}}.{{field_name}}
+    err = messageValidator.validateStructPointer({{packed_field|validate_struct_params}});
+    {{check_err()}}
+{%-   elif packed_field.field|is_handle_field %}
+    // validate {{struct.name}}.{{field_name}}
+    err = messageValidator.validateHandle({{packed_field|validate_handle_params}})
+    {{check_err()}}
+{%-   endif %}
+{%- endfor %}
+
+    return validator.validationError.NONE;
+  };
+
+{#--- Encoding and decoding #}
+
+  {{struct.name}}.encodedSize = codec.kStructHeaderSize + {{struct.packed|payload_size}};
+
+  {{struct.name}}.decode = function(decoder) {
+    var packed;
+    var val = new {{struct.name}}();
+    var numberOfBytes = decoder.readUint32();
+    var numberOfFields = decoder.readUint32();
+{%- for byte in struct.bytes %}
+{%-   if byte.packed_fields|length > 1 %}
+    packed = decoder.readUint8();
+{%-     for packed_field in byte.packed_fields %}
+    val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false;
+{%-     endfor %}
+{%-   else %}
+{%-     for packed_field in byte.packed_fields %}
+    val.{{packed_field.field.name}} = decoder.{{packed_field.field.kind|decode_snippet}};
+{%-     endfor %}
+{%-   endif %}
+{%-   if byte.is_padding %}
+    decoder.skip(1);
+{%-   endif %}
+{%- endfor %}
+    return val;
+  };
+
+  {{struct.name}}.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32({{struct.name}}.encodedSize);
+    encoder.writeUint32({{struct.packed.packed_fields|length}});
+
+{%- for byte in struct.bytes %}
+{%-   if byte.packed_fields|length > 1 %}
+    packed = 0;
+{%-     for packed_field in byte.packed_fields %}
+    packed |= (val.{{packed_field.field.name}} & 1) << {{packed_field.bit}}
+{%-     endfor %}
+    encoder.writeUint8(packed);
+{%-   else %}
+{%-     for packed_field in byte.packed_fields %}
+    encoder.{{packed_field.field.kind|encode_snippet}}val.{{packed_field.field.name}});
+{%-     endfor %}
+{%-   endif %}
+{%-   if byte.is_padding %}
+    encoder.skip(1);
+{%-   endif %}
+{%- endfor %}
+  };
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
new file mode 100644
index 0000000..68e0379
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -0,0 +1,338 @@
+# 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.
+
+"""Generates C++ source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+
+_kind_to_cpp_type = {
+  mojom.BOOL:                  "bool",
+  mojom.INT8:                  "int8_t",
+  mojom.UINT8:                 "uint8_t",
+  mojom.INT16:                 "int16_t",
+  mojom.UINT16:                "uint16_t",
+  mojom.INT32:                 "int32_t",
+  mojom.UINT32:                "uint32_t",
+  mojom.FLOAT:                 "float",
+  mojom.HANDLE:                "mojo::Handle",
+  mojom.DCPIPE:                "mojo::DataPipeConsumerHandle",
+  mojom.DPPIPE:                "mojo::DataPipeProducerHandle",
+  mojom.MSGPIPE:               "mojo::MessagePipeHandle",
+  mojom.SHAREDBUFFER:          "mojo::SharedBufferHandle",
+  mojom.NULLABLE_HANDLE:       "mojo::Handle",
+  mojom.NULLABLE_DCPIPE:       "mojo::DataPipeConsumerHandle",
+  mojom.NULLABLE_DPPIPE:       "mojo::DataPipeProducerHandle",
+  mojom.NULLABLE_MSGPIPE:      "mojo::MessagePipeHandle",
+  mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle",
+  mojom.INT64:                 "int64_t",
+  mojom.UINT64:                "uint64_t",
+  mojom.DOUBLE:                "double",
+}
+
+_kind_to_cpp_literal_suffix = {
+  mojom.UINT8:        "U",
+  mojom.UINT16:       "U",
+  mojom.UINT32:       "U",
+  mojom.FLOAT:        "f",
+  mojom.UINT64:       "ULL",
+}
+
+def ConstantValue(constant):
+  return ExpressionToText(constant.value, kind=constant.kind)
+
+def DefaultValue(field):
+  if field.default:
+    if mojom.IsStructKind(field.kind):
+      assert field.default == "default"
+      return "%s::New()" % GetNameForKind(field.kind)
+    return ExpressionToText(field.default, kind=field.kind)
+  return ""
+
+def NamespaceToArray(namespace):
+  return namespace.split('.') if namespace else []
+
+def GetNameForKind(kind, internal = False):
+  parts = []
+  if kind.imported_from:
+    parts.extend(NamespaceToArray(kind.imported_from["namespace"]))
+  if internal:
+    parts.append("internal")
+  if kind.parent_kind:
+    parts.append(kind.parent_kind.name)
+  parts.append(kind.name)
+  return "::".join(parts)
+
+def GetCppType(kind):
+  if mojom.IsStructKind(kind):
+    return "%s_Data*" % GetNameForKind(kind, internal=True)
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind)
+  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+    return "mojo::MessagePipeHandle"
+  if mojom.IsEnumKind(kind):
+    return "int32_t"
+  if mojom.IsStringKind(kind):
+    return "mojo::internal::String_Data*"
+  return _kind_to_cpp_type[kind]
+
+def GetCppPodType(kind):
+  if mojom.IsStringKind(kind):
+    return "char*"
+  return _kind_to_cpp_type[kind]
+
+def GetCppArrayArgWrapperType(kind):
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStructKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind)
+  if mojom.IsInterfaceKind(kind):
+    raise Exception("Arrays of interfaces not yet supported!")
+  if mojom.IsInterfaceRequestKind(kind):
+    raise Exception("Arrays of interface requests not yet supported!")
+  if mojom.IsStringKind(kind):
+    return "mojo::String"
+  if mojom.IsHandleKind(kind):
+    return "mojo::ScopedHandle"
+  if mojom.IsDataPipeConsumerKind(kind):
+    return "mojo::ScopedDataPipeConsumerHandle"
+  if mojom.IsDataPipeProducerKind(kind):
+    return "mojo::ScopedDataPipeProducerHandle"
+  if mojom.IsMessagePipeKind(kind):
+    return "mojo::ScopedMessagePipeHandle"
+  if mojom.IsSharedBufferKind(kind):
+    return "mojo::ScopedSharedBufferHandle"
+  return _kind_to_cpp_type[kind]
+
+def GetCppResultWrapperType(kind):
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStructKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+  if mojom.IsInterfaceKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+  if mojom.IsStringKind(kind):
+    return "mojo::String"
+  if mojom.IsHandleKind(kind):
+    return "mojo::ScopedHandle"
+  if mojom.IsDataPipeConsumerKind(kind):
+    return "mojo::ScopedDataPipeConsumerHandle"
+  if mojom.IsDataPipeProducerKind(kind):
+    return "mojo::ScopedDataPipeProducerHandle"
+  if mojom.IsMessagePipeKind(kind):
+    return "mojo::ScopedMessagePipeHandle"
+  if mojom.IsSharedBufferKind(kind):
+    return "mojo::ScopedSharedBufferHandle"
+  return _kind_to_cpp_type[kind]
+
+def GetCppWrapperType(kind):
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStructKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+  if mojom.IsInterfaceKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    raise Exception("InterfaceRequest fields not supported!")
+  if mojom.IsStringKind(kind):
+    return "mojo::String"
+  if mojom.IsHandleKind(kind):
+    return "mojo::ScopedHandle"
+  if mojom.IsDataPipeConsumerKind(kind):
+    return "mojo::ScopedDataPipeConsumerHandle"
+  if mojom.IsDataPipeProducerKind(kind):
+    return "mojo::ScopedDataPipeProducerHandle"
+  if mojom.IsMessagePipeKind(kind):
+    return "mojo::ScopedMessagePipeHandle"
+  if mojom.IsSharedBufferKind(kind):
+    return "mojo::ScopedSharedBufferHandle"
+  return _kind_to_cpp_type[kind]
+
+def GetCppConstWrapperType(kind):
+  if mojom.IsStructKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+  if mojom.IsInterfaceKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStringKind(kind):
+    return "const mojo::String&"
+  if mojom.IsHandleKind(kind):
+    return "mojo::ScopedHandle"
+  if mojom.IsDataPipeConsumerKind(kind):
+    return "mojo::ScopedDataPipeConsumerHandle"
+  if mojom.IsDataPipeProducerKind(kind):
+    return "mojo::ScopedDataPipeProducerHandle"
+  if mojom.IsMessagePipeKind(kind):
+    return "mojo::ScopedMessagePipeHandle"
+  if mojom.IsSharedBufferKind(kind):
+    return "mojo::ScopedSharedBufferHandle"
+  if not kind in _kind_to_cpp_type:
+    print "missing:", kind.spec
+  return _kind_to_cpp_type[kind]
+
+def GetCppFieldType(kind):
+  if mojom.IsStructKind(kind):
+    return ("mojo::internal::StructPointer<%s_Data>" %
+        GetNameForKind(kind, internal=True))
+  if mojom.IsAnyArrayKind(kind):
+    return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind)
+  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+    return "mojo::MessagePipeHandle"
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStringKind(kind):
+    return "mojo::internal::StringPointer"
+  return _kind_to_cpp_type[kind]
+
+def IsStructWithHandles(struct):
+  for pf in struct.packed.packed_fields:
+    if mojom.IsAnyHandleKind(pf.field.kind):
+      return True
+  return False
+
+def TranslateConstants(token, kind):
+  if isinstance(token, mojom.NamedValue):
+    # Both variable and enum constants are constructed like:
+    # Namespace::Struct::CONSTANT_NAME
+    # For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE.
+    name = []
+    if token.imported_from:
+      name.extend(NamespaceToArray(token.namespace))
+    if token.parent_kind:
+      name.append(token.parent_kind.name)
+    if isinstance(token, mojom.EnumValue):
+      name.append(
+          "%s_%s" % (generator.CamelCaseToAllCaps(token.enum.name), token.name))
+    else:
+      name.append(token.name)
+    return "::".join(name)
+
+  if isinstance(token, mojom.BuiltinValue):
+    if token.value == "double.INFINITY" or token.value == "float.INFINITY":
+      return "INFINITY";
+    if token.value == "double.NEGATIVE_INFINITY" or \
+       token.value == "float.NEGATIVE_INFINITY":
+      return "-INFINITY";
+    if token.value == "double.NAN" or token.value == "float.NAN":
+      return "NAN";
+
+  if (kind is not None and mojom.IsFloatKind(kind)):
+      return token if token.isdigit() else token + "f";
+
+  return '%s%s' % (token, _kind_to_cpp_literal_suffix.get(kind, ''))
+
+def ExpressionToText(value, kind=None):
+  return TranslateConstants(value, kind)
+
+def ShouldInlineStruct(struct):
+  # TODO(darin): Base this on the size of the wrapper class.
+  if len(struct.fields) > 4:
+    return False
+  for field in struct.fields:
+    if mojom.IsMoveOnlyKind(field.kind):
+      return False
+  return True
+
+def GetArrayValidateParams(kind):
+  if not mojom.IsAnyArrayKind(kind) and not mojom.IsStringKind(kind):
+    return "mojo::internal::NoValidateParams"
+
+  if mojom.IsStringKind(kind):
+    expected_num_elements = 0
+    element_is_nullable = False
+    element_validate_params = "mojo::internal::NoValidateParams"
+  else:
+    expected_num_elements = generator.ExpectedArraySize(kind)
+    element_is_nullable = mojom.IsNullableKind(kind.kind)
+    element_validate_params = GetArrayValidateParams(kind.kind)
+
+  return "mojo::internal::ArrayValidateParams<%d, %s,\n%s> " % (
+      expected_num_elements,
+      'true' if element_is_nullable else 'false',
+      element_validate_params)
+
+_HEADER_SIZE = 8
+
+class Generator(generator.Generator):
+
+  cpp_filters = {
+    "constant_value": ConstantValue,
+    "cpp_const_wrapper_type": GetCppConstWrapperType,
+    "cpp_field_type": GetCppFieldType,
+    "cpp_pod_type": GetCppPodType,
+    "cpp_result_type": GetCppResultWrapperType,
+    "cpp_type": GetCppType,
+    "cpp_wrapper_type": GetCppWrapperType,
+    "default_value": DefaultValue,
+    "expected_array_size": generator.ExpectedArraySize,
+    "expression_to_text": ExpressionToText,
+    "get_array_validate_params": GetArrayValidateParams,
+    "get_name_for_kind": GetNameForKind,
+    "get_pad": pack.GetPad,
+    "has_callbacks": mojom.HasCallbacks,
+    "should_inline": ShouldInlineStruct,
+    "is_any_array_kind": mojom.IsAnyArrayKind,
+    "is_cloneable_kind": mojom.IsCloneableKind,
+    "is_enum_kind": mojom.IsEnumKind,
+    "is_move_only_kind": mojom.IsMoveOnlyKind,
+    "is_any_handle_kind": mojom.IsAnyHandleKind,
+    "is_interface_kind": mojom.IsInterfaceKind,
+    "is_interface_request_kind": mojom.IsInterfaceRequestKind,
+    "is_nullable_kind": mojom.IsNullableKind,
+    "is_object_kind": mojom.IsObjectKind,
+    "is_string_kind": mojom.IsStringKind,
+    "is_struct_kind": mojom.IsStructKind,
+    "is_struct_with_handles": IsStructWithHandles,
+    "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+    "struct_from_method": generator.GetStructFromMethod,
+    "response_struct_from_method": generator.GetResponseStructFromMethod,
+    "stylize_method": generator.StudlyCapsToCamel,
+    "to_all_caps": generator.CamelCaseToAllCaps,
+  }
+
+  def GetJinjaExports(self):
+    return {
+      "module": self.module,
+      "namespace": self.module.namespace,
+      "namespaces_as_array": NamespaceToArray(self.module.namespace),
+      "imports": self.module.imports,
+      "kinds": self.module.kinds,
+      "enums": self.module.enums,
+      "structs": self.GetStructs(),
+      "interfaces": self.module.interfaces,
+    }
+
+  @UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
+  def GenerateModuleHeader(self):
+    return self.GetJinjaExports()
+
+  @UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters)
+  def GenerateModuleInternalHeader(self):
+    return self.GetJinjaExports()
+
+  @UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
+  def GenerateModuleSource(self):
+    return self.GetJinjaExports()
+
+  def GenerateFiles(self, args):
+    self.Write(self.GenerateModuleHeader(), "%s.h" % self.module.name)
+    self.Write(self.GenerateModuleInternalHeader(),
+        "%s-internal.h" % self.module.name)
+    self.Write(self.GenerateModuleSource(), "%s.cc" % self.module.name)
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
new file mode 100644
index 0000000..043d9e3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -0,0 +1,504 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates java source files from a mojom.Module."""
+
+import argparse
+import ast
+import contextlib
+import os
+import re
+import shutil
+import tempfile
+import zipfile
+
+from jinja2 import contextfilter
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+
+GENERATOR_PREFIX = 'java'
+
+_HEADER_SIZE = 8
+
+_spec_to_java_type = {
+  mojom.BOOL.spec: 'boolean',
+  mojom.DCPIPE.spec: 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
+  mojom.DOUBLE.spec: 'double',
+  mojom.DPPIPE.spec: 'org.chromium.mojo.system.DataPipe.ProducerHandle',
+  mojom.FLOAT.spec: 'float',
+  mojom.HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
+  mojom.INT16.spec: 'short',
+  mojom.INT32.spec: 'int',
+  mojom.INT64.spec: 'long',
+  mojom.INT8.spec: 'byte',
+  mojom.MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
+  mojom.NULLABLE_DCPIPE.spec:
+      'org.chromium.mojo.system.DataPipe.ConsumerHandle',
+  mojom.NULLABLE_DPPIPE.spec:
+      'org.chromium.mojo.system.DataPipe.ProducerHandle',
+  mojom.NULLABLE_HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
+  mojom.NULLABLE_MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
+  mojom.NULLABLE_SHAREDBUFFER.spec:
+      'org.chromium.mojo.system.SharedBufferHandle',
+  mojom.NULLABLE_STRING.spec: 'String',
+  mojom.SHAREDBUFFER.spec: 'org.chromium.mojo.system.SharedBufferHandle',
+  mojom.STRING.spec: 'String',
+  mojom.UINT16.spec: 'short',
+  mojom.UINT32.spec: 'int',
+  mojom.UINT64.spec: 'long',
+  mojom.UINT8.spec: 'byte',
+}
+
+_spec_to_decode_method = {
+  mojom.BOOL.spec:                  'readBoolean',
+  mojom.DCPIPE.spec:                'readConsumerHandle',
+  mojom.DOUBLE.spec:                'readDouble',
+  mojom.DPPIPE.spec:                'readProducerHandle',
+  mojom.FLOAT.spec:                 'readFloat',
+  mojom.HANDLE.spec:                'readUntypedHandle',
+  mojom.INT16.spec:                 'readShort',
+  mojom.INT32.spec:                 'readInt',
+  mojom.INT64.spec:                 'readLong',
+  mojom.INT8.spec:                  'readByte',
+  mojom.MSGPIPE.spec:               'readMessagePipeHandle',
+  mojom.NULLABLE_DCPIPE.spec:       'readConsumerHandle',
+  mojom.NULLABLE_DPPIPE.spec:       'readProducerHandle',
+  mojom.NULLABLE_HANDLE.spec:       'readUntypedHandle',
+  mojom.NULLABLE_MSGPIPE.spec:      'readMessagePipeHandle',
+  mojom.NULLABLE_SHAREDBUFFER.spec: 'readSharedBufferHandle',
+  mojom.NULLABLE_STRING.spec:       'readString',
+  mojom.SHAREDBUFFER.spec:          'readSharedBufferHandle',
+  mojom.STRING.spec:                'readString',
+  mojom.UINT16.spec:                'readShort',
+  mojom.UINT32.spec:                'readInt',
+  mojom.UINT64.spec:                'readLong',
+  mojom.UINT8.spec:                 'readByte',
+}
+
+_java_primitive_to_boxed_type = {
+  'boolean': 'Boolean',
+  'byte':    'Byte',
+  'double':  'Double',
+  'float':   'Float',
+  'int':     'Integer',
+  'long':    'Long',
+  'short':   'Short',
+}
+
+
+def NameToComponent(name):
+  # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
+  # HTTP_Entry2_FooBar)
+  name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
+  # insert '_' between non upper and start of upper blocks (e.g.,
+  # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
+  name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
+  return [x.lower() for x in name.split('_')]
+
+def UpperCamelCase(name):
+  return ''.join([x.capitalize() for x in NameToComponent(name)])
+
+def CamelCase(name):
+  uccc = UpperCamelCase(name)
+  return uccc[0].lower() + uccc[1:]
+
+def ConstantStyle(name):
+  components = NameToComponent(name)
+  if components[0] == 'k' and len(components) > 1:
+    components = components[1:]
+  # variable cannot starts with a digit.
+  if components[0][0].isdigit():
+    components[0] = '_' + components[0]
+  return '_'.join([x.upper() for x in components])
+
+def GetNameForElement(element):
+  if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
+      mojom.IsStructKind(element)):
+    return UpperCamelCase(element.name)
+  if mojom.IsInterfaceRequestKind(element):
+    return GetNameForElement(element.kind)
+  if isinstance(element, (mojom.Method,
+                          mojom.Parameter,
+                          mojom.Field)):
+    return CamelCase(element.name)
+  if isinstance(element,  mojom.EnumValue):
+    return (GetNameForElement(element.enum) + '.' +
+            ConstantStyle(element.name))
+  if isinstance(element, (mojom.NamedValue,
+                          mojom.Constant,
+                          mojom.EnumField)):
+    return ConstantStyle(element.name)
+  raise Exception('Unexpected element: %s' % element)
+
+def GetInterfaceResponseName(method):
+  return UpperCamelCase(method.name + 'Response')
+
+def ParseStringAttribute(attribute):
+  assert isinstance(attribute, basestring)
+  return attribute
+
+def GetJavaTrueFalse(value):
+  return 'true' if value else 'false'
+
+def GetArrayNullabilityFlags(kind):
+    """Returns nullability flags for an array type, see Decoder.java.
+
+    As we have dedicated decoding functions for arrays, we have to pass
+    nullability information about both the array itself, as well as the array
+    element type there.
+    """
+    assert mojom.IsAnyArrayKind(kind)
+    ARRAY_NULLABLE   = \
+        'org.chromium.mojo.bindings.BindingsHelper.ARRAY_NULLABLE'
+    ELEMENT_NULLABLE = \
+        'org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE'
+    NOTHING_NULLABLE = \
+        'org.chromium.mojo.bindings.BindingsHelper.NOTHING_NULLABLE'
+
+    flags_to_set = []
+    if mojom.IsNullableKind(kind):
+        flags_to_set.append(ARRAY_NULLABLE)
+    if mojom.IsNullableKind(kind.kind):
+        flags_to_set.append(ELEMENT_NULLABLE)
+
+    if not flags_to_set:
+        flags_to_set = [NOTHING_NULLABLE]
+    return ' | '.join(flags_to_set)
+
+
+def AppendEncodeDecodeParams(initial_params, context, kind, bit):
+  """ Appends standard parameters shared between encode and decode calls. """
+  params = list(initial_params)
+  if (kind == mojom.BOOL):
+    params.append(str(bit))
+  if mojom.IsReferenceKind(kind):
+    if mojom.IsAnyArrayKind(kind):
+      params.append(GetArrayNullabilityFlags(kind))
+    else:
+      params.append(GetJavaTrueFalse(mojom.IsNullableKind(kind)))
+  if mojom.IsAnyArrayKind(kind):
+    if mojom.IsFixedArrayKind(kind):
+      params.append(str(kind.length))
+    else:
+      params.append(
+        'org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH');
+  if mojom.IsInterfaceKind(kind):
+    params.append('%s.MANAGER' % GetJavaType(context, kind))
+  if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind):
+    params.append('%s.MANAGER' % GetJavaType(context, kind.kind))
+  return params
+
+
+@contextfilter
+def DecodeMethod(context, kind, offset, bit):
+  def _DecodeMethodName(kind):
+    if mojom.IsAnyArrayKind(kind):
+      return _DecodeMethodName(kind.kind) + 's'
+    if mojom.IsEnumKind(kind):
+      return _DecodeMethodName(mojom.INT32)
+    if mojom.IsInterfaceRequestKind(kind):
+      return 'readInterfaceRequest'
+    if mojom.IsInterfaceKind(kind):
+      return 'readServiceInterface'
+    return _spec_to_decode_method[kind.spec]
+  methodName = _DecodeMethodName(kind)
+  params = AppendEncodeDecodeParams([ str(offset) ], context, kind, bit)
+  return '%s(%s)' % (methodName, ', '.join(params))
+
+@contextfilter
+def EncodeMethod(context, kind, variable, offset, bit):
+  params = AppendEncodeDecodeParams(
+      [ variable, str(offset) ], context, kind, bit)
+  return 'encode(%s)' % ', '.join(params)
+
+def GetPackage(module):
+  if 'JavaPackage' in module.attributes:
+    return ParseStringAttribute(module.attributes['JavaPackage'])
+  # Default package.
+  if module.namespace:
+    return 'org.chromium.mojom.' + module.namespace
+  return 'org.chromium.mojom'
+
+def GetNameForKind(context, kind):
+  def _GetNameHierachy(kind):
+    hierachy = []
+    if kind.parent_kind:
+      hierachy = _GetNameHierachy(kind.parent_kind)
+    hierachy.append(GetNameForElement(kind))
+    return hierachy
+
+  module = context.resolve('module')
+  elements = []
+  if GetPackage(module) != GetPackage(kind.module):
+    elements += [GetPackage(kind.module)]
+  elements += _GetNameHierachy(kind)
+  return '.'.join(elements)
+
+def GetBoxedJavaType(context, kind):
+  unboxed_type = GetJavaType(context, kind, False)
+  if unboxed_type in _java_primitive_to_boxed_type:
+    return _java_primitive_to_boxed_type[unboxed_type]
+  return unboxed_type
+
+@contextfilter
+def GetJavaType(context, kind, boxed=False):
+  if boxed:
+    return GetBoxedJavaType(context, kind)
+  if mojom.IsStructKind(kind) or mojom.IsInterfaceKind(kind):
+    return GetNameForKind(context, kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return ('org.chromium.mojo.bindings.InterfaceRequest<%s>' %
+            GetNameForKind(context, kind.kind))
+  if mojom.IsAnyArrayKind(kind):
+    return '%s[]' % GetJavaType(context, kind.kind)
+  if mojom.IsEnumKind(kind):
+    return 'int'
+  return _spec_to_java_type[kind.spec]
+
+@contextfilter
+def DefaultValue(context, field):
+  assert field.default
+  if isinstance(field.kind, mojom.Struct):
+    assert field.default == 'default'
+    return 'new %s()' % GetJavaType(context, field.kind)
+  return '(%s) %s' % (
+      GetJavaType(context, field.kind),
+      ExpressionToText(context, field.default, kind_spec=field.kind.spec))
+
+@contextfilter
+def ConstantValue(context, constant):
+  return '(%s) %s' % (
+      GetJavaType(context, constant.kind),
+      ExpressionToText(context, constant.value, kind_spec=constant.kind.spec))
+
+@contextfilter
+def NewArray(context, kind, size):
+  if mojom.IsAnyArrayKind(kind.kind):
+    return NewArray(context, kind.kind, size) + '[]'
+  return 'new %s[%s]' % (GetJavaType(context, kind.kind), size)
+
+@contextfilter
+def ExpressionToText(context, token, kind_spec=''):
+  def _TranslateNamedValue(named_value):
+    entity_name = GetNameForElement(named_value)
+    if named_value.parent_kind:
+      return GetJavaType(context, named_value.parent_kind) + '.' + entity_name
+    # Handle the case where named_value is a module level constant:
+    if not isinstance(named_value, mojom.EnumValue):
+      entity_name = (GetConstantsMainEntityName(named_value.module) + '.' +
+                      entity_name)
+    if GetPackage(named_value.module) == GetPackage(context.resolve('module')):
+      return entity_name
+    return GetPackage(named_value.module) + '.' + entity_name
+
+  if isinstance(token, mojom.NamedValue):
+    return _TranslateNamedValue(token)
+  if kind_spec.startswith('i') or kind_spec.startswith('u'):
+    # Add Long suffix to all integer literals.
+    number = ast.literal_eval(token.lstrip('+ '))
+    if not isinstance(number, (int, long)):
+      raise ValueError('got unexpected type %r for int literal %r' % (
+          type(number), token))
+    # If the literal is too large to fit a signed long, convert it to the
+    # equivalent signed long.
+    if number >= 2 ** 63:
+      number -= 2 ** 64
+    return '%dL' % number
+  if isinstance(token, mojom.BuiltinValue):
+    if token.value == 'double.INFINITY':
+      return 'java.lang.Double.POSITIVE_INFINITY'
+    if token.value == 'double.NEGATIVE_INFINITY':
+      return 'java.lang.Double.NEGATIVE_INFINITY'
+    if token.value == 'double.NAN':
+      return 'java.lang.Double.NaN'
+    if token.value == 'float.INFINITY':
+      return 'java.lang.Float.POSITIVE_INFINITY'
+    if token.value == 'float.NEGATIVE_INFINITY':
+      return 'java.lang.Float.NEGATIVE_INFINITY'
+    if token.value == 'float.NAN':
+      return 'java.lang.Float.NaN'
+  return token
+
+def IsPointerArrayKind(kind):
+  if not mojom.IsAnyArrayKind(kind):
+    return False
+  sub_kind = kind.kind
+  return mojom.IsObjectKind(sub_kind)
+
+def GetResponseStructFromMethod(method):
+  return generator.GetDataHeader(
+      False, generator.GetResponseStructFromMethod(method))
+
+def GetStructFromMethod(method):
+  return generator.GetDataHeader(
+      False, generator.GetStructFromMethod(method))
+
+def GetConstantsMainEntityName(module):
+  if 'JavaConstantsClassName' in module.attributes:
+    return ParseStringAttribute(module.attributes['JavaConstantsClassName'])
+  # This constructs the name of the embedding classes for module level constants
+  # by extracting the mojom's filename and prepending it to Constants.
+  return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) +
+          'Constants')
+
+def GetMethodOrdinalName(method):
+  return ConstantStyle(method.name) + '_ORDINAL'
+
+def HasMethodWithResponse(interface):
+  for method in interface.methods:
+    if method.response_parameters is not None:
+      return True
+  return False
+
+def HasMethodWithoutResponse(interface):
+  for method in interface.methods:
+    if method.response_parameters is None:
+      return True
+  return False
+
+@contextlib.contextmanager
+def TempDir():
+  dirname = tempfile.mkdtemp()
+  try:
+    yield dirname
+  finally:
+    shutil.rmtree(dirname)
+
+def ZipContentInto(root, zip_filename):
+  with zipfile.ZipFile(zip_filename, 'w') as zip_file:
+    for dirname, _, files in os.walk(root):
+      for filename in files:
+        path = os.path.join(dirname, filename)
+        path_in_archive = os.path.relpath(path, root)
+        zip_file.write(path, path_in_archive)
+
+class Generator(generator.Generator):
+
+  java_filters = {
+    'interface_response_name': GetInterfaceResponseName,
+    'constant_value': ConstantValue,
+    'default_value': DefaultValue,
+    'decode_method': DecodeMethod,
+    'expression_to_text': ExpressionToText,
+    'encode_method': EncodeMethod,
+    'has_method_with_response': HasMethodWithResponse,
+    'has_method_without_response': HasMethodWithoutResponse,
+    'is_fixed_array_kind': mojom.IsFixedArrayKind,
+    'is_handle': mojom.IsNonInterfaceHandleKind,
+    'is_nullable_kind': mojom.IsNullableKind,
+    'is_pointer_array_kind': IsPointerArrayKind,
+    'is_struct_kind': mojom.IsStructKind,
+    'java_type': GetJavaType,
+    'java_true_false': GetJavaTrueFalse,
+    'method_ordinal_name': GetMethodOrdinalName,
+    'name': GetNameForElement,
+    'new_array': NewArray,
+    'response_struct_from_method': GetResponseStructFromMethod,
+    'struct_from_method': GetStructFromMethod,
+    'struct_size': lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+  }
+
+  def GetJinjaExports(self):
+    return {
+      'package': GetPackage(self.module),
+    }
+
+  def GetJinjaExportsForInterface(self, interface):
+    exports = self.GetJinjaExports()
+    exports.update({'interface': interface})
+    if interface.client:
+      for client in self.module.interfaces:
+        if client.name == interface.client:
+          exports.update({'client': client})
+    return exports
+
+  @UseJinja('java_templates/enum.java.tmpl', filters=java_filters)
+  def GenerateEnumSource(self, enum):
+    exports = self.GetJinjaExports()
+    exports.update({'enum': enum})
+    return exports
+
+  @UseJinja('java_templates/struct.java.tmpl', filters=java_filters)
+  def GenerateStructSource(self, struct):
+    exports = self.GetJinjaExports()
+    exports.update({'struct': struct})
+    return exports
+
+  @UseJinja('java_templates/interface.java.tmpl', filters=java_filters)
+  def GenerateInterfaceSource(self, interface):
+    return self.GetJinjaExportsForInterface(interface)
+
+  @UseJinja('java_templates/interface_internal.java.tmpl', filters=java_filters)
+  def GenerateInterfaceInternalSource(self, interface):
+    return self.GetJinjaExportsForInterface(interface)
+
+  @UseJinja('java_templates/constants.java.tmpl', filters=java_filters)
+  def GenerateConstantsSource(self, module):
+    exports = self.GetJinjaExports()
+    exports.update({'main_entity': GetConstantsMainEntityName(module),
+                    'constants': module.constants})
+    return exports
+
+  def DoGenerateFiles(self):
+    if not os.path.exists(self.output_dir):
+      try:
+        os.makedirs(self.output_dir)
+      except:
+        # Ignore errors on directory creation.
+        pass
+
+    # Keep this above the others as .GetStructs() changes the state of the
+    # module, annotating structs with required information.
+    for struct in self.GetStructs():
+      self.Write(self.GenerateStructSource(struct),
+                 '%s.java' % GetNameForElement(struct))
+
+    for enum in self.module.enums:
+      self.Write(self.GenerateEnumSource(enum),
+                 '%s.java' % GetNameForElement(enum))
+
+    for interface in self.module.interfaces:
+      self.Write(self.GenerateInterfaceSource(interface),
+                 '%s.java' % GetNameForElement(interface))
+      self.Write(self.GenerateInterfaceInternalSource(interface),
+                 '%s_Internal.java' % GetNameForElement(interface))
+
+    if self.module.constants:
+      self.Write(self.GenerateConstantsSource(self.module),
+                 '%s.java' % GetConstantsMainEntityName(self.module))
+
+  def GenerateFiles(self, unparsed_args):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--java_output_directory', dest='java_output_directory')
+    args = parser.parse_args(unparsed_args)
+    package_path = GetPackage(self.module).replace('.', '/')
+
+    # Generate the java files in a temporary directory and place a single
+    # srcjar in the output directory.
+    zip_filename = os.path.join(self.output_dir,
+                                "%s.srcjar" % self.module.name)
+    with TempDir() as temp_java_root:
+      self.output_dir = os.path.join(temp_java_root, package_path)
+      self.DoGenerateFiles();
+      ZipContentInto(temp_java_root, zip_filename)
+
+    if args.java_output_directory:
+      # If requested, generate the java files directly into indicated directory.
+      self.output_dir = os.path.join(args.java_output_directory, package_path)
+      self.DoGenerateFiles();
+
+  def GetJinjaParameters(self):
+    return {
+      'lstrip_blocks': True,
+      'trim_blocks': True,
+    }
+
+  def GetGlobals(self):
+    return {
+      'namespace': self.module.namespace,
+      'module': self.module,
+    }
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
new file mode 100644
index 0000000..c9109fb
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -0,0 +1,275 @@
+# 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.
+
+"""Generates JavaScript source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+_kind_to_javascript_default_value = {
+  mojom.BOOL:                  "false",
+  mojom.INT8:                  "0",
+  mojom.UINT8:                 "0",
+  mojom.INT16:                 "0",
+  mojom.UINT16:                "0",
+  mojom.INT32:                 "0",
+  mojom.UINT32:                "0",
+  mojom.FLOAT:                 "0",
+  mojom.HANDLE:                "null",
+  mojom.DCPIPE:                "null",
+  mojom.DPPIPE:                "null",
+  mojom.MSGPIPE:               "null",
+  mojom.SHAREDBUFFER:          "null",
+  mojom.NULLABLE_HANDLE:       "null",
+  mojom.NULLABLE_DCPIPE:       "null",
+  mojom.NULLABLE_DPPIPE:       "null",
+  mojom.NULLABLE_MSGPIPE:      "null",
+  mojom.NULLABLE_SHAREDBUFFER: "null",
+  mojom.INT64:                 "0",
+  mojom.UINT64:                "0",
+  mojom.DOUBLE:                "0",
+  mojom.STRING:                "null",
+  mojom.NULLABLE_STRING:       "null"
+}
+
+
+def JavaScriptType(kind):
+  if kind.imported_from:
+    return kind.imported_from["unique_name"] + "." + kind.name
+  return kind.name
+
+
+def JavaScriptDefaultValue(field):
+  if field.default:
+    if mojom.IsStructKind(field.kind):
+      assert field.default == "default"
+      return "new %s()" % JavaScriptType(field.kind)
+    return ExpressionToText(field.default)
+  if field.kind in mojom.PRIMITIVES:
+    return _kind_to_javascript_default_value[field.kind]
+  if mojom.IsStructKind(field.kind):
+    return "null"
+  if mojom.IsAnyArrayKind(field.kind):
+    return "null"
+  if mojom.IsInterfaceKind(field.kind) or \
+     mojom.IsInterfaceRequestKind(field.kind):
+    return _kind_to_javascript_default_value[mojom.MSGPIPE]
+  if mojom.IsEnumKind(field.kind):
+    return "0"
+
+
+def JavaScriptPayloadSize(packed):
+  packed_fields = packed.packed_fields
+  if not packed_fields:
+    return 0
+  last_field = packed_fields[-1]
+  offset = last_field.offset + last_field.size
+  pad = pack.GetPad(offset, 8)
+  return offset + pad
+
+
+_kind_to_codec_type = {
+  mojom.BOOL:                  "codec.Uint8",
+  mojom.INT8:                  "codec.Int8",
+  mojom.UINT8:                 "codec.Uint8",
+  mojom.INT16:                 "codec.Int16",
+  mojom.UINT16:                "codec.Uint16",
+  mojom.INT32:                 "codec.Int32",
+  mojom.UINT32:                "codec.Uint32",
+  mojom.FLOAT:                 "codec.Float",
+  mojom.HANDLE:                "codec.Handle",
+  mojom.DCPIPE:                "codec.Handle",
+  mojom.DPPIPE:                "codec.Handle",
+  mojom.MSGPIPE:               "codec.Handle",
+  mojom.SHAREDBUFFER:          "codec.Handle",
+  mojom.NULLABLE_HANDLE:       "codec.NullableHandle",
+  mojom.NULLABLE_DCPIPE:       "codec.NullableHandle",
+  mojom.NULLABLE_DPPIPE:       "codec.NullableHandle",
+  mojom.NULLABLE_MSGPIPE:      "codec.NullableHandle",
+  mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle",
+  mojom.INT64:                 "codec.Int64",
+  mojom.UINT64:                "codec.Uint64",
+  mojom.DOUBLE:                "codec.Double",
+  mojom.STRING:                "codec.String",
+  mojom.NULLABLE_STRING:       "codec.NullableString",
+}
+
+
+def CodecType(kind):
+  if kind in mojom.PRIMITIVES:
+    return _kind_to_codec_type[kind]
+  if mojom.IsStructKind(kind):
+    pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \
+        else "PointerTo"
+    return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind))
+  if mojom.IsAnyArrayKind(kind):
+    array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
+    element_type = "codec.PackedBool" if mojom.IsBoolKind(kind.kind) \
+        else CodecType(kind.kind)
+    return "new codec.%s(%s)" % (array_type, element_type)
+  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+    return CodecType(mojom.MSGPIPE)
+  if mojom.IsEnumKind(kind):
+    return _kind_to_codec_type[mojom.INT32]
+  return kind
+
+
+def JavaScriptDecodeSnippet(kind):
+  if kind in mojom.PRIMITIVES:
+    return "decodeStruct(%s)" % CodecType(kind)
+  if mojom.IsStructKind(kind):
+    return "decodeStructPointer(%s)" % JavaScriptType(kind)
+  if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+    return "decodeArrayPointer(codec.PackedBool)"
+  if mojom.IsAnyArrayKind(kind):
+    return "decodeArrayPointer(%s)" % CodecType(kind.kind)
+  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+    return JavaScriptDecodeSnippet(mojom.MSGPIPE)
+  if mojom.IsEnumKind(kind):
+    return JavaScriptDecodeSnippet(mojom.INT32)
+
+
+def JavaScriptEncodeSnippet(kind):
+  if kind in mojom.PRIMITIVES:
+    return "encodeStruct(%s, " % CodecType(kind)
+  if mojom.IsStructKind(kind):
+    return "encodeStructPointer(%s, " % JavaScriptType(kind)
+  if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+    return "encodeArrayPointer(codec.PackedBool, ";
+  if mojom.IsAnyArrayKind(kind):
+    return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+    return JavaScriptEncodeSnippet(mojom.MSGPIPE)
+  if mojom.IsEnumKind(kind):
+    return JavaScriptEncodeSnippet(mojom.INT32)
+
+
+def JavaScriptFieldOffset(packed_field):
+  return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
+
+
+def JavaScriptNullableParam(packed_field):
+  return "true" if mojom.IsNullableKind(packed_field.field.kind) else "false"
+
+
+def JavaScriptValidateArrayParams(packed_field):
+  nullable = JavaScriptNullableParam(packed_field)
+  field_offset = JavaScriptFieldOffset(packed_field)
+  element_kind = packed_field.field.kind.kind
+  element_size = pack.PackedField.GetSizeForKind(element_kind)
+  element_count = generator.ExpectedArraySize(packed_field.field.kind)
+  element_type = "codec.PackedBool" if mojom.IsBoolKind(element_kind) \
+      else CodecType(element_kind)
+  return "%s, %s, %s, %s, %s" % \
+      (field_offset, element_size, element_count, element_type, nullable)
+
+
+def JavaScriptValidateStructParams(packed_field):
+  nullable = JavaScriptNullableParam(packed_field)
+  field_offset = JavaScriptFieldOffset(packed_field)
+  struct_type = JavaScriptType(packed_field.field.kind)
+  return "%s, %s, %s" % (field_offset, struct_type, nullable)
+
+
+def JavaScriptValidateStringParams(packed_field):
+  nullable = JavaScriptNullableParam(packed_field)
+  return "%s, %s" % (JavaScriptFieldOffset(packed_field), nullable)
+
+
+def JavaScriptValidateHandleParams(packed_field):
+  nullable = JavaScriptNullableParam(packed_field)
+  field_offset = JavaScriptFieldOffset(packed_field)
+  return "%s, %s" % (field_offset, nullable)
+
+
+def TranslateConstants(token):
+  if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
+    # Both variable and enum constants are constructed like:
+    # NamespaceUid.Struct[.Enum].CONSTANT_NAME
+    name = []
+    if token.imported_from:
+      name.append(token.imported_from["unique_name"])
+    if token.parent_kind:
+      name.append(token.parent_kind.name)
+    if isinstance(token, mojom.EnumValue):
+      name.append(token.enum.name)
+    name.append(token.name)
+    return ".".join(name)
+
+  if isinstance(token, mojom.BuiltinValue):
+    if token.value == "double.INFINITY" or token.value == "float.INFINITY":
+      return "Infinity";
+    if token.value == "double.NEGATIVE_INFINITY" or \
+       token.value == "float.NEGATIVE_INFINITY":
+      return "-Infinity";
+    if token.value == "double.NAN" or token.value == "float.NAN":
+      return "NaN";
+
+  return token
+
+
+def ExpressionToText(value):
+  return TranslateConstants(value)
+
+
+def IsArrayPointerField(field):
+  return mojom.IsAnyArrayKind(field.kind)
+
+def IsStringPointerField(field):
+  return mojom.IsStringKind(field.kind)
+
+def IsStructPointerField(field):
+  return mojom.IsStructKind(field.kind)
+
+def IsHandleField(field):
+  return mojom.IsAnyHandleKind(field.kind)
+
+
+class Generator(generator.Generator):
+
+  js_filters = {
+    "default_value": JavaScriptDefaultValue,
+    "payload_size": JavaScriptPayloadSize,
+    "decode_snippet": JavaScriptDecodeSnippet,
+    "encode_snippet": JavaScriptEncodeSnippet,
+    "expression_to_text": ExpressionToText,
+    "field_offset": JavaScriptFieldOffset,
+    "has_callbacks": mojom.HasCallbacks,
+    "is_array_pointer_field": IsArrayPointerField,
+    "is_struct_pointer_field": IsStructPointerField,
+    "is_string_pointer_field": IsStringPointerField,
+    "is_handle_field": IsHandleField,
+    "js_type": JavaScriptType,
+    "stylize_method": generator.StudlyCapsToCamel,
+    "validate_array_params": JavaScriptValidateArrayParams,
+    "validate_handle_params": JavaScriptValidateHandleParams,
+    "validate_string_params": JavaScriptValidateStringParams,
+    "validate_struct_params": JavaScriptValidateStructParams,
+  }
+
+  @UseJinja("js_templates/module.js.tmpl", filters=js_filters)
+  def GenerateJsModule(self):
+    return {
+      "namespace": self.module.namespace,
+      "imports": self.GetImports(),
+      "kinds": self.module.kinds,
+      "enums": self.module.enums,
+      "module": self.module,
+      "structs": self.GetStructs() + self.GetStructsFromMethods(),
+      "interfaces": self.module.interfaces,
+    }
+
+  def GenerateFiles(self, args):
+    self.Write(self.GenerateJsModule(), "%s.js" % self.module.name)
+
+  def GetImports(self):
+    # Since each import is assigned a variable in JS, they need to have unique
+    # names.
+    counter = 1
+    for each in self.module.imports:
+      each["unique_name"] = "import" + str(counter)
+      counter += 1
+    return self.module.imports
diff --git a/mojo/public/tools/bindings/generators/mojom_python_generator.py b/mojo/public/tools/bindings/generators/mojom_python_generator.py
new file mode 100644
index 0000000..8034480
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_python_generator.py
@@ -0,0 +1,291 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates Python source files from a mojom.Module."""
+
+import re
+from itertools import ifilter
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+_kind_to_type = {
+  mojom.BOOL:                  '_descriptor.TYPE_BOOL',
+  mojom.INT8:                  '_descriptor.TYPE_INT8',
+  mojom.UINT8:                 '_descriptor.TYPE_UINT8',
+  mojom.INT16:                 '_descriptor.TYPE_INT16',
+  mojom.UINT16:                '_descriptor.TYPE_UINT16',
+  mojom.INT32:                 '_descriptor.TYPE_INT32',
+  mojom.UINT32:                '_descriptor.TYPE_UINT32',
+  mojom.INT64:                 '_descriptor.TYPE_INT64',
+  mojom.UINT64:                '_descriptor.TYPE_UINT64',
+  mojom.FLOAT:                 '_descriptor.TYPE_FLOAT',
+  mojom.DOUBLE:                '_descriptor.TYPE_DOUBLE',
+  mojom.STRING:                '_descriptor.TYPE_STRING',
+  mojom.NULLABLE_STRING:       '_descriptor.TYPE_NULLABLE_STRING',
+  mojom.HANDLE:                '_descriptor.TYPE_HANDLE',
+  mojom.DCPIPE:                '_descriptor.TYPE_HANDLE',
+  mojom.DPPIPE:                '_descriptor.TYPE_HANDLE',
+  mojom.MSGPIPE:               '_descriptor.TYPE_HANDLE',
+  mojom.SHAREDBUFFER:          '_descriptor.TYPE_HANDLE',
+  mojom.NULLABLE_HANDLE:       '_descriptor.TYPE_NULLABLE_HANDLE',
+  mojom.NULLABLE_DCPIPE:       '_descriptor.TYPE_NULLABLE_HANDLE',
+  mojom.NULLABLE_DPPIPE:       '_descriptor.TYPE_NULLABLE_HANDLE',
+  mojom.NULLABLE_MSGPIPE:      '_descriptor.TYPE_NULLABLE_HANDLE',
+  mojom.NULLABLE_SHAREDBUFFER: '_descriptor.TYPE_NULLABLE_HANDLE',
+}
+
+# int64 integers are not handled by array.array. int64/uint64 array are
+# supported but storage is not optimized (ie. they are plain python list, not
+# array.array)
+_kind_to_typecode_for_native_array = {
+  mojom.INT8:   'b',
+  mojom.UINT8:  'B',
+  mojom.INT16:  'h',
+  mojom.UINT16: 'H',
+  mojom.INT32:  'i',
+  mojom.UINT32: 'I',
+  mojom.FLOAT:  'f',
+  mojom.DOUBLE: 'd',
+}
+
+_kind_to_typecode = dict(_kind_to_typecode_for_native_array)
+_kind_to_typecode.update({
+  mojom.INT64:                 'q',
+  mojom.UINT64:                'Q',
+  mojom.HANDLE:                'i',
+  mojom.DCPIPE:                'i',
+  mojom.DPPIPE:                'i',
+  mojom.MSGPIPE:               'i',
+  mojom.SHAREDBUFFER:          'i',
+  mojom.NULLABLE_HANDLE:       'i',
+  mojom.NULLABLE_DCPIPE:       'i',
+  mojom.NULLABLE_DPPIPE:       'i',
+  mojom.NULLABLE_MSGPIPE:      'i',
+  mojom.NULLABLE_SHAREDBUFFER: 'i',
+})
+
+
+def NameToComponent(name):
+  # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
+  # HTTP_Entry2_FooBar)
+  name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
+  # insert '_' between non upper and start of upper blocks (e.g.,
+  # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
+  name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
+  return [x.lower() for x in name.split('_')]
+
+def UpperCamelCase(name):
+  return ''.join([x.capitalize() for x in NameToComponent(name)])
+
+def CamelCase(name):
+  uccc = UpperCamelCase(name)
+  return uccc[0].lower() + uccc[1:]
+
+def ConstantStyle(name):
+  components = NameToComponent(name)
+  if components[0] == 'k':
+    components = components[1:]
+  return '_'.join([x.upper() for x in components])
+
+def GetNameForElement(element):
+  if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
+      mojom.IsStructKind(element)):
+    return UpperCamelCase(element.name)
+  if isinstance(element, mojom.EnumValue):
+    return (GetNameForElement(element.enum) + '.' +
+            ConstantStyle(element.name))
+  if isinstance(element, (mojom.NamedValue,
+                          mojom.Constant)):
+    return ConstantStyle(element.name)
+  raise Exception('Unexpected element: ' % element)
+
+def ExpressionToText(token):
+  if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
+    return str(token.computed_value)
+
+  if isinstance(token, mojom.BuiltinValue):
+    if token.value == 'double.INFINITY' or token.value == 'float.INFINITY':
+      return 'float(\'inf\')';
+    if (token.value == 'double.NEGATIVE_INFINITY' or
+        token.value == 'float.NEGATIVE_INFINITY'):
+      return 'float(\'-inf\')'
+    if token.value == 'double.NAN' or token.value == 'float.NAN':
+      return 'float(\'nan\')';
+
+  if token in ['true', 'false']:
+    return str(token == 'true')
+
+  return token
+
+def GetStructClass(kind):
+  name = []
+  if kind.imported_from:
+    name.append(kind.imported_from['python_module'])
+  name.append(GetNameForElement(kind))
+  return '.'.join(name)
+
+def GetFieldType(kind, field=None):
+  if mojom.IsAnyArrayKind(kind):
+    arguments = []
+    if kind.kind in _kind_to_typecode_for_native_array:
+      arguments.append('%r' %_kind_to_typecode_for_native_array[kind.kind])
+    elif kind.kind != mojom.BOOL:
+      arguments.append(GetFieldType(kind.kind))
+    if mojom.IsNullableKind(kind):
+      arguments.append('nullable=True')
+    if mojom.IsFixedArrayKind(kind):
+      arguments.append('length=%d' % kind.length)
+    array_type = 'GenericArrayType'
+    if kind.kind == mojom.BOOL:
+      array_type = 'BooleanArrayType'
+    elif kind.kind in _kind_to_typecode_for_native_array:
+      array_type = 'NativeArrayType'
+    return '_descriptor.%s(%s)' % (array_type, ', '.join(arguments))
+
+  if mojom.IsStructKind(kind):
+    arguments = [ GetStructClass(kind) ]
+    if mojom.IsNullableKind(kind):
+      arguments.append('nullable=True')
+    return '_descriptor.StructType(%s)' % ', '.join(arguments)
+
+  if mojom.IsEnumKind(kind):
+    return GetFieldType(mojom.INT32)
+
+  return _kind_to_type.get(kind, '_descriptor.TYPE_NONE')
+
+def GetFieldDescriptor(packed_field):
+  field = packed_field.field
+  class_name = 'SingleFieldGroup'
+  if field.kind == mojom.BOOL:
+    class_name = 'FieldDescriptor'
+  arguments = [ '%r' % field.name ]
+  arguments.append(GetFieldType(field.kind, field))
+  arguments.append(str(packed_field.index))
+  arguments.append(str(packed_field.ordinal))
+  if field.default:
+    if mojom.IsStructKind(field.kind):
+      arguments.append('default_value=True')
+    else:
+      arguments.append('default_value=%s' % ExpressionToText(field.default))
+  return '_descriptor.%s(%s)' % (class_name, ', '.join(arguments))
+
+def GetFieldGroup(byte):
+  if len(byte.packed_fields) > 1:
+    descriptors = map(GetFieldDescriptor, byte.packed_fields)
+    return '_descriptor.BooleanGroup([%s])' % ', '.join(descriptors)
+  assert len(byte.packed_fields) == 1
+  return GetFieldDescriptor(byte.packed_fields[0])
+
+def ComputeStaticValues(module):
+  in_progress = set()
+  computed = set()
+
+  def GetComputedValue(named_value):
+    if isinstance(named_value, mojom.EnumValue):
+      field = next(ifilter(lambda field: field.name == named_value.name,
+                           named_value.enum.fields), None)
+      if not field:
+        raise RuntimeError(
+            'Unable to get computed value for field %s of enum %s' %
+            (named_value.name, named_value.enum.name))
+      if field not in computed:
+        ResolveEnum(named_value.enum)
+      return field.computed_value
+    elif isinstance(named_value, mojom.ConstantValue):
+      ResolveConstant(named_value.constant)
+      named_value.computed_value = named_value.constant.computed_value
+      return named_value.computed_value
+    else:
+      print named_value
+
+  def ResolveConstant(constant):
+    if constant in computed:
+      return
+    if constant in in_progress:
+      raise RuntimeError('Circular dependency for constant: %s' % constant.name)
+    in_progress.add(constant)
+    if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)):
+      computed_value = GetComputedValue(constant.value)
+    else:
+      computed_value = ExpressionToText(constant.value)
+    constant.computed_value = computed_value
+    in_progress.remove(constant)
+    computed.add(constant)
+
+  def ResolveEnum(enum):
+    def ResolveEnumField(enum, field, default_value):
+      if field in computed:
+        return
+      if field in in_progress:
+        raise RuntimeError('Circular dependency for enum: %s' % enum.name)
+      in_progress.add(field)
+      if field.value:
+        if isinstance(field.value, mojom.EnumValue):
+          computed_value = GetComputedValue(field.value)
+        elif isinstance(field.value, str):
+          computed_value = int(field.value, 0)
+        else:
+          raise RuntimeError('Unexpected value: %s' % field.value)
+      else:
+        computed_value = default_value
+      field.computed_value = computed_value
+      in_progress.remove(field)
+      computed.add(field)
+
+    current_value = 0
+    for field in enum.fields:
+      ResolveEnumField(enum, field, current_value)
+      current_value = field.computed_value + 1
+
+  for constant in module.constants:
+    ResolveConstant(constant)
+
+  for enum in module.enums:
+    ResolveEnum(enum)
+
+  for struct in module.structs:
+    for constant in struct.constants:
+      ResolveConstant(constant)
+    for enum in struct.enums:
+      ResolveEnum(enum)
+    for field in struct.fields:
+      if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)):
+        field.default.computed_value = GetComputedValue(field.default)
+
+  return module
+
+
+class Generator(generator.Generator):
+
+  python_filters = {
+    'expression_to_text': ExpressionToText,
+    'field_group': GetFieldGroup,
+    'name': GetNameForElement,
+  }
+
+  @UseJinja('python_templates/module.py.tmpl', filters=python_filters)
+  def GeneratePythonModule(self):
+    return {
+      'imports': self.GetImports(),
+      'enums': self.module.enums,
+      'module': ComputeStaticValues(self.module),
+      'structs': self.GetStructs(),
+    }
+
+  def GenerateFiles(self, args):
+    self.Write(self.GeneratePythonModule(),
+               '%s.py' % self.module.name.replace('.mojom', '_mojom'))
+
+  def GetImports(self):
+    for each in self.module.imports:
+      each['python_module'] = each['module_name'].replace('.mojom', '_mojom')
+    return self.module.imports
+
+  def GetJinjaParameters(self):
+    return {
+      'lstrip_blocks': True,
+      'trim_blocks': True,
+    }
diff --git a/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
new file mode 100644
index 0000000..d5bff30
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
@@ -0,0 +1,56 @@
+{% from "module_macros.tmpl" import enum_values %}
+# Copyright 2014 The Chromium 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.bindings.descriptor as _descriptor
+import mojo.bindings.reflection as _reflection
+{% if imports %}
+
+{% for import in imports %}
+import {{import.python_module}}
+{% endfor %}
+{% endif %}
+{#--- Constants #}
+{% if module.constants %}
+
+{% for constant in module.constants %}
+{{constant|name}} = {{constant.value|expression_to_text}}
+{% endfor %}
+{% endif %}
+{% for enum in module.enums %}
+
+class {{enum|name}}(object):
+  __metaclass__ = _reflection.MojoEnumType
+  VALUES = {{enum_values(enum)|indent(2)}}
+{% endfor %}
+{% for struct in module.structs %}
+
+class {{struct|name}}(object):
+  __metaclass__ = _reflection.MojoStructType
+  DESCRIPTOR = {
+{%   if struct.constants %}
+    'constants': {
+{%     for constant in struct.constants %}
+      '{{constant|name}}': {{constant.value|expression_to_text}},
+{%     endfor %}
+    },
+{%   endif %}
+{%   if struct.enums %}
+    'enums': {
+{%     for enum in struct.enums %}
+      '{{enum|name}}': {{enum_values(enum)|indent(6)}},
+{%     endfor %}
+    },
+{%   endif %}
+{%   if struct.fields %}
+    'fields': [
+{%    for byte in struct.bytes %}
+{%      if byte.packed_fields %}
+      {{byte|field_group}},
+{%      endif %}
+{%   endfor %}
+    ],
+{%   endif %}
+  }
+{% endfor %}
diff --git a/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl b/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl
new file mode 100644
index 0000000..305b26a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl
@@ -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.
+
+{%- macro enum_values(enum) -%}
+[
+{% for field in enum.fields %}
+  ('{{field.name}}', {{field.computed_value}}),
+{% endfor %}
+]
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/run_cpp_generator.py b/mojo/public/tools/bindings/generators/run_cpp_generator.py
new file mode 100755
index 0000000..4c6f597
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/run_cpp_generator.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# 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.
+
+import ast
+import os
+import sys
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, os.pardir, "pylib"))
+
+from mojom.generate.data
+import mojom_cpp_generator
+
+def ReadDict(file):
+  with open(file, 'r') as f:
+    s = f.read()
+    dict = ast.literal_eval(s)
+    return dict
+
+dict = ReadDict(sys.argv[1])
+module = mojom.generate.data.ModuleFromData(dict)
+dir = None
+if len(sys.argv) > 2:
+  dir = sys.argv[2]
+cpp = mojom_cpp_generator.Generator(module, ".", dir)
+cpp.GenerateFiles([])
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
new file mode 100644
index 0000000..e12d8e0
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -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.
+
+# Generate C++ and JavaScript source files from mojom files. The output files
+# will go under the generated file directory tree with the same path as each
+# input file.
+#
+# Parameters:
+#
+#   sources (required)
+#       List of source .mojom files to compile.
+#
+#   deps (optional)
+#
+#   public_deps (optional)
+#
+#   testonly (optional)
+#
+#   visibility (optional)
+template("mojom") {
+  assert(defined(invoker.sources),
+         "\"sources\" must be defined for the $target_name template.")
+
+  generator_root = "//mojo/public/tools/bindings"
+  generator_script = "$generator_root/mojom_bindings_generator.py"
+  generator_sources = [
+    generator_script,
+    "$generator_root/generators/cpp_templates/enum_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/interface_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/interface_definition.tmpl",
+    "$generator_root/generators/cpp_templates/interface_macros.tmpl",
+    "$generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/module.cc.tmpl",
+    "$generator_root/generators/cpp_templates/module.h.tmpl",
+    "$generator_root/generators/cpp_templates/module-internal.h.tmpl",
+    "$generator_root/generators/cpp_templates/params_definition.tmpl",
+    "$generator_root/generators/cpp_templates/struct_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/struct_definition.tmpl",
+    "$generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl",
+    "$generator_root/generators/cpp_templates/struct_macros.tmpl",
+    "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
+    "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
+    "$generator_root/generators/js_templates/enum_definition.tmpl",
+    "$generator_root/generators/js_templates/interface_definition.tmpl",
+    "$generator_root/generators/js_templates/module.js.tmpl",
+    "$generator_root/generators/js_templates/struct_definition.tmpl",
+    "$generator_root/generators/python_templates/module_macros.tmpl",
+    "$generator_root/generators/python_templates/module.py.tmpl",
+    "$generator_root/generators/mojom_cpp_generator.py",
+    "$generator_root/generators/mojom_js_generator.py",
+    "$generator_root/generators/mojom_python_generator.py",
+    "$generator_root/pylib/mojom/__init__.py",
+    "$generator_root/pylib/mojom/error.py",
+    "$generator_root/pylib/mojom/generate/__init__.py",
+    "$generator_root/pylib/mojom/generate/data.py",
+    "$generator_root/pylib/mojom/generate/generator.py",
+    "$generator_root/pylib/mojom/generate/module.py",
+    "$generator_root/pylib/mojom/generate/pack.py",
+    "$generator_root/pylib/mojom/generate/template_expander.py",
+    "$generator_root/pylib/mojom/parse/__init__.py",
+    "$generator_root/pylib/mojom/parse/ast.py",
+    "$generator_root/pylib/mojom/parse/lexer.py",
+    "$generator_root/pylib/mojom/parse/parser.py",
+    "$generator_root/pylib/mojom/parse/translate.py",
+  ]
+  generator_cpp_outputs = [
+    "{{source_gen_dir}}/{{source_name_part}}.mojom.cc",
+    "{{source_gen_dir}}/{{source_name_part}}.mojom.h",
+    "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h",
+  ]
+  generator_js_outputs = [
+    "{{source_gen_dir}}/{{source_name_part}}.mojom.js",
+  ]
+  generator_python_outputs = [
+    "{{source_gen_dir}}/{{source_name_part}}_mojom.py",
+  ]
+
+  if (defined(invoker.visibility)) {
+    # Need to save this because the the target_name is overwritten inside the
+    # action to be that of the action itself. Only define this in the case the
+    # var is used to avoid unused var error.
+    target_visibility = [ ":$target_name" ]
+  }
+
+  generator_target_name = target_name + "__generator"
+  action_foreach(generator_target_name) {
+    if (defined(invoker.visibility)) {
+      visibility = target_visibility + invoker.visibility
+    }
+    script = generator_script
+    inputs = generator_sources
+    sources = invoker.sources
+    outputs = generator_cpp_outputs +
+              generator_js_outputs +
+              generator_python_outputs
+    args = [
+      "{{source}}",
+      "--use_chromium_bundled_pylibs",
+      "-d", rebase_path("//", root_build_dir),
+      "-I", rebase_path("//", root_build_dir),
+      "-o", "{{source_gen_dir}}",
+    ]
+  }
+
+  source_set(target_name) {
+    if (defined(invoker.visibility)) {
+      visibility = invoker.visibility
+    }
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+    sources = process_file_template(invoker.sources, generator_cpp_outputs)
+    data = process_file_template(invoker.sources, generator_js_outputs)
+    deps = [
+      ":$generator_target_name",
+      "//mojo/public/cpp/bindings",
+    ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    if (defined(invoker.public_deps)) {
+      public_deps = invoker.public_deps
+    }
+  }
+}
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.gypi b/mojo/public/tools/bindings/mojom_bindings_generator.gypi
new file mode 100644
index 0000000..c6e9f98
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.gypi
@@ -0,0 +1,111 @@
+# 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.
+
+{
+  'rules': [
+    {
+      'rule_name': '<(_target_name)_mojom_bindings_generator',
+      'extension': 'mojom',
+      'variables': {
+        'mojom_base_output_dir':
+             '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+        'mojom_bindings_generator':
+            '<(DEPTH)/mojo/public/tools/bindings/mojom_bindings_generator.py',
+        'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        'mojom_import_args%': [
+         '-I<(DEPTH)'
+        ],
+      },
+      'inputs': [
+        '<(mojom_bindings_generator)',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_cpp_generator.py',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_java_generator.py',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_js_generator.py',
+        '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_python_generator.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/__init__.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/error.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/data.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/generator.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/module.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/pack.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/ast.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/parser.py',
+        '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/translate.py',
+      ],
+      'outputs': [
+        '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.cc',
+        '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.h',
+        '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.js',
+        '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)_mojom.py',
+        '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-internal.h',
+      ],
+      'action': [
+        'python', '<@(mojom_bindings_generator)',
+        './<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+        '--use_chromium_bundled_pylibs',
+        '-d', '<(DEPTH)',
+        '<@(mojom_import_args)',
+        '-o', '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)',
+        '--java_output_directory=<(java_out_dir)',
+      ],
+      'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+      'process_outputs_as_sources': 1,
+    }
+  ],
+  'include_dirs': [
+    '<(DEPTH)',
+    '<(SHARED_INTERMEDIATE_DIR)',
+  ],
+  'direct_dependent_settings': {
+    'include_dirs': [
+      '<(DEPTH)',
+      '<(SHARED_INTERMEDIATE_DIR)',
+    ],
+    'variables': {
+      'generated_src_dirs': [
+        '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+      ],
+    },
+  },
+  'hard_dependency': 1,
+}
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
new file mode 100755
index 0000000..53e3a33
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# 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.
+
+"""The frontend for the Mojo bindings system."""
+
+
+import argparse
+import imp
+import os
+import pprint
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+# Manually check for the command-line flag. (This isn't quite right, since it
+# ignores, e.g., "--", but it's close enough.)
+if "--use_chromium_bundled_pylibs" in sys.argv[1:]:
+  sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                                "pylib"))
+
+from mojom.error import Error
+from mojom.generate.data import OrderedModuleFromData
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def LoadGenerators(generators_string):
+  if not generators_string:
+    return []  # No generators.
+
+  script_dir = os.path.dirname(os.path.abspath(__file__))
+  generators = []
+  for generator_name in [s.strip() for s in generators_string.split(",")]:
+    # "Built-in" generators:
+    if generator_name.lower() == "c++":
+      generator_name = os.path.join(script_dir, "generators",
+                                    "mojom_cpp_generator.py")
+    elif generator_name.lower() == "javascript":
+      generator_name = os.path.join(script_dir, "generators",
+                                    "mojom_js_generator.py")
+    elif generator_name.lower() == "java":
+      generator_name = os.path.join(script_dir, "generators",
+                                    "mojom_java_generator.py")
+    elif generator_name.lower() == "python":
+      generator_name = os.path.join(script_dir, "generators",
+                                    "mojom_python_generator.py")
+    # Specified generator python module:
+    elif generator_name.endswith(".py"):
+      pass
+    else:
+      print "Unknown generator name %s" % generator_name
+      sys.exit(1)
+    generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
+                                       generator_name)
+    generators.append(generator_module)
+  return generators
+
+
+def MakeImportStackMessage(imported_filename_stack):
+  """Make a (human-readable) message listing a chain of imports. (Returned
+  string begins with a newline (if nonempty) and does not end with one.)"""
+  return ''.join(
+      reversed(["\n  %s was imported by %s" % (a, b) for (a, b) in \
+                    zip(imported_filename_stack[1:], imported_filename_stack)]))
+
+
+def FindImportFile(dir_name, file_name, search_dirs):
+  for search_dir in [dir_name] + search_dirs:
+    path = os.path.join(search_dir, file_name)
+    if os.path.isfile(path):
+      return path
+  return os.path.join(dir_name, file_name)
+
+
+# Disable check for dangerous default arguments (they're "private" keyword
+# arguments; note that we want |_processed_files| to memoize across invocations
+# of |ProcessFile()|):
+# pylint: disable=W0102
+def ProcessFile(args, remaining_args, generator_modules, filename,
+                _processed_files={}, _imported_filename_stack=None):
+  # Memoized results.
+  if filename in _processed_files:
+    return _processed_files[filename]
+
+  if _imported_filename_stack is None:
+    _imported_filename_stack = []
+
+  # Ensure we only visit each file once.
+  if filename in _imported_filename_stack:
+    print "%s: Error: Circular dependency" % filename + \
+        MakeImportStackMessage(_imported_filename_stack + [filename])
+    sys.exit(1)
+
+  try:
+    with open(filename) as f:
+      source = f.read()
+  except IOError as e:
+    print "%s: Error: %s" % (e.filename, e.strerror) + \
+        MakeImportStackMessage(_imported_filename_stack + [filename])
+    sys.exit(1)
+
+  try:
+    tree = Parse(source, filename)
+  except Error as e:
+    print str(e) + MakeImportStackMessage(_imported_filename_stack + [filename])
+    sys.exit(1)
+
+  dirname, name = os.path.split(filename)
+  mojom = Translate(tree, name)
+  if args.debug_print_intermediate:
+    pprint.PrettyPrinter().pprint(mojom)
+
+  # Process all our imports first and collect the module object for each.
+  # We use these to generate proper type info.
+  for import_data in mojom['imports']:
+    import_filename = FindImportFile(dirname,
+                                     import_data['filename'],
+                                     args.import_directories)
+    import_data['module'] = ProcessFile(
+        args, remaining_args, generator_modules, import_filename,
+        _processed_files=_processed_files,
+        _imported_filename_stack=_imported_filename_stack + [filename])
+
+  module = OrderedModuleFromData(mojom)
+
+  # Set the path as relative to the source root.
+  module.path = os.path.relpath(os.path.abspath(filename),
+                                os.path.abspath(args.depth))
+
+  # Normalize to unix-style path here to keep the generators simpler.
+  module.path = module.path.replace('\\', '/')
+
+  for generator_module in generator_modules:
+    generator = generator_module.Generator(module, args.output_dir)
+    filtered_args = []
+    if hasattr(generator_module, 'GENERATOR_PREFIX'):
+      prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
+      filtered_args = [arg for arg in remaining_args if arg.startswith(prefix)]
+    generator.GenerateFiles(filtered_args)
+
+  # Save result.
+  _processed_files[filename] = module
+  return module
+# pylint: enable=W0102
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Generate bindings from mojom files.")
+  parser.add_argument("filename", nargs="+",
+                      help="mojom input file")
+  parser.add_argument("-d", "--depth", dest="depth", default=".",
+                      help="depth from source root")
+  parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
+                      help="output directory for generated files")
+  parser.add_argument("-g", "--generators", dest="generators_string",
+                      metavar="GENERATORS",
+                      default="c++,javascript,java,python",
+                      help="comma-separated list of generators")
+  parser.add_argument("--debug_print_intermediate", action="store_true",
+                      help="print the intermediate representation")
+  parser.add_argument("-I", dest="import_directories", action="append",
+                      metavar="directory", default=[],
+                      help="add a directory to be searched for import files")
+  parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
+                      help="use Python modules bundled in the Chromium source")
+  (args, remaining_args) = parser.parse_known_args()
+
+  generator_modules = LoadGenerators(args.generators_string)
+
+  if not os.path.exists(args.output_dir):
+    os.makedirs(args.output_dir)
+
+  for filename in args.filename:
+    ProcessFile(args, remaining_args, generator_modules, filename)
+
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
new file mode 100644
index 0000000..de38856
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
@@ -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.
+
+import unittest
+
+from mojom_bindings_generator import MakeImportStackMessage
+
+
+class MojoBindingsGeneratorTest(unittest.TestCase):
+  """Tests mojo_bindings_generator."""
+
+  def testMakeImportStackMessage(self):
+    """Tests MakeImportStackMessage()."""
+    self.assertEquals(MakeImportStackMessage(["x"]), "")
+    self.assertEquals(MakeImportStackMessage(["x", "y"]),
+        "\n  y was imported by x")
+    self.assertEquals(MakeImportStackMessage(["x", "y", "z"]),
+        "\n  z was imported by y\n  y was imported by x")
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom/__init__.py b/mojo/public/tools/bindings/pylib/mojom/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/error.py b/mojo/public/tools/bindings/pylib/mojom/error.py
new file mode 100644
index 0000000..99522b9
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/error.py
@@ -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.
+
+class Error(Exception):
+  """Base class for Mojo IDL bindings parser/generator errors."""
+
+  def __init__(self, filename, message, lineno=None, addenda=None, **kwargs):
+    """|filename| is the (primary) file which caused the error, |message| is the
+    error message, |lineno| is the 1-based line number (or |None| if not
+    applicable/available), and |addenda| is a list of additional lines to append
+    to the final error message."""
+    Exception.__init__(self, **kwargs)
+    self.filename = filename
+    self.message = message
+    self.lineno = lineno
+    self.addenda = addenda
+
+  def __str__(self):
+    if self.lineno:
+      s = "%s:%d: Error: %s" % (self.filename, self.lineno, self.message)
+    else:
+      s = "%s: Error: %s" % (self.filename, self.message)
+    return "\n".join([s] + self.addenda) if self.addenda else s
+
+  def __repr__(self):
+    return str(self)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
new file mode 100644
index 0000000..fab7e2e
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -0,0 +1,383 @@
+# 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.
+
+# TODO(vtl): "data" is a pretty vague name. Rename it?
+
+import copy
+
+import module as mojom
+
+# This module provides a mechanism to turn mojom Modules to dictionaries and
+# back again. This can be used to persist a mojom Module created progromatically
+# or to read a dictionary from code or a file.
+# Example:
+# test_dict = {
+#   'name': 'test',
+#   'namespace': 'testspace',
+#   'structs': [{
+#     'name': 'teststruct',
+#     'fields': [
+#       {'name': 'testfield1', 'kind': 'i32'},
+#       {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+#   'interfaces': [{
+#     'name': 'Server',
+#     'methods': [{
+#       'name': 'Foo',
+#       'parameters': [{
+#         'name': 'foo', 'kind': 'i32'},
+#         {'name': 'bar', 'kind': 'a:x:teststruct'}],
+#     'ordinal': 42}]}]
+# }
+# test_module = data.ModuleFromData(test_dict)
+
+# Used to create a subclass of str that supports sorting by index, to make
+# pretty printing maintain the order.
+def istr(index, string):
+  class IndexedString(str):
+    def __lt__(self, other):
+      return self.__index__ < other.__index__
+
+  rv = IndexedString(string)
+  rv.__index__ = index
+  return rv
+
+builtin_values = frozenset([
+    "double.INFINITY",
+    "double.NEGATIVE_INFINITY",
+    "double.NAN",
+    "float.INFINITY",
+    "float.NEGATIVE_INFINITY",
+    "float.NAN"])
+
+def IsBuiltinValue(value):
+  return value in builtin_values
+
+def LookupKind(kinds, spec, scope):
+  """Tries to find which Kind a spec refers to, given the scope in which its
+  referenced. Starts checking from the narrowest scope to most general. For
+  example, given a struct field like
+    Foo.Bar x;
+  Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner
+  type 'Bar' in the struct 'Foo' in the current namespace.
+
+  |scope| is a tuple that looks like (namespace, struct/interface), referring
+  to the location where the type is referenced."""
+  if spec.startswith('x:'):
+    name = spec[2:]
+    for i in xrange(len(scope), -1, -1):
+      test_spec = 'x:'
+      if i > 0:
+        test_spec += '.'.join(scope[:i]) + '.'
+      test_spec += name
+      kind = kinds.get(test_spec)
+      if kind:
+        return kind
+
+  return kinds.get(spec)
+
+def LookupValue(values, name, scope, kind):
+  """Like LookupKind, but for constant values."""
+  # If the type is an enum, the value can be specified as a qualified name, in
+  # which case the form EnumName.ENUM_VALUE must be used. We use the presence
+  # of a '.' in the requested name to identify this. Otherwise, we prepend the
+  # enum name.
+  if isinstance(kind, mojom.Enum) and '.' not in name:
+    name = '%s.%s' % (kind.spec.split(':', 1)[1], name)
+  for i in reversed(xrange(len(scope) + 1)):
+    test_spec = '.'.join(scope[:i])
+    if test_spec:
+      test_spec += '.'
+    test_spec += name
+    value = values.get(test_spec)
+    if value:
+      return value
+
+  return values.get(name)
+
+def FixupExpression(module, value, scope, kind):
+  """Translates an IDENTIFIER into a built-in value or structured NamedValue
+     object."""
+  if isinstance(value, tuple) and value[0] == 'IDENTIFIER':
+    # Allow user defined values to shadow builtins.
+    result = LookupValue(module.values, value[1], scope, kind)
+    if result:
+      if isinstance(result, tuple):
+        raise Exception('Unable to resolve expression: %r' % value[1])
+      return result
+    if IsBuiltinValue(value[1]):
+      return mojom.BuiltinValue(value[1])
+  return value
+
+def KindToData(kind):
+  return kind.spec
+
+def KindFromData(kinds, data, scope):
+  kind = LookupKind(kinds, data, scope)
+  if kind:
+    return kind
+
+  if data.startswith('?'):
+    kind = KindFromData(kinds, data[1:], scope).MakeNullableKind()
+  elif data.startswith('a:'):
+    kind = mojom.Array(KindFromData(kinds, data[2:], scope))
+  elif data.startswith('r:'):
+    kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope))
+  elif data.startswith('a'):
+    colon = data.find(':')
+    length = int(data[1:colon])
+    kind = mojom.FixedArray(length, KindFromData(kinds, data[colon+1:], scope))
+  else:
+    kind = mojom.Kind(data)
+
+  kinds[data] = kind
+  return kind
+
+def KindFromImport(original_kind, imported_from):
+  """Used with 'import module' - clones the kind imported from the given
+  module's namespace. Only used with Structs, Interfaces and Enums."""
+  kind = copy.copy(original_kind)
+  # |shared_definition| is used to store various properties (see
+  # |AddSharedProperty()| in module.py), including |imported_from|. We don't
+  # want the copy to share these with the original, so copy it if necessary.
+  if hasattr(original_kind, 'shared_definition'):
+    kind.shared_definition = copy.copy(original_kind.shared_definition)
+  kind.imported_from = imported_from
+  return kind
+
+def ImportFromData(module, data):
+  import_module = data['module']
+
+  import_item = {}
+  import_item['module_name'] = import_module.name
+  import_item['namespace'] = import_module.namespace
+  import_item['module'] = import_module
+
+  # Copy the struct kinds from our imports into the current module.
+  for kind in import_module.kinds.itervalues():
+    if (isinstance(kind, (mojom.Struct, mojom.Enum, mojom.Interface)) and
+        kind.imported_from is None):
+      kind = KindFromImport(kind, import_item)
+      module.kinds[kind.spec] = kind
+  # Ditto for values.
+  for value in import_module.values.itervalues():
+    if value.imported_from is None:
+      # Values don't have shared definitions (since they're not nullable), so no
+      # need to do anything special.
+      value = copy.copy(value)
+      value.imported_from = import_item
+      module.values[value.GetSpec()] = value
+
+  return import_item
+
+def StructToData(struct):
+  return {
+    istr(0, 'name'): struct.name,
+    istr(1, 'fields'): map(FieldToData, struct.fields)
+  }
+
+def StructFromData(module, data):
+  struct = mojom.Struct(module=module)
+  struct.name = data['name']
+  struct.attributes = data['attributes']
+  struct.spec = 'x:' + module.namespace + '.' + struct.name
+  module.kinds[struct.spec] = struct
+  struct.enums = map(lambda enum:
+      EnumFromData(module, enum, struct), data['enums'])
+  struct.constants = map(lambda constant:
+      ConstantFromData(module, constant, struct), data['constants'])
+  # Stash fields data here temporarily.
+  struct.fields_data = data['fields']
+  return struct
+
+def FieldToData(field):
+  data = {
+    istr(0, 'name'): field.name,
+    istr(1, 'kind'): KindToData(field.kind)
+  }
+  if field.ordinal != None:
+    data[istr(2, 'ordinal')] = field.ordinal
+  if field.default != None:
+    data[istr(3, 'default')] = field.default
+  return data
+
+def FieldFromData(module, data, struct):
+  field = mojom.Field()
+  field.name = data['name']
+  field.kind = KindFromData(
+      module.kinds, data['kind'], (module.namespace, struct.name))
+  field.ordinal = data.get('ordinal')
+  field.default = FixupExpression(
+      module, data.get('default'), (module.namespace, struct.name), field.kind)
+  return field
+
+def ParameterToData(parameter):
+  data = {
+    istr(0, 'name'): parameter.name,
+    istr(1, 'kind'): parameter.kind.spec
+  }
+  if parameter.ordinal != None:
+    data[istr(2, 'ordinal')] = parameter.ordinal
+  if parameter.default != None:
+    data[istr(3, 'default')] = parameter.default
+  return data
+
+def ParameterFromData(module, data, interface):
+  parameter = mojom.Parameter()
+  parameter.name = data['name']
+  parameter.kind = KindFromData(
+      module.kinds, data['kind'], (module.namespace, interface.name))
+  parameter.ordinal = data.get('ordinal')
+  parameter.default = data.get('default')
+  return parameter
+
+def MethodToData(method):
+  data = {
+    istr(0, 'name'):       method.name,
+    istr(1, 'parameters'): map(ParameterToData, method.parameters)
+  }
+  if method.ordinal != None:
+    data[istr(2, 'ordinal')] = method.ordinal
+  if method.response_parameters != None:
+    data[istr(3, 'response_parameters')] = map(
+        ParameterToData, method.response_parameters)
+  return data
+
+def MethodFromData(module, data, interface):
+  method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal'))
+  method.default = data.get('default')
+  method.parameters = map(lambda parameter:
+      ParameterFromData(module, parameter, interface), data['parameters'])
+  if data.has_key('response_parameters'):
+    method.response_parameters = map(
+        lambda parameter: ParameterFromData(module, parameter, interface),
+                          data['response_parameters'])
+  return method
+
+def InterfaceToData(interface):
+  return {
+    istr(0, 'name'):    interface.name,
+    istr(1, 'client'):  interface.client,
+    istr(2, 'methods'): map(MethodToData, interface.methods)
+  }
+
+def InterfaceFromData(module, data):
+  interface = mojom.Interface(module=module)
+  interface.name = data['name']
+  interface.spec = 'x:' + module.namespace + '.' + interface.name
+  interface.client = data['client'] if data.has_key('client') else None
+  module.kinds[interface.spec] = interface
+  interface.enums = map(lambda enum:
+      EnumFromData(module, enum, interface), data['enums'])
+  interface.constants = map(lambda constant:
+      ConstantFromData(module, constant, interface), data['constants'])
+  # Stash methods data here temporarily.
+  interface.methods_data = data['methods']
+  return interface
+
+def EnumFieldFromData(module, enum, data, parent_kind):
+  field = mojom.EnumField()
+  field.name = data['name']
+  # TODO(mpcomplete): FixupExpression should be done in the second pass,
+  # so constants and enums can refer to each other.
+  # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
+  # vice versa?
+  if parent_kind:
+    field.value = FixupExpression(
+        module, data['value'], (module.namespace, parent_kind.name), enum)
+  else:
+    field.value = FixupExpression(
+        module, data['value'], (module.namespace, ), enum)
+  value = mojom.EnumValue(module, enum, field)
+  module.values[value.GetSpec()] = value
+  return field
+
+def EnumFromData(module, data, parent_kind):
+  enum = mojom.Enum(module=module)
+  enum.name = data['name']
+  name = enum.name
+  if parent_kind:
+    name = parent_kind.name + '.' + name
+  enum.spec = 'x:%s.%s' % (module.namespace, name)
+  enum.parent_kind = parent_kind
+
+  enum.fields = map(
+      lambda field: EnumFieldFromData(module, enum, field, parent_kind),
+      data['fields'])
+  module.kinds[enum.spec] = enum
+  return enum
+
+def ConstantFromData(module, data, parent_kind):
+  constant = mojom.Constant()
+  constant.name = data['name']
+  if parent_kind:
+    scope = (module.namespace, parent_kind.name)
+  else:
+    scope = (module.namespace, )
+  # TODO(mpcomplete): maybe we should only support POD kinds.
+  constant.kind = KindFromData(module.kinds, data['kind'], scope)
+  constant.value = FixupExpression(module, data.get('value'), scope, None)
+
+  value = mojom.ConstantValue(module, parent_kind, constant)
+  module.values[value.GetSpec()] = value
+  return constant
+
+def ModuleToData(module):
+  return {
+    istr(0, 'name'):       module.name,
+    istr(1, 'namespace'):  module.namespace,
+    istr(2, 'structs'):    map(StructToData, module.structs),
+    istr(3, 'interfaces'): map(InterfaceToData, module.interfaces)
+  }
+
+def ModuleFromData(data):
+  module = mojom.Module()
+  module.kinds = {}
+  for kind in mojom.PRIMITIVES:
+    module.kinds[kind.spec] = kind
+
+  module.values = {}
+
+  module.name = data['name']
+  module.namespace = data['namespace']
+  module.attributes = data['attributes']
+  # Imports must come first, because they add to module.kinds which is used
+  # by by the others.
+  module.imports = map(
+      lambda import_data: ImportFromData(module, import_data),
+      data['imports'])
+
+  # First pass collects kinds.
+  module.enums = map(
+      lambda enum: EnumFromData(module, enum, None), data['enums'])
+  module.structs = map(
+      lambda struct: StructFromData(module, struct), data['structs'])
+  module.interfaces = map(
+      lambda interface: InterfaceFromData(module, interface),
+      data['interfaces'])
+  module.constants = map(
+      lambda constant: ConstantFromData(module, constant, None),
+      data['constants'])
+
+  # Second pass expands fields and methods. This allows fields and parameters
+  # to refer to kinds defined anywhere in the mojom.
+  for struct in module.structs:
+    struct.fields = map(lambda field:
+        FieldFromData(module, field, struct), struct.fields_data)
+    del struct.fields_data
+  for interface in module.interfaces:
+    interface.methods = map(lambda method:
+        MethodFromData(module, method, interface), interface.methods_data)
+    del interface.methods_data
+
+  return module
+
+def OrderedModuleFromData(data):
+  module = ModuleFromData(data)
+  for interface in module.interfaces:
+    next_ordinal = 0
+    for method in interface.methods:
+      if method.ordinal is None:
+        method.ordinal = next_ordinal
+      next_ordinal = method.ordinal + 1
+  return module
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
new file mode 100644
index 0000000..096554c
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
@@ -0,0 +1,86 @@
+# 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.
+
+import sys
+
+import data
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def DeepEquals(d1, d2):
+  if d1 == d2:
+    return True
+  if d2.__class__ != d2.__class__:
+    return False
+  if isinstance(d1, dict):
+    if set(d1.keys()) != set(d2.keys()):
+      return False
+    for key in d1.keys():
+      if not DeepEquals(d1[key], d2[key]):
+        return False
+    return True
+  if isinstance(d1, (list, tuple)):
+    if len(d1) != len(d2):
+      return False
+    for i in range(len(d1)):
+      if not DeepEquals(d1[i], d2[i]):
+        return False
+    return True
+  return False
+
+
+test_dict = {
+  'name': 'test',
+  'namespace': 'testspace',
+  'structs': [{
+    'name': 'teststruct',
+    'fields': [
+      {'name': 'testfield1', 'kind': 'i32'},
+      {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+  'interfaces': [{
+    'name': 'Server',
+    'client': None,
+    'methods': [{
+      'name': 'Foo',
+      'parameters': [
+        {'name': 'foo', 'kind': 'i32'},
+        {'name': 'bar', 'kind': 'a:x:teststruct'}],
+    'ordinal': 42}]}]
+}
+
+
+def TestRead():
+  module = data.ModuleFromData(test_dict)
+  return test_support.TestTestModule(module)
+
+
+def TestWrite():
+  module = test_support.BuildTestModule()
+  d = data.ModuleToData(module)
+  return EXPECT_TRUE(DeepEquals(test_dict, d))
+
+
+def TestWriteRead():
+  module1 = test_support.BuildTestModule()
+
+  dict1 = data.ModuleToData(module1)
+  module2 = data.ModuleFromData(dict1)
+  return EXPECT_TRUE(test_support.ModulesAreEqual(module1, module2))
+
+
+def Main(args):
+  errors = 0
+  errors += RunTest(TestWriteRead)
+  errors += RunTest(TestRead)
+  errors += RunTest(TestWrite)
+
+  return errors
+
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
new file mode 100644
index 0000000..eb433bf
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -0,0 +1,85 @@
+# 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.
+
+"""Code shared by the various language-specific code generators."""
+
+from functools import partial
+import os.path
+import re
+
+import module as mojom
+import pack
+
+def GetStructFromMethod(method):
+  """Converts a method's parameters into the fields of a struct."""
+  params_class = "%s_%s_Params" % (method.interface.name, method.name)
+  struct = mojom.Struct(params_class, module=method.interface.module)
+  for param in method.parameters:
+    struct.AddField(param.name, param.kind, param.ordinal)
+  struct.packed = pack.PackedStruct(struct)
+  return struct
+
+def GetResponseStructFromMethod(method):
+  """Converts a method's response_parameters into the fields of a struct."""
+  params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
+  struct = mojom.Struct(params_class, module=method.interface.module)
+  for param in method.response_parameters:
+    struct.AddField(param.name, param.kind, param.ordinal)
+  struct.packed = pack.PackedStruct(struct)
+  return struct
+
+def GetDataHeader(exported, struct):
+  struct.packed = pack.PackedStruct(struct)
+  struct.bytes = pack.GetByteLayout(struct.packed)
+  struct.exported = exported
+  return struct
+
+def ExpectedArraySize(kind):
+  if mojom.IsFixedArrayKind(kind):
+    return kind.length
+  return 0
+
+def StudlyCapsToCamel(studly):
+  return studly[0].lower() + studly[1:]
+
+def CamelCaseToAllCaps(camel_case):
+  return '_'.join(
+      word for word in re.split(r'([A-Z][^A-Z]+)', camel_case) if word).upper()
+
+class Generator(object):
+  # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
+  # files to stdout.
+  def __init__(self, module, output_dir=None):
+    self.module = module
+    self.output_dir = output_dir
+
+  def GetStructsFromMethods(self):
+    result = []
+    for interface in self.module.interfaces:
+      for method in interface.methods:
+        result.append(GetStructFromMethod(method))
+        if method.response_parameters != None:
+          result.append(GetResponseStructFromMethod(method))
+    return map(partial(GetDataHeader, False), result)
+
+  def GetStructs(self):
+    return map(partial(GetDataHeader, True), self.module.structs)
+
+  def Write(self, contents, filename):
+    if self.output_dir is None:
+      print contents
+      return
+    with open(os.path.join(self.output_dir, filename), "w+") as f:
+      f.write(contents)
+
+  def GenerateFiles(self, args):
+    raise NotImplementedError("Subclasses must override/implement this method")
+
+  def GetJinjaParameters(self):
+    """Returns default constructor parameters for the jinja environment."""
+    return {}
+
+  def GetGlobals(self):
+    """Returns global mappings for the template generation."""
+    return {}
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
new file mode 100644
index 0000000..7ae7a83
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -0,0 +1,448 @@
+# 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.
+
+# This module's classes provide an interface to mojo modules. Modules are
+# collections of interfaces and structs to be used by mojo ipc clients and
+# servers.
+#
+# A simple interface would be created this way:
+# module = mojom.generate.module.Module('Foo')
+# interface = module.AddInterface('Bar')
+# method = interface.AddMethod('Tat', 0)
+# method.AddParameter('baz', 0, mojom.INT32)
+
+
+class Kind(object):
+  def __init__(self, spec=None):
+    self.spec = spec
+    self.parent_kind = None
+
+
+class ReferenceKind(Kind):
+  """ReferenceKind represents pointer types and handle types.
+  A type is nullable if null (for pointer types) or invalid handle (for handle
+  types) is a legal value for the type.
+  """
+
+  def __init__(self, spec=None, is_nullable=False):
+    assert spec is None or is_nullable == spec.startswith('?')
+    Kind.__init__(self, spec)
+    self.is_nullable = is_nullable
+    self.shared_definition = {}
+
+  def MakeNullableKind(self):
+    assert not self.is_nullable
+
+    if self == STRING:
+      return NULLABLE_STRING
+    if self == HANDLE:
+      return NULLABLE_HANDLE
+    if self == DCPIPE:
+      return NULLABLE_DCPIPE
+    if self == DPPIPE:
+      return NULLABLE_DPPIPE
+    if self == MSGPIPE:
+      return NULLABLE_MSGPIPE
+    if self == SHAREDBUFFER:
+      return NULLABLE_SHAREDBUFFER
+
+    nullable_kind = type(self)()
+    nullable_kind.shared_definition = self.shared_definition
+    if self.spec is not None:
+      nullable_kind.spec = '?' + self.spec
+    nullable_kind.is_nullable = True
+
+    return nullable_kind
+
+  @classmethod
+  def AddSharedProperty(cls, name):
+    """Adds a property |name| to |cls|, which accesses the corresponding item in
+       |shared_definition|.
+
+       The reason of adding such indirection is to enable sharing definition
+       between a reference kind and its nullable variation. For example:
+         a = Struct('test_struct_1')
+         b = a.MakeNullableKind()
+         a.name = 'test_struct_2'
+         print b.name  # Outputs 'test_struct_2'.
+    """
+    def Get(self):
+      return self.shared_definition[name]
+
+    def Set(self, value):
+      self.shared_definition[name] = value
+
+    setattr(cls, name, property(Get, Set))
+
+
+# Initialize the set of primitive types. These can be accessed by clients.
+BOOL                  = Kind('b')
+INT8                  = Kind('i8')
+INT16                 = Kind('i16')
+INT32                 = Kind('i32')
+INT64                 = Kind('i64')
+UINT8                 = Kind('u8')
+UINT16                = Kind('u16')
+UINT32                = Kind('u32')
+UINT64                = Kind('u64')
+FLOAT                 = Kind('f')
+DOUBLE                = Kind('d')
+STRING                = ReferenceKind('s')
+HANDLE                = ReferenceKind('h')
+DCPIPE                = ReferenceKind('h:d:c')
+DPPIPE                = ReferenceKind('h:d:p')
+MSGPIPE               = ReferenceKind('h:m')
+SHAREDBUFFER          = ReferenceKind('h:s')
+NULLABLE_STRING       = ReferenceKind('?s', True)
+NULLABLE_HANDLE       = ReferenceKind('?h', True)
+NULLABLE_DCPIPE       = ReferenceKind('?h:d:c', True)
+NULLABLE_DPPIPE       = ReferenceKind('?h:d:p', True)
+NULLABLE_MSGPIPE      = ReferenceKind('?h:m', True)
+NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True)
+
+
+# Collection of all Primitive types
+PRIMITIVES = (
+  BOOL,
+  INT8,
+  INT16,
+  INT32,
+  INT64,
+  UINT8,
+  UINT16,
+  UINT32,
+  UINT64,
+  FLOAT,
+  DOUBLE,
+  STRING,
+  HANDLE,
+  DCPIPE,
+  DPPIPE,
+  MSGPIPE,
+  SHAREDBUFFER,
+  NULLABLE_STRING,
+  NULLABLE_HANDLE,
+  NULLABLE_DCPIPE,
+  NULLABLE_DPPIPE,
+  NULLABLE_MSGPIPE,
+  NULLABLE_SHAREDBUFFER
+)
+
+
+class NamedValue(object):
+  def __init__(self, module, parent_kind, name):
+    self.module = module
+    self.namespace = module.namespace
+    self.parent_kind = parent_kind
+    self.name = name
+    self.imported_from = None
+
+  def GetSpec(self):
+    return (self.namespace + '.' +
+        (self.parent_kind and (self.parent_kind.name + '.') or "") +
+        self.name)
+
+
+class BuiltinValue(object):
+  def __init__(self, value):
+    self.value = value
+
+
+class ConstantValue(NamedValue):
+  def __init__(self, module, parent_kind, constant):
+    NamedValue.__init__(self, module, parent_kind, constant.name)
+    self.constant = constant
+
+
+class EnumValue(NamedValue):
+  def __init__(self, module, enum, field):
+    NamedValue.__init__(self, module, enum.parent_kind, field.name)
+    self.enum = enum
+
+  def GetSpec(self):
+    return (self.namespace + '.' +
+        (self.parent_kind and (self.parent_kind.name + '.') or "") +
+        self.enum.name + '.' + self.name)
+
+
+class Constant(object):
+  def __init__(self, name=None, kind=None, value=None):
+    self.name = name
+    self.kind = kind
+    self.value = value
+
+
+class Field(object):
+  def __init__(self, name=None, kind=None, ordinal=None, default=None):
+    self.name = name
+    self.kind = kind
+    self.ordinal = ordinal
+    self.default = default
+
+
+class Struct(ReferenceKind):
+  ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('module')
+  ReferenceKind.AddSharedProperty('imported_from')
+  ReferenceKind.AddSharedProperty('fields')
+
+  def __init__(self, name=None, module=None):
+    if name is not None:
+      spec = 'x:' + name
+    else:
+      spec = None
+    ReferenceKind.__init__(self, spec)
+    self.name = name
+    self.module = module
+    self.imported_from = None
+    self.fields = []
+
+  def AddField(self, name, kind, ordinal=None, default=None):
+    field = Field(name, kind, ordinal, default)
+    self.fields.append(field)
+    return field
+
+
+class Array(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+
+  def __init__(self, kind=None):
+    if kind is not None:
+      ReferenceKind.__init__(self, 'a:' + kind.spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+
+
+class FixedArray(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+  ReferenceKind.AddSharedProperty('length')
+
+  def __init__(self, length=-1, kind=None):
+    if kind is not None:
+      ReferenceKind.__init__(self, 'a%d:%s' % (length, kind.spec))
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+    self.length = length
+
+
+class InterfaceRequest(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+
+  def __init__(self, kind=None):
+    if kind is not None:
+      ReferenceKind.__init__(self, 'r:' + kind.spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+
+
+class Parameter(object):
+  def __init__(self, name=None, kind=None, ordinal=None, default=None):
+    self.name = name
+    self.ordinal = ordinal
+    self.kind = kind
+    self.default = default
+
+
+class Method(object):
+  def __init__(self, interface, name, ordinal=None):
+    self.interface = interface
+    self.name = name
+    self.ordinal = ordinal
+    self.parameters = []
+    self.response_parameters = None
+
+  def AddParameter(self, name, kind, ordinal=None, default=None):
+    parameter = Parameter(name, kind, ordinal, default)
+    self.parameters.append(parameter)
+    return parameter
+
+  def AddResponseParameter(self, name, kind, ordinal=None, default=None):
+    if self.response_parameters == None:
+      self.response_parameters = []
+    parameter = Parameter(name, kind, ordinal, default)
+    self.response_parameters.append(parameter)
+    return parameter
+
+
+class Interface(ReferenceKind):
+  ReferenceKind.AddSharedProperty('module')
+  ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('imported_from')
+  ReferenceKind.AddSharedProperty('client')
+  ReferenceKind.AddSharedProperty('methods')
+
+  def __init__(self, name=None, client=None, module=None):
+    if name is not None:
+      spec = 'x:' + name
+    else:
+      spec = None
+    ReferenceKind.__init__(self, spec)
+    self.module = module
+    self.name = name
+    self.imported_from = None
+    self.client = client
+    self.methods = []
+
+  def AddMethod(self, name, ordinal=None):
+    method = Method(self, name, ordinal=ordinal)
+    self.methods.append(method)
+    return method
+
+
+class EnumField(object):
+  def __init__(self, name=None, value=None):
+    self.name = name
+    self.value = value
+
+
+class Enum(Kind):
+  def __init__(self, name=None, module=None):
+    self.module = module
+    self.name = name
+    self.imported_from = None
+    if name is not None:
+      spec = 'x:' + name
+    else:
+      spec = None
+    Kind.__init__(self, spec)
+    self.fields = []
+
+
+class Module(object):
+  def __init__(self, name=None, namespace=None):
+    self.name = name
+    self.path = name
+    self.namespace = namespace
+    self.structs = []
+    self.interfaces = []
+
+  def AddInterface(self, name):
+    self.interfaces.append(Interface(name, module=self))
+    return interface
+
+  def AddStruct(self, name):
+    struct=Struct(name, module=self)
+    self.structs.append(struct)
+    return struct
+
+
+def IsBoolKind(kind):
+  return kind.spec == BOOL.spec
+
+
+def IsFloatKind(kind):
+  return kind.spec == FLOAT.spec
+
+
+def IsStringKind(kind):
+  return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
+
+
+def IsHandleKind(kind):
+  return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec
+
+
+def IsDataPipeConsumerKind(kind):
+  return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec
+
+
+def IsDataPipeProducerKind(kind):
+  return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec
+
+
+def IsMessagePipeKind(kind):
+  return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec
+
+
+def IsSharedBufferKind(kind):
+  return (kind.spec == SHAREDBUFFER.spec or
+          kind.spec == NULLABLE_SHAREDBUFFER.spec)
+
+
+def IsStructKind(kind):
+  return isinstance(kind, Struct)
+
+
+def IsArrayKind(kind):
+  return isinstance(kind, Array)
+
+
+def IsFixedArrayKind(kind):
+  return isinstance(kind, FixedArray)
+
+
+def IsInterfaceKind(kind):
+  return isinstance(kind, Interface)
+
+
+def IsInterfaceRequestKind(kind):
+  return isinstance(kind, InterfaceRequest)
+
+
+def IsEnumKind(kind):
+  return isinstance(kind, Enum)
+
+
+def IsReferenceKind(kind):
+  return isinstance(kind, ReferenceKind)
+
+
+def IsNullableKind(kind):
+  return IsReferenceKind(kind) and kind.is_nullable
+
+
+def IsAnyArrayKind(kind):
+  return IsArrayKind(kind) or IsFixedArrayKind(kind)
+
+
+def IsObjectKind(kind):
+  return IsStructKind(kind) or IsAnyArrayKind(kind) or IsStringKind(kind)
+
+
+def IsNonInterfaceHandleKind(kind):
+  return (IsHandleKind(kind) or
+          IsDataPipeConsumerKind(kind) or
+          IsDataPipeProducerKind(kind) or
+          IsMessagePipeKind(kind) or
+          IsSharedBufferKind(kind))
+
+
+def IsAnyHandleKind(kind):
+  return (IsNonInterfaceHandleKind(kind) or
+          IsInterfaceKind(kind) or
+          IsInterfaceRequestKind(kind))
+
+
+def IsMoveOnlyKind(kind):
+  return IsObjectKind(kind) or IsAnyHandleKind(kind)
+
+
+def IsCloneableKind(kind):
+  def ContainsHandles(kind, visited_kinds):
+    if kind in visited_kinds:
+      # No need to examine the kind again.
+      return False
+    visited_kinds.add(kind)
+    if IsAnyHandleKind(kind):
+      return True
+    if IsAnyArrayKind(kind):
+      return ContainsHandles(kind.kind, visited_kinds)
+    if IsStructKind(kind):
+      for field in kind.fields:
+        if ContainsHandles(field.kind, visited_kinds):
+          return True
+    return False
+
+  return not ContainsHandles(kind, set())
+
+
+def HasCallbacks(interface):
+  for method in interface.methods:
+    if method.response_parameters != None:
+      return True
+  return False
+
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
new file mode 100644
index 0000000..a887686
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
@@ -0,0 +1,34 @@
+# 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.
+
+import sys
+
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+ModulesAreEqual = test_support.ModulesAreEqual
+BuildTestModule = test_support.BuildTestModule
+TestTestModule = test_support.TestTestModule
+
+
+def BuildAndTestModule():
+  return TestTestModule(BuildTestModule())
+
+
+def TestModulesEqual():
+  return EXPECT_TRUE(ModulesAreEqual(BuildTestModule(), BuildTestModule()))
+
+
+def Main(args):
+  errors = 0
+  errors += RunTest(BuildAndTestModule)
+  errors += RunTest(TestModulesEqual)
+
+  return errors
+
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
new file mode 100644
index 0000000..4661941
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -0,0 +1,163 @@
+# 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.
+
+import module as mojom
+
+# This module provides a mechanism for determining the packed order and offsets
+# of a mojom.Struct.
+#
+# ps = pack.PackedStruct(struct)
+# ps.packed_fields will access a list of PackedField objects, each of which
+# will have an offset, a size and a bit (for mojom.BOOLs).
+
+class PackedField(object):
+  kind_to_size = {
+    mojom.BOOL:                  1,
+    mojom.INT8:                  1,
+    mojom.UINT8:                 1,
+    mojom.INT16:                 2,
+    mojom.UINT16:                2,
+    mojom.INT32:                 4,
+    mojom.UINT32:                4,
+    mojom.FLOAT:                 4,
+    mojom.HANDLE:                4,
+    mojom.MSGPIPE:               4,
+    mojom.SHAREDBUFFER:          4,
+    mojom.DCPIPE:                4,
+    mojom.DPPIPE:                4,
+    mojom.NULLABLE_HANDLE:       4,
+    mojom.NULLABLE_MSGPIPE:      4,
+    mojom.NULLABLE_SHAREDBUFFER: 4,
+    mojom.NULLABLE_DCPIPE:       4,
+    mojom.NULLABLE_DPPIPE:       4,
+    mojom.INT64:                 8,
+    mojom.UINT64:                8,
+    mojom.DOUBLE:                8,
+    mojom.STRING:                8,
+    mojom.NULLABLE_STRING:       8
+  }
+
+  @classmethod
+  def GetSizeForKind(cls, kind):
+    if isinstance(kind, (mojom.Array, mojom.Struct, mojom.FixedArray)):
+      return 8
+    if isinstance(kind, mojom.Interface) or \
+       isinstance(kind, mojom.InterfaceRequest):
+      kind = mojom.MSGPIPE
+    if isinstance(kind, mojom.Enum):
+      # TODO(mpcomplete): what about big enums?
+      return cls.kind_to_size[mojom.INT32]
+    if not kind in cls.kind_to_size:
+      raise Exception("Invalid kind: %s" % kind.spec)
+    return cls.kind_to_size[kind]
+
+  def __init__(self, field, index, ordinal):
+    """
+    Args:
+      field: the original field.
+      index: the position of the original field in the struct.
+      ordinal: the ordinal of the field for serialization.
+    """
+    self.field = field
+    self.index = index
+    self.ordinal = ordinal
+    self.size = self.GetSizeForKind(field.kind)
+    self.offset = None
+    self.bit = None
+
+
+# Returns the pad necessary to reserve space for alignment of |size|.
+def GetPad(offset, size):
+  return (size - (offset % size)) % size
+
+
+# Returns a 2-tuple of the field offset and bit (for BOOLs)
+def GetFieldOffset(field, last_field):
+  if field.field.kind == mojom.BOOL and \
+      last_field.field.kind == mojom.BOOL and \
+      last_field.bit < 7:
+    return (last_field.offset, last_field.bit + 1)
+
+  offset = last_field.offset + last_field.size
+  pad = GetPad(offset, field.size)
+  return (offset + pad, 0)
+
+
+class PackedStruct(object):
+  def __init__(self, struct):
+    self.struct = struct
+    self.packed_fields = []
+
+    # No fields.
+    if (len(struct.fields) == 0):
+      return
+
+    # Start by sorting by ordinal.
+    src_fields = []
+    ordinal = 0
+    for index, field in enumerate(struct.fields):
+      if field.ordinal is not None:
+        ordinal = field.ordinal
+      src_fields.append(PackedField(field, index, ordinal))
+      ordinal += 1
+    src_fields.sort(key=lambda field: field.ordinal)
+
+    src_field = src_fields[0]
+    src_field.offset = 0
+    src_field.bit = 0
+    # dst_fields will contain each of the fields, in increasing offset order.
+    dst_fields = self.packed_fields
+    dst_fields.append(src_field)
+
+    # Then find first slot that each field will fit.
+    for src_field in src_fields[1:]:
+      last_field = dst_fields[0]
+      for i in xrange(1, len(dst_fields)):
+        next_field = dst_fields[i]
+        offset, bit = GetFieldOffset(src_field, last_field)
+        if offset + src_field.size <= next_field.offset:
+          # Found hole.
+          src_field.offset = offset
+          src_field.bit = bit
+          dst_fields.insert(i, src_field)
+          break
+        last_field = next_field
+      if src_field.offset is None:
+        # Add to end
+        src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
+        dst_fields.append(src_field)
+
+  def GetTotalSize(self):
+    if not self.packed_fields:
+      return 0
+    last_field = self.packed_fields[-1]
+    offset = last_field.offset + last_field.size
+    pad = GetPad(offset, 8)
+    return offset + pad
+
+
+class ByteInfo(object):
+  def __init__(self):
+    self.is_padding = False
+    self.packed_fields = []
+
+
+def GetByteLayout(packed_struct):
+  bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
+
+  limit_of_previous_field = 0
+  for packed_field in packed_struct.packed_fields:
+    for i in xrange(limit_of_previous_field, packed_field.offset):
+      bytes[i].is_padding = True
+    bytes[packed_field.offset].packed_fields.append(packed_field)
+    limit_of_previous_field = packed_field.offset + packed_field.size
+
+  for i in xrange(limit_of_previous_field, len(bytes)):
+    bytes[i].is_padding = True
+
+  for byte in bytes:
+    # A given byte cannot both be padding and have a fields packed into it.
+    assert not (byte.is_padding and byte.packed_fields)
+
+  return bytes
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
new file mode 100644
index 0000000..a0f664b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
@@ -0,0 +1,193 @@
+# 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.
+
+import sys
+
+import module as mojom
+import pack
+import test_support
+
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def TestOrdinalOrder():
+  errors = 0
+  struct = mojom.Struct('test')
+  struct.AddField('testfield1', mojom.INT32, 2)
+  struct.AddField('testfield2', mojom.INT32, 1)
+  ps = pack.PackedStruct(struct)
+
+  errors += EXPECT_EQ(2, len(ps.packed_fields))
+  errors += EXPECT_EQ('testfield2', ps.packed_fields[0].field.name)
+  errors += EXPECT_EQ('testfield1', ps.packed_fields[1].field.name)
+
+  return errors
+
+def TestZeroFields():
+  errors = 0
+  struct = mojom.Struct('test')
+  ps = pack.PackedStruct(struct)
+  errors += EXPECT_EQ(0, len(ps.packed_fields))
+  return errors
+
+
+def TestOneField():
+  errors = 0
+  struct = mojom.Struct('test')
+  struct.AddField('testfield1', mojom.INT8)
+  ps = pack.PackedStruct(struct)
+  errors += EXPECT_EQ(1, len(ps.packed_fields))
+  return errors
+
+# Pass three tuples.
+# |kinds| is a sequence of mojom.Kinds that specify the fields that are to
+# be created.
+# |fields| is the expected order of the resulting fields, with the integer
+# "1" first.
+# |offsets| is the expected order of offsets, with the integer "0" first.
+def TestSequence(kinds, fields, offsets):
+  errors = 0
+  struct = mojom.Struct('test')
+  index = 1
+  for kind in kinds:
+    struct.AddField("%d" % index, kind)
+    index += 1
+  ps = pack.PackedStruct(struct)
+  num_fields = len(ps.packed_fields)
+  errors += EXPECT_EQ(len(kinds), num_fields)
+  for i in xrange(num_fields):
+    EXPECT_EQ("%d" % fields[i], ps.packed_fields[i].field.name)
+    EXPECT_EQ(offsets[i], ps.packed_fields[i].offset)
+
+  return errors
+
+
+def TestPaddingPackedInOrder():
+  return TestSequence(
+      (mojom.INT8, mojom.UINT8, mojom.INT32),
+      (1, 2, 3),
+      (0, 1, 4))
+
+
+def TestPaddingPackedOutOfOrder():
+  return TestSequence(
+      (mojom.INT8, mojom.INT32, mojom.UINT8),
+      (1, 3, 2),
+      (0, 1, 4))
+
+
+def TestPaddingPackedOverflow():
+  kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8)
+  # 2 bytes should be packed together first, followed by short, then by int.
+  fields = (1, 4, 3, 2, 5)
+  offsets = (0, 1, 2, 4, 8)
+  return TestSequence(kinds, fields, offsets)
+
+
+def TestNullableTypes():
+  kinds = (mojom.STRING.MakeNullableKind(),
+           mojom.HANDLE.MakeNullableKind(),
+           mojom.Struct('test_struct').MakeNullableKind(),
+           mojom.DCPIPE.MakeNullableKind(),
+           mojom.Array().MakeNullableKind(),
+           mojom.DPPIPE.MakeNullableKind(),
+           mojom.FixedArray(5).MakeNullableKind(),
+           mojom.MSGPIPE.MakeNullableKind(),
+           mojom.Interface('test_inteface').MakeNullableKind(),
+           mojom.SHAREDBUFFER.MakeNullableKind(),
+           mojom.InterfaceRequest().MakeNullableKind())
+  fields = (1, 2, 4, 3, 5, 6, 8, 7, 9, 10, 11)
+  offsets = (0, 8, 12, 16, 24, 32, 36, 40, 48, 52, 56)
+  return TestSequence(kinds, fields, offsets)
+
+
+def TestAllTypes():
+  return TestSequence(
+      (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8,
+       mojom.INT16, mojom.DOUBLE, mojom.UINT16,
+       mojom.INT32, mojom.UINT32, mojom.INT64,
+       mojom.FLOAT, mojom.STRING, mojom.HANDLE,
+       mojom.UINT64, mojom.Struct('test'), mojom.Array(),
+       mojom.STRING.MakeNullableKind()),
+      (1, 2, 4, 5, 7, 3, 6,  8,  9,  10, 11, 13, 12, 14, 15, 16, 17, 18),
+      (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80, 88))
+
+
+def TestPaddingPackedOutOfOrderByOrdinal():
+  errors = 0
+  struct = mojom.Struct('test')
+  struct.AddField('testfield1', mojom.INT8)
+  struct.AddField('testfield3', mojom.UINT8, 3)
+  struct.AddField('testfield2', mojom.INT32, 2)
+  ps = pack.PackedStruct(struct)
+  errors += EXPECT_EQ(3, len(ps.packed_fields))
+
+  # Second byte should be packed in behind first, altering order.
+  errors += EXPECT_EQ('testfield1', ps.packed_fields[0].field.name)
+  errors += EXPECT_EQ('testfield3', ps.packed_fields[1].field.name)
+  errors += EXPECT_EQ('testfield2', ps.packed_fields[2].field.name)
+
+  # Second byte should be packed with first.
+  errors += EXPECT_EQ(0, ps.packed_fields[0].offset)
+  errors += EXPECT_EQ(1, ps.packed_fields[1].offset)
+  errors += EXPECT_EQ(4, ps.packed_fields[2].offset)
+
+  return errors
+
+
+def TestBools():
+  errors = 0
+  struct = mojom.Struct('test')
+  struct.AddField('bit0', mojom.BOOL)
+  struct.AddField('bit1', mojom.BOOL)
+  struct.AddField('int', mojom.INT32)
+  struct.AddField('bit2', mojom.BOOL)
+  struct.AddField('bit3', mojom.BOOL)
+  struct.AddField('bit4', mojom.BOOL)
+  struct.AddField('bit5', mojom.BOOL)
+  struct.AddField('bit6', mojom.BOOL)
+  struct.AddField('bit7', mojom.BOOL)
+  struct.AddField('bit8', mojom.BOOL)
+  ps = pack.PackedStruct(struct)
+  errors += EXPECT_EQ(10, len(ps.packed_fields))
+
+  # First 8 bits packed together.
+  for i in xrange(8):
+    pf = ps.packed_fields[i]
+    errors += EXPECT_EQ(0, pf.offset)
+    errors += EXPECT_EQ("bit%d" % i, pf.field.name)
+    errors += EXPECT_EQ(i, pf.bit)
+
+  # Ninth bit goes into second byte.
+  errors += EXPECT_EQ("bit8", ps.packed_fields[8].field.name)
+  errors += EXPECT_EQ(1, ps.packed_fields[8].offset)
+  errors += EXPECT_EQ(0, ps.packed_fields[8].bit)
+
+  # int comes last.
+  errors += EXPECT_EQ("int", ps.packed_fields[9].field.name)
+  errors += EXPECT_EQ(4, ps.packed_fields[9].offset)
+
+  return errors
+
+
+def Main(args):
+  errors = 0
+  errors += RunTest(TestZeroFields)
+  errors += RunTest(TestOneField)
+  errors += RunTest(TestPaddingPackedInOrder)
+  errors += RunTest(TestPaddingPackedOutOfOrder)
+  errors += RunTest(TestPaddingPackedOverflow)
+  errors += RunTest(TestNullableTypes)
+  errors += RunTest(TestAllTypes)
+  errors += RunTest(TestPaddingPackedOutOfOrderByOrdinal)
+  errors += RunTest(TestBools)
+
+  return errors
+
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
new file mode 100755
index 0000000..41f11a2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# 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.
+
+""" Test runner for Mojom """
+
+import subprocess
+import sys
+
+def TestMojom(testname, args):
+  print '\nRunning unit tests for %s.' % testname
+  try:
+    args = [sys.executable, testname] + args
+    subprocess.check_call(args, stdout=sys.stdout)
+    print 'Succeeded'
+    return 0
+  except subprocess.CalledProcessError as err:
+    print 'Failed with %s.' % str(err)
+    return 1
+
+
+def main(args):
+  errors = 0
+  errors += TestMojom('data_tests.py', ['--test'])
+  errors += TestMojom('module_tests.py', ['--test'])
+  errors += TestMojom('pack_tests.py', ['--test'])
+
+  if errors:
+    print '\nFailed tests.'
+  return min(errors, 127)  # Make sure the return value doesn't "wrap".
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
new file mode 100644
index 0000000..3801d43
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -0,0 +1,59 @@
+# 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.
+
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
+
+import imp
+import inspect
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("jinja2")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+import jinja2
+
+
+def ApplyTemplate(mojo_generator, base_dir, path_to_template, params,
+                  filters=None, **kwargs):
+  template_directory, template_name = os.path.split(path_to_template)
+  path_to_templates = os.path.join(base_dir, template_directory)
+  loader = jinja2.FileSystemLoader([path_to_templates])
+  final_kwargs = dict(mojo_generator.GetJinjaParameters())
+  final_kwargs.update(kwargs)
+  jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True,
+                                 **final_kwargs)
+  jinja_env.globals.update(mojo_generator.GetGlobals())
+  if filters:
+    jinja_env.filters.update(filters)
+  template = jinja_env.get_template(template_name)
+  return template.render(params)
+
+
+def UseJinja(path_to_template, **kwargs):
+  # Get the directory of our caller's file.
+  base_dir = os.path.dirname(inspect.getfile(sys._getframe(1)))
+  def RealDecorator(generator):
+    def GeneratorInternal(*args, **kwargs2):
+      parameters = generator(*args, **kwargs2)
+      return ApplyTemplate(args[0], base_dir, path_to_template, parameters,
+                           **kwargs)
+    GeneratorInternal.func_name = generator.func_name
+    return GeneratorInternal
+  return RealDecorator
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
new file mode 100644
index 0000000..eb39461
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
@@ -0,0 +1,193 @@
+# 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.
+
+import sys
+import traceback
+
+import module as mojom
+
+# Support for writing mojom test cases.
+# RunTest(fn) will execute fn, catching any exceptions. fn should return
+# the number of errors that are encountered.
+#
+# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
+# expectations are not true and return a non zero value. This allows test cases
+# to be written like this
+#
+# def Foo():
+#   errors = 0
+#   errors += EXPECT_EQ('test', test())
+#   ...
+#   return errors
+#
+# RunTest(foo)
+
+def FieldsAreEqual(field1, field2):
+  if field1 == field2:
+    return True
+  return field1.name == field2.name and \
+      KindsAreEqual(field1.kind, field2.kind) and \
+      field1.ordinal == field2.ordinal and \
+      field1.default == field2.default
+
+
+def KindsAreEqual(kind1, kind2):
+  if kind1 == kind2:
+    return True
+  if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
+    return False
+  if kind1.__class__ == mojom.Kind:
+    return kind1.spec == kind2.spec
+  if kind1.__class__ == mojom.Struct:
+    if kind1.name != kind2.name or \
+        kind1.spec != kind2.spec or \
+        len(kind1.fields) != len(kind2.fields):
+      return False
+    for i in range(len(kind1.fields)):
+      if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
+        return False
+    return True
+  if kind1.__class__ == mojom.Array:
+    return KindsAreEqual(kind1.kind, kind2.kind)
+  print 'Unknown Kind class: ', kind1.__class__.__name__
+  return False
+
+
+def ParametersAreEqual(parameter1, parameter2):
+  if parameter1 == parameter2:
+    return True
+  return parameter1.name == parameter2.name and \
+     parameter1.ordinal == parameter2.ordinal and \
+     parameter1.default == parameter2.default and \
+     KindsAreEqual(parameter1.kind, parameter2.kind)
+
+
+def MethodsAreEqual(method1, method2):
+  if method1 == method2:
+    return True
+  if method1.name != method2.name or \
+      method1.ordinal != method2.ordinal or \
+      len(method1.parameters) != len(method2.parameters):
+    return False
+  for i in range(len(method1.parameters)):
+    if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
+      return False
+  return True
+
+
+def InterfacesAreEqual(interface1, interface2):
+  if interface1 == interface2:
+    return True
+  if interface1.name != interface2.name or \
+      len(interface1.methods) != len(interface2.methods):
+    return False
+  for i in range(len(interface1.methods)):
+    if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
+      return False
+  return True
+
+
+def ModulesAreEqual(module1, module2):
+  if module1 == module2:
+    return True
+  if module1.name != module2.name or \
+      module1.namespace != module2.namespace or \
+      len(module1.structs) != len(module2.structs) or \
+      len(module1.interfaces) != len(module2.interfaces):
+    return False
+  for i in range(len(module1.structs)):
+    if not KindsAreEqual(module1.structs[i], module2.structs[i]):
+      return False
+  for i in range(len(module1.interfaces)):
+    if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
+      return False
+  return True
+
+
+# Builds and returns a Module suitable for testing/
+def BuildTestModule():
+  module = mojom.Module('test', 'testspace')
+  struct = module.AddStruct('teststruct')
+  struct.AddField('testfield1', mojom.INT32)
+  struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
+
+  interface = module.AddInterface('Server')
+  method = interface.AddMethod('Foo', 42)
+  method.AddParameter('foo', mojom.INT32)
+  method.AddParameter('bar', mojom.Array(struct))
+
+  return module
+
+
+# Tests if |module| is as built by BuildTestModule(). Returns the number of
+# errors
+def TestTestModule(module):
+  errors = 0
+
+  errors += EXPECT_EQ('test', module.name)
+  errors += EXPECT_EQ('testspace', module.namespace)
+  errors += EXPECT_EQ(1, len(module.structs))
+  errors += EXPECT_EQ('teststruct', module.structs[0].name)
+  errors += EXPECT_EQ(2, len(module.structs[0].fields))
+  errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
+  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
+  errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
+  errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
+  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
+
+  errors += EXPECT_EQ(1, len(module.interfaces))
+  errors += EXPECT_EQ('Server', module.interfaces[0].name)
+  errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
+  errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
+  errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
+  errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
+  errors += EXPECT_EQ(mojom.INT32,
+                      module.interfaces[0].methods[0].parameters[0].kind)
+  errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
+  errors += EXPECT_EQ(
+    mojom.Array,
+    module.interfaces[0].methods[0].parameters[1].kind.__class__)
+  errors += EXPECT_EQ(
+    module.structs[0],
+    module.interfaces[0].methods[0].parameters[1].kind.kind)
+  return errors
+
+
+def PrintFailure(string):
+  stack = traceback.extract_stack()
+  frame = stack[len(stack)-3]
+  sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
+  print "Traceback:"
+  for line in traceback.format_list(stack[:len(stack)-2]):
+    sys.stderr.write(line)
+
+
+def EXPECT_EQ(a, b):
+  if a != b:
+    PrintFailure("%s != %s" % (a, b))
+    return 1
+  return 0
+
+
+def EXPECT_TRUE(a):
+  if not a:
+    PrintFailure('Expecting True')
+    return 1
+  return 0
+
+
+def RunTest(fn):
+  sys.stdout.write('Running %s...' % fn.__name__)
+  try:
+    errors = fn()
+  except:
+    traceback.print_exc(sys.stderr)
+    errors = 1
+  if errors == 0:
+    sys.stdout.write('OK\n')
+  elif errors == 1:
+    sys.stdout.write('1 ERROR\n')
+  else:
+    sys.stdout.write('%d ERRORS\n' % errors)
+  return errors
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
new file mode 100644
index 0000000..1612d98
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -0,0 +1,354 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Node classes for the AST for a Mojo IDL file."""
+
+# Note: For convenience of testing, you probably want to define __eq__() methods
+# for all node types; it's okay to be slightly lax (e.g., not compare filename
+# and lineno). You may also define __repr__() to help with analyzing test
+# failures, especially for more complex types.
+
+
+class NodeBase(object):
+  """Base class for nodes in the AST."""
+
+  def __init__(self, filename=None, lineno=None):
+    self.filename = filename
+    self.lineno = lineno
+
+  def __eq__(self, other):
+    return type(self) == type(other)
+
+  # Make != the inverse of ==. (Subclasses shouldn't have to override this.)
+  def __ne__(self, other):
+    return not self == other
+
+
+# TODO(vtl): Some of this is complicated enough that it should be tested.
+class NodeListBase(NodeBase):
+  """Represents a list of other nodes, all having the same type. (This is meant
+  to be subclassed, with subclasses defining _list_item_type to be the class (or
+  classes, in a tuple) of the members of the list.)"""
+
+  def __init__(self, item_or_items=None, **kwargs):
+    super(NodeListBase, self).__init__(**kwargs)
+    self.items = []
+    if item_or_items is None:
+      pass
+    elif isinstance(item_or_items, list):
+      for item in item_or_items:
+        assert isinstance(item, self._list_item_type)
+        self.Append(item)
+    else:
+      assert isinstance(item_or_items, self._list_item_type)
+      self.Append(item_or_items)
+
+  # Support iteration. For everything else, users should just access |items|
+  # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so
+  # |bool(NodeListBase())| is true.)
+  def __iter__(self):
+    return self.items.__iter__()
+
+  def __eq__(self, other):
+    return super(NodeListBase, self).__eq__(other) and \
+           self.items == other.items
+
+  # Implement this so that on failure, we get slightly more sensible output.
+  def __repr__(self):
+    return self.__class__.__name__ + "([" + \
+           ", ".join([repr(elem) for elem in self.items]) + "])"
+
+  def Insert(self, item):
+    """Inserts item at the front of the list."""
+
+    assert isinstance(item, self._list_item_type)
+    self.items.insert(0, item)
+    self._UpdateFilenameAndLineno()
+
+  def Append(self, item):
+    """Appends item to the end of the list."""
+
+    assert isinstance(item, self._list_item_type)
+    self.items.append(item)
+    self._UpdateFilenameAndLineno()
+
+  def _UpdateFilenameAndLineno(self):
+    if self.items:
+      self.filename = self.items[0].filename
+      self.lineno = self.items[0].lineno
+
+
+class Definition(NodeBase):
+  """Represents a definition of anything that has a global name (e.g., enums,
+  enum values, consts, structs, struct fields, interfaces). (This does not
+  include parameter definitions.) This class is meant to be subclassed."""
+
+  def __init__(self, name, **kwargs):
+    assert isinstance(name, str)
+    NodeBase.__init__(self, **kwargs)
+    self.name = name
+
+
+################################################################################
+
+
+class Attribute(NodeBase):
+  """Represents an attribute."""
+
+  def __init__(self, key, value, **kwargs):
+    assert isinstance(key, str)
+    super(Attribute, self).__init__(**kwargs)
+    self.key = key
+    self.value = value
+
+  def __eq__(self, other):
+    return super(Attribute, self).__eq__(other) and \
+           self.key == other.key and \
+           self.value == other.value
+
+
+class AttributeList(NodeListBase):
+  """Represents a list attributes."""
+
+  _list_item_type = Attribute
+
+
+class Const(Definition):
+  """Represents a const definition."""
+
+  def __init__(self, name, typename, value, **kwargs):
+    # The typename is currently passed through as a string.
+    assert isinstance(typename, str)
+    # The value is either a literal (currently passed through as a string) or a
+    # "wrapped identifier".
+    assert isinstance(value, str) or isinstance(value, tuple)
+    super(Const, self).__init__(name, **kwargs)
+    self.typename = typename
+    self.value = value
+
+  def __eq__(self, other):
+    return super(Const, self).__eq__(other) and \
+           self.typename == other.typename and \
+           self.value == other.value
+
+
+class Enum(Definition):
+  """Represents an enum definition."""
+
+  def __init__(self, name, enum_value_list, **kwargs):
+    assert isinstance(enum_value_list, EnumValueList)
+    super(Enum, self).__init__(name, **kwargs)
+    self.enum_value_list = enum_value_list
+
+  def __eq__(self, other):
+    return super(Enum, self).__eq__(other) and \
+           self.enum_value_list == other.enum_value_list
+
+
+class EnumValue(Definition):
+  """Represents a definition of an enum value."""
+
+  def __init__(self, name, value, **kwargs):
+    # The optional value is either an int (which is current a string) or a
+    # "wrapped identifier".
+    assert value is None or isinstance(value, (str, tuple))
+    super(EnumValue, self).__init__(name, **kwargs)
+    self.value = value
+
+  def __eq__(self, other):
+    return super(EnumValue, self).__eq__(other) and \
+           self.value == other.value
+
+
+class EnumValueList(NodeListBase):
+  """Represents a list of enum value definitions (i.e., the "body" of an enum
+  definition)."""
+
+  _list_item_type = EnumValue
+
+
+class Import(NodeBase):
+  """Represents an import statement."""
+
+  def __init__(self, import_filename, **kwargs):
+    assert isinstance(import_filename, str)
+    super(Import, self).__init__(**kwargs)
+    self.import_filename = import_filename
+
+  def __eq__(self, other):
+    return super(Import, self).__eq__(other) and \
+           self.import_filename == other.import_filename
+
+
+class ImportList(NodeListBase):
+  """Represents a list (i.e., sequence) of import statements."""
+
+  _list_item_type = Import
+
+
+class Interface(Definition):
+  """Represents an interface definition."""
+
+  def __init__(self, name, attribute_list, body, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert isinstance(body, InterfaceBody)
+    super(Interface, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.body = body
+
+  def __eq__(self, other):
+    return super(Interface, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
+           self.body == other.body
+
+
+class Method(Definition):
+  """Represents a method definition."""
+
+  def __init__(self, name, ordinal, parameter_list, response_parameter_list,
+               **kwargs):
+    assert ordinal is None or isinstance(ordinal, Ordinal)
+    assert isinstance(parameter_list, ParameterList)
+    assert response_parameter_list is None or \
+           isinstance(response_parameter_list, ParameterList)
+    super(Method, self).__init__(name, **kwargs)
+    self.ordinal = ordinal
+    self.parameter_list = parameter_list
+    self.response_parameter_list = response_parameter_list
+
+  def __eq__(self, other):
+    return super(Method, self).__eq__(other) and \
+           self.ordinal == other.ordinal and \
+           self.parameter_list == other.parameter_list and \
+           self.response_parameter_list == other.response_parameter_list
+
+
+# This needs to be declared after |Method|.
+class InterfaceBody(NodeListBase):
+  """Represents the body of (i.e., list of definitions inside) an interface."""
+
+  _list_item_type = (Const, Enum, Method)
+
+
+class Module(NodeBase):
+  """Represents a module statement."""
+
+  def __init__(self, name, attribute_list, **kwargs):
+    # |name| is either none or a "wrapped identifier".
+    assert name is None or isinstance(name, tuple)
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    super(Module, self).__init__(**kwargs)
+    self.name = name
+    self.attribute_list = attribute_list
+
+  def __eq__(self, other):
+    return super(Module, self).__eq__(other) and \
+           self.name == other.name and \
+           self.attribute_list == other.attribute_list
+
+
+class Mojom(NodeBase):
+  """Represents an entire .mojom file. (This is the root node."""
+
+  def __init__(self, module, import_list, definition_list, **kwargs):
+    assert module is None or isinstance(module, Module)
+    assert isinstance(import_list, ImportList)
+    assert isinstance(definition_list, list)
+    super(Mojom, self).__init__(**kwargs)
+    self.module = module
+    self.import_list = import_list
+    self.definition_list = definition_list
+
+  def __eq__(self, other):
+    return super(Mojom, self).__eq__(other) and \
+           self.module == other.module and \
+           self.import_list == other.import_list and \
+           self.definition_list == other.definition_list
+
+  def __repr__(self):
+    return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module,
+                               self.import_list, self.definition_list)
+
+
+class Ordinal(NodeBase):
+  """Represents an ordinal value labeling, e.g., a struct field."""
+
+  def __init__(self, value, **kwargs):
+    assert isinstance(value, int)
+    super(Ordinal, self).__init__(**kwargs)
+    self.value = value
+
+  def __eq__(self, other):
+    return super(Ordinal, self).__eq__(other) and \
+           self.value == other.value
+
+
+class Parameter(NodeBase):
+  """Represents a method request or response parameter."""
+
+  def __init__(self, name, ordinal, typename, **kwargs):
+    assert isinstance(name, str)
+    assert ordinal is None or isinstance(ordinal, Ordinal)
+    assert isinstance(typename, str)
+    super(Parameter, self).__init__(**kwargs)
+    self.name = name
+    self.ordinal = ordinal
+    self.typename = typename
+
+  def __eq__(self, other):
+    return super(Parameter, self).__eq__(other) and \
+           self.name == other.name and \
+           self.ordinal == other.ordinal and \
+           self.typename == other.typename
+
+
+class ParameterList(NodeListBase):
+  """Represents a list of (method request or response) parameters."""
+
+  _list_item_type = Parameter
+
+
+class Struct(Definition):
+  """Represents a struct definition."""
+
+  def __init__(self, name, attribute_list, body, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert isinstance(body, StructBody)
+    super(Struct, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.body = body
+
+  def __eq__(self, other):
+    return super(Struct, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
+           self.body == other.body
+
+
+class StructField(Definition):
+  """Represents a struct field definition."""
+
+  def __init__(self, name, ordinal, typename, default_value, **kwargs):
+    assert isinstance(name, str)
+    assert ordinal is None or isinstance(ordinal, Ordinal)
+    assert isinstance(typename, str)
+    # The optional default value is currently either a value as a string or a
+    # "wrapped identifier".
+    assert default_value is None or isinstance(default_value, (str, tuple))
+    super(StructField, self).__init__(name, **kwargs)
+    self.ordinal = ordinal
+    self.typename = typename
+    self.default_value = default_value
+
+  def __eq__(self, other):
+    return super(StructField, self).__eq__(other) and \
+           self.ordinal == other.ordinal and \
+           self.typename == other.typename and \
+           self.default_value == other.default_value
+
+
+# This needs to be declared after |StructField|.
+class StructBody(NodeListBase):
+  """Represents the body of (i.e., list of definitions inside) a struct."""
+
+  _list_item_type = (Const, Enum, StructField)
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
new file mode 100644
index 0000000..b13fac3
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
@@ -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.
+
+import imp
+import os.path
+import sys
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("ply")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply.lex import TOKEN
+
+from ..error import Error
+
+
+class LexError(Error):
+  """Class for errors from the lexer."""
+
+  def __init__(self, filename, message, lineno):
+    Error.__init__(self, filename, message, lineno=lineno)
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Lexer(object):
+
+  def __init__(self, filename):
+    self.filename = filename
+
+  ######################--   PRIVATE   --######################
+
+  ##
+  ## Internal auxiliary methods
+  ##
+  def _error(self, msg, token):
+    raise LexError(self.filename, msg, token.lineno)
+
+  ##
+  ## Reserved keywords
+  ##
+  keywords = (
+    'HANDLE',
+
+    'IMPORT',
+    'MODULE',
+    'STRUCT',
+    'INTERFACE',
+    'ENUM',
+    'CONST',
+    'TRUE',
+    'FALSE',
+    'DEFAULT',
+    'ARRAY'
+  )
+
+  keyword_map = {}
+  for keyword in keywords:
+    keyword_map[keyword.lower()] = keyword
+
+  ##
+  ## All the tokens recognized by the lexer
+  ##
+  tokens = keywords + (
+    # Identifiers
+    'NAME',
+
+    # Constants
+    'ORDINAL',
+    'INT_CONST_DEC', 'INT_CONST_HEX',
+    'FLOAT_CONST',
+
+    # String literals
+    'STRING_LITERAL',
+
+    # Operators
+    'MINUS',
+    'PLUS',
+    'AMP',
+    'QSTN',
+
+    # Assignment
+    'EQUALS',
+
+    # Request / response
+    'RESPONSE',
+
+    # Delimiters
+    'LPAREN', 'RPAREN',         # ( )
+    'LBRACKET', 'RBRACKET',     # [ ]
+    'LBRACE', 'RBRACE',         # { }
+    'LANGLE', 'RANGLE',         # < >
+    'SEMI',                     # ;
+    'COMMA', 'DOT'              # , .
+  )
+
+  ##
+  ## Regexes for use in tokens
+  ##
+
+  # valid C identifiers (K&R2: A.2.3)
+  identifier = r'[a-zA-Z_][0-9a-zA-Z_]*'
+
+  hex_prefix = '0[xX]'
+  hex_digits = '[0-9a-fA-F]+'
+
+  # integer constants (K&R2: A.2.5.1)
+  decimal_constant = '0|([1-9][0-9]*)'
+  hex_constant = hex_prefix+hex_digits
+  # Don't allow octal constants (even invalid octal).
+  octal_constant_disallowed = '0[0-9]+'
+
+  # character constants (K&R2: A.2.5.2)
+  # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
+  # directives with Windows paths as filenames (..\..\dir\file)
+  # For the same reason, decimal_escape allows all digit sequences. We want to
+  # parse all correct code, even if it means to sometimes parse incorrect
+  # code.
+  #
+  simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
+  decimal_escape = r"""(\d+)"""
+  hex_escape = r"""(x[0-9a-fA-F]+)"""
+  bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
+
+  escape_sequence = \
+      r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'
+
+  # string literals (K&R2: A.2.6)
+  string_char = r"""([^"\\\n]|"""+escape_sequence+')'
+  string_literal = '"'+string_char+'*"'
+  bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"'
+
+  # floating constants (K&R2: A.2.5.3)
+  exponent_part = r"""([eE][-+]?[0-9]+)"""
+  fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
+  floating_constant = \
+      '(((('+fractional_constant+')'+ \
+      exponent_part+'?)|([0-9]+'+exponent_part+')))'
+
+  # Ordinals
+  ordinal = r'@[0-9]+'
+  missing_ordinal_value = r'@'
+  # Don't allow ordinal values in octal (even invalid octal, like 09) or
+  # hexadecimal.
+  octal_or_hex_ordinal_disallowed = r'@((0[0-9]+)|('+hex_prefix+hex_digits+'))'
+
+  ##
+  ## Rules for the normal state
+  ##
+  t_ignore = ' \t\r'
+
+  # Newlines
+  def t_NEWLINE(self, t):
+    r'\n+'
+    t.lexer.lineno += len(t.value)
+
+  # Operators
+  t_MINUS             = r'-'
+  t_PLUS              = r'\+'
+  t_AMP               = r'&'
+  t_QSTN              = r'\?'
+
+  # =
+  t_EQUALS            = r'='
+
+  # =>
+  t_RESPONSE          = r'=>'
+
+  # Delimiters
+  t_LPAREN            = r'\('
+  t_RPAREN            = r'\)'
+  t_LBRACKET          = r'\['
+  t_RBRACKET          = r'\]'
+  t_LBRACE            = r'\{'
+  t_RBRACE            = r'\}'
+  t_LANGLE            = r'<'
+  t_RANGLE            = r'>'
+  t_COMMA             = r','
+  t_DOT               = r'\.'
+  t_SEMI              = r';'
+
+  t_STRING_LITERAL    = string_literal
+
+  # The following floating and integer constants are defined as
+  # functions to impose a strict order (otherwise, decimal
+  # is placed before the others because its regex is longer,
+  # and this is bad)
+  #
+  @TOKEN(floating_constant)
+  def t_FLOAT_CONST(self, t):
+    return t
+
+  @TOKEN(hex_constant)
+  def t_INT_CONST_HEX(self, t):
+    return t
+
+  @TOKEN(octal_constant_disallowed)
+  def t_OCTAL_CONSTANT_DISALLOWED(self, t):
+    msg = "Octal values not allowed"
+    self._error(msg, t)
+
+  @TOKEN(decimal_constant)
+  def t_INT_CONST_DEC(self, t):
+    return t
+
+  # unmatched string literals are caught by the preprocessor
+
+  @TOKEN(bad_string_literal)
+  def t_BAD_STRING_LITERAL(self, t):
+    msg = "String contains invalid escape code"
+    self._error(msg, t)
+
+  # Handle ordinal-related tokens in the right order:
+  @TOKEN(octal_or_hex_ordinal_disallowed)
+  def t_OCTAL_OR_HEX_ORDINAL_DISALLOWED(self, t):
+    msg = "Octal and hexadecimal ordinal values not allowed"
+    self._error(msg, t)
+
+  @TOKEN(ordinal)
+  def t_ORDINAL(self, t):
+    return t
+
+  @TOKEN(missing_ordinal_value)
+  def t_BAD_ORDINAL(self, t):
+    msg = "Missing ordinal value"
+    self._error(msg, t)
+
+  @TOKEN(identifier)
+  def t_NAME(self, t):
+    t.type = self.keyword_map.get(t.value, "NAME")
+    return t
+
+  # Ignore C and C++ style comments
+  def t_COMMENT(self, t):
+    r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)'
+    t.lexer.lineno += t.value.count("\n")
+
+  def t_error(self, t):
+    msg = "Illegal character %s" % repr(t.value[0])
+    self._error(msg, t)
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
new file mode 100644
index 0000000..551c049
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,383 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates a syntax tree from a Mojo IDL file."""
+
+import imp
+import os.path
+import sys
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("ply")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+from ply import yacc
+
+from ..error import Error
+from . import ast
+from .lexer import Lexer
+
+
+_MAX_ORDINAL_VALUE = 0xffffffff
+_MAX_ARRAY_SIZE = 0xffffffff
+
+
+class ParseError(Error):
+  """Class for errors from the parser."""
+
+  def __init__(self, filename, message, lineno=None, snippet=None):
+    Error.__init__(self, filename, message, lineno=lineno,
+                   addenda=([snippet] if snippet else None))
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Parser(object):
+
+  def __init__(self, lexer, source, filename):
+    self.tokens = lexer.tokens
+    self.source = source
+    self.filename = filename
+
+  # Names of functions
+  #
+  # In general, we name functions after the left-hand-side of the rule(s) that
+  # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|.
+  #
+  # There may be multiple functions handling rules for the same left-hand-side;
+  # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|),
+  # where N is a number (numbered starting from 1). Note that using multiple
+  # functions is actually more efficient than having single functions handle
+  # multiple rules (and, e.g., distinguishing them by examining |len(p)|).
+  #
+  # It's also possible to have a function handling multiple rules with different
+  # left-hand-sides. We do not do this.
+  #
+  # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details.
+
+  # TODO(vtl): Get rid of the braces in the module "statement". (Consider
+  # renaming "module" -> "package".) Then we'll be able to have a single rule
+  # for root (by making module "optional").
+  def p_root_1(self, p):
+    """root : import_list module LBRACE definition_list RBRACE"""
+    p[0] = ast.Mojom(p[2], p[1], p[4])
+
+  def p_root_2(self, p):
+    """root : import_list definition_list"""
+    p[0] = ast.Mojom(None, p[1], p[2])
+
+  def p_import_list_1(self, p):
+    """import_list : """
+    p[0] = ast.ImportList()
+
+  def p_import_list_2(self, p):
+    """import_list : import_list import"""
+    p[0] = p[1]
+    p[0].Append(p[2])
+
+  def p_import(self, p):
+    """import : IMPORT STRING_LITERAL"""
+    # 'eval' the literal to strip the quotes.
+    # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves.
+    p[0] = ast.Import(eval(p[2]))
+
+  def p_module(self, p):
+    """module : attribute_section MODULE identifier_wrapped """
+    p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))
+
+  def p_definition_list(self, p):
+    """definition_list : definition definition_list
+                       | """
+    if len(p) > 1:
+      p[0] = p[2]
+      p[0].insert(0, p[1])
+    else:
+      p[0] = []
+
+  def p_definition(self, p):
+    """definition : struct
+                  | interface
+                  | enum
+                  | const"""
+    p[0] = p[1]
+
+  def p_attribute_section_1(self, p):
+    """attribute_section : """
+    p[0] = None
+
+  def p_attribute_section_2(self, p):
+    """attribute_section : LBRACKET attribute_list RBRACKET"""
+    p[0] = p[2]
+
+  def p_attribute_list_1(self, p):
+    """attribute_list : """
+    p[0] = ast.AttributeList()
+
+  def p_attribute_list_2(self, p):
+    """attribute_list : nonempty_attribute_list"""
+    p[0] = p[1]
+
+  def p_nonempty_attribute_list_1(self, p):
+    """nonempty_attribute_list : attribute"""
+    p[0] = ast.AttributeList(p[1])
+
+  def p_nonempty_attribute_list_2(self, p):
+    """nonempty_attribute_list : nonempty_attribute_list COMMA attribute"""
+    p[0] = p[1]
+    p[0].Append(p[3])
+
+  def p_attribute(self, p):
+    """attribute : NAME EQUALS evaled_literal
+                 | NAME EQUALS NAME"""
+    p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
+
+  def p_evaled_literal(self, p):
+    """evaled_literal : literal"""
+    # 'eval' the literal to strip the quotes.
+    p[0] = eval(p[1])
+
+  def p_struct(self, p):
+    """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
+    p[0] = ast.Struct(p[3], p[1], p[5])
+
+  def p_struct_body_1(self, p):
+    """struct_body : """
+    p[0] = ast.StructBody()
+
+  def p_struct_body_2(self, p):
+    """struct_body : struct_body const
+                   | struct_body enum
+                   | struct_body struct_field"""
+    p[0] = p[1]
+    p[0].Append(p[2])
+
+  def p_struct_field(self, p):
+    """struct_field : typename NAME ordinal default SEMI"""
+    p[0] = ast.StructField(p[2], p[3], p[1], p[4])
+
+  def p_default_1(self, p):
+    """default : """
+    p[0] = None
+
+  def p_default_2(self, p):
+    """default : EQUALS constant"""
+    p[0] = p[2]
+
+  def p_interface(self, p):
+    """interface : attribute_section INTERFACE NAME LBRACE interface_body \
+                       RBRACE SEMI"""
+    p[0] = ast.Interface(p[3], p[1], p[5])
+
+  def p_interface_body_1(self, p):
+    """interface_body : """
+    p[0] = ast.InterfaceBody()
+
+  def p_interface_body_2(self, p):
+    """interface_body : interface_body const
+                      | interface_body enum
+                      | interface_body method"""
+    p[0] = p[1]
+    p[0].Append(p[2])
+
+  def p_response_1(self, p):
+    """response : """
+    p[0] = None
+
+  def p_response_2(self, p):
+    """response : RESPONSE LPAREN parameter_list RPAREN"""
+    p[0] = p[3]
+
+  def p_method(self, p):
+    """method : NAME ordinal LPAREN parameter_list RPAREN response SEMI"""
+    p[0] = ast.Method(p[1], p[2], p[4], p[6])
+
+  def p_parameter_list_1(self, p):
+    """parameter_list : """
+    p[0] = ast.ParameterList()
+
+  def p_parameter_list_2(self, p):
+    """parameter_list : nonempty_parameter_list"""
+    p[0] = p[1]
+
+  def p_nonempty_parameter_list_1(self, p):
+    """nonempty_parameter_list : parameter"""
+    p[0] = ast.ParameterList(p[1])
+
+  def p_nonempty_parameter_list_2(self, p):
+    """nonempty_parameter_list : nonempty_parameter_list COMMA parameter"""
+    p[0] = p[1]
+    p[0].Append(p[3])
+
+  def p_parameter(self, p):
+    """parameter : typename NAME ordinal"""
+    p[0] = ast.Parameter(p[2], p[3], p[1],
+                         filename=self.filename, lineno=p.lineno(2))
+
+  def p_typename(self, p):
+    """typename : nonnullable_typename QSTN
+                | nonnullable_typename"""
+    if len(p) == 2:
+      p[0] = p[1]
+    else:
+      p[0] = p[1] + "?"
+
+  def p_nonnullable_typename(self, p):
+    """nonnullable_typename : basictypename
+                            | array
+                            | fixed_array
+                            | interfacerequest"""
+    p[0] = p[1]
+
+  def p_basictypename(self, p):
+    """basictypename : identifier
+                     | handletype"""
+    p[0] = p[1]
+
+  def p_handletype(self, p):
+    """handletype : HANDLE
+                  | HANDLE LANGLE NAME RANGLE"""
+    if len(p) == 2:
+      p[0] = p[1]
+    else:
+      if p[3] not in ('data_pipe_consumer',
+                      'data_pipe_producer',
+                      'message_pipe',
+                      'shared_buffer'):
+        # Note: We don't enable tracking of line numbers for everything, so we
+        # can't use |p.lineno(3)|.
+        raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
+                         lineno=p.lineno(1),
+                         snippet=self._GetSnippet(p.lineno(1)))
+      p[0] = "handle<" + p[3] + ">"
+
+  def p_array(self, p):
+    """array : ARRAY LANGLE typename RANGLE"""
+    p[0] = p[3] + "[]"
+
+  def p_fixed_array(self, p):
+    """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE"""
+    value = int(p[5])
+    if value == 0 or value > _MAX_ARRAY_SIZE:
+      raise ParseError(self.filename, "Fixed array size %d invalid" % value,
+                       lineno=p.lineno(5),
+                       snippet=self._GetSnippet(p.lineno(5)))
+    p[0] = p[3] + "[" + p[5] + "]"
+
+  def p_interfacerequest(self, p):
+    """interfacerequest : identifier AMP"""
+    p[0] = p[1] + "&"
+
+  def p_ordinal_1(self, p):
+    """ordinal : """
+    p[0] = None
+
+  def p_ordinal_2(self, p):
+    """ordinal : ORDINAL"""
+    value = int(p[1][1:])
+    if value > _MAX_ORDINAL_VALUE:
+      raise ParseError(self.filename, "Ordinal value %d too large:" % value,
+                       lineno=p.lineno(1),
+                       snippet=self._GetSnippet(p.lineno(1)))
+    p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
+
+  def p_enum(self, p):
+    """enum : ENUM NAME LBRACE nonempty_enum_value_list RBRACE SEMI
+            | ENUM NAME LBRACE nonempty_enum_value_list COMMA RBRACE SEMI"""
+    p[0] = ast.Enum(p[2], p[4], filename=self.filename, lineno=p.lineno(1))
+
+  def p_nonempty_enum_value_list_1(self, p):
+    """nonempty_enum_value_list : enum_value"""
+    p[0] = ast.EnumValueList(p[1])
+
+  def p_nonempty_enum_value_list_2(self, p):
+    """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value"""
+    p[0] = p[1]
+    p[0].Append(p[3])
+
+  def p_enum_value(self, p):
+    """enum_value : NAME
+                  | NAME EQUALS int
+                  | NAME EQUALS identifier_wrapped"""
+    p[0] = ast.EnumValue(p[1], p[3] if len(p) == 4 else None,
+                         filename=self.filename, lineno=p.lineno(1))
+
+  def p_const(self, p):
+    """const : CONST typename NAME EQUALS constant SEMI"""
+    p[0] = ast.Const(p[3], p[2], p[5])
+
+  def p_constant(self, p):
+    """constant : literal
+                | identifier_wrapped"""
+    p[0] = p[1]
+
+  def p_identifier_wrapped(self, p):
+    """identifier_wrapped : identifier"""
+    p[0] = ('IDENTIFIER', p[1])
+
+  # TODO(vtl): Make this produce a "wrapped" identifier (probably as an
+  # |ast.Identifier|, to be added) and get rid of identifier_wrapped.
+  def p_identifier(self, p):
+    """identifier : NAME
+                  | NAME DOT identifier"""
+    p[0] = ''.join(p[1:])
+
+  def p_literal(self, p):
+    """literal : int
+               | float
+               | TRUE
+               | FALSE
+               | DEFAULT
+               | STRING_LITERAL"""
+    p[0] = p[1]
+
+  def p_int(self, p):
+    """int : int_const
+           | PLUS int_const
+           | MINUS int_const"""
+    p[0] = ''.join(p[1:])
+
+  def p_int_const(self, p):
+    """int_const : INT_CONST_DEC
+                 | INT_CONST_HEX"""
+    p[0] = p[1]
+
+  def p_float(self, p):
+    """float : FLOAT_CONST
+             | PLUS FLOAT_CONST
+             | MINUS FLOAT_CONST"""
+    p[0] = ''.join(p[1:])
+
+  def p_error(self, e):
+    if e is None:
+      # Unexpected EOF.
+      # TODO(vtl): Can we figure out what's missing?
+      raise ParseError(self.filename, "Unexpected end of file")
+
+    raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
+                     snippet=self._GetSnippet(e.lineno))
+
+  def _GetSnippet(self, lineno):
+    return self.source.split('\n')[lineno - 1]
+
+
+def Parse(source, filename):
+  lexer = Lexer(filename)
+  parser = Parser(lexer, source, filename)
+
+  lex.lex(object=lexer)
+  yacc.yacc(module=parser, debug=0, write_tables=0)
+
+  tree = yacc.parse(source)
+  return tree
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
new file mode 100644
index 0000000..77e92c5
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -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.
+
+"""Translates parse tree to Mojom IR."""
+
+
+import re
+
+from . import ast
+
+
+def _MapTreeForType(func, tree, type_to_map):
+  assert isinstance(type_to_map, type)
+  if not tree:
+    return []
+  return [func(subtree) for subtree in tree if isinstance(subtree, type_to_map)]
+
+_FIXED_ARRAY_REGEXP = re.compile(r'\[[0-9]+\]')
+
+def _MapKind(kind):
+  map_to_kind = {'bool': 'b',
+                 'int8': 'i8',
+                 'int16': 'i16',
+                 'int32': 'i32',
+                 'int64': 'i64',
+                 'uint8': 'u8',
+                 'uint16': 'u16',
+                 'uint32': 'u32',
+                 'uint64': 'u64',
+                 'float': 'f',
+                 'double': 'd',
+                 'string': 's',
+                 'handle': 'h',
+                 'handle<data_pipe_consumer>': 'h:d:c',
+                 'handle<data_pipe_producer>': 'h:d:p',
+                 'handle<message_pipe>': 'h:m',
+                 'handle<shared_buffer>': 'h:s'}
+  if kind.endswith('?'):
+    base_kind = _MapKind(kind[0:-1])
+    # NOTE: This doesn't rule out enum types. Those will be detected later, when
+    # cross-reference is established.
+    reference_kinds = ('s', 'h', 'a', 'r', 'x')
+    if base_kind[0] not in reference_kinds:
+      raise Exception(
+          'A type (spec "%s") cannot be made nullable' % base_kind)
+    return '?' + base_kind
+  if kind.endswith('[]'):
+    typename = kind[0:-2]
+    if _FIXED_ARRAY_REGEXP.search(typename):
+      raise Exception('Arrays of fixed sized arrays not supported')
+    return 'a:' + _MapKind(typename)
+  if kind.endswith(']'):
+    lbracket = kind.rfind('[')
+    typename = kind[0:lbracket]
+    if typename.find('[') != -1:
+      raise Exception('Fixed sized arrays of arrays not supported')
+    return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
+  if kind.endswith('&'):
+    return 'r:' + _MapKind(kind[0:-1])
+  if kind in map_to_kind:
+    return map_to_kind[kind]
+  return 'x:' + kind
+
+def _AttributeListToDict(attribute_list):
+  if attribute_list is None:
+    return {}
+  assert isinstance(attribute_list, ast.AttributeList)
+  # TODO(vtl): Check for duplicate keys here.
+  return dict([(attribute.key, attribute.value)
+                   for attribute in attribute_list])
+
+def _EnumToDict(enum):
+  def EnumValueToDict(enum_value):
+    assert isinstance(enum_value, ast.EnumValue)
+    return {'name': enum_value.name,
+            'value': enum_value.value}
+
+  assert isinstance(enum, ast.Enum)
+  return {'name': enum.name,
+          'fields': map(EnumValueToDict, enum.enum_value_list)}
+
+def _ConstToDict(const):
+  assert isinstance(const, ast.Const)
+  return {'name': const.name,
+          'kind': _MapKind(const.typename),
+          'value': const.value}
+
+
+class _MojomBuilder(object):
+  def __init__(self):
+    self.mojom = {}
+
+  def Build(self, tree, name):
+    def StructToDict(struct):
+      def StructFieldToDict(struct_field):
+        assert isinstance(struct_field, ast.StructField)
+        return {'name': struct_field.name,
+                'kind': _MapKind(struct_field.typename),
+                'ordinal': struct_field.ordinal.value \
+                    if struct_field.ordinal else None,
+                'default': struct_field.default_value}
+
+      assert isinstance(struct, ast.Struct)
+      return {'name': struct.name,
+              'attributes': _AttributeListToDict(struct.attribute_list),
+              'fields': _MapTreeForType(StructFieldToDict, struct.body,
+                                        ast.StructField),
+              'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum),
+              'constants': _MapTreeForType(_ConstToDict, struct.body,
+                                           ast.Const)}
+
+    def InterfaceToDict(interface):
+      def MethodToDict(method):
+        def ParameterToDict(param):
+          assert isinstance(param, ast.Parameter)
+          return {'name': param.name,
+                  'kind': _MapKind(param.typename),
+                  'ordinal': param.ordinal.value if param.ordinal else None}
+
+        assert isinstance(method, ast.Method)
+        rv = {'name': method.name,
+              'parameters': map(ParameterToDict, method.parameter_list),
+              'ordinal': method.ordinal.value if method.ordinal else None}
+        if method.response_parameter_list is not None:
+          rv['response_parameters'] = map(ParameterToDict,
+                                          method.response_parameter_list)
+        return rv
+
+      assert isinstance(interface, ast.Interface)
+      attributes = _AttributeListToDict(interface.attribute_list)
+      return {'name': interface.name,
+              'attributes': attributes,
+              'client': attributes.get('Client'),
+              'methods': _MapTreeForType(MethodToDict, interface.body,
+                                         ast.Method),
+              'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum),
+              'constants': _MapTreeForType(_ConstToDict, interface.body,
+                                           ast.Const)}
+
+    assert isinstance(tree, ast.Mojom)
+    self.mojom['name'] = name
+    self.mojom['namespace'] = tree.module.name[1] if tree.module else ''
+    self.mojom['imports'] = \
+        [{'filename': imp.import_filename} for imp in tree.import_list]
+    self.mojom['attributes'] = \
+        _AttributeListToDict(tree.module.attribute_list) if tree.module else {}
+    self.mojom['structs'] = \
+        _MapTreeForType(StructToDict, tree.definition_list, ast.Struct)
+    self.mojom['interfaces'] = \
+        _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface)
+    self.mojom['enums'] = \
+        _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum)
+    self.mojom['constants'] = \
+        _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const)
+    return self.mojom
+
+
+def Translate(tree, name):
+  return _MojomBuilder().Build(tree, name)
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py
new file mode 100644
index 0000000..dd28cdd
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py
@@ -0,0 +1,135 @@
+# Copyright 2014 The Chromium 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("mojom")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.ast as ast
+
+
+class _TestNode(ast.NodeBase):
+  """Node type for tests."""
+
+  def __init__(self, value, **kwargs):
+    super(_TestNode, self).__init__(**kwargs)
+    self.value = value
+
+  def __eq__(self, other):
+    return super(_TestNode, self).__eq__(other) and self.value == other.value
+
+
+class _TestNodeList(ast.NodeListBase):
+  """Node list type for tests."""
+
+  _list_item_type = _TestNode
+
+
+class ASTTest(unittest.TestCase):
+  """Tests various AST classes."""
+
+  def testNodeBase(self):
+    # Test |__eq__()|; this is only used for testing, where we want to do
+    # comparison by value and ignore filenames/line numbers (for convenience).
+    node1 = ast.NodeBase(filename="hello.mojom", lineno=123)
+    node2 = ast.NodeBase()
+    self.assertEquals(node1, node2)
+    self.assertEquals(node2, node1)
+
+    # Check that |__ne__()| just defers to |__eq__()| properly.
+    self.assertFalse(node1 != node2)
+    self.assertFalse(node2 != node1)
+
+    # Check that |filename| and |lineno| are set properly (and are None by
+    # default).
+    self.assertEquals(node1.filename, "hello.mojom")
+    self.assertEquals(node1.lineno, 123)
+    self.assertIsNone(node2.filename)
+    self.assertIsNone(node2.lineno)
+
+    # |NodeBase|'s |__eq__()| should compare types (and a subclass's |__eq__()|
+    # should first defer to its superclass's).
+    node3 = _TestNode(123)
+    self.assertNotEqual(node1, node3)
+    self.assertNotEqual(node3, node1)
+    # Also test |__eq__()| directly.
+    self.assertFalse(node1 == node3)
+    self.assertFalse(node3 == node1)
+
+    node4 = _TestNode(123, filename="world.mojom", lineno=123)
+    self.assertEquals(node4, node3)
+    node5 = _TestNode(456)
+    self.assertNotEquals(node5, node4)
+
+  def testNodeListBase(self):
+    node1 = _TestNode(1, filename="foo.mojom", lineno=1)
+    # Equal to, but not the same as, |node1|:
+    node1b = _TestNode(1, filename="foo.mojom", lineno=1)
+    node2 = _TestNode(2, filename="foo.mojom", lineno=2)
+
+    nodelist1 = _TestNodeList()  # Contains: (empty).
+    self.assertEquals(nodelist1, nodelist1)
+    self.assertEquals(nodelist1.items, [])
+    self.assertIsNone(nodelist1.filename)
+    self.assertIsNone(nodelist1.lineno)
+
+    nodelist2 = _TestNodeList(node1)  # Contains: 1.
+    self.assertEquals(nodelist2, nodelist2)
+    self.assertEquals(nodelist2.items, [node1])
+    self.assertNotEqual(nodelist2, nodelist1)
+    self.assertEquals(nodelist2.filename, "foo.mojom")
+    self.assertEquals(nodelist2.lineno, 1)
+
+    nodelist3 = _TestNodeList([node2])  # Contains: 2.
+    self.assertEquals(nodelist3.items, [node2])
+    self.assertNotEqual(nodelist3, nodelist1)
+    self.assertNotEqual(nodelist3, nodelist2)
+    self.assertEquals(nodelist3.filename, "foo.mojom")
+    self.assertEquals(nodelist3.lineno, 2)
+
+    nodelist1.Append(node1b)  # Contains: 1.
+    self.assertEquals(nodelist1.items, [node1])
+    self.assertEquals(nodelist1, nodelist2)
+    self.assertNotEqual(nodelist1, nodelist3)
+    self.assertEquals(nodelist1.filename, "foo.mojom")
+    self.assertEquals(nodelist1.lineno, 1)
+
+    nodelist1.Append(node2)  # Contains: 1, 2.
+    self.assertEquals(nodelist1.items, [node1, node2])
+    self.assertNotEqual(nodelist1, nodelist2)
+    self.assertNotEqual(nodelist1, nodelist3)
+    self.assertEquals(nodelist1.lineno, 1)
+
+    nodelist2.Append(node2)  # Contains: 1, 2.
+    self.assertEquals(nodelist2.items, [node1, node2])
+    self.assertEquals(nodelist2, nodelist1)
+    self.assertNotEqual(nodelist2, nodelist3)
+    self.assertEquals(nodelist2.lineno, 1)
+
+    nodelist3.Insert(node1)  # Contains: 1, 2.
+    self.assertEquals(nodelist3.items, [node1, node2])
+    self.assertEquals(nodelist3, nodelist1)
+    self.assertEquals(nodelist3, nodelist2)
+    self.assertEquals(nodelist3.lineno, 1)
+
+    # Test iteration:
+    i = 1
+    for item in nodelist1:
+      self.assertEquals(item.value, i)
+      i += 1
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
new file mode 100644
index 0000000..d24b7e2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
@@ -0,0 +1,186 @@
+# Copyright 2014 The Chromium 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("ply")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+
+try:
+  imp.find_module("mojom")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.lexer
+
+
+# This (monkey-patching LexToken to make comparison value-based) is evil, but
+# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
+# for object identity.)
+def _LexTokenEq(self, other):
+  return self.type == other.type and self.value == other.value and \
+         self.lineno == other.lineno and self.lexpos == other.lexpos
+setattr(lex.LexToken, '__eq__', _LexTokenEq)
+
+
+def _MakeLexToken(token_type, value, lineno=1, lexpos=0):
+  """Makes a LexToken with the given parameters. (Note that lineno is 1-based,
+  but lexpos is 0-based.)"""
+  rv = lex.LexToken()
+  rv.type, rv.value, rv.lineno, rv.lexpos = token_type, value, lineno, lexpos
+  return rv
+
+
+def _MakeLexTokenForKeyword(keyword, **kwargs):
+  """Makes a LexToken for the given keyword."""
+  return _MakeLexToken(keyword.upper(), keyword.lower(), **kwargs)
+
+
+class LexerTest(unittest.TestCase):
+  """Tests |mojom.parse.lexer.Lexer|."""
+
+  def __init__(self, *args, **kwargs):
+    unittest.TestCase.__init__(self, *args, **kwargs)
+    # Clone all lexer instances from this one, since making a lexer is slow.
+    self._zygote_lexer = lex.lex(mojom.parse.lexer.Lexer("my_file.mojom"))
+
+  def testValidKeywords(self):
+    """Tests valid keywords."""
+    self.assertEquals(self._SingleTokenForInput("handle"),
+                      _MakeLexTokenForKeyword("handle"))
+    self.assertEquals(self._SingleTokenForInput("import"),
+                      _MakeLexTokenForKeyword("import"))
+    self.assertEquals(self._SingleTokenForInput("module"),
+                      _MakeLexTokenForKeyword("module"))
+    self.assertEquals(self._SingleTokenForInput("struct"),
+                      _MakeLexTokenForKeyword("struct"))
+    self.assertEquals(self._SingleTokenForInput("interface"),
+                      _MakeLexTokenForKeyword("interface"))
+    self.assertEquals(self._SingleTokenForInput("enum"),
+                      _MakeLexTokenForKeyword("enum"))
+    self.assertEquals(self._SingleTokenForInput("const"),
+                      _MakeLexTokenForKeyword("const"))
+    self.assertEquals(self._SingleTokenForInput("true"),
+                      _MakeLexTokenForKeyword("true"))
+    self.assertEquals(self._SingleTokenForInput("false"),
+                      _MakeLexTokenForKeyword("false"))
+    self.assertEquals(self._SingleTokenForInput("default"),
+                      _MakeLexTokenForKeyword("default"))
+    self.assertEquals(self._SingleTokenForInput("array"),
+                      _MakeLexTokenForKeyword("array"))
+
+  def testValidIdentifiers(self):
+    """Tests identifiers."""
+    self.assertEquals(self._SingleTokenForInput("abcd"),
+                      _MakeLexToken("NAME", "abcd"))
+    self.assertEquals(self._SingleTokenForInput("AbC_d012_"),
+                      _MakeLexToken("NAME", "AbC_d012_"))
+    self.assertEquals(self._SingleTokenForInput("_0123"),
+                      _MakeLexToken("NAME", "_0123"))
+
+  def testInvalidIdentifiers(self):
+    with self.assertRaisesRegexp(
+        mojom.parse.lexer.LexError,
+        r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+      self._TokensForInput("$abc")
+    with self.assertRaisesRegexp(
+        mojom.parse.lexer.LexError,
+        r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+      self._TokensForInput("a$bc")
+
+  def testDecimalIntegerConstants(self):
+    self.assertEquals(self._SingleTokenForInput("0"),
+                      _MakeLexToken("INT_CONST_DEC", "0"))
+    self.assertEquals(self._SingleTokenForInput("1"),
+                      _MakeLexToken("INT_CONST_DEC", "1"))
+    self.assertEquals(self._SingleTokenForInput("123"),
+                      _MakeLexToken("INT_CONST_DEC", "123"))
+    self.assertEquals(self._SingleTokenForInput("10"),
+                      _MakeLexToken("INT_CONST_DEC", "10"))
+
+  def testValidTokens(self):
+    """Tests valid tokens (which aren't tested elsewhere)."""
+    # Keywords tested in |testValidKeywords|.
+    # NAME tested in |testValidIdentifiers|.
+    self.assertEquals(self._SingleTokenForInput("@123"),
+                      _MakeLexToken("ORDINAL", "@123"))
+    self.assertEquals(self._SingleTokenForInput("456"),
+                      _MakeLexToken("INT_CONST_DEC", "456"))
+    self.assertEquals(self._SingleTokenForInput("0x01aB2eF3"),
+                      _MakeLexToken("INT_CONST_HEX", "0x01aB2eF3"))
+    self.assertEquals(self._SingleTokenForInput("123.456"),
+                      _MakeLexToken("FLOAT_CONST", "123.456"))
+    self.assertEquals(self._SingleTokenForInput("\"hello\""),
+                      _MakeLexToken("STRING_LITERAL", "\"hello\""))
+    self.assertEquals(self._SingleTokenForInput("+"),
+                      _MakeLexToken("PLUS", "+"))
+    self.assertEquals(self._SingleTokenForInput("-"),
+                      _MakeLexToken("MINUS", "-"))
+    self.assertEquals(self._SingleTokenForInput("&"),
+                      _MakeLexToken("AMP", "&"))
+    self.assertEquals(self._SingleTokenForInput("?"),
+                      _MakeLexToken("QSTN", "?"))
+    self.assertEquals(self._SingleTokenForInput("="),
+                      _MakeLexToken("EQUALS", "="))
+    self.assertEquals(self._SingleTokenForInput("=>"),
+                      _MakeLexToken("RESPONSE", "=>"))
+    self.assertEquals(self._SingleTokenForInput("("),
+                      _MakeLexToken("LPAREN", "("))
+    self.assertEquals(self._SingleTokenForInput(")"),
+                      _MakeLexToken("RPAREN", ")"))
+    self.assertEquals(self._SingleTokenForInput("["),
+                      _MakeLexToken("LBRACKET", "["))
+    self.assertEquals(self._SingleTokenForInput("]"),
+                      _MakeLexToken("RBRACKET", "]"))
+    self.assertEquals(self._SingleTokenForInput("{"),
+                      _MakeLexToken("LBRACE", "{"))
+    self.assertEquals(self._SingleTokenForInput("}"),
+                      _MakeLexToken("RBRACE", "}"))
+    self.assertEquals(self._SingleTokenForInput("<"),
+                      _MakeLexToken("LANGLE", "<"))
+    self.assertEquals(self._SingleTokenForInput(">"),
+                      _MakeLexToken("RANGLE", ">"))
+    self.assertEquals(self._SingleTokenForInput(";"),
+                      _MakeLexToken("SEMI", ";"))
+    self.assertEquals(self._SingleTokenForInput(","),
+                      _MakeLexToken("COMMA", ","))
+    self.assertEquals(self._SingleTokenForInput("."),
+                      _MakeLexToken("DOT", "."))
+
+  def _TokensForInput(self, input_string):
+    """Gets a list of tokens for the given input string."""
+    lexer = self._zygote_lexer.clone()
+    lexer.input(input_string)
+    rv = []
+    while True:
+      tok = lexer.token()
+      if not tok:
+        return rv
+      rv.append(tok)
+
+  def _SingleTokenForInput(self, input_string):
+    """Gets the single token for the given input string. (Raises an exception if
+    the input string does not result in exactly one token.)"""
+    toks = self._TokensForInput(input_string)
+    assert len(toks) == 1
+    return toks[0]
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
new file mode 100644
index 0000000..8671b1a
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -0,0 +1,1058 @@
+# Copyright 2014 The Chromium 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+try:
+  imp.find_module("mojom")
+except ImportError:
+  sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.ast as ast
+import mojom.parse.lexer as lexer
+import mojom.parse.parser as parser
+
+
+class ParserTest(unittest.TestCase):
+  """Tests |parser.Parse()|."""
+
+  def testTrivialValidSource(self):
+    """Tests a trivial, but valid, .mojom source."""
+
+    source = """\
+        // This is a comment.
+
+        module my_module {
+        }
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testSourceWithCrLfs(self):
+    """Tests a .mojom source with CR-LFs instead of LFs."""
+
+    source = "// This is a comment.\r\n\r\nmodule my_module {\r\n}\r\n"
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testUnexpectedEOF(self):
+    """Tests a "truncated" .mojom source."""
+
+    source = """\
+        // This is a comment.
+
+        module my_module {
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom: Error: Unexpected end of file$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testCommentLineNumbers(self):
+    """Tests that line numbers are correctly tracked when comments are
+    present."""
+
+    source1 = """\
+        // Isolated C++-style comments.
+
+        // Foo.
+        asdf1
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\n *asdf1$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        // Consecutive C++-style comments.
+        // Foo.
+          // Bar.
+
+        struct Yada {  // Baz.
+        // Quux.
+          int32 x;
+        };
+
+        asdf2
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\n *asdf2$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        /* Single-line C-style comments. */
+        /* Foobar. */
+
+        /* Baz. */
+        asdf3
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\n *asdf3$"):
+      parser.Parse(source3, "my_file.mojom")
+
+    source4 = """\
+        /* Multi-line C-style comments.
+        */
+        /*
+        Foo.
+        Bar.
+        */
+
+        /* Baz
+           Quux. */
+        asdf4
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\n *asdf4$"):
+      parser.Parse(source4, "my_file.mojom")
+
+
+  def testSimpleStruct(self):
+    """Tests a simple .mojom source that just defines a struct."""
+
+    source = """\
+        module my_module {
+
+        struct MyStruct {
+          int32 a;
+          double b;
+        };
+
+        }  // module my_module
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, 'int32', None),
+                 ast.StructField('b', None, 'double', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testSimpleStructWithoutModule(self):
+    """Tests a simple struct without an enclosing module."""
+
+    source = """\
+        struct MyStruct {
+          int32 a;
+          double b;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, 'int32', None),
+                 ast.StructField('b', None, 'double', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testValidStructDefinitions(self):
+    """Tests all types of definitions that can occur in a struct."""
+
+    source = """\
+        struct MyStruct {
+          enum MyEnum { VALUE };
+          const double kMyConst = 1.23;
+          int32 a;
+          SomeOtherStruct b;  // Invalidity detected at another stage.
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.Enum('MyEnum',
+                          ast.EnumValueList(ast.EnumValue('VALUE', None))),
+                 ast.Const('kMyConst', 'double', '1.23'),
+                 ast.StructField('a', None, 'int32', None),
+                 ast.StructField('b', None, 'SomeOtherStruct', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidStructDefinitions(self):
+    """Tests that definitions that aren't allowed in a struct are correctly
+    detected."""
+
+    source1 = """\
+        struct MyStruct {
+          MyMethod(int32 a);
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '\(':\n"
+            r" *MyMethod\(int32 a\);$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        struct MyStruct {
+          struct MyInnerStruct {
+            int32 a;
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+            r" *struct MyInnerStruct {$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        struct MyStruct {
+          interface MyInterface {
+            MyMethod(int32 a);
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+            r" *interface MyInterface {$"):
+      parser.Parse(source3, "my_file.mojom")
+
+  def testMissingModuleName(self):
+    """Tests an (invalid) .mojom with a missing module name."""
+
+    source1 = """\
+        // Missing module name.
+        module {
+        struct MyStruct {
+          int32 a;
+        };
+        }
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '{':\n *module {$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    # Another similar case, but make sure that line-number tracking/reporting
+    # is correct.
+    source2 = """\
+        module
+        // This line intentionally left unblank.
+
+        {
+        }
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected '{':\n *{$"):
+      parser.Parse(source2, "my_file.mojom")
+
+  def testEnums(self):
+    """Tests that enum statements are correctly parsed."""
+
+    source = """\
+        module my_module {
+        enum MyEnum1 { VALUE1, VALUE2 };  // No trailing comma.
+        enum MyEnum2 {
+          VALUE1 = -1,
+          VALUE2 = 0,
+          VALUE3 = + 987,  // Check that space is allowed.
+          VALUE4 = 0xAF12,
+          VALUE5 = -0x09bcd,
+          VALUE6 = VALUE5,
+          VALUE7,  // Leave trailing comma.
+        };
+        }  // my_module
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Enum(
+            'MyEnum1',
+            ast.EnumValueList([ast.EnumValue('VALUE1', None),
+                               ast.EnumValue('VALUE2', None)])),
+         ast.Enum(
+            'MyEnum2',
+            ast.EnumValueList([ast.EnumValue('VALUE1', '-1'),
+                               ast.EnumValue('VALUE2', '0'),
+                               ast.EnumValue('VALUE3', '+987'),
+                               ast.EnumValue('VALUE4', '0xAF12'),
+                               ast.EnumValue('VALUE5', '-0x09bcd'),
+                               ast.EnumValue('VALUE6', ('IDENTIFIER',
+                                                        'VALUE5')),
+                               ast.EnumValue('VALUE7', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidEnumInitializers(self):
+    """Tests that invalid enum initializers are correctly detected."""
+
+    # No values.
+    source1 = """\
+        enum MyEnum {
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '}':\n"
+            r" *};$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    # Floating point value.
+    source2 = "enum MyEnum { VALUE = 0.123 };"
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:1: Error: Unexpected '0\.123':\n"
+            r"enum MyEnum { VALUE = 0\.123 };$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    # Boolean value.
+    source2 = "enum MyEnum { VALUE = true };"
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:1: Error: Unexpected 'true':\n"
+            r"enum MyEnum { VALUE = true };$"):
+      parser.Parse(source2, "my_file.mojom")
+
+  def testConsts(self):
+    """Tests some constants and struct members initialized with them."""
+
+    source = """\
+        module my_module {
+
+        struct MyStruct {
+          const int8 kNumber = -1;
+          int8 number@0 = kNumber;
+        };
+
+        }  // my_module
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct', None,
+            ast.StructBody(
+                [ast.Const('kNumber', 'int8', '-1'),
+                 ast.StructField('number', ast.Ordinal(0), 'int8',
+                                 ('IDENTIFIER', 'kNumber'))]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testNoConditionals(self):
+    """Tests that ?: is not allowed."""
+
+    source = """\
+        module my_module {
+
+        enum MyEnum {
+          MY_ENUM_1 = 1 ? 2 : 3
+        };
+
+        }  // my_module
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected '\?':\n"
+            r" *MY_ENUM_1 = 1 \? 2 : 3$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testSimpleOrdinals(self):
+    """Tests that (valid) ordinal values are scanned correctly."""
+
+    source = """\
+        module my_module {
+
+        // This isn't actually valid .mojom, but the problem (missing ordinals)
+        // should be handled at a different level.
+        struct MyStruct {
+          int32 a0@0;
+          int32 a1@1;
+          int32 a2@2;
+          int32 a9@9;
+          int32 a10 @10;
+          int32 a11 @11;
+          int32 a29 @29;
+          int32 a1234567890 @1234567890;
+        };
+
+        }  // module my_module
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a0', ast.Ordinal(0), 'int32', None),
+                 ast.StructField('a1', ast.Ordinal(1), 'int32', None),
+                 ast.StructField('a2', ast.Ordinal(2), 'int32', None),
+                 ast.StructField('a9', ast.Ordinal(9), 'int32', None),
+                 ast.StructField('a10', ast.Ordinal(10), 'int32', None),
+                 ast.StructField('a11', ast.Ordinal(11), 'int32', None),
+                 ast.StructField('a29', ast.Ordinal(29), 'int32', None),
+                 ast.StructField('a1234567890', ast.Ordinal(1234567890),
+                                 'int32', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidOrdinals(self):
+    """Tests that (lexically) invalid ordinals are correctly detected."""
+
+    source1 = """\
+        module my_module {
+
+        struct MyStruct {
+          int32 a_missing@;
+        };
+
+        }  // module my_module
+        """
+    with self.assertRaisesRegexp(
+        lexer.LexError,
+        r"^my_file\.mojom:4: Error: Missing ordinal value$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        module my_module {
+
+        struct MyStruct {
+          int32 a_octal@01;
+        };
+
+        }  // module my_module
+        """
+    with self.assertRaisesRegexp(
+        lexer.LexError,
+        r"^my_file\.mojom:4: Error: "
+            r"Octal and hexadecimal ordinal values not allowed$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        module my_module { struct MyStruct { int32 a_invalid_octal@08; }; }
+        """
+    with self.assertRaisesRegexp(
+        lexer.LexError,
+        r"^my_file\.mojom:1: Error: "
+            r"Octal and hexadecimal ordinal values not allowed$"):
+      parser.Parse(source3, "my_file.mojom")
+
+    source4 = "module my_module { struct MyStruct { int32 a_hex@0x1aB9; }; }"
+    with self.assertRaisesRegexp(
+        lexer.LexError,
+        r"^my_file\.mojom:1: Error: "
+            r"Octal and hexadecimal ordinal values not allowed$"):
+      parser.Parse(source4, "my_file.mojom")
+
+    source5 = "module my_module { struct MyStruct { int32 a_hex@0X0; }; }"
+    with self.assertRaisesRegexp(
+        lexer.LexError,
+        r"^my_file\.mojom:1: Error: "
+            r"Octal and hexadecimal ordinal values not allowed$"):
+      parser.Parse(source5, "my_file.mojom")
+
+    source6 = """\
+        struct MyStruct {
+          int32 a_too_big@999999999999;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: "
+            r"Ordinal value 999999999999 too large:\n"
+            r" *int32 a_too_big@999999999999;$"):
+      parser.Parse(source6, "my_file.mojom")
+
+  def testNestedNamespace(self):
+    """Tests that "nested" namespaces work."""
+
+    source = """\
+        module my.mod {
+
+        struct MyStruct {
+          int32 a;
+        };
+
+        }  // module my.mod
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my.mod'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(ast.StructField('a', None, 'int32', None)))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testValidHandleTypes(self):
+    """Tests (valid) handle types."""
+
+    source = """\
+        struct MyStruct {
+          handle a;
+          handle<data_pipe_consumer> b;
+          handle <data_pipe_producer> c;
+          handle < message_pipe > d;
+          handle
+            < shared_buffer
+            > e;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, 'handle', None),
+                 ast.StructField('b', None, 'handle<data_pipe_consumer>', None),
+                 ast.StructField('c', None, 'handle<data_pipe_producer>', None),
+                 ast.StructField('d', None, 'handle<message_pipe>', None),
+                 ast.StructField('e', None, 'handle<shared_buffer>', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidHandleType(self):
+    """Tests an invalid (unknown) handle type."""
+
+    source = """\
+        struct MyStruct {
+          handle<wtf_is_this> foo;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: "
+            r"Invalid handle type 'wtf_is_this':\n"
+            r" *handle<wtf_is_this> foo;$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testValidDefaultValues(self):
+    """Tests default values that are valid (to the parser)."""
+
+    source = """\
+        struct MyStruct {
+          int16 a0 = 0;
+          uint16 a1 = 0x0;
+          uint16 a2 = 0x00;
+          uint16 a3 = 0x01;
+          uint16 a4 = 0xcd;
+          int32 a5 = 12345;
+          int64 a6 = -12345;
+          int64 a7 = +12345;
+          uint32 a8 = 0x12cd3;
+          uint32 a9 = -0x12cD3;
+          uint32 a10 = +0x12CD3;
+          bool a11 = true;
+          bool a12 = false;
+          float a13 = 1.2345;
+          float a14 = -1.2345;
+          float a15 = +1.2345;
+          float a16 = 123.;
+          float a17 = .123;
+          double a18 = 1.23E10;
+          double a19 = 1.E-10;
+          double a20 = .5E+10;
+          double a21 = -1.23E10;
+          double a22 = +.123E10;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a0', None, 'int16', '0'),
+                 ast.StructField('a1', None, 'uint16', '0x0'),
+                 ast.StructField('a2', None, 'uint16', '0x00'),
+                 ast.StructField('a3', None, 'uint16', '0x01'),
+                 ast.StructField('a4', None, 'uint16', '0xcd'),
+                 ast.StructField('a5' , None, 'int32', '12345'),
+                 ast.StructField('a6', None, 'int64', '-12345'),
+                 ast.StructField('a7', None, 'int64', '+12345'),
+                 ast.StructField('a8', None, 'uint32', '0x12cd3'),
+                 ast.StructField('a9', None, 'uint32', '-0x12cD3'),
+                 ast.StructField('a10', None, 'uint32', '+0x12CD3'),
+                 ast.StructField('a11', None, 'bool', 'true'),
+                 ast.StructField('a12', None, 'bool', 'false'),
+                 ast.StructField('a13', None, 'float', '1.2345'),
+                 ast.StructField('a14', None, 'float', '-1.2345'),
+                 ast.StructField('a15', None, 'float', '+1.2345'),
+                 ast.StructField('a16', None, 'float', '123.'),
+                 ast.StructField('a17', None, 'float', '.123'),
+                 ast.StructField('a18', None, 'double', '1.23E10'),
+                 ast.StructField('a19', None, 'double', '1.E-10'),
+                 ast.StructField('a20', None, 'double', '.5E+10'),
+                 ast.StructField('a21', None, 'double', '-1.23E10'),
+                 ast.StructField('a22', None, 'double', '+.123E10')]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testValidFixedSizeArray(self):
+    """Tests parsing a fixed size array."""
+
+    source = """\
+        struct MyStruct {
+          array<int32> normal_array;
+          array<int32, 1> fixed_size_array_one_entry;
+          array<int32, 10> fixed_size_array_ten_entries;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('normal_array', None, 'int32[]', None),
+                 ast.StructField('fixed_size_array_one_entry', None, 'int32[1]',
+                                 None),
+                 ast.StructField('fixed_size_array_ten_entries', None,
+                                 'int32[10]', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testValidNestedArray(self):
+    """Tests parsing a nested array."""
+
+    source = "struct MyStruct { array<array<int32>> nested_array; };"
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                ast.StructField('nested_array', None, 'int32[][]', None)))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidFixedArraySize(self):
+    """Tests that invalid fixed array bounds are correctly detected."""
+
+    source1 = """\
+        struct MyStruct {
+          array<int32, 0> zero_size_array;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Fixed array size 0 invalid\n"
+            r" *array<int32, 0> zero_size_array;$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        struct MyStruct {
+          array<int32, 999999999999> too_big_array;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Fixed array size 999999999999 invalid\n"
+            r" *array<int32, 999999999999> too_big_array;$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        struct MyStruct {
+          array<int32, abcdefg> not_a_number;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'abcdefg':\n"
+        r" *array<int32, abcdefg> not_a_number;"):
+      parser.Parse(source3, "my_file.mojom")
+
+  def testValidMethod(self):
+    """Tests parsing method declarations."""
+
+    source1 = "interface MyInterface { MyMethod(int32 a); };"
+    expected1 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                ast.Method(
+                    'MyMethod',
+                    None,
+                    ast.ParameterList(ast.Parameter('a', None, 'int32')),
+                    None)))])
+    self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+    source2 = """\
+        interface MyInterface {
+          MyMethod1@0(int32 a@0, int64 b@1);
+          MyMethod2@1() => ();
+        };
+        """
+    expected2 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                [ast.Method(
+                    'MyMethod1',
+                    ast.Ordinal(0),
+                    ast.ParameterList([ast.Parameter('a', ast.Ordinal(0),
+                                                     'int32'),
+                                       ast.Parameter('b', ast.Ordinal(1),
+                                                     'int64')]),
+                    None),
+                  ast.Method(
+                    'MyMethod2',
+                    ast.Ordinal(1),
+                    ast.ParameterList(),
+                    ast.ParameterList())]))])
+    self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+    source3 = """\
+        interface MyInterface {
+          MyMethod(string a) => (int32 a, bool b);
+        };
+        """
+    expected3 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                ast.Method(
+                    'MyMethod',
+                    None,
+                    ast.ParameterList(ast.Parameter('a', None, 'string')),
+                    ast.ParameterList([ast.Parameter('a', None, 'int32'),
+                                       ast.Parameter('b', None, 'bool')]))))])
+    self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+  def testInvalidMethods(self):
+    """Tests that invalid method declarations are correctly detected."""
+
+    # No trailing commas.
+    source1 = """\
+        interface MyInterface {
+          MyMethod(string a,);
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '\)':\n"
+            r" *MyMethod\(string a,\);$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    # No leading commas.
+    source2 = """\
+        interface MyInterface {
+          MyMethod(, string a);
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected ',':\n"
+            r" *MyMethod\(, string a\);$"):
+      parser.Parse(source2, "my_file.mojom")
+
+  def testValidInterfaceDefinitions(self):
+    """Tests all types of definitions that can occur in an interface."""
+
+    source = """\
+        interface MyInterface {
+          enum MyEnum { VALUE };
+          const int32 kMyConst = 123;
+          MyMethod(int32 x) => (MyEnum y);
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                [ast.Enum('MyEnum',
+                          ast.EnumValueList(ast.EnumValue('VALUE', None))),
+                 ast.Const('kMyConst', 'int32', '123'),
+                 ast.Method(
+                    'MyMethod',
+                    None,
+                    ast.ParameterList(ast.Parameter('x', None, 'int32')),
+                    ast.ParameterList(ast.Parameter('y', None, 'MyEnum')))]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidInterfaceDefinitions(self):
+    """Tests that definitions that aren't allowed in an interface are correctly
+    detected."""
+
+    source1 = """\
+        interface MyInterface {
+          struct MyStruct {
+            int32 a;
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+            r" *struct MyStruct {$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        interface MyInterface {
+          interface MyInnerInterface {
+            MyMethod(int32 x);
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+            r" *interface MyInnerInterface {$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        interface MyInterface {
+          int32 my_field;
+        };
+        """
+    # The parser thinks that "int32" is a plausible name for a method, so it's
+    # "my_field" that gives it away.
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'my_field':\n"
+            r" *int32 my_field;$"):
+      parser.Parse(source3, "my_file.mojom")
+
+  def testValidAttributes(self):
+    """Tests parsing attributes (and attribute lists)."""
+
+    # Note: We use structs because they have (optional) attribute lists.
+
+    # Empty attribute list.
+    source1 = "[] struct MyStruct {};"
+    expected1 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())])
+    self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+    # One-element attribute list, with name value.
+    source2 = "[MyAttribute=MyName] struct MyStruct {};"
+    expected2 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            ast.AttributeList(ast.Attribute("MyAttribute", "MyName")),
+            ast.StructBody())])
+    self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+    # Two-element attribute list, with one string value and one integer value.
+    source3 = "[MyAttribute1 = \"hello\", MyAttribute2 = 5] struct MyStruct {};"
+    expected3 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            ast.AttributeList([ast.Attribute("MyAttribute1", "hello"),
+                               ast.Attribute("MyAttribute2", 5)]),
+            ast.StructBody())])
+    self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+    # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()|
+    # literal (non-name) values, which is extremely dubious.)
+
+  def testInvalidAttributes(self):
+    """Tests that invalid attributes and attribute lists are correctly
+    detected."""
+
+    # Trailing commas not allowed.
+    source1 = "[MyAttribute=MyName,] struct MyStruct {};"
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+            r"\[MyAttribute=MyName,\] struct MyStruct {};$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    # Missing value.
+    source2 = "[MyAttribute=] struct MyStruct {};"
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+            r"\[MyAttribute=\] struct MyStruct {};$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    # Missing key.
+    source3 = "[=MyName] struct MyStruct {};"
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:1: Error: Unexpected '=':\n"
+            r"\[=MyName\] struct MyStruct {};$"):
+      parser.Parse(source3, "my_file.mojom")
+
+  def testValidImports(self):
+    """Tests parsing import statements."""
+
+    # One import (no module statement).
+    source1 = "import \"somedir/my.mojom\""
+    expected1 = ast.Mojom(
+        None,
+        ast.ImportList(ast.Import("somedir/my.mojom")),
+        [])
+    self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+    # Two imports (no module statement).
+    source2 = """\
+        import "somedir/my1.mojom"
+        import "somedir/my2.mojom"
+        """
+    expected2 = ast.Mojom(
+        None,
+        ast.ImportList([ast.Import("somedir/my1.mojom"),
+                        ast.Import("somedir/my2.mojom")]),
+        [])
+    self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+    # Imports with module statement.
+    source3 = """\
+        import "somedir/my1.mojom"
+        import "somedir/my2.mojom"
+        module my_module {}
+        """
+    expected3 = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList([ast.Import("somedir/my1.mojom"),
+                        ast.Import("somedir/my2.mojom")]),
+        [])
+    self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+  def testInvalidImports(self):
+    """Tests that invalid import statements are correctly detected."""
+
+    source1 = """\
+        // Make the error occur on line 2.
+        import invalid
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'invalid':\n"
+            r" *import invalid$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        import  // Missing string.
+        module {}
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'module':\n"
+            r" *module {}$"):
+      parser.Parse(source2, "my_file.mojom")
+
+  def testValidNullableTypes(self):
+    """Tests parsing nullable types."""
+
+    source = """\
+        struct MyStruct {
+          int32? a;  // This is actually invalid, but handled at a different
+                     // level.
+          string? b;
+          array<int32> ? c;
+          array<string ? > ? d;
+          array<array<int32>?>? e;
+          array<int32, 1>? f;
+          array<string?, 1>? g;
+          some_struct? h;
+          handle? i;
+          handle<data_pipe_consumer>? j;
+          handle<data_pipe_producer>? k;
+          handle<message_pipe>? l;
+          handle<shared_buffer>? m;
+          some_interface&? n;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, 'int32?', None),
+                 ast.StructField('b', None, 'string?', None),
+                 ast.StructField('c', None, 'int32[]?', None),
+                 ast.StructField('d', None, 'string?[]?', None),
+                 ast.StructField('e', None, 'int32[]?[]?', None),
+                 ast.StructField('f', None, 'int32[1]?', None),
+                 ast.StructField('g', None, 'string?[1]?', None),
+                 ast.StructField('h', None, 'some_struct?', None),
+                 ast.StructField('i', None, 'handle?', None),
+                 ast.StructField('j', None, 'handle<data_pipe_consumer>?',
+                                 None),
+                 ast.StructField('k', None, 'handle<data_pipe_producer>?',
+                                 None),
+                 ast.StructField('l', None, 'handle<message_pipe>?', None),
+                 ast.StructField('m', None, 'handle<shared_buffer>?', None),
+                 ast.StructField('n', None, 'some_interface&?', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testInvalidNullableTypes(self):
+    """Tests that invalid nullable types are correctly detected."""
+    source1 = """\
+        struct MyStruct {
+          string?? a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+            r" *string\?\? a;$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        struct MyStruct {
+          handle?<data_pipe_consumer> a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '<':\n"
+            r" *handle\?<data_pipe_consumer> a;$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        struct MyStruct {
+          some_interface?& a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '&':\n"
+            r" *some_interface\?& a;$"):
+      parser.Parse(source3, "my_file.mojom")
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
new file mode 100755
index 0000000..b160de6
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Simple testing utility to just run the mojom parser."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                                os.path.pardir, os.path.pardir))
+
+from mojom.parse.parser import Parse, ParseError
+
+
+def main(argv):
+  if len(argv) < 2:
+    print "usage: %s filename" % argv[0]
+    return 0
+
+  for filename in argv[1:]:
+    with open(filename) as f:
+      print "%s:" % filename
+      try:
+        print Parse(f.read(), filename)
+      except ParseError, e:
+        print e
+        return 1
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
new file mode 100755
index 0000000..899d40e
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Simple testing utility to just run the mojom translate stage."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                                os.path.pardir, os.path.pardir))
+
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def main(argv):
+  if len(argv) < 2:
+    print "usage: %s filename" % sys.argv[0]
+    return 1
+
+  for filename in argv[1:]:
+    with open(filename) as f:
+      print "%s:" % filename
+      print Translate(Parse(f.read(), filename),
+                      os.path.splitext(os.path.basename(filename))[0])
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
new file mode 100644
index 0000000..2a4b17b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
@@ -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.
+
+import fnmatch
+from os import walk
+from os.path import join
+import sys
+
+
+def FindFiles(top, pattern, **kwargs):
+  """Finds files under |top| matching the glob pattern |pattern|, returning a
+  list of paths."""
+  matches = []
+  for dirpath, _, filenames in walk(top, **kwargs):
+    for filename in fnmatch.filter(filenames, pattern):
+      matches.append(join(dirpath, filename))
+  return matches
+
+
+def main(argv):
+  if len(argv) != 3:
+    print "usage: %s path pattern" % argv[0]
+    return 1
+
+  for filename in FindFiles(argv[1], argv[2]):
+    print filename
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
new file mode 100644
index 0000000..20ef461
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
@@ -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.
+
+import os.path
+from subprocess import check_call
+import sys
+
+
+def RunBindingsGenerator(out_dir, root_dir, mojom_file, extra_flags=None):
+  out_dir = os.path.abspath(out_dir)
+  root_dir = os.path.abspath(root_dir)
+  mojom_file = os.path.abspath(mojom_file)
+
+  # The mojom file should be under the root directory somewhere.
+  assert mojom_file.startswith(root_dir)
+  mojom_reldir = os.path.dirname(os.path.relpath(mojom_file, root_dir))
+
+  # TODO(vtl): Abstract out the "main" functions, so that we can just import
+  # the bindings generator (which would be more portable and easier to use in
+  # tests).
+  this_dir = os.path.dirname(os.path.abspath(__file__))
+  # We're in src/mojo/public/tools/bindings/pylib/mojom_tests/support;
+  # mojom_bindings_generator.py is in .../bindings.
+  bindings_generator = os.path.join(this_dir, os.pardir, os.pardir, os.pardir,
+                                    "mojom_bindings_generator.py")
+
+  args = ["python", bindings_generator,
+          "-o", os.path.join(out_dir, mojom_reldir)]
+  if extra_flags:
+    args.extend(extra_flags)
+  args.append(mojom_file)
+
+  check_call(args)
+
+
+def main(argv):
+  if len(argv) < 4:
+    print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0]
+    return 1
+
+  RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:])
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/python/BUILD.gn b/mojo/python/BUILD.gn
new file mode 100644
index 0000000..7b07f4f
--- /dev/null
+++ b/mojo/python/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.
+
+import("//third_party/cython/rules.gni")
+
+# GYP version: mojo/mojo.gyp:mojo_python
+group("python") {
+  deps = [
+    ":embedder",
+    "//mojo/public/python",
+  ]
+}
+
+# GYP version: mojo/mojo.gyp:mojo_python_embedder
+python_binary_module("embedder") {
+  python_base_module = "mojo"
+  sources = [
+    "system/mojo/embedder.pyx",
+  ]
+  deps = [
+    "//mojo/edk/system",
+  ]
+  datadeps = [
+    "//mojo/public/python:system",
+  ]
+}
diff --git a/mojo/python/system/mojo/embedder.pyx b/mojo/python/system/mojo/embedder.pyx
new file mode 100644
index 0000000..c409999
--- /dev/null
+++ b/mojo/python/system/mojo/embedder.pyx
@@ -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.
+
+# distutils: language = c++
+
+from libc.stdint cimport uintptr_t
+
+from mojo import system
+
+cdef extern from "third_party/cython/python_export.h":
+  pass
+
+cdef extern from "base/memory/scoped_ptr.h":
+  cdef cppclass scoped_ptr[T]:
+    scoped_ptr(T*)
+
+cdef extern from "mojo/edk/embedder/platform_support.h" \
+    namespace "mojo::embedder" nogil:
+  cdef cppclass PlatformSupport:
+    pass
+
+cdef extern from "mojo/edk/embedder/simple_platform_support.h" \
+    namespace "mojo::embedder" nogil:
+  cdef cppclass SimplePlatformSupport(PlatformSupport):
+    SimplePlatformSupport()
+
+cdef extern from "mojo/edk/embedder/embedder.h" nogil:
+  cdef void InitCEmbedder "mojo::embedder::Init"(
+      scoped_ptr[PlatformSupport] platform_support)
+
+cdef extern from "mojo/public/platform/native/system_thunks.h" nogil:
+  cdef struct MojoSystemThunks:
+    pass
+  cdef MojoSystemThunks MojoMakeSystemThunks()
+
+def Init():
+  InitCEmbedder(scoped_ptr[PlatformSupport](
+      new SimplePlatformSupport()))
+  cdef MojoSystemThunks thunks = MojoMakeSystemThunks()
+  system.SetSystemThunks(<uintptr_t>(&thunks))
diff --git a/mojo/python/tests/async_wait_unittest.py b/mojo/python/tests/async_wait_unittest.py
new file mode 100644
index 0000000..849b3cb
--- /dev/null
+++ b/mojo/python/tests/async_wait_unittest.py
@@ -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.
+
+import unittest
+
+# pylint: disable=F0401
+import mojo.embedder
+from mojo import system
+
+
+class AsyncWaitTest(unittest.TestCase):
+
+  def setUp(self):
+    mojo.embedder.Init()
+    self.loop = system.RunLoop()
+    self.array = []
+    self.handles = system.MessagePipe()
+    self.cancel = self.handles.handle0.AsyncWait(system.HANDLE_SIGNAL_READABLE,
+                                                 system.DEADLINE_INDEFINITE,
+                                                 self._OnResult)
+
+  def tearDown(self):
+    self.cancel()
+    self.handles = None
+    self.array = None
+    self.loop = None
+
+  def _OnResult(self, value):
+    self.array.append(value)
+
+  def _WriteToHandle(self):
+    self.handles.handle1.WriteMessage()
+
+  def _PostWriteAndRun(self):
+    self.loop.PostDelayedTask(self._WriteToHandle, 0)
+    self.loop.RunUntilIdle()
+
+  def testAsyncWait(self):
+    self._PostWriteAndRun()
+    self.assertEquals(len(self.array), 1)
+    self.assertEquals(system.RESULT_OK, self.array[0])
+
+  def testAsyncWaitCancel(self):
+    self.loop.PostDelayedTask(self.cancel, 0)
+    self._PostWriteAndRun()
+    self.assertEquals(len(self.array), 0)
+
+  def testAsyncWaitImmediateCancel(self):
+    self.cancel()
+    self._PostWriteAndRun()
+    self.assertEquals(len(self.array), 0)
diff --git a/mojo/python/tests/bindings_constants_unittest.py b/mojo/python/tests/bindings_constants_unittest.py
new file mode 100644
index 0000000..85e97c6
--- /dev/null
+++ b/mojo/python/tests/bindings_constants_unittest.py
@@ -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 math
+import unittest
+
+# Generated files
+# pylint: disable=F0401
+import sample_service_mojom
+
+
+class ConstantBindingsTest(unittest.TestCase):
+
+  def testConstantGeneration(self):
+    self.assertEquals(sample_service_mojom.TWELVE, 12)
+    self.assertEquals(sample_service_mojom.TOO_BIG_FOR_SIGNED_INT64,
+                      9999999999999999999)
+    self.assertEquals(sample_service_mojom.DOUBLE_INFINITY,
+                      float('inf'))
+    self.assertEquals(sample_service_mojom.DOUBLE_NEGATIVE_INFINITY,
+                      float('-inf'))
+    self.assertTrue(math.isnan(sample_service_mojom.DOUBLE_NA_N))
+    self.assertEquals(sample_service_mojom.FLOAT_INFINITY,
+                      float('inf'))
+    self.assertEquals(sample_service_mojom.FLOAT_NEGATIVE_INFINITY,
+                      float('-inf'))
+    self.assertTrue(math.isnan(sample_service_mojom.FLOAT_NA_N))
+
+  def testConstantOnStructGeneration(self):
+    self.assertEquals(sample_service_mojom.Foo.FOOBY, "Fooby")
+
+  def testStructImmutability(self):
+    with self.assertRaises(AttributeError):
+      sample_service_mojom.Foo.FOOBY = 0
+    with self.assertRaises(AttributeError):
+      del sample_service_mojom.Foo.FOOBY
+    with self.assertRaises(AttributeError):
+      sample_service_mojom.Foo.BAR = 1
diff --git a/mojo/python/tests/bindings_enums_unittest.py b/mojo/python/tests/bindings_enums_unittest.py
new file mode 100644
index 0000000..6ec5d8c
--- /dev/null
+++ b/mojo/python/tests/bindings_enums_unittest.py
@@ -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 unittest
+
+# Generated files
+# pylint: disable=F0401
+import sample_import_mojom
+import sample_service_mojom
+
+
+class EnumBindingsTest(unittest.TestCase):
+
+  # Testing enum classes are in the right module.
+  def testModule(self):
+    self.assertEquals(sample_import_mojom.Shape.__module__,
+                      'sample_import_mojom')
+
+  # Testing that enum class have expected constant values.
+  def testTopLevelEnumGeneration(self):
+    self.assertEquals(sample_import_mojom.Shape.RECTANGLE, 1)
+    self.assertEquals(sample_import_mojom.Shape.CIRCLE, 2)
+    self.assertEquals(sample_import_mojom.Shape.TRIANGLE, 3)
+    self.assertEquals(sample_import_mojom.Shape.LAST,
+                      sample_import_mojom.Shape.TRIANGLE)
+
+    self.assertEquals(sample_import_mojom.AnotherShape.RECTANGLE, 10)
+    self.assertEquals(sample_import_mojom.AnotherShape.CIRCLE, 11)
+    self.assertEquals(sample_import_mojom.AnotherShape.TRIANGLE, 12)
+
+    self.assertEquals(sample_import_mojom.YetAnotherShape.RECTANGLE, 20)
+    self.assertEquals(sample_import_mojom.YetAnotherShape.CIRCLE, 21)
+    self.assertEquals(sample_import_mojom.YetAnotherShape.TRIANGLE, 22)
+
+  # Testing that internal enum class have expected constant values.
+  def testInternalEnumGeneration(self):
+    self.assertEquals(sample_service_mojom.Bar.Type.VERTICAL, 1)
+    self.assertEquals(sample_service_mojom.Bar.Type.HORIZONTAL, 2)
+    self.assertEquals(sample_service_mojom.Bar.Type.BOTH, 3)
+    self.assertEquals(sample_service_mojom.Bar.Type.INVALID, 4)
+
+  # Testing an enum class cannot be instantiated.
+  def testNonInstantiableEnum(self):
+    with self.assertRaises(TypeError):
+      sample_import_mojom.Shape()
+
+  # Testing an enum does not contain the VALUES constant.
+  def testNoVALUESConstant(self):
+    with self.assertRaises(AttributeError):
+      # pylint: disable=W0104
+      sample_import_mojom.Shape.VALUES
+
+  # Testing enum values are frozen.
+  def testEnumFrozen(self):
+    with self.assertRaises(AttributeError):
+      sample_import_mojom.Shape.RECTANGLE = 2
+    with self.assertRaises(AttributeError):
+      del sample_import_mojom.Shape.RECTANGLE
+    with self.assertRaises(AttributeError):
+      sample_import_mojom.Shape.NewShape = 4
diff --git a/mojo/python/tests/bindings_serialization_deserialization_unittest.py b/mojo/python/tests/bindings_serialization_deserialization_unittest.py
new file mode 100644
index 0000000..8d86495
--- /dev/null
+++ b/mojo/python/tests/bindings_serialization_deserialization_unittest.py
@@ -0,0 +1,130 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import math
+import unittest
+
+# pylint: disable=F0401
+import mojo.bindings.reflection as reflection
+import mojo.system
+
+# Generated files
+# pylint: disable=F0401
+import sample_import_mojom
+import sample_import2_mojom
+import sample_service_mojom
+
+
+def _NewHandle():
+  return mojo.system.MessagePipe().handle0
+
+
+def _TestEquality(x, y):
+  if x == y:
+    return True
+
+  if type(x) != type(y):
+    print '\n%r != %r. Element are not of the same type.' % (x, y)
+    return False
+
+  if isinstance(x, float) and math.isnan(x) and math.isnan(y):
+    return True
+
+  if hasattr(x, '__len__'):
+    if len(x) != len(y):
+      print '\n%r != %r. Iterables are not of the same size.' % (x, y)
+      return False
+    for (x1, y1) in zip(x, y):
+      if not _TestEquality(x1, y1):
+        return False
+    return True
+
+  if (hasattr(x, '__metaclass__') and
+      x.__metaclass__ == reflection.MojoStructType):
+    properties = [p for p in dir(x) if not p.startswith('_')]
+    for p in properties:
+      p1 = getattr(x, p)
+      p2 = getattr(y, p)
+      if not hasattr(p1, '__call__') and not _TestEquality(p1, p2):
+        print '\n%r != %r. Not equal for property %r.' % (x, y, p)
+        return False
+    return True
+
+  return False
+
+
+def _NewBar():
+  bar_instance = sample_service_mojom.Bar()
+  bar_instance.alpha = 22
+  bar_instance.beta = 87
+  bar_instance.gamma = 122
+  bar_instance.type = sample_service_mojom.Bar.Type.BOTH
+  return bar_instance
+
+
+def _NewFoo():
+  foo_instance = sample_service_mojom.Foo()
+  foo_instance.name = "Foo.name"
+  foo_instance.x = 23
+  foo_instance.y = -23
+  foo_instance.a = False
+  foo_instance.b = True
+  foo_instance.c = True
+  foo_instance.bar = _NewBar()
+  foo_instance.extra_bars = [
+      _NewBar(),
+      _NewBar(),
+  ]
+  foo_instance.data = 'Hello world'
+  foo_instance.source = _NewHandle()
+  foo_instance.input_streams = [ _NewHandle() ]
+  foo_instance.output_streams = [ _NewHandle(), _NewHandle() ]
+  foo_instance.array_of_array_of_bools = [ [ True, False ], [] ]
+  foo_instance.multi_array_of_strings = [
+      [
+          [ "1", "2" ],
+          [],
+          [ "3", "4" ],
+      ],
+      [],
+  ]
+  foo_instance.array_of_bools = [ True, 0, 1, 2, 0, 0, 0, 0, 0, True ]
+  return foo_instance
+
+
+class SerializationDeserializationTest(unittest.TestCase):
+
+  def testTestEquality(self):
+    self.assertFalse(_TestEquality(1, 2))
+
+  def testFooSerialization(self):
+    (data, _) = _NewFoo().Serialize()
+    self.assertTrue(len(data))
+    self.assertEquals(len(data) % 8, 0)
+
+  def testFooDeserialization(self):
+    (data, handles) = _NewFoo().Serialize()
+    self.assertTrue(
+        sample_service_mojom.Foo.Deserialize(data, handles))
+
+  def testFooSerializationDeserialization(self):
+    foo1 = _NewFoo()
+    (data, handles) = foo1.Serialize()
+    foo2 = sample_service_mojom.Foo.Deserialize(data, handles)
+    self.assertTrue(_TestEquality(foo1, foo2))
+
+  def testDefaultsTestSerializationDeserialization(self):
+    v1 = sample_service_mojom.DefaultsTest()
+    v1.a18 = []
+    v1.a19 = ""
+    v1.a21 = sample_import_mojom.Point()
+    v1.a22.location = sample_import_mojom.Point()
+    v1.a22.size = sample_import2_mojom.Size()
+    (data, handles) = v1.Serialize()
+    v2 = sample_service_mojom.DefaultsTest.Deserialize(data, handles)
+    self.assertTrue(_TestEquality(v1, v2))
+
+  def testFooDeserializationError(self):
+    with self.assertRaises(Exception):
+      sample_service_mojom.Foo.Deserialize("", [])
diff --git a/mojo/python/tests/bindings_structs_unittest.py b/mojo/python/tests/bindings_structs_unittest.py
new file mode 100644
index 0000000..97c1245
--- /dev/null
+++ b/mojo/python/tests/bindings_structs_unittest.py
@@ -0,0 +1,208 @@
+# Copyright 2014 The Chromium 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 math
+import unittest
+
+# pylint: disable=F0401
+import mojo.system
+
+# Generated files
+# pylint: disable=F0401
+import sample_import_mojom
+import sample_import2_mojom
+import sample_service_mojom
+
+
+class StructBindingsTest(unittest.TestCase):
+
+  def testModule(self):
+    self.assertEquals(sample_service_mojom.DefaultsTest.__module__,
+                      'sample_service_mojom')
+
+  def testDefaultsTest(self):
+    defaults_test = sample_service_mojom.DefaultsTest()
+    self.assertEquals(defaults_test.a0, -12)
+    self.assertEquals(defaults_test.a1, 12)
+    self.assertEquals(defaults_test.a2, 1234)
+    self.assertEquals(defaults_test.a3, 34567)
+    self.assertEquals(defaults_test.a4, 123456)
+    self.assertEquals(defaults_test.a5, 3456789012)
+    self.assertEquals(defaults_test.a6, -111111111111)
+    self.assertEquals(defaults_test.a7, 9999999999999999999)
+    self.assertEquals(defaults_test.a8, 0x12345)
+    self.assertEquals(defaults_test.a9, -0x12345)
+    self.assertEquals(defaults_test.a10, 1234)
+    self.assertEquals(defaults_test.a11, True)
+    self.assertEquals(defaults_test.a12, False)
+    self.assertEquals(defaults_test.a13, 123.25)
+    self.assertEquals(defaults_test.a14, 1234567890.123)
+    self.assertEquals(defaults_test.a15, 1E10)
+    self.assertEquals(defaults_test.a16, -1.2E+20)
+    self.assertEquals(defaults_test.a17, 1.23E-20)
+    self.assertEquals(defaults_test.a18, None)
+    self.assertEquals(defaults_test.a19, None)
+    self.assertEquals(defaults_test.a20, sample_service_mojom.Bar.Type.BOTH)
+    self.assertEquals(defaults_test.a21, None)
+    self.assertTrue(isinstance(defaults_test.a22, sample_import2_mojom.Thing))
+    self.assertEquals(defaults_test.a23, 0xFFFFFFFFFFFFFFFF)
+    self.assertEquals(defaults_test.a24, 0x123456789)
+    self.assertEquals(defaults_test.a25, -0x123456789)
+    self.assertEquals(defaults_test.a26, float('inf'))
+    self.assertEquals(defaults_test.a27, float('-inf'))
+    self.assertTrue(math.isnan(defaults_test.a28))
+    self.assertEquals(defaults_test.a29, float('inf'))
+    self.assertEquals(defaults_test.a30, float('-inf'))
+    self.assertTrue(math.isnan(defaults_test.a31))
+
+  def testNoAliasing(self):
+    foo1 = sample_service_mojom.Foo()
+    foo2 = sample_service_mojom.Foo()
+    foo1.name = "foo1"
+    foo2.name = "foo2"
+    self.assertEquals(foo1.name, "foo1")
+    self.assertEquals(foo2.name, "foo2")
+
+    defaults_test1 = sample_service_mojom.DefaultsTest()
+    defaults_test2 = sample_service_mojom.DefaultsTest()
+    self.assertNotEquals(defaults_test1.a22, defaults_test2.a22)
+
+  def testImmutableAttributeSet(self):
+    foo_instance = sample_service_mojom.Foo()
+    with self.assertRaises(AttributeError):
+      foo_instance.new_attribute = None
+    with self.assertRaises(AttributeError):
+      del foo_instance.name
+
+  def _TestIntegerField(self, entity, field_name, bits, signed):
+    if signed:
+      min_value = -(1 << (bits - 1))
+      max_value = (1 << (bits - 1)) - 1
+    else:
+      min_value = 0
+      max_value = (1 << bits) - 1
+    entity.__setattr__(field_name, min_value)
+    entity.__setattr__(field_name, max_value)
+    with self.assertRaises(TypeError):
+      entity.__setattr__(field_name, None)
+    with self.assertRaises(OverflowError):
+      entity.__setattr__(field_name, min_value - 1)
+    with self.assertRaises(OverflowError):
+      entity.__setattr__(field_name, max_value + 1)
+    with self.assertRaises(TypeError):
+      entity.__setattr__(field_name, 'hello world')
+
+  def testTypes(self):
+    defaults_test = sample_service_mojom.DefaultsTest()
+    # Integer types
+    self._TestIntegerField(defaults_test, 'a0', 8, True)
+    self._TestIntegerField(defaults_test, 'a1', 8, False)
+    self._TestIntegerField(defaults_test, 'a2', 16, True)
+    self._TestIntegerField(defaults_test, 'a3', 16, False)
+    self._TestIntegerField(defaults_test, 'a4', 32, True)
+    self._TestIntegerField(defaults_test, 'a5', 32, False)
+    self._TestIntegerField(defaults_test, 'a6', 64, True)
+    self._TestIntegerField(defaults_test, 'a7', 64, False)
+
+    # Boolean types
+    defaults_test.a11 = False
+    self.assertEquals(defaults_test.a11, False)
+    defaults_test.a11 = None
+    self.assertEquals(defaults_test.a11, False)
+    defaults_test.a11 = []
+    self.assertEquals(defaults_test.a11, False)
+    defaults_test.a12 = True
+    self.assertEquals(defaults_test.a12, True)
+    defaults_test.a12 = 1
+    self.assertEquals(defaults_test.a12, True)
+    defaults_test.a12 = [[]]
+    self.assertEquals(defaults_test.a12, True)
+
+    # Floating point types
+    with self.assertRaises(TypeError):
+      defaults_test.a13 = 'hello'
+    with self.assertRaises(TypeError):
+      defaults_test.a14 = 'hello'
+
+    # Array type
+    defaults_test.a18 = None
+    defaults_test.a18 = []
+    defaults_test.a18 = [ 0 ]
+    defaults_test.a18 = [ 255 ]
+    defaults_test.a18 = [ 0, 255 ]
+    with self.assertRaises(TypeError):
+      defaults_test.a18 = [[]]
+    with self.assertRaises(OverflowError):
+      defaults_test.a18 = [ -1 ]
+    with self.assertRaises(OverflowError):
+      defaults_test.a18 = [ 256 ]
+
+    # String type
+    defaults_test.a19 = None
+    defaults_test.a19 = ''
+    defaults_test.a19 = 'hello world'
+    with self.assertRaises(TypeError):
+      defaults_test.a19 = [[]]
+    with self.assertRaises(TypeError):
+      defaults_test.a19 = [ -1 ]
+    with self.assertRaises(TypeError):
+      defaults_test.a19 = [ 256 ]
+
+    # Structs
+    defaults_test.a21 = None
+    defaults_test.a21 = sample_import_mojom.Point()
+    with self.assertRaises(TypeError):
+      defaults_test.a21 = 1
+    with self.assertRaises(TypeError):
+      defaults_test.a21 = sample_import2_mojom.Thing()
+
+    # Handles
+    foo_instance = sample_service_mojom.Foo()
+    foo_instance.source = None
+    foo_instance.source = mojo.system.Handle()
+    with self.assertRaises(TypeError):
+      foo_instance.source = 1
+    with self.assertRaises(TypeError):
+      foo_instance.source = object()
+
+  def testConstructor(self):
+    bar_instance = sample_service_mojom.Bar()
+    foo_instance = sample_service_mojom.Foo(name="Foo",
+                                            x=-1,
+                                            y=5,
+                                            a=False,
+                                            bar=bar_instance)
+    self.assertEquals(foo_instance.name, "Foo")
+    self.assertEquals(foo_instance.x, -1)
+    self.assertEquals(foo_instance.y, 5)
+    self.assertEquals(foo_instance.a, False)
+    self.assertEquals(foo_instance.bar, bar_instance)
+
+  def testPositionalConstructor(self):
+    p = sample_import_mojom.Point()
+    self.assertEquals(p.x, 0)
+    self.assertEquals(p.y, 0)
+
+    p = sample_import_mojom.Point(34)
+    self.assertEquals(p.x, 34)
+    self.assertEquals(p.y, 0)
+
+    p = sample_import_mojom.Point(34, 12)
+    self.assertEquals(p.x, 34)
+    self.assertEquals(p.y, 12)
+
+    p = sample_import_mojom.Point(x=34, y=12)
+    self.assertEquals(p.x, 34)
+    self.assertEquals(p.y, 12)
+
+    p = sample_import_mojom.Point(34, y=12)
+    self.assertEquals(p.x, 34)
+    self.assertEquals(p.y, 12)
+
+    with self.assertRaises(TypeError):
+      p = sample_import_mojom.Point(0, 0, 0)
+    with self.assertRaises(TypeError):
+      p = sample_import_mojom.Point(0, x=0)
+    with self.assertRaises(TypeError):
+      p = sample_import_mojom.Point(c=0)
diff --git a/mojo/python/tests/messaging_unittest.py b/mojo/python/tests/messaging_unittest.py
new file mode 100644
index 0000000..2d08941
--- /dev/null
+++ b/mojo/python/tests/messaging_unittest.py
@@ -0,0 +1,207 @@
+# Copyright 2014 The Chromium 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 unittest
+
+# pylint: disable=F0401
+import mojo.embedder
+from mojo.bindings import messaging
+from mojo import system
+
+
+class _ForwardingConnectionErrorHandler(messaging.ConnectionErrorHandler):
+
+  def __init__(self, callback):
+    self._callback = callback
+
+  def OnError(self, result):
+    self._callback(result)
+
+
+class ConnectorTest(unittest.TestCase):
+
+  def setUp(self):
+    mojo.embedder.Init()
+    self.loop = system.RunLoop()
+    self.received_messages = []
+    self.received_errors = []
+    def _OnMessage(message):
+      self.received_messages.append(message)
+      return True
+    def _OnError(result):
+      self.received_errors.append(result)
+    handles = system.MessagePipe()
+    self.connector = messaging.Connector(handles.handle1)
+    self.connector.SetIncomingMessageReceiver(
+        messaging.ForwardingMessageReceiver(_OnMessage))
+    self.connector.SetErrorHandler(
+        _ForwardingConnectionErrorHandler(_OnError))
+    self.connector.Start()
+    self.handle = handles.handle0
+
+
+  def tearDown(self):
+    self.connector = None
+    self.handle = None
+    self.loop = None
+
+  def testConnectorRead(self):
+    self.handle.WriteMessage()
+    self.loop.RunUntilIdle()
+    self.assertTrue(self.received_messages)
+    self.assertFalse(self.received_errors)
+
+  def testConnectorWrite(self):
+    self.connector.Accept(messaging.Message())
+    (result, _, _) = self.handle.ReadMessage()
+    self.assertEquals(result, system.RESULT_OK)
+    self.assertFalse(self.received_errors)
+
+  def testConnectorCloseRemoteHandle(self):
+    self.handle.Close()
+    self.loop.RunUntilIdle()
+    self.assertFalse(self.received_messages)
+    self.assertTrue(self.received_errors)
+    self.assertEquals(self.received_errors[0],
+                      system.RESULT_FAILED_PRECONDITION)
+
+  def testConnectorDeleteConnector(self):
+    self.connector = None
+    (result, _, _) = self.handle.ReadMessage()
+    self.assertEquals(result, system.RESULT_FAILED_PRECONDITION)
+
+
+class HeaderTest(unittest.TestCase):
+
+  def testSimpleMessageHeader(self):
+    header = messaging.MessageHeader(0xdeadbeaf, messaging.NO_FLAG)
+    self.assertEqual(header.message_type, 0xdeadbeaf)
+    self.assertFalse(header.has_request_id)
+    self.assertFalse(header.expects_response)
+    self.assertFalse(header.is_response)
+    data = header.Serialize()
+    other_header = messaging.MessageHeader.Deserialize(data)
+    self.assertEqual(other_header.message_type, 0xdeadbeaf)
+    self.assertFalse(other_header.has_request_id)
+    self.assertFalse(other_header.expects_response)
+    self.assertFalse(other_header.is_response)
+
+  def testMessageHeaderWithRequestID(self):
+    # Request message.
+    header = messaging.MessageHeader(0xdeadbeaf,
+                                     messaging.MESSAGE_EXPECTS_RESPONSE_FLAG)
+
+    self.assertEqual(header.message_type, 0xdeadbeaf)
+    self.assertTrue(header.has_request_id)
+    self.assertTrue(header.expects_response)
+    self.assertFalse(header.is_response)
+    self.assertEqual(header.request_id, 0)
+
+    data = header.Serialize()
+    other_header = messaging.MessageHeader.Deserialize(data)
+
+    self.assertEqual(other_header.message_type, 0xdeadbeaf)
+    self.assertTrue(other_header.has_request_id)
+    self.assertTrue(other_header.expects_response)
+    self.assertFalse(other_header.is_response)
+    self.assertEqual(other_header.request_id, 0)
+
+    header.request_id = 0xdeadbeafdeadbeaf
+    data = header.Serialize()
+    other_header = messaging.MessageHeader.Deserialize(data)
+
+    self.assertEqual(other_header.request_id, 0xdeadbeafdeadbeaf)
+
+    # Response message.
+    header = messaging.MessageHeader(0xdeadbeaf,
+                                     messaging.MESSAGE_IS_RESPONSE_FLAG,
+                                     0xdeadbeafdeadbeaf)
+
+    self.assertEqual(header.message_type, 0xdeadbeaf)
+    self.assertTrue(header.has_request_id)
+    self.assertFalse(header.expects_response)
+    self.assertTrue(header.is_response)
+    self.assertEqual(header.request_id, 0xdeadbeafdeadbeaf)
+
+    data = header.Serialize()
+    other_header = messaging.MessageHeader.Deserialize(data)
+
+    self.assertEqual(other_header.message_type, 0xdeadbeaf)
+    self.assertTrue(other_header.has_request_id)
+    self.assertFalse(other_header.expects_response)
+    self.assertTrue(other_header.is_response)
+    self.assertEqual(other_header.request_id, 0xdeadbeafdeadbeaf)
+
+
+class RouterTest(unittest.TestCase):
+
+  def setUp(self):
+    mojo.embedder.Init()
+    self.loop = system.RunLoop()
+    self.received_messages = []
+    self.received_errors = []
+    def _OnMessage(message):
+      self.received_messages.append(message)
+      return True
+    def _OnError(result):
+      self.received_errors.append(result)
+    handles = system.MessagePipe()
+    self.router = messaging.Router(handles.handle1)
+    self.router.SetIncomingMessageReceiver(
+        messaging.ForwardingMessageReceiver(_OnMessage))
+    self.router.SetErrorHandler(
+        _ForwardingConnectionErrorHandler(_OnError))
+    self.router.Start()
+    self.handle = handles.handle0
+
+  def tearDown(self):
+    self.router = None
+    self.handle = None
+    self.loop = None
+
+  def testSimpleMessage(self):
+    header_data = messaging.MessageHeader(0, messaging.NO_FLAG).Serialize()
+    message = messaging.Message(header_data)
+    self.router.Accept(message)
+    self.loop.RunUntilIdle()
+    self.assertFalse(self.received_errors)
+    self.assertFalse(self.received_messages)
+    (res, data, _) = self.handle.ReadMessage(bytearray(len(header_data)))
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertEquals(data[0], header_data)
+
+  def testSimpleReception(self):
+    header_data = messaging.MessageHeader(0, messaging.NO_FLAG).Serialize()
+    self.handle.WriteMessage(header_data)
+    self.loop.RunUntilIdle()
+    self.assertFalse(self.received_errors)
+    self.assertEquals(len(self.received_messages), 1)
+    self.assertEquals(self.received_messages[0].data, header_data)
+
+  def testRequestResponse(self):
+    header_data = messaging.MessageHeader(
+        0, messaging.MESSAGE_EXPECTS_RESPONSE_FLAG).Serialize()
+    message = messaging.Message(header_data)
+    back_messages = []
+    def OnBackMessage(message):
+      back_messages.append(message)
+    self.router.AcceptWithResponder(message,
+                                    messaging.ForwardingMessageReceiver(
+                                        OnBackMessage))
+    self.loop.RunUntilIdle()
+    self.assertFalse(self.received_errors)
+    self.assertFalse(self.received_messages)
+    (res, data, _) = self.handle.ReadMessage(bytearray(len(header_data)))
+    self.assertEquals(system.RESULT_OK, res)
+    message_header = messaging.MessageHeader.Deserialize(data[0])
+    self.assertNotEquals(message_header.request_id, 0)
+    response_header_data = messaging.MessageHeader(
+        0,
+        messaging.MESSAGE_IS_RESPONSE_FLAG,
+        message_header.request_id).Serialize()
+    self.handle.WriteMessage(response_header_data)
+    self.loop.RunUntilIdle()
+    self.assertFalse(self.received_errors)
+    self.assertEquals(len(back_messages), 1)
+    self.assertEquals(back_messages[0].data, response_header_data)
diff --git a/mojo/python/tests/promise_unittest.py b/mojo/python/tests/promise_unittest.py
new file mode 100644
index 0000000..44427ba
--- /dev/null
+++ b/mojo/python/tests/promise_unittest.py
@@ -0,0 +1,181 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+# pylint: disable=F0401
+from mojo.bindings import promise
+
+
+class PromiseTest(unittest.TestCase):
+
+  def setUp(self):
+    self.accumulated = []
+
+  def _AddToAccumulated(self, res):
+    self.accumulated.append(res)
+    return res
+
+  def testResolve(self):
+    p = promise.Promise.Resolve(0)
+    self.assertEquals(p.state, promise.Promise.STATE_FULLFILLED)
+    p.Then(self._AddToAccumulated)
+    self.assertEquals(self.accumulated, [0])
+
+  def testResolveToPromise(self):
+    p = promise.Promise.Resolve(0)
+    self.assertEquals(p.state, promise.Promise.STATE_FULLFILLED)
+    q = promise.Promise.Resolve(p)
+    self.assertEquals(p.state, promise.Promise.STATE_FULLFILLED)
+    q.Then(self._AddToAccumulated)
+    self.assertEquals(self.accumulated, [0])
+
+  def testReject(self):
+    p = promise.Promise.Reject(0)
+    self.assertEquals(p.state, promise.Promise.STATE_REJECTED)
+    p.Then(onRejected=self._AddToAccumulated)
+    self.assertEquals(self.accumulated, [0])
+
+  def testGeneratorFunctionResolve(self):
+    (p, resolve, _) = _GetPromiseAndFunctions()
+    self.assertEquals(p.state, promise.Promise.STATE_PENDING)
+    p.Then(self._AddToAccumulated)
+    resolve(0)
+    self.assertEquals(p.state, promise.Promise.STATE_FULLFILLED)
+    self.assertEquals(self.accumulated, [0])
+
+  def testGeneratorFunctionReject(self):
+    (p, _, reject) = _GetPromiseAndFunctions()
+    self.assertEquals(p.state, promise.Promise.STATE_PENDING)
+    p.Then(None, self._AddToAccumulated)
+    reject(0)
+    self.assertEquals(p.state, promise.Promise.STATE_REJECTED)
+    self.assertEquals(self.accumulated, [0])
+
+  def testGeneratorFunctionResolveToPromise(self):
+    (p1, resolve, _) = _GetPromiseAndFunctions()
+    p2 = promise.Promise(lambda x, y: x(p1))
+    self.assertEquals(p2.state, promise.Promise.STATE_PENDING)
+    p2.Then(self._AddToAccumulated)
+    resolve(promise.Promise.Resolve(0))
+    self.assertEquals(self.accumulated, [0])
+
+  def testComputation(self):
+    (p, resolve, _) = _GetPromiseAndFunctions()
+    p.Then(lambda x: x+1).Then(lambda x: x+2).Then(self._AddToAccumulated)
+    self.assertEquals(self.accumulated, [])
+    resolve(0)
+    self.assertEquals(self.accumulated, [3])
+
+  def testRecoverAfterException(self):
+    (p, resolve, _) = _GetPromiseAndFunctions()
+    p.Then(_ThrowException).Catch(self._AddToAccumulated)
+    self.assertEquals(self.accumulated, [])
+    resolve(0)
+    self.assertEquals(len(self.accumulated), 1)
+    self.assertIsInstance(self.accumulated[0], RuntimeError)
+    self.assertEquals(self.accumulated[0].message, 0)
+
+  def testMultipleRejectResolve(self):
+    (p, resolve, reject) = _GetPromiseAndFunctions()
+    p.Then(self._AddToAccumulated, self._AddToAccumulated)
+    resolve(0)
+    self.assertEquals(self.accumulated, [0])
+    resolve(0)
+    self.assertEquals(self.accumulated, [0])
+    reject(0)
+    self.assertEquals(self.accumulated, [0])
+
+    self.accumulated = []
+    (p, resolve, reject) = _GetPromiseAndFunctions()
+    p.Then(self._AddToAccumulated, self._AddToAccumulated)
+    reject(0)
+    self.assertEquals(self.accumulated, [0])
+    resolve(0)
+    self.assertEquals(self.accumulated, [0])
+    reject(0)
+    self.assertEquals(self.accumulated, [0])
+
+  def testAll(self):
+    promises_and_functions = [_GetPromiseAndFunctions() for x in xrange(10)]
+    promises = [x[0] for x in promises_and_functions]
+    all_promise = promise.Promise.All(*promises)
+    res = []
+    def AddToRes(values):
+      res.append(values)
+    all_promise.Then(AddToRes, AddToRes)
+    for i, (_, resolve, _) in enumerate(promises_and_functions):
+      self.assertEquals(len(res), 0)
+      resolve(i)
+    self.assertEquals(len(res), 1)
+    self.assertEquals(res[0], [i for i in xrange(10)])
+    self.assertEquals(all_promise.state, promise.Promise.STATE_FULLFILLED)
+
+  def testAllFailure(self):
+    promises_and_functions = [_GetPromiseAndFunctions() for x in xrange(10)]
+    promises = [x[0] for x in promises_and_functions]
+    all_promise = promise.Promise.All(*promises)
+    res = []
+    def AddToRes(values):
+      res.append(values)
+    all_promise.Then(AddToRes, AddToRes)
+    for i in xrange(10):
+      if i <= 5:
+        self.assertEquals(len(res), 0)
+      else:
+        self.assertEquals(len(res), 1)
+      if i != 5:
+        promises_and_functions[i][1](i)
+      else:
+        promises_and_functions[i][2]('error')
+    self.assertEquals(len(res), 1)
+    self.assertEquals(res[0], 'error')
+    self.assertEquals(all_promise.state, promise.Promise.STATE_REJECTED)
+
+  def testRace(self):
+    promises_and_functions = [_GetPromiseAndFunctions() for x in xrange(10)]
+    promises = [x[0] for x in promises_and_functions]
+    race_promise = promise.Promise.Race(*promises)
+    res = []
+    def AddToRes(values):
+      res.append(values)
+    race_promise.Then(AddToRes, AddToRes)
+    self.assertEquals(len(res), 0)
+    promises_and_functions[7][1]('success')
+    self.assertEquals(len(res), 1)
+    for i, (f) in enumerate(promises_and_functions):
+      f[1 + (i % 2)](i)
+    self.assertEquals(len(res), 1)
+    self.assertEquals(res[0], 'success')
+    self.assertEquals(race_promise.state, promise.Promise.STATE_FULLFILLED)
+
+  def testRaceFailure(self):
+    promises_and_functions = [_GetPromiseAndFunctions() for x in xrange(10)]
+    promises = [x[0] for x in promises_and_functions]
+    race_promise = promise.Promise.Race(*promises)
+    res = []
+    def AddToRes(values):
+      res.append(values)
+    race_promise.Then(AddToRes, AddToRes)
+    self.assertEquals(len(res), 0)
+    promises_and_functions[7][2]('error')
+    self.assertEquals(len(res), 1)
+    for i, (f) in enumerate(promises_and_functions):
+      f[1 + (i % 2)](i)
+    self.assertEquals(len(res), 1)
+    self.assertEquals(res[0], 'error')
+    self.assertEquals(race_promise.state, promise.Promise.STATE_REJECTED)
+
+
+def _GetPromiseAndFunctions():
+  functions = {}
+  def GeneratorFunction(resolve, reject):
+    functions['resolve'] = resolve
+    functions['reject'] = reject
+  p = promise.Promise(GeneratorFunction)
+  return (p, functions['resolve'], functions['reject'])
+
+
+def _ThrowException(x):
+  raise RuntimeError(x)
diff --git a/mojo/python/tests/runloop_unittest.py b/mojo/python/tests/runloop_unittest.py
new file mode 100644
index 0000000..3549dbe
--- /dev/null
+++ b/mojo/python/tests/runloop_unittest.py
@@ -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.
+
+import unittest
+
+# pylint: disable=F0401
+import mojo.embedder
+from mojo import system
+
+
+def _Increment(array):
+  def _Closure():
+    array.append(0)
+  return _Closure
+
+
+class RunLoopTest(unittest.TestCase):
+
+  def setUp(self):
+    mojo.embedder.Init()
+
+  def testRunLoop(self):
+    loop = system.RunLoop()
+    array = []
+    for _ in xrange(10):
+      loop.PostDelayedTask(_Increment(array))
+    loop.RunUntilIdle()
+    self.assertEquals(len(array), 10)
+
+  def testRunLoopWithException(self):
+    loop = system.RunLoop()
+    def Throw():
+      raise Exception("error")
+    array = []
+    loop.PostDelayedTask(Throw)
+    loop.PostDelayedTask(_Increment(array))
+    with self.assertRaisesRegexp(Exception, '^error$'):
+      loop.Run()
+    self.assertEquals(len(array), 0)
+    loop.RunUntilIdle()
+    self.assertEquals(len(array), 1)
diff --git a/mojo/python/tests/system_unittest.py b/mojo/python/tests/system_unittest.py
new file mode 100644
index 0000000..2f0e36a
--- /dev/null
+++ b/mojo/python/tests/system_unittest.py
@@ -0,0 +1,310 @@
+# Copyright 2014 The Chromium 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 random
+import sys
+import time
+import unittest
+
+# pylint: disable=F0401
+import mojo.embedder
+from mojo import system
+
+DATA_SIZE = 1024
+
+
+def _GetRandomBuffer(size):
+  random.seed(size)
+  return bytearray(''.join(chr(random.randint(0, 255)) for i in xrange(size)))
+
+
+class BaseMojoTest(unittest.TestCase):
+
+  def setUp(self):
+    mojo.embedder.Init()
+
+
+class CoreTest(BaseMojoTest):
+
+  def testResults(self):
+    self.assertEquals(system.RESULT_OK, 0)
+    self.assertLess(system.RESULT_CANCELLED, 0)
+    self.assertLess(system.RESULT_UNKNOWN, 0)
+    self.assertLess(system.RESULT_INVALID_ARGUMENT, 0)
+    self.assertLess(system.RESULT_DEADLINE_EXCEEDED, 0)
+    self.assertLess(system.RESULT_NOT_FOUND, 0)
+    self.assertLess(system.RESULT_ALREADY_EXISTS, 0)
+    self.assertLess(system.RESULT_PERMISSION_DENIED, 0)
+    self.assertLess(system.RESULT_RESOURCE_EXHAUSTED, 0)
+    self.assertLess(system.RESULT_FAILED_PRECONDITION, 0)
+    self.assertLess(system.RESULT_ABORTED, 0)
+    self.assertLess(system.RESULT_OUT_OF_RANGE, 0)
+    self.assertLess(system.RESULT_UNIMPLEMENTED, 0)
+    self.assertLess(system.RESULT_INTERNAL, 0)
+    self.assertLess(system.RESULT_UNAVAILABLE, 0)
+    self.assertLess(system.RESULT_DATA_LOSS, 0)
+    self.assertLess(system.RESULT_BUSY, 0)
+    self.assertLess(system.RESULT_SHOULD_WAIT, 0)
+
+  def testConstants(self):
+    self.assertGreaterEqual(system.DEADLINE_INDEFINITE, 0)
+    self.assertGreaterEqual(system.HANDLE_SIGNAL_NONE, 0)
+    self.assertGreaterEqual(system.HANDLE_SIGNAL_READABLE, 0)
+    self.assertGreaterEqual(system.HANDLE_SIGNAL_WRITABLE, 0)
+    self.assertGreaterEqual(system.WRITE_MESSAGE_FLAG_NONE, 0)
+    self.assertGreaterEqual(system.READ_MESSAGE_FLAG_NONE, 0)
+    self.assertGreaterEqual(system.READ_MESSAGE_FLAG_MAY_DISCARD, 0)
+    self.assertGreaterEqual(system.WRITE_DATA_FLAG_NONE, 0)
+    self.assertGreaterEqual(system.WRITE_DATA_FLAG_ALL_OR_NONE, 0)
+    self.assertGreaterEqual(system.READ_DATA_FLAG_NONE, 0)
+    self.assertGreaterEqual(system.READ_DATA_FLAG_ALL_OR_NONE, 0)
+    self.assertGreaterEqual(system.READ_DATA_FLAG_DISCARD, 0)
+    self.assertGreaterEqual(system.READ_DATA_FLAG_QUERY, 0)
+    self.assertGreaterEqual(system.MAP_BUFFER_FLAG_NONE, 0)
+
+  def testGetTimeTicksNow(self):
+    pt1 = time.time()
+    v1 = system.GetTimeTicksNow()
+    time.sleep(1e-3)
+    v2 = system.GetTimeTicksNow()
+    pt2 = time.time()
+    self.assertGreater(v1, 0)
+    self.assertGreater(v2, v1 + 1000)
+    self.assertGreater(1e6 * (pt2 - pt1), v2 - v1)
+
+  def _testHandlesCreation(self, *args):
+    for handle in args:
+      self.assertTrue(handle.IsValid())
+      handle.Close()
+      self.assertFalse(handle.IsValid())
+
+  def _TestMessageHandleCreation(self, handles):
+    self._testHandlesCreation(handles.handle0, handles.handle1)
+
+  def testCreateMessagePipe(self):
+    self._TestMessageHandleCreation(system.MessagePipe())
+
+  def testCreateMessagePipeWithNoneOptions(self):
+    self._TestMessageHandleCreation(system.MessagePipe(None))
+
+  def testCreateMessagePipeWithOptions(self):
+    self._TestMessageHandleCreation(
+        system.MessagePipe(system.CreateMessagePipeOptions()))
+
+  def testWaitOverMessagePipe(self):
+    handles = system.MessagePipe()
+    handle = handles.handle0
+
+    self.assertEquals(system.RESULT_OK, handle.Wait(
+        system.HANDLE_SIGNAL_WRITABLE, system.DEADLINE_INDEFINITE))
+    self.assertEquals(system.RESULT_DEADLINE_EXCEEDED,
+                      handle.Wait(system.HANDLE_SIGNAL_READABLE, 0))
+
+    handles.handle1.WriteMessage()
+
+    self.assertEquals(
+        system.RESULT_OK,
+        handle.Wait(
+            system.HANDLE_SIGNAL_READABLE,
+            system.DEADLINE_INDEFINITE))
+
+  def testWaitOverManyMessagePipe(self):
+    handles = system.MessagePipe()
+    handle0 = handles.handle0
+    handle1 = handles.handle1
+
+    self.assertEquals(
+        0,
+        system.WaitMany(
+            [(handle0, system.HANDLE_SIGNAL_WRITABLE),
+             (handle1, system.HANDLE_SIGNAL_WRITABLE)],
+            system.DEADLINE_INDEFINITE))
+    self.assertEquals(
+        system.RESULT_DEADLINE_EXCEEDED,
+        system.WaitMany(
+            [(handle0, system.HANDLE_SIGNAL_READABLE),
+             (handle1, system.HANDLE_SIGNAL_READABLE)], 0))
+
+    handle0.WriteMessage()
+
+    self.assertEquals(
+        1,
+        system.WaitMany(
+            [(handle0, system.HANDLE_SIGNAL_READABLE),
+             (handle1, system.HANDLE_SIGNAL_READABLE)],
+            system.DEADLINE_INDEFINITE))
+
+  def testSendBytesOverMessagePipe(self):
+    handles = system.MessagePipe()
+    data = _GetRandomBuffer(DATA_SIZE)
+    handles.handle0.WriteMessage(data)
+    (res, buffers, next_message) = handles.handle1.ReadMessage()
+    self.assertEquals(system.RESULT_RESOURCE_EXHAUSTED, res)
+    self.assertEquals(None, buffers)
+    self.assertEquals((DATA_SIZE, 0), next_message)
+    result = bytearray(DATA_SIZE)
+    (res, buffers, next_message) = handles.handle1.ReadMessage(result)
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertEquals(None, next_message)
+    self.assertEquals((data, []), buffers)
+
+  def testSendEmptyDataOverMessagePipe(self):
+    handles = system.MessagePipe()
+    handles.handle0.WriteMessage(None)
+    (res, buffers, next_message) = handles.handle1.ReadMessage()
+
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertEquals(None, next_message)
+    self.assertEquals((None, []), buffers)
+
+  def testSendHandleOverMessagePipe(self):
+    handles = system.MessagePipe()
+    handles_to_send = system.MessagePipe()
+    handles.handle0.WriteMessage(handles=[handles_to_send.handle0,
+                                           handles_to_send.handle1])
+    (res, buffers, next_message) = handles.handle1.ReadMessage(
+        max_number_of_handles=2)
+
+    self.assertFalse(handles_to_send.handle0.IsValid())
+    self.assertFalse(handles_to_send.handle1.IsValid())
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertEquals(None, next_message)
+    self.assertEquals(None, buffers[0])
+    self.assertEquals(2, len(buffers[1]))
+
+    handles = buffers[1]
+    for handle in handles:
+      self.assertTrue(handle.IsValid())
+      (res, buffers, next_message) = handle.ReadMessage()
+      self.assertEquals(system.RESULT_SHOULD_WAIT, res)
+
+    for handle in handles:
+      handle.WriteMessage()
+
+    for handle in handles:
+      (res, buffers, next_message) = handle.ReadMessage()
+      self.assertEquals(system.RESULT_OK, res)
+
+  def _TestDataHandleCreation(self, handles):
+    self._testHandlesCreation(
+        handles.producer_handle, handles.consumer_handle)
+
+  def testCreateDataPipe(self):
+    self._TestDataHandleCreation(system.DataPipe())
+
+  def testCreateDataPipeWithNoneOptions(self):
+    self._TestDataHandleCreation(system.DataPipe(None))
+
+  def testCreateDataPipeWithDefaultOptions(self):
+    self._TestDataHandleCreation(
+        system.DataPipe(system.CreateDataPipeOptions()))
+
+  def testCreateDataPipeWithDiscardFlag(self):
+    options = system.CreateDataPipeOptions()
+    options.flags = system.CreateDataPipeOptions.FLAG_MAY_DISCARD
+    self._TestDataHandleCreation(system.DataPipe(options))
+
+  def testCreateDataPipeWithElementSize(self):
+    options = system.CreateDataPipeOptions()
+    options.element_num_bytes = 5
+    self._TestDataHandleCreation(system.DataPipe(options))
+
+  def testCreateDataPipeWithCapacity(self):
+    options = system.CreateDataPipeOptions()
+    options.element_capacity_num_bytes = DATA_SIZE
+    self._TestDataHandleCreation(system.DataPipe(options))
+
+  def testCreateDataPipeWithIncorrectParameters(self):
+    options = system.CreateDataPipeOptions()
+    options.element_num_bytes = 5
+    options.capacity_num_bytes = DATA_SIZE
+    with self.assertRaises(system.MojoException) as cm:
+      self._TestDataHandleCreation(system.DataPipe(options))
+    self.assertEquals(system.RESULT_INVALID_ARGUMENT, cm.exception.mojo_result)
+
+  def testSendEmptyDataOverDataPipe(self):
+    pipes = system.DataPipe()
+    self.assertEquals((system.RESULT_OK, 0), pipes.producer_handle.WriteData())
+    self.assertEquals(
+        (system.RESULT_OK, None), pipes.consumer_handle.ReadData())
+
+  def testSendDataOverDataPipe(self):
+    pipes = system.DataPipe()
+    data = _GetRandomBuffer(DATA_SIZE)
+    self.assertEquals((system.RESULT_OK, DATA_SIZE),
+                      pipes.producer_handle.WriteData(data))
+    self.assertEquals((system.RESULT_OK, data),
+                      pipes.consumer_handle.ReadData(bytearray(DATA_SIZE)))
+
+  def testTwoPhaseWriteOnDataPipe(self):
+    pipes = system.DataPipe()
+    (res, buf) = pipes.producer_handle.BeginWriteData(DATA_SIZE)
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertGreaterEqual(len(buf.buffer), DATA_SIZE)
+    data = _GetRandomBuffer(DATA_SIZE)
+    buf.buffer[0:DATA_SIZE] = data
+    self.assertEquals(system.RESULT_OK, buf.End(DATA_SIZE))
+    self.assertEquals((system.RESULT_OK, data),
+                      pipes.consumer_handle.ReadData(bytearray(DATA_SIZE)))
+
+  def testTwoPhaseReadOnDataPipe(self):
+    pipes = system.DataPipe()
+    data = _GetRandomBuffer(DATA_SIZE)
+    self.assertEquals((system.RESULT_OK, DATA_SIZE),
+                      pipes.producer_handle.WriteData(data))
+    (res, buf) = pipes.consumer_handle.BeginReadData()
+    self.assertEquals(system.RESULT_OK, res)
+    self.assertEquals(DATA_SIZE, len(buf.buffer))
+    self.assertEquals(data, buf.buffer)
+    self.assertEquals(system.RESULT_OK, buf.End(DATA_SIZE))
+
+  def testCreateSharedBuffer(self):
+    self._testHandlesCreation(system.CreateSharedBuffer(DATA_SIZE))
+
+  def testCreateSharedBufferWithNoneOptions(self):
+    self._testHandlesCreation(system.CreateSharedBuffer(DATA_SIZE, None))
+
+  def testCreateSharedBufferWithDefaultOptions(self):
+    self._testHandlesCreation(
+        system.CreateSharedBuffer(
+            DATA_SIZE,
+            system.CreateSharedBufferOptions()))
+
+  def testDuplicateSharedBuffer(self):
+    handle = system.CreateSharedBuffer(DATA_SIZE)
+    self._testHandlesCreation(handle.Duplicate())
+
+  def testDuplicateSharedBufferWithNoneOptions(self):
+    handle = system.CreateSharedBuffer(DATA_SIZE)
+    self._testHandlesCreation(handle.Duplicate(None))
+
+  def testDuplicateSharedBufferWithDefaultOptions(self):
+    handle = system.CreateSharedBuffer(DATA_SIZE)
+    self._testHandlesCreation(
+        handle.Duplicate(system.DuplicateSharedBufferOptions()))
+
+  def testSendBytesOverSharedBuffer(self):
+    handle = system.CreateSharedBuffer(DATA_SIZE)
+    duplicated = handle.Duplicate()
+    data = _GetRandomBuffer(DATA_SIZE)
+    (res1, buf1) = handle.Map(0, DATA_SIZE)
+    (res2, buf2) = duplicated.Map(0, DATA_SIZE)
+    self.assertEquals(system.RESULT_OK, res1)
+    self.assertEquals(system.RESULT_OK, res2)
+    self.assertEquals(DATA_SIZE, len(buf1.buffer))
+    self.assertEquals(DATA_SIZE, len(buf2.buffer))
+    self.assertEquals(buf1.buffer, buf2.buffer)
+
+    buf1.buffer[:] = data
+    self.assertEquals(data, buf1.buffer)
+    self.assertEquals(data, buf2.buffer)
+    self.assertEquals(buf1.buffer, buf2.buffer)
+
+
+if __name__ == '__main__':
+  suite = unittest.TestLoader().loadTestsFromTestCase(CoreTest)
+  test_results = unittest.TextTestRunner(verbosity=0).run(suite)
+  if not test_results.wasSuccessful():
+    sys.exit(1)
+  sys.exit(0)
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)));
+}
diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn
new file mode 100644
index 0000000..f405bc9
--- /dev/null
+++ b/mojo/shell/BUILD.gn
@@ -0,0 +1,199 @@
+# Copyright 2014 The Chromium 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")
+import("//build/config/ui.gni")
+
+executable("mojo_shell") {
+  deps = [
+    ":lib",
+    "//base",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+  ]
+
+  if (is_component_build) {
+    deps += ["//ui/gl"]
+  }
+
+  sources = [
+    "desktop/mojo_main.cc"
+  ]
+}
+
+# GYP version: mojo/mojo.gyp:mojo_shell_lib
+source_set("lib") {
+  deps = [
+    ":app_child_process_bindings",
+    ":external_application_registrar_bindings",
+    ":external_service_bindings",
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//base:base_static",
+    "//mojo/application",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/gles2",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/interfaces/network",
+    "//mojo/spy",
+  ]
+
+  sources = [
+    "app_child_process.cc",
+    "app_child_process.h",
+    "app_child_process_host.cc",
+    "app_child_process_host.h",
+    "child_process.cc",
+    "child_process.h",
+    "child_process_host.cc",
+    "child_process_host.h",
+    "context.cc",
+    "context.h",
+    "dynamic_application_loader.cc",
+    "dynamic_application_loader.h",
+    "dynamic_service_runner.h",
+    "external_application_listener_posix.cc",
+    "external_application_listener_win.cc",
+    "external_application_listener.h",
+    "incoming_connection_listener_posix.cc",
+    "incoming_connection_listener_posix.h",
+    "init.cc",
+    "init.h",
+    "in_process_dynamic_service_runner.cc",
+    "in_process_dynamic_service_runner.h",
+    "mojo_url_resolver.cc",
+    "mojo_url_resolver.h",
+    "out_of_process_dynamic_service_runner.cc",
+    "out_of_process_dynamic_service_runner.h",
+    "switches.cc",
+    "switches.h",
+    "task_runners.cc",
+    "task_runners.h",
+    "test_child_process.cc",
+    "test_child_process.h",
+    "ui_application_loader_android.cc",
+    "ui_application_loader_android.h",
+  ]
+
+  if (is_android) {
+    deps += [
+      "//mojo/services/native_viewport:lib",
+      "//mojo/services/network:lib",
+    ]
+    sources += [
+      "network_application_loader.cc",
+      "network_application_loader.h",
+    ]
+  }
+}
+
+mojom("app_child_process_bindings") {
+  sources = [
+    "app_child_process.mojom"
+  ]
+}
+
+mojom("external_service_bindings") {
+  sources = [
+    "external_service.mojom"
+  ]
+}
+
+mojom("external_application_registrar_bindings") {
+  sources = [
+    "external_application_registrar.mojom"
+  ]
+
+  deps = [ "//mojo/public/interfaces/application" ]
+}
+
+source_set("external_application_registrar_connection") {
+  sources = [
+    "external_application_registrar_connection.cc",
+    "external_application_registrar_connection.h",
+  ]
+
+  deps = [
+   ":external_application_registrar_bindings",
+   "//net"
+  ]
+}
+
+# GYP version: mojo/mojo.gyp:mojo_shell_tests
+test("mojo_shell_tests") {
+  deps = [
+    ":lib",
+    "//base",
+    "//base:i18n",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//net:test_support",
+    "//url",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/services/test_service:bindings",
+  ]
+
+  datadeps = [
+    "//mojo/services/test_service:mojo_test_app",
+    "//mojo/services/test_service:mojo_test_request_tracker_app",
+  ]
+
+  sources = [
+    "child_process_host_unittest.cc",
+    "dynamic_application_loader_unittest.cc",
+    "in_process_dynamic_service_runner_unittest.cc",
+    "shell_test_base.cc",
+    "shell_test_base.h",
+    "shell_test_base_unittest.cc",
+    "shell_test_main.cc",
+  ]
+
+  if (is_android) {
+    deps += [
+      # TODO(GYP):
+      #'../testing/android/native_test.gyp:native_test_native_code',
+    ]
+  }
+}
+
+# GYP version: mojo/mojo.gyp:mojo_shell_test_support
+source_set("test_support") {
+  sources = [
+    "shell_test_helper.cc",
+    "shell_test_helper.h",
+  ]
+
+  deps = [
+    ":lib",
+    "//mojo/edk/system",
+  ]
+}
+
+# GYP version: mojo/mojo.gyp:external_application_tests
+test("mojo_external_application_tests") {
+  deps = [
+    ":lib",
+    ":external_application_registrar_connection",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//net:test_support",
+    "//url",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+  ]
+
+  sources = [
+    "incoming_connection_listener_unittest.cc",
+    "external_application_listener_unittest.cc",
+    "external_application_test_main.cc",
+  ]
+}
diff --git a/mojo/shell/DEPS b/mojo/shell/DEPS
new file mode 100644
index 0000000..1370103
--- /dev/null
+++ b/mojo/shell/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+gpu",
+  "+mojo/edk/embedder",
+  "+net",
+  "+ui/gfx/switches.h",
+  "+ui/gl",
+]
diff --git a/mojo/shell/android/DEPS b/mojo/shell/android/DEPS
new file mode 100644
index 0000000..c80012b
--- /dev/null
+++ b/mojo/shell/android/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+jni",
+]
diff --git a/mojo/shell/android/apk/AndroidManifest.xml b/mojo/shell/android/apk/AndroidManifest.xml
new file mode 100644
index 0000000..32b48e9
--- /dev/null
+++ b/mojo/shell/android/apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.chromium.mojo_shell_apk">
+
+    <application android:name="MojoShellApplication"
+            android:label="Mojo Shell">
+        <activity android:name="MojoShellActivity"
+                  android:launchMode="singleTask"
+                  android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+                  android:hardwareAccelerated="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="20" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml b/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml
new file mode 100644
index 0000000..d319941
--- /dev/null
+++ b/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+</LinearLayout>
diff --git a/mojo/shell/android/apk/res/values/strings.xml b/mojo/shell/android/apk/res/values/strings.xml
new file mode 100644
index 0000000..ff3f8bb
--- /dev/null
+++ b/mojo/shell/android/apk/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+ -->
+
+<resources>
+</resources>
diff --git a/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java
new file mode 100644
index 0000000..7a27003
--- /dev/null
+++ b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java
@@ -0,0 +1,43 @@
+// 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_shell_apk;
+
+import android.content.Context;
+
+import org.chromium.base.JNINamespace;
+
+/**
+ * A placeholder class to call native functions.
+ **/
+@JNINamespace("mojo")
+public class MojoMain {
+    /**
+     * A guard flag for calling nativeInit() only once.
+     **/
+    private static boolean sInitialized = false;
+
+    /**
+     * Initializes the native system.
+     **/
+    public static void ensureInitialized(Context context) {
+        if (sInitialized)
+            return;
+        nativeInit(context);
+        sInitialized = true;
+    }
+
+    /**
+     * Starts the specified application in the specified context.
+     **/
+    public static void start(final String appUrl) {
+        nativeStart(appUrl);
+    }
+
+    /**
+     * Initializes the native system. This API should be called only once per process.
+     **/
+    private static native void nativeInit(Context context);
+    private static native void nativeStart(String appUrl);
+};
diff --git a/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java
new file mode 100644
index 0000000..ba2a324
--- /dev/null
+++ b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java
@@ -0,0 +1,67 @@
+// 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_shell_apk;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.EditText;
+
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.ProcessInitException;
+
+/**
+ * Activity for managing the Mojo Shell.
+ */
+public class MojoShellActivity extends Activity {
+    private static final String TAG = "MojoShellActivity";
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        try {
+            LibraryLoader.ensureInitialized();
+        } catch (ProcessInitException e) {
+            Log.e(TAG, "libmojo_shell initialization failed.", e);
+            finish();
+            return;
+        }
+
+        MojoMain.ensureInitialized(this);
+
+        String appUrl = getUrlFromIntent(getIntent());
+        if (appUrl == null) {
+            Log.i(TAG, "No URL provided via intent, prompting user...");
+            AlertDialog.Builder alert = new AlertDialog.Builder(this);
+            alert.setTitle("Enter a URL");
+            alert.setMessage("Enter a URL");
+            final EditText input = new EditText(this);
+            alert.setView(input);
+            alert.setPositiveButton("Load", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int button) {
+                    String url = input.getText().toString();
+                    startWithURL(url);
+                }
+            });
+            alert.show();
+        } else {
+            startWithURL(appUrl);
+        }
+    }
+
+    private static String getUrlFromIntent(Intent intent) {
+        return intent != null ? intent.getDataString() : null;
+    }
+
+    private void startWithURL(String url) {
+        MojoMain.start(url);
+        Log.i(TAG, "Mojo started: " + url);
+    }
+}
diff --git a/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java
new file mode 100644
index 0000000..df927ee
--- /dev/null
+++ b/mojo/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java
@@ -0,0 +1,27 @@
+// 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_shell_apk;
+
+import android.util.Log;
+
+import org.chromium.base.BaseChromiumApplication;
+import org.chromium.base.PathUtils;
+
+public class MojoShellApplication extends BaseChromiumApplication {
+    private static final String TAG = "MojoShellApplication";
+    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "mojo_shell";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        initializeApplicationParameters();
+    }
+
+    public static void initializeApplicationParameters() {
+        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
+        Log.i(TAG, "MojoShellApplication.initializeApplicationParameters() success.");
+    }
+
+}
diff --git a/mojo/shell/android/library_loader.cc b/mojo/shell/android/library_loader.cc
new file mode 100644
index 0000000..26a99a5
--- /dev/null
+++ b/mojo/shell/android/library_loader.cc
@@ -0,0 +1,47 @@
+// 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/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/logging.h"
+#include "mojo/services/native_viewport/platform_viewport_android.h"
+#include "mojo/shell/android/mojo_main.h"
+#include "net/android/net_jni_registrar.h"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+  { "MojoMain", mojo::RegisterMojoMain },
+  { "PlatformViewportAndroid",
+     mojo::PlatformViewportAndroid::Register },
+};
+
+bool RegisterMojoJni(JNIEnv* env) {
+  return RegisterNativeMethods(env, kMojoRegisteredMethods,
+                               arraysize(kMojoRegisteredMethods));
+}
+
+}  // namespace
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!base::android::RegisterLibraryLoaderEntryHook(env))
+    return -1;
+
+  if (!base::android::RegisterJni(env))
+    return -1;
+
+  if (!net::android::RegisterJni(env))
+    return -1;
+
+  if (!RegisterMojoJni(env))
+    return -1;
+
+  return JNI_VERSION_1_4;
+}
diff --git a/mojo/shell/android/mojo_main.cc b/mojo/shell/android/mojo_main.cc
new file mode 100644
index 0000000..832e0aa
--- /dev/null
+++ b/mojo/shell/android/mojo_main.cc
@@ -0,0 +1,94 @@
+// 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/shell/android/mojo_main.h"
+
+#include "base/android/java_handler_thread.h"
+#include "base/android/jni_string.h"
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/MojoMain_jni.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/init.h"
+#include "ui/gl/gl_surface_egl.h"
+
+using base::LazyInstance;
+
+namespace mojo {
+
+namespace {
+
+LazyInstance<scoped_ptr<base::MessageLoop> > g_java_message_loop =
+    LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<scoped_ptr<shell::Context> > g_context =
+    LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<scoped_ptr<base::android::JavaHandlerThread> > g_shell_thread =
+    LAZY_INSTANCE_INITIALIZER;
+
+void RunShell(std::vector<GURL> app_urls) {
+  shell::Context* context = g_context.Pointer()->get();
+  context->Init();
+  context->set_ui_loop(g_java_message_loop.Get().get());
+  for (std::vector<GURL>::const_iterator it = app_urls.begin();
+       it != app_urls.end(); ++it) {
+    context->Run(*it);
+  }
+}
+
+}  // namespace
+
+static void Init(JNIEnv* env, jclass clazz, jobject context) {
+  base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
+
+  base::android::InitApplicationContext(env, scoped_context);
+
+  base::CommandLine::Init(0, 0);
+  mojo::shell::InitializeLogging();
+
+  // We want ~MessageLoop to happen prior to ~Context. Initializing
+  // LazyInstances is akin to stack-allocating objects; their destructors
+  // will be invoked first-in-last-out.
+  shell::Context* shell_context = new shell::Context();
+  g_context.Get().reset(shell_context);
+  g_java_message_loop.Get().reset(new base::MessageLoopForUI);
+  base::MessageLoopForUI::current()->Start();
+
+  // TODO(abarth): At which point should we switch to cross-platform
+  // initialization?
+
+  gfx::GLSurface::InitializeOneOff();
+}
+
+static void Start(JNIEnv* env, jclass clazz, jstring jurl) {
+  std::vector<GURL> app_urls;
+#if defined(MOJO_SHELL_DEBUG_URL)
+  app_urls.push_back(GURL(MOJO_SHELL_DEBUG_URL));
+  // Sleep for 5 seconds to give the debugger a chance to attach.
+  sleep(5);
+#else
+  if (jurl)
+    app_urls.push_back(GURL(base::android::ConvertJavaStringToUTF8(env, jurl)));
+#endif
+
+  g_shell_thread.Get().reset(
+      new base::android::JavaHandlerThread("shell_thread"));
+  g_shell_thread.Get()->Start();
+  g_shell_thread.Get()->message_loop()->PostTask(
+      FROM_HERE, base::Bind(&RunShell, app_urls));
+}
+
+bool RegisterMojoMain(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace mojo
diff --git a/mojo/shell/android/mojo_main.h b/mojo/shell/android/mojo_main.h
new file mode 100644
index 0000000..161ca71
--- /dev/null
+++ b/mojo/shell/android/mojo_main.h
@@ -0,0 +1,16 @@
+// 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_SHELL_ANDROID_MOJO_MAIN_H_
+#define MOJO_SHELL_ANDROID_MOJO_MAIN_H_
+
+#include <jni.h>
+
+namespace mojo {
+
+bool RegisterMojoMain(JNIEnv* env);
+
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_ANDROID_MOJO_MAIN_H_
diff --git a/mojo/shell/app_child_process.cc b/mojo/shell/app_child_process.cc
new file mode 100644
index 0000000..7bf5420
--- /dev/null
+++ b/mojo/shell/app_child_process.cc
@@ -0,0 +1,292 @@
+// Copyright 2014 The Chromium 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/shell/app_child_process.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/scoped_native_library.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/shell/app_child_process.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Blocker ---------------------------------------------------------------------
+
+// Blocks a thread until another thread unblocks it, at which point it unblocks
+// and runs a closure provided by that thread.
+class Blocker {
+ public:
+  class Unblocker {
+   public:
+    ~Unblocker() {}
+
+    void Unblock(base::Closure run_after) {
+      DCHECK(blocker_);
+      DCHECK(blocker_->run_after_.is_null());
+      blocker_->run_after_ = run_after;
+      blocker_->event_.Signal();
+      blocker_ = NULL;
+    }
+
+   private:
+    friend class Blocker;
+    Unblocker(Blocker* blocker) : blocker_(blocker) {
+      DCHECK(blocker_);
+    }
+
+    Blocker* blocker_;
+
+    // Copy and assign allowed.
+  };
+
+  Blocker() : event_(true, false) {}
+  ~Blocker() {}
+
+  void Block() {
+    DCHECK(run_after_.is_null());
+    event_.Wait();
+    run_after_.Run();
+  }
+
+  Unblocker GetUnblocker() {
+    return Unblocker(this);
+  }
+
+ private:
+  base::WaitableEvent event_;
+  base::Closure run_after_;
+
+  DISALLOW_COPY_AND_ASSIGN(Blocker);
+};
+
+// AppContext ------------------------------------------------------------------
+
+class AppChildControllerImpl;
+
+static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) {
+}
+
+// Should be created and initialized on the main thread.
+class AppContext {
+ public:
+  AppContext()
+      : io_thread_("io_thread"),
+        controller_thread_("controller_thread") {}
+  ~AppContext() {}
+
+  void Init() {
+    // Initialize Mojo before starting any threads.
+    embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+        new mojo::embedder::SimplePlatformSupport()));
+
+    // Create and start our I/O thread.
+    base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
+    CHECK(io_thread_.StartWithOptions(io_thread_options));
+    io_runner_ = io_thread_.message_loop_proxy().get();
+    CHECK(io_runner_.get());
+
+    // Create and start our controller thread.
+    base::Thread::Options controller_thread_options;
+    controller_thread_options.message_loop_type =
+        base::MessageLoop::TYPE_CUSTOM;
+    controller_thread_options.message_pump_factory =
+        base::Bind(&common::MessagePumpMojo::Create);
+    CHECK(controller_thread_.StartWithOptions(controller_thread_options));
+    controller_runner_ = controller_thread_.message_loop_proxy().get();
+    CHECK(controller_runner_.get());
+  }
+
+  void Shutdown() {
+    controller_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&DestroyController, base::Passed(&controller_)));
+  }
+
+  base::SingleThreadTaskRunner* io_runner() const {
+    return io_runner_.get();
+  }
+
+  base::SingleThreadTaskRunner* controller_runner() const {
+    return controller_runner_.get();
+  }
+
+  AppChildControllerImpl* controller() const {
+    return controller_.get();
+  }
+
+  void set_controller(scoped_ptr<AppChildControllerImpl> controller) {
+    controller_ = controller.Pass();
+  }
+
+ private:
+  // Accessed only on the controller thread.
+  // IMPORTANT: This must be BEFORE |controller_thread_|, so that the controller
+  // thread gets joined (and thus |controller_| reset) before |controller_| is
+  // destroyed.
+  scoped_ptr<AppChildControllerImpl> controller_;
+
+  base::Thread io_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
+
+  base::Thread controller_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppContext);
+};
+
+// AppChildControllerImpl ------------------------------------------------------
+
+class AppChildControllerImpl : public InterfaceImpl<AppChildController> {
+ public:
+  virtual ~AppChildControllerImpl() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    // TODO(vtl): Pass in the result from |MainMain()|.
+    client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED);
+  }
+
+  // To be executed on the controller thread. Creates the |AppChildController|,
+  // etc.
+  static void Init(
+      AppContext* app_context,
+      embedder::ScopedPlatformHandle platform_channel,
+      const Blocker::Unblocker& unblocker) {
+    DCHECK(app_context);
+    DCHECK(platform_channel.is_valid());
+
+    DCHECK(!app_context->controller());
+
+    scoped_ptr<AppChildControllerImpl> impl(
+        new AppChildControllerImpl(app_context, unblocker));
+
+    ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
+        platform_channel.Pass(),
+        app_context->io_runner(),
+        base::Bind(&AppChildControllerImpl::DidCreateChannel,
+                   base::Unretained(impl.get())),
+        base::MessageLoopProxy::current()));
+
+    BindToPipe(impl.get(), host_message_pipe.Pass());
+
+    app_context->set_controller(impl.Pass());
+  }
+
+  virtual void OnConnectionError() override {
+    // TODO(darin): How should we handle a connection error here?
+  }
+
+  // |AppChildController| methods:
+  virtual void StartApp(const String& app_path,
+                        ScopedMessagePipeHandle service) override {
+    DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path << ", ...)";
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    unblocker_.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread,
+                                  base::FilePath::FromUTF8Unsafe(app_path),
+                                  base::Passed(&service)));
+  }
+
+ private:
+  AppChildControllerImpl(AppContext* app_context,
+                         const Blocker::Unblocker& unblocker)
+      : app_context_(app_context),
+        unblocker_(unblocker),
+        channel_info_(NULL) {
+  }
+
+  // Callback for |embedder::CreateChannel()|.
+  void DidCreateChannel(embedder::ChannelInfo* channel_info) {
+    DVLOG(2) << "AppChildControllerImpl::DidCreateChannel()";
+    DCHECK(thread_checker_.CalledOnValidThread());
+    channel_info_ = channel_info;
+  }
+
+  static void StartAppOnMainThread(const base::FilePath& app_path,
+                                   ScopedMessagePipeHandle service) {
+    // TODO(vtl): This is copied from in_process_dynamic_service_runner.cc.
+    DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
+             << " out of process";
+
+    do {
+      base::NativeLibraryLoadError load_error;
+      base::ScopedNativeLibrary app_library(
+          base::LoadNativeLibrary(app_path, &load_error));
+      if (!app_library.is_valid()) {
+        LOG(ERROR) << "Failed to load library (error: " << load_error.ToString()
+                   << ")";
+        break;
+      }
+
+      typedef MojoResult (*MojoMainFunction)(MojoHandle);
+      MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
+          app_library.GetFunctionPointer("MojoMain"));
+      if (!main_function) {
+        LOG(ERROR) << "Entrypoint MojoMain not found";
+        break;
+      }
+
+      // TODO(vtl): Report the result back to our parent process.
+      // |MojoMain()| takes ownership of the service handle.
+      MojoResult result = main_function(service.release().value());
+      if (result < MOJO_RESULT_OK)
+        LOG(ERROR) << "MojoMain returned an error: " << result;
+    } while (false);
+  }
+
+  base::ThreadChecker thread_checker_;
+  AppContext* const app_context_;
+  Blocker::Unblocker unblocker_;
+
+  embedder::ChannelInfo* channel_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
+};
+
+}  // namespace
+
+// AppChildProcess -------------------------------------------------------------
+
+AppChildProcess::AppChildProcess() {
+}
+
+AppChildProcess::~AppChildProcess() {
+}
+
+void AppChildProcess::Main() {
+  DVLOG(2) << "AppChildProcess::Main()";
+
+  AppContext app_context;
+  app_context.Init();
+
+  Blocker blocker;
+  app_context.controller_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&AppChildControllerImpl::Init, base::Unretained(&app_context),
+                 base::Passed(platform_channel()), blocker.GetUnblocker()));
+  // This will block, then run whatever the controller wants.
+  blocker.Block();
+
+  app_context.Shutdown();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/app_child_process.h b/mojo/shell/app_child_process.h
new file mode 100644
index 0000000..9b3b8cf
--- /dev/null
+++ b/mojo/shell/app_child_process.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_SHELL_APP_CHILD_PROCESS_H_
+#define MOJO_SHELL_APP_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "mojo/shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |ChildProcess| for a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system) on its main thread.
+class AppChildProcess : public ChildProcess {
+ public:
+  AppChildProcess();
+  virtual ~AppChildProcess();
+
+  virtual void Main() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_APP_CHILD_PROCESS_H_
diff --git a/mojo/shell/app_child_process.mojom b/mojo/shell/app_child_process.mojom
new file mode 100644
index 0000000..d8cef12
--- /dev/null
+++ b/mojo/shell/app_child_process.mojom
@@ -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.
+
+module mojo.shell {
+
+[Client=AppChildControllerClient]
+interface AppChildController {
+  // TODO(vtl): |service| should be of a more specific type.
+  StartApp(string? app_path, handle<message_pipe>? service);
+};
+
+interface AppChildControllerClient {
+  AppCompleted(int32 result);
+};
+
+}  // module mojo.shell
diff --git a/mojo/shell/app_child_process_host.cc b/mojo/shell/app_child_process_host.cc
new file mode 100644
index 0000000..8da4772
--- /dev/null
+++ b/mojo/shell/app_child_process_host.cc
@@ -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.
+
+#include "mojo/shell/app_child_process_host.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/task_runners.h"
+
+namespace mojo {
+namespace shell {
+
+AppChildProcessHost::AppChildProcessHost(
+    Context* context,
+    AppChildControllerClient* controller_client)
+    : ChildProcessHost(context, this, ChildProcess::TYPE_APP),
+      controller_client_(controller_client),
+      channel_info_(NULL) {
+}
+
+AppChildProcessHost::~AppChildProcessHost() {
+}
+
+void AppChildProcessHost::WillStart() {
+  DCHECK(platform_channel()->is_valid());
+
+  mojo::ScopedMessagePipeHandle handle(embedder::CreateChannel(
+      platform_channel()->Pass(),
+      context()->task_runners()->io_runner(),
+      base::Bind(&AppChildProcessHost::DidCreateChannel,
+                 base::Unretained(this)),
+      base::MessageLoop::current()->message_loop_proxy()));
+
+  controller_.Bind(handle.Pass());
+  controller_.set_client(controller_client_);
+}
+
+void AppChildProcessHost::DidStart(bool success) {
+  DVLOG(2) << "AppChildProcessHost::DidStart()";
+
+  if (!success) {
+    LOG(ERROR) << "Failed to start app child process";
+    controller_client_->AppCompleted(MOJO_RESULT_UNKNOWN);
+    return;
+  }
+}
+
+// Callback for |embedder::CreateChannel()|.
+void AppChildProcessHost::DidCreateChannel(
+    embedder::ChannelInfo* channel_info) {
+  DVLOG(2) << "AppChildProcessHost::DidCreateChannel()";
+
+  CHECK(channel_info);
+  channel_info_ = channel_info;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/app_child_process_host.h b/mojo/shell/app_child_process_host.h
new file mode 100644
index 0000000..f65e2a4
--- /dev/null
+++ b/mojo/shell/app_child_process_host.h
@@ -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.
+
+#ifndef MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
+#define MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "mojo/shell/app_child_process.mojom.h"
+#include "mojo/shell/child_process_host.h"
+
+namespace mojo {
+
+namespace embedder {
+struct ChannelInfo;
+}
+
+namespace shell {
+
+// A subclass of |ChildProcessHost| to host a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system).
+//
+// Note: After |Start()|, this object must remain alive until the controller
+// client's |AppCompleted()| is called.
+class AppChildProcessHost : public ChildProcessHost,
+                            public ChildProcessHost::Delegate {
+ public:
+  AppChildProcessHost(Context* context,
+                      AppChildControllerClient* controller_client);
+  virtual ~AppChildProcessHost();
+
+  AppChildController* controller() {
+    return controller_.get();
+  }
+
+ private:
+  // |ChildProcessHost::Delegate| methods:
+  virtual void WillStart() override;
+  virtual void DidStart(bool success) override;
+
+  // Callback for |embedder::CreateChannel()|.
+  void DidCreateChannel(embedder::ChannelInfo* channel_info);
+
+  AppChildControllerClient* const controller_client_;
+
+  AppChildControllerPtr controller_;
+  embedder::ChannelInfo* channel_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppChildProcessHost);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
diff --git a/mojo/shell/child_process.cc b/mojo/shell/child_process.cc
new file mode 100644
index 0000000..79b9940
--- /dev/null
+++ b/mojo/shell/child_process.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/child_process.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/shell/app_child_process.h"
+#include "mojo/shell/switches.h"
+#include "mojo/shell/test_child_process.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcess::~ChildProcess() {
+}
+
+// static
+scoped_ptr<ChildProcess> ChildProcess::Create(
+    const base::CommandLine& command_line) {
+  if (!command_line.HasSwitch(switches::kChildProcessType))
+    return scoped_ptr<ChildProcess>();
+
+  int type_as_int;
+  CHECK(base::StringToInt(command_line.GetSwitchValueASCII(
+            switches::kChildProcessType), &type_as_int));
+
+  scoped_ptr<ChildProcess> rv;
+  switch (type_as_int) {
+    case TYPE_TEST:
+      rv.reset(new TestChildProcess());
+      break;
+    case TYPE_APP:
+      rv.reset(new AppChildProcess());
+      break;
+    default:
+      CHECK(false) << "Invalid child process type";
+      break;
+  }
+
+  if (rv) {
+    rv->platform_channel_ =
+        embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
+            command_line);
+    CHECK(rv->platform_channel_.is_valid());
+  }
+
+  return rv.Pass();
+}
+
+ChildProcess::ChildProcess() {
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/child_process.h b/mojo/shell/child_process.h
new file mode 100644
index 0000000..81780a3
--- /dev/null
+++ b/mojo/shell/child_process.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_CHILD_PROCESS_H_
+#define MOJO_SHELL_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace mojo {
+namespace shell {
+
+// A base class for child processes -- i.e., code that is actually run within
+// the child process. (Instances are manufactured by |Create()|.)
+class ChildProcess {
+ public:
+  enum Type {
+    TYPE_TEST,
+    // Hosts a single app (see app_child_process(_host).*).
+    TYPE_APP
+  };
+
+  virtual ~ChildProcess();
+
+  // Returns null if the command line doesn't indicate that this is a child
+  // process. |main()| should call this, and if it returns non-null it should
+  // call |Main()| (without a message loop on the current thread).
+  static scoped_ptr<ChildProcess> Create(const base::CommandLine& command_line);
+
+  // To be implemented by subclasses. This is the "entrypoint" for a child
+  // process. Run with no message loop for the main thread.
+  virtual void Main() = 0;
+
+ protected:
+  ChildProcess();
+
+  embedder::ScopedPlatformHandle* platform_channel() {
+    return &platform_channel_;
+  }
+
+ private:
+  // Available in |Main()| (after a successful |Create()|).
+  embedder::ScopedPlatformHandle platform_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_CHILD_PROCESS_H_
diff --git a/mojo/shell/child_process_host.cc b/mojo/shell/child_process_host.cc
new file mode 100644
index 0000000..5d79758
--- /dev/null
+++ b/mojo/shell/child_process_host.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/child_process_host.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/switches.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcessHost::ChildProcessHost(Context* context,
+                                   Delegate* delegate,
+                                   ChildProcess::Type type)
+    : context_(context),
+      delegate_(delegate),
+      type_(type),
+      child_process_handle_(base::kNullProcessHandle) {
+  DCHECK(delegate);
+  platform_channel_ = platform_channel_pair_.PassServerHandle();
+  CHECK(platform_channel_.is_valid());
+}
+
+ChildProcessHost::~ChildProcessHost() {
+  if (child_process_handle_ != base::kNullProcessHandle) {
+    LOG(WARNING) << "Destroying ChildProcessHost with unjoined child";
+    base::CloseProcessHandle(child_process_handle_);
+    child_process_handle_ = base::kNullProcessHandle;
+  }
+}
+
+void ChildProcessHost::Start() {
+  DCHECK_EQ(child_process_handle_, base::kNullProcessHandle);
+
+  delegate_->WillStart();
+
+  CHECK(base::PostTaskAndReplyWithResult(
+      context_->task_runners()->blocking_pool(),
+      FROM_HERE,
+      base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)),
+      base::Bind(&ChildProcessHost::DidLaunch, base::Unretained(this))));
+}
+
+int ChildProcessHost::Join() {
+  DCHECK_NE(child_process_handle_, base::kNullProcessHandle);
+  int rv = -1;
+  // Note: |WaitForExitCode()| closes the process handle.
+  LOG_IF(ERROR, !base::WaitForExitCode(child_process_handle_, &rv))
+      << "Failed to wait for child process";
+  child_process_handle_ = base::kNullProcessHandle;
+  return rv;
+}
+
+bool ChildProcessHost::DoLaunch() {
+  static const char* kForwardSwitches[] = {
+    switches::kTraceToConsole,
+    switches::kV,
+    switches::kVModule,
+  };
+
+  const base::CommandLine* parent_command_line =
+      base::CommandLine::ForCurrentProcess();
+  base::CommandLine child_command_line(parent_command_line->GetProgram());
+  child_command_line.CopySwitchesFrom(*parent_command_line, kForwardSwitches,
+                                      arraysize(kForwardSwitches));
+  child_command_line.AppendSwitchASCII(
+      switches::kChildProcessType, base::IntToString(static_cast<int>(type_)));
+
+  embedder::HandlePassingInformation handle_passing_info;
+  platform_channel_pair_.PrepareToPassClientHandleToChildProcess(
+      &child_command_line, &handle_passing_info);
+
+  base::LaunchOptions options;
+#if defined(OS_WIN)
+  options.start_hidden = true;
+  options.handles_to_inherit = &handle_passing_info;
+#elif defined(OS_POSIX)
+  options.fds_to_remap = &handle_passing_info;
+#endif
+
+  if (!base::LaunchProcess(child_command_line, options, &child_process_handle_))
+    return false;
+
+  platform_channel_pair_.ChildProcessLaunched();
+  return true;
+}
+
+void ChildProcessHost::DidLaunch(bool success) {
+  delegate_->DidStart(success);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/child_process_host.h b/mojo/shell/child_process_host.h
new file mode 100644
index 0000000..6e94b96
--- /dev/null
+++ b/mojo/shell/child_process_host.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_CHILD_PROCESS_HOST_H_
+#define MOJO_SHELL_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/shell/child_process.h"  // For |ChildProcess::Type|.
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// (Base) class for a "child process host". Handles launching and connecting a
+// platform-specific "pipe" to the child, and supports joining the child
+// process. Intended for use as a base class, but may be used on its own in
+// simple cases.
+//
+// This class is not thread-safe. It should be created/used/destroyed on a
+// single thread.
+//
+// Note: Does not currently work on Windows before Vista.
+class ChildProcessHost {
+ public:
+  class Delegate {
+   public:
+    virtual void WillStart() = 0;
+    virtual void DidStart(bool success) = 0;
+  };
+
+  ChildProcessHost(Context* context,
+                   Delegate* delegate,
+                   ChildProcess::Type type);
+  virtual ~ChildProcessHost();
+
+  // |Start()|s the child process; calls the delegate's |DidStart()| (on the
+  // thread on which |Start()| was called) when the child has been started (or
+  // failed to start). After calling |Start()|, this object must not be
+  // destroyed until |DidStart()| has been called.
+  // TODO(vtl): Consider using weak pointers and removing this requirement.
+  void Start();
+
+  // Waits for the child process to terminate, and returns its exit code.
+  // Note: If |Start()| has been called, this must not be called until the
+  // callback has been called.
+  int Join();
+
+  embedder::ScopedPlatformHandle* platform_channel() {
+    return &platform_channel_;
+  }
+
+ protected:
+  Context* context() const {
+    return context_;
+  }
+
+ private:
+  bool DoLaunch();
+  void DidLaunch(bool success);
+
+  Context* const context_;
+  Delegate* const delegate_;
+  const ChildProcess::Type type_;
+
+  base::ProcessHandle child_process_handle_;
+
+  embedder::PlatformChannelPair platform_channel_pair_;
+
+  // Platform-specific "pipe" to the child process. Valid immediately after
+  // creation.
+  embedder::ScopedPlatformHandle platform_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildProcessHost);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_CHILD_PROCESS_HOST_H_
diff --git a/mojo/shell/child_process_host_unittest.cc b/mojo/shell/child_process_host_unittest.cc
new file mode 100644
index 0000000..5223018
--- /dev/null
+++ b/mojo/shell/child_process_host_unittest.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.
+
+// Note: This file also tests child_process.*.
+
+#include "mojo/shell/child_process_host.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/shell/context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+class TestChildProcessHostDelegate : public ChildProcessHost::Delegate {
+ public:
+  TestChildProcessHostDelegate() {}
+  virtual ~TestChildProcessHostDelegate() {}
+  virtual void WillStart() override {
+    VLOG(2) << "TestChildProcessHostDelegate::WillStart()";
+  }
+  virtual void DidStart(bool success) override {
+    VLOG(2) << "TestChildProcessHostDelegate::DidStart(" << success << ")";
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+};
+
+typedef testing::Test ChildProcessHostTest;
+
+TEST_F(ChildProcessHostTest, Basic) {
+  Context context;
+  base::MessageLoop message_loop(
+      scoped_ptr<base::MessagePump>(new common::MessagePumpMojo()));
+  context.Init();
+  TestChildProcessHostDelegate child_process_host_delegate;
+  ChildProcessHost child_process_host(&context,
+                                      &child_process_host_delegate,
+                                      ChildProcess::TYPE_TEST);
+  child_process_host.Start();
+  message_loop.Run();
+  int exit_code = child_process_host.Join();
+  VLOG(2) << "Joined child: exit_code = " << exit_code;
+  EXPECT_EQ(0, exit_code);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc
new file mode 100644
index 0000000..e1c8d53
--- /dev/null
+++ b/mojo/shell/context.cc
@@ -0,0 +1,268 @@
+// 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/shell/context.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_split.h"
+#include "build/build_config.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/application_manager/background_shell_application_loader.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.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/shell/dynamic_application_loader.h"
+#include "mojo/shell/external_application_listener.h"
+#include "mojo/shell/in_process_dynamic_service_runner.h"
+#include "mojo/shell/out_of_process_dynamic_service_runner.h"
+#include "mojo/shell/switches.h"
+#include "mojo/shell/ui_application_loader_android.h"
+#include "mojo/spy/spy.h"
+
+#if defined(OS_ANDROID)
+#include "mojo/services/native_viewport/gpu_impl.h"
+#include "mojo/services/native_viewport/native_viewport_impl.h"
+#include "mojo/shell/network_application_loader.h"
+#include "ui/gl/gl_share_group.h"
+#endif  // defined(OS_ANDROID)
+
+namespace mojo {
+namespace shell {
+namespace {
+
+// These mojo: URLs are loaded directly from the local filesystem. They
+// correspond to shared libraries bundled alongside the mojo_shell.
+const char* kLocalMojoURLs[] = {
+  "mojo:mojo_network_service",
+};
+
+// Used to ensure we only init once.
+class Setup {
+ public:
+  Setup() {
+    embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+        new mojo::embedder::SimplePlatformSupport()));
+  }
+
+  ~Setup() {
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
+
+void InitContentHandlers(DynamicApplicationLoader* loader,
+                         base::CommandLine* command_line) {
+  // Default content handlers.
+  loader->RegisterContentHandler("image/png", GURL("mojo://mojo_png_viewer/"));
+  loader->RegisterContentHandler("text/html", GURL("mojo://mojo_html_viewer/"));
+
+  // Command-line-specified content handlers.
+  std::string handlers_spec = command_line->GetSwitchValueASCII(
+      switches::kContentHandlers);
+  if (handlers_spec.empty())
+    return;
+
+  std::vector<std::string> parts;
+  base::SplitString(handlers_spec, ',', &parts);
+  if (parts.size() % 2 != 0) {
+    LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
+               << ": must be a comma-separated list of mimetype/url pairs.";
+    return;
+  }
+
+  for (size_t i = 0; i < parts.size(); i += 2) {
+    GURL url(parts[i + 1]);
+    if (!url.is_valid()) {
+      LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
+                 << ": '" << parts[i + 1] << "' is not a valid URL.";
+      return;
+    }
+    loader->RegisterContentHandler(parts[i], url);
+  }
+}
+
+class EmptyServiceProvider : public InterfaceImpl<ServiceProvider> {
+ private:
+  virtual void ConnectToService(
+      const mojo::String& service_name,
+      ScopedMessagePipeHandle client_handle) override {}
+};
+
+}  // namespace
+
+#if defined(OS_ANDROID)
+class Context::NativeViewportApplicationLoader
+    : public ApplicationLoader,
+      public ApplicationDelegate,
+      public InterfaceFactory<NativeViewport>,
+      public InterfaceFactory<Gpu> {
+ public:
+  NativeViewportApplicationLoader()
+      : share_group_(new gfx::GLShareGroup),
+        mailbox_manager_(new gpu::gles2::MailboxManager) {}
+  virtual ~NativeViewportApplicationLoader() {}
+
+ private:
+  // 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())
+      app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override {}
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService<NativeViewport>(this);
+    connection->AddService<Gpu>(this);
+    return true;
+  }
+
+  // InterfaceFactory<NativeViewport> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NativeViewport> request) override {
+    BindToRequest(new NativeViewportImpl(app_.get(), false), &request);
+  }
+
+  // InterfaceFactory<Gpu> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Gpu> request) override {
+    BindToRequest(new GpuImpl(share_group_.get(), mailbox_manager_.get()),
+                  &request);
+  }
+
+  scoped_refptr<gfx::GLShareGroup> share_group_;
+  scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+  scoped_ptr<ApplicationImpl> app_;
+  DISALLOW_COPY_AND_ASSIGN(NativeViewportApplicationLoader);
+};
+#endif
+
+Context::Context() {
+  DCHECK(!base::MessageLoop::current());
+}
+
+Context::~Context() {
+  DCHECK(!base::MessageLoop::current());
+}
+
+void Context::Init() {
+  application_manager_.set_delegate(this);
+  setup.Get();
+  task_runners_.reset(
+      new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
+
+  for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i)
+    mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i]));
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  if (command_line->HasSwitch(switches::kEnableExternalApplications)) {
+    listener_ = ExternalApplicationListener::Create(
+        task_runners_->shell_runner(), task_runners_->io_runner());
+
+    base::FilePath socket_path =
+        command_line->GetSwitchValuePath(switches::kEnableExternalApplications);
+    if (socket_path.empty())
+      socket_path = ExternalApplicationListener::ConstructDefaultSocketPath();
+
+    listener_->ListenInBackground(
+        socket_path,
+        base::Bind(&ApplicationManager::RegisterExternalApplication,
+                   base::Unretained(&application_manager_)));
+  }
+  scoped_ptr<DynamicServiceRunnerFactory> runner_factory;
+  if (command_line->HasSwitch(switches::kEnableMultiprocess))
+    runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory());
+  else
+    runner_factory.reset(new InProcessDynamicServiceRunnerFactory());
+
+  DynamicApplicationLoader* dynamic_application_loader =
+      new DynamicApplicationLoader(this, runner_factory.Pass());
+  InitContentHandlers(dynamic_application_loader, command_line);
+  application_manager_.set_default_loader(
+      scoped_ptr<ApplicationLoader>(dynamic_application_loader));
+
+  // The native viewport service synchronously waits for certain messages. If we
+  // don't run it on its own thread we can easily deadlock. Long term native
+  // viewport should run its own process so that this isn't an issue.
+#if defined(OS_ANDROID)
+  application_manager_.SetLoaderForURL(
+      scoped_ptr<ApplicationLoader>(new UIApplicationLoader(
+          scoped_ptr<ApplicationLoader>(new NativeViewportApplicationLoader()),
+          this)),
+      GURL("mojo:mojo_native_viewport_service"));
+#endif
+
+  if (command_line->HasSwitch(switches::kSpy)) {
+    spy_.reset(
+        new mojo::Spy(&application_manager_,
+                      command_line->GetSwitchValueASCII(switches::kSpy)));
+  }
+
+#if defined(OS_ANDROID)
+  // On android, the network service is bundled with the shell because the
+  // network stack depends on the android runtime.
+  {
+    scoped_ptr<BackgroundShellApplicationLoader> loader(
+        new BackgroundShellApplicationLoader(
+            scoped_ptr<ApplicationLoader>(new NetworkApplicationLoader()),
+            "network_service",
+            base::MessageLoop::TYPE_IO));
+    application_manager_.SetLoaderForURL(loader.PassAs<ApplicationLoader>(),
+                                         GURL("mojo:mojo_network_service"));
+  }
+#endif
+
+  if (listener_)
+    listener_->WaitForListening();
+}
+
+void Context::OnApplicationError(const GURL& gurl) {
+  if (app_urls_.find(gurl) != app_urls_.end()) {
+    app_urls_.erase(gurl);
+    if (app_urls_.empty() && base::MessageLoop::current()->is_running())
+      base::MessageLoop::current()->Quit();
+  }
+}
+
+void Context::Run(const GURL& url) {
+  EmptyServiceProvider* sp = new EmptyServiceProvider;
+  ServiceProviderPtr spp;
+  BindToProxy(sp, &spp);
+
+  app_urls_.insert(url);
+  application_manager_.ConnectToApplication(url, GURL(), spp.Pass());
+}
+
+ScopedMessagePipeHandle Context::ConnectToServiceByName(
+    const GURL& application_url,
+    const std::string& service_name) {
+  app_urls_.insert(application_url);
+  return application_manager_.ConnectToServiceByName(
+      application_url, service_name).Pass();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/context.h b/mojo/shell/context.h
new file mode 100644
index 0000000..1f098c7
--- /dev/null
+++ b/mojo/shell/context.h
@@ -0,0 +1,72 @@
+// 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_SHELL_CONTEXT_H_
+#define MOJO_SHELL_CONTEXT_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/shell/mojo_url_resolver.h"
+#include "mojo/shell/task_runners.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif  // defined(OS_ANDROID)
+
+namespace mojo {
+
+class Spy;
+
+namespace shell {
+
+class DynamicApplicationLoader;
+class ExternalApplicationListener;
+
+// The "global" context for the shell's main process.
+class Context : ApplicationManager::Delegate {
+ public:
+  Context();
+  virtual ~Context();
+
+  void Init();
+
+  // ApplicationManager::Delegate override.
+  virtual void OnApplicationError(const GURL& gurl) override;
+
+  void Run(const GURL& url);
+  ScopedMessagePipeHandle ConnectToServiceByName(
+      const GURL& application_url,
+      const std::string& service_name);
+
+  TaskRunners* task_runners() { return task_runners_.get(); }
+  ApplicationManager* application_manager() { return &application_manager_; }
+  MojoURLResolver* mojo_url_resolver() { return &mojo_url_resolver_; }
+
+#if defined(OS_ANDROID)
+  base::MessageLoop* ui_loop() const { return ui_loop_; }
+  void set_ui_loop(base::MessageLoop* ui_loop) { ui_loop_ = ui_loop; }
+#endif  // defined(OS_ANDROID)
+
+ private:
+  class NativeViewportApplicationLoader;
+
+  std::set<GURL> app_urls_;
+  scoped_ptr<TaskRunners> task_runners_;
+  scoped_ptr<ExternalApplicationListener> listener_;
+  ApplicationManager application_manager_;
+  MojoURLResolver mojo_url_resolver_;
+  scoped_ptr<Spy> spy_;
+#if defined(OS_ANDROID)
+  base::MessageLoop* ui_loop_;
+#endif  // defined(OS_ANDROID)
+
+  DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_CONTEXT_H_
diff --git a/mojo/shell/desktop/mojo_main.cc b/mojo/shell/desktop/mojo_main.cc
new file mode 100644
index 0000000..0331d3e
--- /dev/null
+++ b/mojo/shell/desktop/mojo_main.cc
@@ -0,0 +1,107 @@
+// 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/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/shell/child_process.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/init.h"
+#include "mojo/shell/switches.h"
+
+#if defined(COMPONENT_BUILD)
+#include "ui/gl/gl_surface.h"
+#endif
+
+namespace {
+
+#if defined(OS_LINUX)
+// Copied from ui/gfx/switches.cc to avoid a dependency on //ui/gfx
+const char kEnableHarfBuzzRenderText[] = "enable-harfbuzz-rendertext";
+#endif
+
+#if defined(OS_WIN)
+void SplitString(const base::string16& str, std::vector<std::string>* argv) {
+  base::SplitString(base::UTF16ToUTF8(str), ' ', argv);
+}
+#elif defined(OS_POSIX)
+void SplitString(const std::string& str, std::vector<std::string>* argv) {
+  base::SplitString(str, ' ', argv);
+}
+#endif
+
+// The value of app_url_and_args is "<mojo_app_url> [<args>...]", where args
+// is a list of "configuration" arguments separated by spaces. If one or more
+// arguments are specified they will be available when the Mojo application
+// is initialized. See ApplicationImpl::args().
+GURL GetAppURLAndSetArgs(const base::CommandLine::StringType& app_url_and_args,
+                         mojo::shell::Context* context) {
+  std::vector<std::string> argv;
+  SplitString(app_url_and_args, &argv);
+  if (argv.empty())
+    return GURL::EmptyGURL();
+  GURL app_url(argv[0]);
+  if (argv.size() > 1)
+    context->application_manager()->SetArgsForURL(argv, app_url);
+  return app_url;
+}
+
+void RunApps(mojo::shell::Context* context) {
+  const auto& command_line = *base::CommandLine::ForCurrentProcess();
+  for (const auto& arg : command_line.GetArgs())
+    context->Run(GetAppURLAndSetArgs(arg, context));
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  base::CommandLine::Init(argc, argv);
+#if defined(OS_LINUX)
+  // We use gfx::RenderText from multiple threads concurrently and the pango
+  // backend (currently the default on linux) is not close to threadsafe. Force
+  // use of the harfbuzz backend for now.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      kEnableHarfBuzzRenderText);
+#endif
+  mojo::shell::InitializeLogging();
+
+  // TODO(vtl): Unify parent and child process cases to the extent possible.
+  if (scoped_ptr<mojo::shell::ChildProcess> child_process =
+          mojo::shell::ChildProcess::Create(
+              *base::CommandLine::ForCurrentProcess())) {
+    child_process->Main();
+  } else {
+#if defined(COMPONENT_BUILD)
+    gfx::GLSurface::InitializeOneOff();
+#endif
+    // We want the shell::Context to outlive the MessageLoop so that pipes are
+    // all gracefully closed / error-out before we try to shut the Context down.
+    mojo::shell::Context shell_context;
+    {
+      base::MessageLoop message_loop;
+      shell_context.Init();
+
+      const base::CommandLine& command_line =
+          *base::CommandLine::ForCurrentProcess();
+      if (command_line.HasSwitch(switches::kOrigin)) {
+        shell_context.mojo_url_resolver()->SetBaseURL(
+            GURL(command_line.GetSwitchValueASCII(switches::kOrigin)));
+      }
+
+      for (const auto& kv : command_line.GetSwitches()) {
+        if (kv.first == switches::kArgsFor)
+          GetAppURLAndSetArgs(kv.second, &shell_context);
+      }
+
+      message_loop.PostTask(FROM_HERE, base::Bind(RunApps, &shell_context));
+      message_loop.Run();
+    }
+  }
+  return 0;
+}
diff --git a/mojo/shell/dynamic_application_loader.cc b/mojo/shell/dynamic_application_loader.cc
new file mode 100644
index 0000000..ac0686e
--- /dev/null
+++ b/mojo/shell/dynamic_application_loader.cc
@@ -0,0 +1,248 @@
+// Copyright 2014 The Chromium 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/shell/dynamic_application_loader.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/switches.h"
+#include "net/base/filename_util.h"
+
+namespace mojo {
+namespace shell {
+
+// Encapsulates loading and running one individual application.
+//
+// Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must
+// ensure that all the parameters passed to Loader subclasses stay valid through
+// Loader's lifetime.
+//
+// Async operations are done with WeakPtr to protect against
+// DynamicApplicationLoader going away (and taking all the Loaders with it)
+// while the async operation is outstanding.
+class DynamicApplicationLoader::Loader {
+ public:
+  Loader(Context* context,
+         DynamicServiceRunnerFactory* runner_factory,
+         scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks,
+         const LoaderCompleteCallback& loader_complete_callback)
+      : load_callbacks_(load_callbacks),
+        loader_complete_callback_(loader_complete_callback),
+        context_(context),
+        runner_factory_(runner_factory),
+        weak_ptr_factory_(this) {}
+
+  virtual ~Loader() {}
+
+ protected:
+  void RunLibrary(const base::FilePath& path, bool path_exists) {
+    ScopedMessagePipeHandle shell_handle =
+        load_callbacks_->RegisterApplication();
+    if (!shell_handle.is_valid()) {
+      LoaderComplete();
+      return;
+    }
+
+    if (!path_exists) {
+      DVLOG(1) << "Library not started because library path '" << path.value()
+               << "' does not exist.";
+      LoaderComplete();
+      return;
+    }
+
+    runner_ = runner_factory_->Create(context_);
+    runner_->Start(
+        path,
+        shell_handle.Pass(),
+        base::Bind(&Loader::LoaderComplete, weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void LoaderComplete() { loader_complete_callback_.Run(this); }
+
+  scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks_;
+  LoaderCompleteCallback loader_complete_callback_;
+  Context* context_;
+
+ private:
+  DynamicServiceRunnerFactory* runner_factory_;
+  scoped_ptr<DynamicServiceRunner> runner_;
+  base::WeakPtrFactory<Loader> weak_ptr_factory_;
+};
+
+// A loader for local files.
+class DynamicApplicationLoader::LocalLoader : public Loader {
+ public:
+  LocalLoader(const GURL& url,
+              Context* context,
+              DynamicServiceRunnerFactory* runner_factory,
+              scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks,
+              const LoaderCompleteCallback& loader_complete_callback)
+      : Loader(context,
+               runner_factory,
+               load_callbacks,
+               loader_complete_callback),
+        weak_ptr_factory_(this) {
+    base::FilePath path;
+    net::FileURLToFilePath(url, &path);
+
+    // Async for consistency with network case.
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&LocalLoader::RunLibrary,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   path,
+                   base::PathExists(path)));
+  }
+
+  virtual ~LocalLoader() {}
+
+ private:
+  base::WeakPtrFactory<LocalLoader> weak_ptr_factory_;
+};
+
+// A loader for network files.
+class DynamicApplicationLoader::NetworkLoader : public Loader {
+ public:
+  NetworkLoader(const GURL& url,
+                MimeTypeToURLMap* mime_type_to_url,
+                Context* context,
+                DynamicServiceRunnerFactory* runner_factory,
+                NetworkService* network_service,
+                scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks,
+                const LoaderCompleteCallback& loader_complete_callback)
+      : Loader(context,
+               runner_factory,
+               load_callbacks,
+               loader_complete_callback),
+        mime_type_to_url_(mime_type_to_url),
+        weak_ptr_factory_(this) {
+    URLRequestPtr request(URLRequest::New());
+    request->url = String::From(url);
+    request->auto_follow_redirects = true;
+
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kDisableCache)) {
+      request->bypass_cache = true;
+    }
+
+    network_service->CreateURLLoader(Get(&url_loader_));
+    url_loader_->Start(request.Pass(),
+                       base::Bind(&NetworkLoader::OnLoadComplete,
+                                  weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  virtual ~NetworkLoader() {
+    if (!file_.empty())
+      base::DeleteFile(file_, false);
+  }
+
+ private:
+  void OnLoadComplete(URLResponsePtr response) {
+    if (response->error) {
+      LOG(ERROR) << "Error (" << response->error->code << ": "
+                 << response->error->description << ") while fetching "
+                 << response->url;
+      LoaderComplete();
+      return;
+    }
+
+    MimeTypeToURLMap::iterator iter =
+        mime_type_to_url_->find(response->mime_type);
+    if (iter != mime_type_to_url_->end()) {
+      load_callbacks_->LoadWithContentHandler(iter->second, response.Pass());
+      return;
+    }
+
+    base::CreateTemporaryFile(&file_);
+    common::CopyToFile(
+        response->body.Pass(),
+        file_,
+        context_->task_runners()->blocking_pool(),
+        base::Bind(
+            &NetworkLoader::RunLibrary, weak_ptr_factory_.GetWeakPtr(), file_));
+  }
+
+  MimeTypeToURLMap* mime_type_to_url_;
+  URLLoaderPtr url_loader_;
+  base::FilePath file_;
+  base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_;
+};
+
+DynamicApplicationLoader::DynamicApplicationLoader(
+    Context* context,
+    scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
+    : context_(context),
+      runner_factory_(runner_factory.Pass()),
+
+      // Unretained() is correct here because DynamicApplicationLoader owns the
+      // loaders that we pass this callback to.
+      loader_complete_callback_(
+          base::Bind(&DynamicApplicationLoader::LoaderComplete,
+                     base::Unretained(this))) {
+}
+
+DynamicApplicationLoader::~DynamicApplicationLoader() {
+}
+
+void DynamicApplicationLoader::RegisterContentHandler(
+    const std::string& mime_type,
+    const GURL& content_handler_url) {
+  mime_type_to_url_[mime_type] = content_handler_url;
+}
+
+void DynamicApplicationLoader::Load(
+    ApplicationManager* manager,
+    const GURL& url,
+    scoped_refptr<LoadCallbacks> load_callbacks) {
+  GURL resolved_url;
+  if (url.SchemeIs("mojo")) {
+    resolved_url = context_->mojo_url_resolver()->Resolve(url);
+  } else {
+    resolved_url = url;
+  }
+
+  if (resolved_url.SchemeIsFile()) {
+    loaders_.push_back(new LocalLoader(resolved_url,
+                                       context_,
+                                       runner_factory_.get(),
+                                       load_callbacks,
+                                       loader_complete_callback_));
+    return;
+  }
+
+  if (!network_service_) {
+    context_->application_manager()->ConnectToService(
+        GURL("mojo:mojo_network_service"), &network_service_);
+  }
+
+  loaders_.push_back(new NetworkLoader(resolved_url,
+                                       &mime_type_to_url_,
+                                       context_,
+                                       runner_factory_.get(),
+                                       network_service_.get(),
+                                       load_callbacks,
+                                       loader_complete_callback_));
+}
+
+void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                                  const GURL& url) {
+  // TODO(darin): What should we do about service errors? This implies that
+  // the app closed its handle to the service manager. Maybe we don't care?
+}
+
+void DynamicApplicationLoader::LoaderComplete(Loader* loader) {
+  loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/dynamic_application_loader.h b/mojo/shell/dynamic_application_loader.h
new file mode 100644
index 0000000..6d8df9f
--- /dev/null
+++ b/mojo/shell/dynamic_application_loader.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_SHELL_DYNAMIC_APPLICATION_LOADER_H_
+#define MOJO_SHELL_DYNAMIC_APPLICATION_LOADER_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/shell/dynamic_service_runner.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class Context;
+class DynamicServiceRunnerFactory;
+class DynamicServiceRunner;
+
+// An implementation of ApplicationLoader that retrieves a dynamic library
+// containing the implementation of the service and loads/runs it (via a
+// DynamicServiceRunner).
+class DynamicApplicationLoader : public ApplicationLoader {
+ public:
+  DynamicApplicationLoader(
+      Context* context,
+      scoped_ptr<DynamicServiceRunnerFactory> runner_factory);
+  virtual ~DynamicApplicationLoader();
+
+  void RegisterContentHandler(const std::string& mime_type,
+                              const GURL& content_handler_url);
+
+  // ApplicationLoader methods:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+ private:
+  class Loader;
+  class LocalLoader;
+  class NetworkLoader;
+
+  typedef std::map<std::string, GURL> MimeTypeToURLMap;
+  typedef base::Callback<void(Loader*)> LoaderCompleteCallback;
+
+  void LoaderComplete(Loader* loader);
+
+  Context* const context_;
+  scoped_ptr<DynamicServiceRunnerFactory> runner_factory_;
+  NetworkServicePtr network_service_;
+  MimeTypeToURLMap mime_type_to_url_;
+  ScopedVector<Loader> loaders_;
+  LoaderCompleteCallback loader_complete_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DynamicApplicationLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_DYNAMIC_APPLICATION_LOADER_H_
diff --git a/mojo/shell/dynamic_application_loader_unittest.cc b/mojo/shell/dynamic_application_loader_unittest.cc
new file mode 100644
index 0000000..ea6fb1d
--- /dev/null
+++ b/mojo/shell/dynamic_application_loader_unittest.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 "base/files/scoped_temp_dir.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/dynamic_application_loader.h"
+#include "mojo/shell/dynamic_service_runner.h"
+#include "net/base/filename_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+struct TestState {
+  TestState()
+      : runner_was_created(false),
+        runner_was_started(false),
+        runner_was_destroyed(false) {}
+
+  bool runner_was_created;
+  bool runner_was_started;
+  bool runner_was_destroyed;
+};
+
+class TestDynamicServiceRunner : public DynamicServiceRunner {
+ public:
+  explicit TestDynamicServiceRunner(TestState* state) : state_(state) {
+    state_->runner_was_created = true;
+  }
+  virtual ~TestDynamicServiceRunner() {
+    state_->runner_was_destroyed = true;
+    base::MessageLoop::current()->Quit();
+  }
+  virtual void Start(const base::FilePath& app_path,
+                     ScopedMessagePipeHandle service_handle,
+                     const base::Closure& app_completed_callback) override {
+    state_->runner_was_started = true;
+  }
+
+ private:
+  TestState* state_;
+};
+
+class TestDynamicServiceRunnerFactory : public DynamicServiceRunnerFactory {
+ public:
+  explicit TestDynamicServiceRunnerFactory(TestState* state) : state_(state) {}
+  virtual ~TestDynamicServiceRunnerFactory() {}
+  virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) override {
+    return scoped_ptr<DynamicServiceRunner>(
+        new TestDynamicServiceRunner(state_));
+  }
+
+ private:
+  TestState* state_;
+};
+
+}  // namespace
+
+class DynamicApplicationLoaderTest : public testing::Test {
+ public:
+  DynamicApplicationLoaderTest() {}
+  virtual ~DynamicApplicationLoaderTest() {}
+  virtual void SetUp() override {
+    context_.Init();
+    scoped_ptr<DynamicServiceRunnerFactory> factory(
+        new TestDynamicServiceRunnerFactory(&state_));
+    loader_.reset(new DynamicApplicationLoader(&context_, factory.Pass()));
+  }
+
+ protected:
+  Context context_;
+  base::MessageLoop loop_;
+  scoped_ptr<DynamicApplicationLoader> loader_;
+  TestState state_;
+};
+
+TEST_F(DynamicApplicationLoaderTest, DoesNotExist) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath nonexistent_file(FILE_PATH_LITERAL("nonexistent.txt"));
+  GURL url(net::FilePathToFileURL(temp_dir.path().Append(nonexistent_file)));
+  MessagePipe pipe;
+  scoped_refptr<ApplicationLoader::SimpleLoadCallbacks> callbacks(
+      new ApplicationLoader::SimpleLoadCallbacks(pipe.handle0.Pass()));
+  loader_->Load(context_.application_manager(), url, callbacks);
+  EXPECT_FALSE(state_.runner_was_created);
+  EXPECT_FALSE(state_.runner_was_started);
+  EXPECT_FALSE(state_.runner_was_destroyed);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/dynamic_service_runner.h b/mojo/shell/dynamic_service_runner.h
new file mode 100644
index 0000000..aa392f8
--- /dev/null
+++ b/mojo/shell/dynamic_service_runner.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_SHELL_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// Abstraction for loading a service (from the file system) and running it (on
+// another thread or in a separate process).
+class DynamicServiceRunner {
+ public:
+  virtual ~DynamicServiceRunner() {}
+
+  // Takes ownership of the file at |app_path|. Loads the app in that file and
+  // runs it on some other thread/process. |app_completed_callback| is posted
+  // (to the thread on which |Start()| was called) after |MojoMain()| completes.
+  virtual void Start(const base::FilePath& app_path,
+                     ScopedMessagePipeHandle service_handle,
+                     const base::Closure& app_completed_callback) = 0;
+};
+
+class DynamicServiceRunnerFactory {
+ public:
+  virtual ~DynamicServiceRunnerFactory() {}
+  virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) = 0;
+};
+
+// A generic factory.
+template <class DynamicServiceRunnerImpl>
+class DynamicServiceRunnerFactoryImpl : public DynamicServiceRunnerFactory {
+ public:
+  DynamicServiceRunnerFactoryImpl() {}
+  virtual ~DynamicServiceRunnerFactoryImpl() {}
+  virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) override {
+    return scoped_ptr<DynamicServiceRunner>(
+        new DynamicServiceRunnerImpl(context));
+  }
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/mojo/shell/external_application_listener.h b/mojo/shell/external_application_listener.h
new file mode 100644
index 0000000..fed735e
--- /dev/null
+++ b/mojo/shell/external_application_listener.h
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium 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_SHELL_EXTERNAL_APPLICATION_LISTENER_H_
+#define MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "net/socket/socket_descriptor.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// In order to support Mojo apps whose lifetime is managed by
+// something other than mojo_shell, mojo_shell needs to support a
+// mechanism by which such an application can discover a running shell
+// instance, connect to it, and ask to be "registered" at a given
+// URL. Registration implies that the app can be connected to at that
+// URL from then on out, and that the app has received a usable ShellPtr.
+//
+// External applications can connect to the shell using the
+// ExternalApplicationRegistrarConnection class.
+class ExternalApplicationListener {
+ public:
+  // When run, a RegisterCallback should note that an app has asked to be
+  // registered at app_url and Bind the provided pipe handle to a ShellImpl.
+  typedef base::Callback<void(const GURL& app_url,
+                              ScopedMessagePipeHandle shell)> RegisterCallback;
+  typedef base::Callback<void(int rv)> ErrorCallback;
+
+  virtual ~ExternalApplicationListener() {}
+
+  // Implementations of this class may use two threads, an IO thread for
+  // listening and accepting incoming sockets, and a "main" thread
+  // where all Mojo traffic is processed and provided callbacks are run.
+  static scoped_ptr<ExternalApplicationListener> Create(
+      const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& io_runner);
+
+  static base::FilePath ConstructDefaultSocketPath();
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are ignored.
+  virtual void ListenInBackground(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback) = 0;
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are reported via error_callback.
+  virtual void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) = 0;
+
+  // Block the current thread until listening has started on io_runner.
+  // If listening has already started, returns immediately.
+  virtual void WaitForListening() = 0;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_H_
diff --git a/mojo/shell/external_application_listener_posix.cc b/mojo/shell/external_application_listener_posix.cc
new file mode 100644
index 0000000..42afa25
--- /dev/null
+++ b/mojo/shell/external_application_listener_posix.cc
@@ -0,0 +1,201 @@
+// Copyright 2014 The Chromium 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/shell/external_application_listener_posix.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_checker.h"
+#include "base/tracked_objects.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/shell/external_application_registrar.mojom.h"
+#include "mojo/shell/incoming_connection_listener_posix.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket_descriptor.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+const char kDefaultListenSocketPath[] = "/var/run/mojo/system_socket";
+}  // namespace
+
+// static
+base::FilePath ExternalApplicationListener::ConstructDefaultSocketPath() {
+  return base::FilePath(FILE_PATH_LITERAL(kDefaultListenSocketPath));
+}
+
+// static
+scoped_ptr<ExternalApplicationListener> ExternalApplicationListener::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner) {
+  return make_scoped_ptr(
+      new ExternalApplicationListenerPosix(shell_runner, io_runner));
+}
+
+class ExternalApplicationListenerPosix::RegistrarImpl
+    : public InterfaceImpl<ExternalApplicationRegistrar> {
+ public:
+  explicit RegistrarImpl(const RegisterCallback& callback);
+  virtual ~RegistrarImpl() override;
+
+  virtual void OnConnectionError() override;
+
+  embedder::ChannelInit channel_init;
+
+ private:
+  virtual void Register(const String& app_url,
+                        InterfaceRequest<Shell> shell,
+                        const mojo::Closure& callback) override;
+
+  const RegisterCallback register_callback_;
+};
+
+ExternalApplicationListenerPosix::ExternalApplicationListenerPosix(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner)
+    : shell_runner_(shell_runner),
+      io_runner_(io_runner),
+      signal_on_listening_(true, false),
+      weak_ptr_factory_(this) {
+  DCHECK(shell_runner_.get() && shell_runner_->RunsTasksOnCurrentThread());
+  DCHECK(io_runner_.get());
+  listener_thread_checker_.DetachFromThread();  // Will attach in StartListener.
+}
+
+ExternalApplicationListenerPosix::~ExternalApplicationListenerPosix() {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  weak_ptr_factory_.InvalidateWeakPtrs();
+
+  // listener_ needs to be destroyed on io_runner_, and it has to die before
+  // this object does, as it holds a pointer back to this instance.
+  base::WaitableEvent stop_listening_event(true, false);
+  io_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ExternalApplicationListenerPosix::StopListening,
+                 base::Unretained(this),
+                 &stop_listening_event));
+  stop_listening_event.Wait();
+}
+
+void ExternalApplicationListenerPosix::ListenInBackground(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  ListenInBackgroundWithErrorCallback(
+      listen_socket_path, register_callback, ErrorCallback());
+}
+
+void ExternalApplicationListenerPosix::ListenInBackgroundWithErrorCallback(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  register_callback_ = register_callback;
+  error_callback_ = error_callback;
+
+  io_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ExternalApplicationListenerPosix::StartListening,
+                 base::Unretained(this),
+                 listen_socket_path));
+}
+
+void ExternalApplicationListenerPosix::WaitForListening() {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  signal_on_listening_.Wait();
+}
+
+void ExternalApplicationListenerPosix::StartListening(
+    const base::FilePath& listen_socket_path) {
+  CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  listener_.reset(
+      new IncomingConnectionListenerPosix(listen_socket_path, this));
+  listener_->StartListening();
+}
+
+void ExternalApplicationListenerPosix::StopListening(
+    base::WaitableEvent* event) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  listener_.reset();
+  event->Signal();
+}
+
+void ExternalApplicationListenerPosix::OnListening(int rv) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  signal_on_listening_.Signal();
+  shell_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &ExternalApplicationListenerPosix::RunErrorCallbackIfListeningFailed,
+          weak_ptr_factory_.GetWeakPtr(),
+          rv));
+}
+
+void ExternalApplicationListenerPosix::OnConnection(
+    net::SocketDescriptor incoming) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  shell_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &ExternalApplicationListenerPosix::CreatePipeAndBindToRegistrarImpl,
+          weak_ptr_factory_.GetWeakPtr(),
+          incoming));
+}
+
+void ExternalApplicationListenerPosix::RunErrorCallbackIfListeningFailed(
+    int rv) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  if (rv != net::OK && !error_callback_.is_null()) {
+    error_callback_.Run(rv);
+  }
+}
+
+void ExternalApplicationListenerPosix::CreatePipeAndBindToRegistrarImpl(
+    net::SocketDescriptor incoming_socket) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+
+  DVLOG(1) << "Accepted incoming connection";
+  scoped_ptr<RegistrarImpl> registrar(new RegistrarImpl(register_callback_));
+  // Passes ownership of incoming_socket to registrar->channel_init.
+  mojo::ScopedMessagePipeHandle message_pipe =
+      registrar->channel_init.Init(incoming_socket, io_runner_);
+  CHECK(message_pipe.is_valid());
+
+  BindToPipe(registrar.release(), message_pipe.Pass());
+}
+
+ExternalApplicationListenerPosix::RegistrarImpl::RegistrarImpl(
+    const RegisterCallback& callback)
+    : register_callback_(callback) {
+}
+
+ExternalApplicationListenerPosix::RegistrarImpl::~RegistrarImpl() {
+}
+
+void ExternalApplicationListenerPosix::RegistrarImpl::OnConnectionError() {
+  channel_init.WillDestroySoon();
+}
+
+void ExternalApplicationListenerPosix::RegistrarImpl::Register(
+    const String& app_url,
+    InterfaceRequest<Shell> shell,
+    const mojo::Closure& callback) {
+  register_callback_.Run(app_url.To<GURL>(), shell.PassMessagePipe());
+  callback.Run();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/external_application_listener_posix.h b/mojo/shell/external_application_listener_posix.h
new file mode 100644
index 0000000..788a4ff
--- /dev/null
+++ b/mojo/shell/external_application_listener_posix.h
@@ -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.
+
+#ifndef MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
+#define MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
+
+#include "mojo/shell/external_application_listener.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/shell/external_application_registrar.mojom.h"
+#include "mojo/shell/incoming_connection_listener_posix.h"
+#include "net/socket/socket_descriptor.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// In order to support Mojo apps whose lifetime is managed by
+// something other than mojo_shell, mojo_shell needs to support a
+// mechanism by which such an application can discover a running shell
+// instance, connect to it, and ask to be "registered" at a given
+// URL. Registration implies that the app can be connected to at that
+// URL from then on out, and that the app has received a usable ShellPtr.
+//
+// This class implements most of the mojo_shell side of external application
+// registration. It handles:
+//  1) discoverability - sets up a unix domain socket at a well-known location,
+//  2) incoming connections - listens for and accepts incoming connections on
+//     that socket, and
+//  3) registration requests - forwarded to a RegisterCallback that implements
+//     the actual registration logic.
+//
+// External applications can connect to the shell using the
+// ExternalApplicationRegistrarConnection class.
+class ExternalApplicationListenerPosix
+    : public ExternalApplicationListener,
+      public IncomingConnectionListenerPosix::Delegate {
+ public:
+  // This class uses two threads, an IO thread for listening and accepting
+  // incoming sockets, and a "main" thread where all Mojo traffic is processed
+  // and provided callbacks are run.
+  ExternalApplicationListenerPosix(
+      const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& io_runner);
+
+  // Some of this class' internal state needs to be destroyed on io_runner_,
+  // so the destructor will post a task to that thread to call StopListening()
+  // and then wait for it to complete.
+  virtual ~ExternalApplicationListenerPosix();
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are ignored.
+  virtual void ListenInBackground(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback) override;
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are reported via error_callback.
+  virtual void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) override;
+
+  // Block the current thread until listening has started on io_runner.
+  // If listening has already started, returns immediately.
+  virtual void WaitForListening() override;
+
+ private:
+  class RegistrarImpl;
+
+  // MUST be called on io_runner.
+  // Creates listener_ and tells it to StartListening() on a socket it creates
+  // at listen_socket_path.
+  void StartListening(const base::FilePath& listen_socket_path);
+
+  // MUST be called on io_runner.
+  // Destroys listener_ and signals event when done.
+  void StopListening(base::WaitableEvent* event);
+
+  // Implementation of IncomingConnectionListener::Delegate
+  virtual void OnListening(int rv) override;
+  virtual void OnConnection(net::SocketDescriptor incoming) override;
+
+  // If listener_ fails to start listening, this method is run on shell_runner_
+  // to report the error.
+  void RunErrorCallbackIfListeningFailed(int rv);
+
+  // When a connection succeeds, it is passed to this method running
+  // on shell_runner_, where it is "promoted" to a Mojo MessagePipe and
+  // bound to a RegistrarImpl.
+  void CreatePipeAndBindToRegistrarImpl(net::SocketDescriptor incoming_socket);
+
+  scoped_refptr<base::SequencedTaskRunner> shell_runner_;
+  scoped_refptr<base::SequencedTaskRunner> io_runner_;
+
+  // MUST be created, used, and destroyed on io_runner_.
+  scoped_ptr<IncomingConnectionListenerPosix> listener_;
+
+  // Callers can wait on this event, which will be signalled once listening
+  // has either successfully begun or definitively failed.
+  base::WaitableEvent signal_on_listening_;
+
+  // Locked to thread on which StartListening() is run (should be io_runner_).
+  // All methods that touch listener_ check that they're on that same thread.
+  base::ThreadChecker listener_thread_checker_;
+
+  ErrorCallback error_callback_;
+  RegisterCallback register_callback_;
+  base::ThreadChecker register_thread_checker_;
+
+  // Used on shell_runner_.
+  base::WeakPtrFactory<ExternalApplicationListenerPosix> weak_ptr_factory_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
diff --git a/mojo/shell/external_application_listener_unittest.cc b/mojo/shell/external_application_listener_unittest.cc
new file mode 100644
index 0000000..5676b68
--- /dev/null
+++ b/mojo/shell/external_application_listener_unittest.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 "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/shell/external_application_listener_posix.h"
+#include "mojo/shell/external_application_registrar.mojom.h"
+#include "mojo/shell/external_application_registrar_connection.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class ExternalApplicationListenerTest : public testing::Test {
+ public:
+  ExternalApplicationListenerTest() : io_thread_("io thread") {}
+  virtual ~ExternalApplicationListenerTest() {}
+
+  virtual void SetUp() override {
+    base::Thread::Options options;
+    options.message_loop_type = base::MessageLoop::TYPE_IO;
+    io_thread_.StartWithOptions(options);
+
+    listener_.reset(new ExternalApplicationListenerPosix(
+        loop_.task_runner(), io_thread_.task_runner()));
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket"));
+  }
+
+ protected:
+  base::MessageLoop loop_;
+  base::RunLoop run_loop_;
+  base::Thread io_thread_;
+
+  base::ScopedTempDir temp_dir_;
+  ApplicationManager application_manager_;
+  base::FilePath socket_path_;
+  scoped_ptr<ExternalApplicationListener> listener_;
+};
+
+namespace {
+
+class StubShellImpl : public InterfaceImpl<Shell> {
+ private:
+  virtual void ConnectToApplication(
+      const String& requestor_url,
+      InterfaceRequest<ServiceProvider> in_service_provider) override {
+    ServiceProviderPtr out_service_provider;
+    out_service_provider.Bind(in_service_provider.PassMessagePipe());
+    client()->AcceptConnection(requestor_url, out_service_provider.Pass());
+  }
+};
+
+void DoLocalRegister(const GURL& app_url, ScopedMessagePipeHandle shell) {
+  BindToPipe(new StubShellImpl, shell.Pass());
+}
+
+void QuitLoopOnConnect(scoped_refptr<base::TaskRunner> loop,
+                       base::Closure quit_callback,
+                       int rv) {
+  EXPECT_EQ(net::OK, rv);
+  loop->PostTask(FROM_HERE, quit_callback);
+}
+
+void ConnectOnIOThread(const base::FilePath& socket_path,
+                       scoped_refptr<base::TaskRunner> to_quit,
+                       base::Closure quit_callback) {
+  ExternalApplicationRegistrarConnection connection(socket_path);
+  connection.Connect(base::Bind(&QuitLoopOnConnect, to_quit, quit_callback));
+}
+
+}  // namespace
+
+TEST_F(ExternalApplicationListenerTest, ConnectConnection) {
+  listener_->ListenInBackground(socket_path_, base::Bind(&DoLocalRegister));
+  listener_->WaitForListening();
+  io_thread_.task_runner()->PostTask(FROM_HERE,
+                                     base::Bind(&ConnectOnIOThread,
+                                                socket_path_,
+                                                loop_.task_runner(),
+                                                run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+namespace {
+
+class QuitLoopOnConnectApplicationImpl : public InterfaceImpl<Application> {
+ public:
+  QuitLoopOnConnectApplicationImpl(const std::string& url,
+                                   scoped_refptr<base::TaskRunner> loop,
+                                   base::Closure quit_callback)
+      : url_(url), to_quit_(loop), quit_callback_(quit_callback) {}
+
+ private:
+  virtual void Initialize(Array<String> args) override {}
+
+  virtual void AcceptConnection(const String& requestor_url,
+                                ServiceProviderPtr p) override {
+    DVLOG(1) << url_ << " accepting connection from " << requestor_url;
+    to_quit_->PostTask(FROM_HERE, quit_callback_);
+  }
+
+  const std::string url_;
+  scoped_refptr<base::TaskRunner> to_quit_;
+  base::Closure quit_callback_;
+};
+
+class FakeExternalApplication {
+ public:
+  FakeExternalApplication(const std::string& url) : url_(url) {}
+
+  void ConnectSynchronously(const base::FilePath& socket_path) {
+    connection_.reset(new ExternalApplicationRegistrarConnection(socket_path));
+    net::TestCompletionCallback connect_callback;
+    connection_->Connect(connect_callback.callback());
+    connect_callback.WaitForResult();
+  }
+
+  // application_impl is the the actual implementation to be registered.
+  void Register(scoped_ptr<InterfaceImpl<Application>> application_impl,
+                base::Closure register_complete_callback) {
+    connection_->Register(GURL(url_), &ptr_, register_complete_callback);
+    application_impl_ = application_impl.Pass();
+    ptr_.set_client(application_impl_.get());
+  }
+
+  void ConnectToAppByUrl(std::string app_url) {
+    ServiceProviderPtr sp;
+    ptr_->ConnectToApplication(app_url, Get(&sp));
+  }
+
+  const std::string& url() { return url_; }
+
+ private:
+  const std::string url_;
+  scoped_ptr<InterfaceImpl<Application>> application_impl_;
+  ShellPtr ptr_;
+
+  scoped_ptr<ExternalApplicationRegistrarConnection> connection_;
+};
+
+void ConnectToApp(FakeExternalApplication* connector,
+                  FakeExternalApplication* connectee) {
+  connector->ConnectToAppByUrl(connectee->url());
+}
+
+void NoOp() {
+}
+
+void ConnectAndRegisterOnIOThread(const base::FilePath& socket_path,
+                                  scoped_refptr<base::TaskRunner> loop,
+                                  base::Closure quit_callback,
+                                  FakeExternalApplication* connector,
+                                  FakeExternalApplication* connectee) {
+  // Connect the first app to the registrar.
+  connector->ConnectSynchronously(socket_path);
+  // connector will use this implementation of the Mojo Application interface
+  // once registration complete.
+  scoped_ptr<QuitLoopOnConnectApplicationImpl> connector_app_impl(
+      new QuitLoopOnConnectApplicationImpl(
+          connector->url(), loop, quit_callback));
+  // Since connectee won't be ready when connector is done registering, pass
+  // in a do-nothing callback.
+  connector->Register(connector_app_impl.PassAs<InterfaceImpl<Application>>(),
+                      base::Bind(&NoOp));
+
+  // Connect the second app to the registrar.
+  connectee->ConnectSynchronously(socket_path);
+  scoped_ptr<QuitLoopOnConnectApplicationImpl> connectee_app_impl(
+      new QuitLoopOnConnectApplicationImpl(
+          connectee->url(), loop, quit_callback));
+  // After connectee is successfully registered, connector should be
+  // able to connect to is by URL. Pass in a callback to attempt the
+  // app -> app connection.
+  connectee->Register(connectee_app_impl.PassAs<InterfaceImpl<Application>>(),
+                      base::Bind(&ConnectToApp, connector, connectee));
+}
+
+void DestroyOnIOThread(scoped_ptr<FakeExternalApplication> doomed1,
+                       scoped_ptr<FakeExternalApplication> doomed2) {
+}
+
+}  // namespace
+
+// Create two external applications, have them discover and connect to
+// the registrar, and then have one app connect to the other by URL.
+TEST_F(ExternalApplicationListenerTest, ConnectTwoExternalApplications) {
+  listener_->ListenInBackground(
+      socket_path_,
+      base::Bind(&ApplicationManager::RegisterExternalApplication,
+                 base::Unretained(&application_manager_)));
+  listener_->WaitForListening();
+
+  // Create two external apps.
+  scoped_ptr<FakeExternalApplication> supersweet_app(
+      new FakeExternalApplication("http://my.supersweet.app"));
+  scoped_ptr<FakeExternalApplication> awesome_app(
+      new FakeExternalApplication("http://my.awesome.app"));
+
+  // Connecting and talking to the registrar has to happen on the IO thread.
+  io_thread_.task_runner()->PostTask(FROM_HERE,
+                                     base::Bind(&ConnectAndRegisterOnIOThread,
+                                                socket_path_,
+                                                loop_.task_runner(),
+                                                run_loop_.QuitClosure(),
+                                                supersweet_app.get(),
+                                                awesome_app.get()));
+  run_loop_.Run();
+
+  // The apps need to be destroyed on the thread where they did socket stuff.
+  io_thread_.task_runner()->PostTask(FROM_HERE,
+                                     base::Bind(&DestroyOnIOThread,
+                                                base::Passed(&supersweet_app),
+                                                base::Passed(&awesome_app)));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/external_application_listener_win.cc b/mojo/shell/external_application_listener_win.cc
new file mode 100644
index 0000000..f2c3151
--- /dev/null
+++ b/mojo/shell/external_application_listener_win.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/shell/external_application_listener_win.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// static
+base::FilePath ExternalApplicationListener::ConstructDefaultSocketPath() {
+  return base::FilePath(FILE_PATH_LITERAL(""));
+}
+
+// static
+scoped_ptr<ExternalApplicationListener> ExternalApplicationListener::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner) {
+  return make_scoped_ptr(new ExternalApplicationListenerStub);
+}
+
+ExternalApplicationListenerStub::ExternalApplicationListenerStub() {
+}
+
+ExternalApplicationListenerStub::~ExternalApplicationListenerStub() {
+}
+
+void ExternalApplicationListenerStub::ListenInBackground(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback) {
+}
+
+void ExternalApplicationListenerStub::ListenInBackgroundWithErrorCallback(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback,
+    const ErrorCallback& error_callback) {
+}
+
+void ExternalApplicationListenerStub::WaitForListening() {
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/external_application_listener_win.h b/mojo/shell/external_application_listener_win.h
new file mode 100644
index 0000000..8280e75
--- /dev/null
+++ b/mojo/shell/external_application_listener_win.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_SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
+#define MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
+
+#include "mojo/shell/external_application_listener.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+
+namespace mojo {
+namespace shell {
+
+// External application registration is not supported on Windows, hence this
+// stub.
+class ExternalApplicationListenerStub : public ExternalApplicationListener {
+ public:
+  ExternalApplicationListenerStub();
+  virtual ~ExternalApplicationListenerStub() override;
+
+  void ListenInBackground(const base::FilePath& listen_socket_path,
+                          const RegisterCallback& register_callback) override;
+  void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) override;
+  void WaitForListening() override;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
diff --git a/mojo/shell/external_application_registrar.mojom b/mojo/shell/external_application_registrar.mojom
new file mode 100644
index 0000000..db76aec
--- /dev/null
+++ b/mojo/shell/external_application_registrar.mojom
@@ -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/interfaces/application/shell.mojom"
+
+module mojo {
+
+// This interface allows applications running outside the auspices of a Shell
+// to request registration at the given application_url.
+// The shell implementing this interface will make the calling application
+// available to other apps at that URL and bind the provided Shell& to
+// an impl capable of servicing the external application's connection requests.
+interface ExternalApplicationRegistrar {
+  Register(string application_url, Shell& shell) => ();
+};
+
+}
diff --git a/mojo/shell/external_application_registrar_connection.cc b/mojo/shell/external_application_registrar_connection.cc
new file mode 100644
index 0000000..57498a9
--- /dev/null
+++ b/mojo/shell/external_application_registrar_connection.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium 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/shell/external_application_registrar_connection.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/shell/external_application_registrar.mojom.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket_descriptor.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+ExternalApplicationRegistrarConnection::ExternalApplicationRegistrarConnection(
+    const base::FilePath& socket_path)
+    : client_socket_(
+          new net::UnixDomainClientSocket(socket_path.value(), false)) {
+}
+
+ExternalApplicationRegistrarConnection::
+    ~ExternalApplicationRegistrarConnection() {
+  channel_init_.WillDestroySoon();
+}
+
+void ExternalApplicationRegistrarConnection::OnConnectionError() {
+  channel_init_.WillDestroySoon();
+}
+
+void ExternalApplicationRegistrarConnection::Connect(
+    const net::CompletionCallback& callback) {
+  DCHECK(client_socket_) << "Single use only.";
+  int rv = client_socket_->Connect(
+      base::Bind(&ExternalApplicationRegistrarConnection::OnConnect,
+                 base::Unretained(this),
+                 callback));
+  if (rv != net::ERR_IO_PENDING) {
+    DVLOG(1) << "Connect returning immediately: " << net::ErrorToString(rv);
+    OnConnect(callback, rv);
+    return;
+  }
+  DVLOG(1) << "Waiting for connection.";
+}
+
+void ExternalApplicationRegistrarConnection::Register(
+    const GURL& app_url,
+    ShellPtr* shell,
+    base::Closure register_complete_callback) {
+  DCHECK(!client_socket_);
+  registrar_->Register(
+      String::From(app_url), Get(shell), register_complete_callback);
+}
+
+void ExternalApplicationRegistrarConnection::OnConnect(
+    net::CompletionCallback callback,
+    int rv) {
+  DVLOG(1) << "OnConnect called: " << net::ErrorToString(rv);
+  if (rv != net::OK) {
+    callback.Run(rv);
+    return;
+  }
+
+  mojo::ScopedMessagePipeHandle ptr_message_pipe_handle =
+      channel_init_.Init(client_socket_->ReleaseConnectedSocket(),
+                         base::MessageLoopProxy::current());
+  CHECK(ptr_message_pipe_handle.is_valid());
+  client_socket_.reset();  // This is dead now, ensure it can't be reused.
+
+  registrar_.Bind(ptr_message_pipe_handle.Pass());
+  callback.Run(rv);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/external_application_registrar_connection.h b/mojo/shell/external_application_registrar_connection.h
new file mode 100644
index 0000000..327d19b
--- /dev/null
+++ b/mojo/shell/external_application_registrar_connection.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_SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
+#define MOJO_SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/shell/external_application_registrar.mojom.h"
+#include "net/socket/socket_descriptor.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// Externally-running applications can use this class to discover and register
+// with a running mojo_shell instance.
+// MUST be run on an IO thread
+class ExternalApplicationRegistrarConnection : public ErrorHandler {
+ public:
+  // Configures client_socket_ to point at socket_path.
+  explicit ExternalApplicationRegistrarConnection(
+      const base::FilePath& socket_path);
+  virtual ~ExternalApplicationRegistrarConnection();
+
+  // Implementation of ErrorHandler
+  virtual void OnConnectionError() override;
+
+  // Connects client_socket_ and binds it to registrar_.
+  // Status code is passed to callback upon success or failure.
+  // May return either synchronously or asynchronously, depending on the
+  // status of the underlying socket.
+  void Connect(const net::CompletionCallback& callback);
+
+  // Registers this app with the shell at the provided URL.
+  // shell is not ready for use until register_complete_callback fires.
+  // TODO(cmasone): Once the pipe for shell can be placed in a FIFO relationship
+  // with the one underlying registrar_, the callback becomes unneeded.
+  void Register(const GURL& app_url,
+                ShellPtr* shell,
+                base::Closure register_complete_callback);
+
+ private:
+  // Handles the result of Connect(). If it was successful, promotes the socket
+  // to a MessagePipe and binds it to registrar_.
+  // Hands rv to callback regardless.
+  void OnConnect(net::CompletionCallback callback, int rv);
+
+  scoped_ptr<net::UnixDomainClientSocket> client_socket_;
+  mojo::embedder::ChannelInit channel_init_;
+  ExternalApplicationRegistrarPtr registrar_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
diff --git a/mojo/shell/external_application_test_main.cc b/mojo/shell/external_application_test_main.cc
new file mode 100644
index 0000000..859f52b
--- /dev/null
+++ b/mojo/shell/external_application_test_main.cc
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc,
+      argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/shell/external_service.mojom b/mojo/shell/external_service.mojom
new file mode 100644
index 0000000..8cc9e0c
--- /dev/null
+++ b/mojo/shell/external_service.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 {
+
+interface ExternalService {
+  Activate(handle<message_pipe>? service_provider_handle);
+};
+
+}
diff --git a/mojo/shell/in_process_dynamic_service_runner.cc b/mojo/shell/in_process_dynamic_service_runner.cc
new file mode 100644
index 0000000..c98ef0f
--- /dev/null
+++ b/mojo/shell/in_process_dynamic_service_runner.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium 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/shell/in_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h"
+#include "mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h"
+#include "mojo/public/platform/native/gles2_impl_thunks.h"
+#include "mojo/public/platform/native/gles2_thunks.h"
+#include "mojo/public/platform/native/system_thunks.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+template <typename Thunks>
+bool SetThunks(Thunks (*make_thunks)(),
+               const char* function_name,
+               base::ScopedNativeLibrary* library) {
+  typedef size_t (*SetThunksFn)(const Thunks* thunks);
+  SetThunksFn set_thunks =
+      reinterpret_cast<SetThunksFn>(library->GetFunctionPointer(function_name));
+  if (!set_thunks)
+    return false;
+  Thunks thunks = make_thunks();
+  size_t expected_size = set_thunks(&thunks);
+  if (expected_size > sizeof(Thunks)) {
+    LOG(ERROR) << "Invalid app library: expected " << function_name
+               << " to return thunks of size: " << expected_size;
+    return false;
+  }
+  return true;
+}
+}
+
+InProcessDynamicServiceRunner::InProcessDynamicServiceRunner(
+    Context* context) {
+}
+
+InProcessDynamicServiceRunner::~InProcessDynamicServiceRunner() {
+  if (thread_) {
+    DCHECK(thread_->HasBeenStarted());
+    DCHECK(!thread_->HasBeenJoined());
+    thread_->Join();
+  }
+
+  // It is important to let the thread exit before unloading the DSO because
+  // the library may have registered thread-local data and destructors to run
+  // on thread termination.
+  app_library_.Reset(base::NativeLibrary());
+}
+
+void InProcessDynamicServiceRunner::Start(
+    const base::FilePath& app_path,
+    ScopedMessagePipeHandle service_handle,
+    const base::Closure& app_completed_callback) {
+  app_path_ = app_path;
+
+  DCHECK(!service_handle_.is_valid());
+  service_handle_ = service_handle.Pass();
+
+  DCHECK(app_completed_callback_runner_.is_null());
+  app_completed_callback_runner_ = base::Bind(&base::TaskRunner::PostTask,
+                                              base::MessageLoopProxy::current(),
+                                              FROM_HERE,
+                                              app_completed_callback);
+
+  DCHECK(!thread_);
+  thread_.reset(new base::DelegateSimpleThread(this, "app_thread"));
+  thread_->Start();
+}
+
+void InProcessDynamicServiceRunner::Run() {
+  DVLOG(2) << "Loading/running Mojo app in process from library: "
+           << app_path_.value();
+
+  do {
+    base::NativeLibraryLoadError error;
+    app_library_.Reset(base::LoadNativeLibrary(app_path_, &error));
+    if (!app_library_.is_valid()) {
+      LOG(ERROR) << "Failed to load app library (error: " << error.ToString()
+                 << ")";
+      break;
+    }
+
+    if (!SetThunks(
+            &MojoMakeSystemThunks, "MojoSetSystemThunks", &app_library_)) {
+      // In the component build, Mojo Apps link against mojo_system_impl.
+#if !defined(COMPONENT_BUILD)
+      // Strictly speaking this is not required, but it's very unusual to have
+      // an app that doesn't require the basic system library.
+      LOG(WARNING) << "MojoSetSystemThunks not found in app library";
+#endif
+    }
+
+    if (SetThunks(&MojoMakeGLES2ControlThunks,
+                  "MojoSetGLES2ControlThunks",
+                  &app_library_)) {
+      // If we have the control thunks, we probably also have the
+      // GLES2 implementation thunks.
+      if (!SetThunks(&MojoMakeGLES2ImplThunks,
+                     "MojoSetGLES2ImplThunks",
+                     &app_library_)) {
+        // In the component build, Mojo Apps link against mojo_gles2_impl.
+#if !defined(COMPONENT_BUILD)
+        // Warn on this really weird case: The library requires the GLES2
+        // control functions, but doesn't require the GLES2 implementation.
+        LOG(WARNING) << app_path_.value()
+                     << " has MojoSetGLES2ControlThunks, "
+                        "but doesn't have MojoSetGLES2ImplThunks.";
+#endif
+      }
+
+      // If the application is using GLES2 extension points, register those
+      // thunks. Applications may use or not use any of these, so don't warn if
+      // they are missing.
+      SetThunks(MojoMakeGLES2ImplChromiumTextureMailboxThunks,
+                "MojoSetGLES2ImplChromiumTextureMailboxThunks",
+                &app_library_);
+      SetThunks(MojoMakeGLES2ImplChromiumSyncPointThunks,
+                "MojoSetGLES2ImplChromiumSyncPointThunks",
+                &app_library_);
+    }
+    // Unlike system thunks, we don't warn on a lack of GLES2 thunks because
+    // not everything is a visual app.
+
+    typedef MojoResult (*MojoMainFunction)(MojoHandle);
+    MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
+        app_library_.GetFunctionPointer("MojoMain"));
+    if (!main_function) {
+      LOG(ERROR) << "Entrypoint MojoMain not found: " << app_path_.value();
+      break;
+    }
+
+    // |MojoMain()| takes ownership of the service handle.
+    MojoResult result = main_function(service_handle_.release().value());
+    if (result < MOJO_RESULT_OK)
+      LOG(ERROR) << "MojoMain returned an error: " << result;
+  } while (false);
+
+  app_completed_callback_runner_.Run();
+  app_completed_callback_runner_.Reset();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/in_process_dynamic_service_runner.h b/mojo/shell/in_process_dynamic_service_runner.h
new file mode 100644
index 0000000..0e0cade
--- /dev/null
+++ b/mojo/shell/in_process_dynamic_service_runner.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_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/scoped_native_library.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/shell/dynamic_service_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) on a separate thread (in the current process).
+class InProcessDynamicServiceRunner
+    : public DynamicServiceRunner,
+      public base::DelegateSimpleThread::Delegate {
+ public:
+  explicit InProcessDynamicServiceRunner(Context* context);
+  virtual ~InProcessDynamicServiceRunner();
+
+  // |DynamicServiceRunner| method:
+  virtual void Start(const base::FilePath& app_path,
+                     ScopedMessagePipeHandle service_handle,
+                     const base::Closure& app_completed_callback) override;
+
+ private:
+  // |base::DelegateSimpleThread::Delegate| method:
+  virtual void Run() override;
+
+  base::FilePath app_path_;
+  ScopedMessagePipeHandle service_handle_;
+  base::Callback<bool(void)> app_completed_callback_runner_;
+
+  base::ScopedNativeLibrary app_library_;
+  scoped_ptr<base::DelegateSimpleThread> thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<InProcessDynamicServiceRunner>
+    InProcessDynamicServiceRunnerFactory;
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/mojo/shell/in_process_dynamic_service_runner_unittest.cc b/mojo/shell/in_process_dynamic_service_runner_unittest.cc
new file mode 100644
index 0000000..2ade6ec
--- /dev/null
+++ b/mojo/shell/in_process_dynamic_service_runner_unittest.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/shell/context.h"
+#include "mojo/shell/in_process_dynamic_service_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+
+TEST(InProcessDynamicServiceRunnerTest, NotStarted) {
+  Context context;
+  base::MessageLoop loop;
+  context.Init();
+  InProcessDynamicServiceRunner runner(&context);
+  // Shouldn't crash or DCHECK on destruction.
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/incoming_connection_listener_posix.cc b/mojo/shell/incoming_connection_listener_posix.cc
new file mode 100644
index 0000000..8684d15
--- /dev/null
+++ b/mojo/shell/incoming_connection_listener_posix.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/incoming_connection_listener_posix.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/tracked_objects.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket_descriptor.h"
+#include "net/socket/unix_domain_server_socket_posix.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+// TODO(cmasone): Figure out what we should be doing about "authenticating" the
+// process trying to connect.
+bool Yes(const net::UnixDomainServerSocket::Credentials& ignored) {
+  return true;
+}
+}  // anonymous namespace
+
+IncomingConnectionListenerPosix::IncomingConnectionListenerPosix(
+    const base::FilePath& socket_path,
+    Delegate* delegate)
+    : delegate_(delegate),
+      socket_path_(socket_path),
+      listen_socket_(base::Bind(&Yes), false),
+      incoming_socket_(net::kInvalidSocket),
+      weak_ptr_factory_(this) {
+  DCHECK(delegate_);
+}
+
+IncomingConnectionListenerPosix::~IncomingConnectionListenerPosix() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  if (!base::DeleteFile(socket_path_, false))
+    PLOG(ERROR) << "Listening Unix domain socket can't be destroyed.";
+}
+
+void IncomingConnectionListenerPosix::StartListening() {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+
+  int rv = net::OK;
+  if (!base::DirectoryExists(socket_path_.DirName())) {
+    LOG(ERROR) << "Directorty for listening socket does not exist.";
+    rv = net::ERR_FILE_NOT_FOUND;
+  } else if (!base::PathIsWritable(socket_path_.DirName())) {
+    LOG(ERROR) << "Listening socket file path is not writable.";
+    rv = net::ERR_ACCESS_DENIED;
+  } else if (!base::DeleteFile(socket_path_, false)) {
+    PLOG(ERROR) << "Listening socket file exists and can't be deleted";
+    rv = net::ERR_FILE_EXISTS;
+  } else {
+    const std::string& socket_address = socket_path_.value();
+    rv = listen_socket_.ListenWithAddressAndPort(socket_address, 0, 100);
+  }
+
+  // Call OnListening() before Accept(), so that the delegate is certain to
+  // hear about listening before a connection might be accepted below.
+  delegate_->OnListening(rv);
+  if (rv == net::OK)
+    Accept();
+}
+
+void IncomingConnectionListenerPosix::Accept() {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+  int rv = listen_socket_.AcceptSocketDescriptor(
+      &incoming_socket_,
+      base::Bind(&IncomingConnectionListenerPosix::OnAccept,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  // If rv == net::ERR_IO_PENDING), listen_socket_ will call
+  // OnAccept() later, when a connection attempt comes in.
+  if (rv != net::ERR_IO_PENDING) {
+    DVLOG_IF(1, rv == net::OK) << "Accept succeeded immediately";
+    OnAccept(rv);
+  }
+}
+
+void IncomingConnectionListenerPosix::OnAccept(int rv) {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+
+  if (rv != net::OK || incoming_socket_ == net::kInvalidSocket) {
+    LOG_IF(ERROR, rv != net::OK) << "Accept failed " << net::ErrorToString(rv);
+    PLOG_IF(ERROR, rv == net::OK) << "Socket invalid";
+  } else {
+    // Passes ownership of incoming_socket_ to delegate_.
+    delegate_->OnConnection(incoming_socket_);
+    incoming_socket_ = net::kInvalidSocket;
+  }
+
+  // Continue waiting to accept incoming connections...
+  Accept();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/incoming_connection_listener_posix.h b/mojo/shell/incoming_connection_listener_posix.h
new file mode 100644
index 0000000..3981702
--- /dev/null
+++ b/mojo/shell/incoming_connection_listener_posix.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_SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
+#define MOJO_SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "net/socket/socket_descriptor.h"
+#include "net/socket/unix_domain_server_socket_posix.h"
+
+namespace mojo {
+namespace shell {
+
+// Asynchronously listens for incoming connections on a unix domain
+// socket at the provided path. Expects the parent directory in the
+// path to exist.  Must be run on an IO thread.
+class IncomingConnectionListenerPosix {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}  // Abstract base class, so this is safe.
+
+    // Called when listening has started. rv is from net/base/net_error_list.h
+    virtual void OnListening(int rv) = 0;
+
+    // Called every time an incoming connection is accepted. The delegate
+    // takes ownership of incoming.
+    virtual void OnConnection(net::SocketDescriptor incoming) = 0;
+  };
+
+  IncomingConnectionListenerPosix(const base::FilePath& socket_path,
+                                  Delegate* delegate);
+  virtual ~IncomingConnectionListenerPosix();
+
+  // Attempts to bind a unix domain socket, set up for listening, at
+  // socket_path_.
+  // Regardless of success or failure, calls delegate->OnListening() with a
+  // status code. If the socket was successfully created, begins asynchronously
+  // waiting to accept incoming connections.
+  void StartListening();
+
+ private:
+  // Tells listen_socket_ to perform a non-blocking accept(). It may succeed
+  // or fail immediately, or asynchronously wait for a later connection attempt.
+  // Regardless, when it returns a definitive result (OK or a failing error),
+  // calls OnAccept().
+  void Accept();
+
+  // If rv indicates success, incoming_socket_ should be populated with a
+  // connected FD. Hands this off to delegate->OnConnection() and goes
+  // back to non-blocking accept().
+  // Upon error, logs the error and goes back to non-blocking accept().
+  void OnAccept(int rv);
+
+  Delegate* const delegate_;
+
+  const base::FilePath socket_path_;
+  net::UnixDomainServerSocket listen_socket_;
+  base::ThreadChecker listen_thread_checker_;
+
+  net::SocketDescriptor incoming_socket_;
+
+  base::WeakPtrFactory<IncomingConnectionListenerPosix> weak_ptr_factory_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
diff --git a/mojo/shell/incoming_connection_listener_unittest.cc b/mojo/shell/incoming_connection_listener_unittest.cc
new file mode 100644
index 0000000..5488c98
--- /dev/null
+++ b/mojo/shell/incoming_connection_listener_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright 2014 The Chromium 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/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/shell/external_application_registrar_connection.h"
+#include "mojo/shell/incoming_connection_listener_posix.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/socket_descriptor.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+// Delegate implementation that expects success.
+class TestDelegate : public IncomingConnectionListenerPosix::Delegate {
+ public:
+  TestDelegate() {}
+  virtual ~TestDelegate() {}
+
+  virtual void OnListening(int rv) override { EXPECT_EQ(net::OK, rv); }
+  virtual void OnConnection(net::SocketDescriptor incoming) override {
+    EXPECT_NE(net::kInvalidSocket, incoming);
+  }
+};
+
+// Delegate implementation that expects a (configurable) failure to listen.
+class ListeningFailsDelegate
+    : public IncomingConnectionListenerPosix::Delegate {
+ public:
+  explicit ListeningFailsDelegate(int expected) : expected_error_(expected) {}
+  virtual ~ListeningFailsDelegate() {}
+
+  virtual void OnListening(int rv) override { EXPECT_EQ(expected_error_, rv); }
+  virtual void OnConnection(net::SocketDescriptor incoming) override {
+    FAIL() << "No connection should be attempted.";
+  }
+
+ private:
+  const int expected_error_;
+};
+
+// For ExternalApplicationRegistrarConnection::Connect() callbacks.
+void OnConnect(base::Closure quit_callback, int rv) {
+  EXPECT_EQ(net::OK, rv);
+  base::MessageLoop::current()->PostTask(FROM_HERE, quit_callback);
+}
+
+}  // namespace
+
+class IncomingConnectionListenerTest : public testing::Test {
+ public:
+  IncomingConnectionListenerTest() {}
+  virtual ~IncomingConnectionListenerTest() {}
+
+  virtual void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket"));
+  }
+
+ protected:
+  base::MessageLoopForIO loop_;
+  base::RunLoop run_loop_;
+
+  base::ScopedTempDir temp_dir_;
+  base::FilePath socket_path_;
+};
+
+TEST_F(IncomingConnectionListenerTest, CleanupCheck) {
+  TestDelegate delegate;
+  {
+    IncomingConnectionListenerPosix cleanup_check(socket_path_, &delegate);
+    cleanup_check.StartListening();
+    ASSERT_TRUE(base::PathExists(socket_path_));
+  }
+  ASSERT_FALSE(base::PathExists(socket_path_));
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectSuccess) {
+  TestDelegate delegate;
+  IncomingConnectionListenerPosix listener(socket_path_, &delegate);
+
+  ASSERT_FALSE(base::PathExists(socket_path_));
+  listener.StartListening();
+  ASSERT_TRUE(base::PathExists(socket_path_));
+
+  ExternalApplicationRegistrarConnection connection(socket_path_);
+  connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectSuccess_SocketFileExists) {
+  TestDelegate delegate;
+  IncomingConnectionListenerPosix listener(socket_path_, &delegate);
+
+  ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
+  ASSERT_TRUE(base::PathExists(socket_path_));
+  listener.StartListening();
+
+  ExternalApplicationRegistrarConnection connection(socket_path_);
+  connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketFileUndeletable) {
+  ListeningFailsDelegate fail_delegate(net::ERR_FILE_EXISTS);
+  IncomingConnectionListenerPosix listener(socket_path_, &fail_delegate);
+
+  // Create the socket file.
+  ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
+  ASSERT_TRUE(base::PathExists(socket_path_));
+
+  // Render it undeletable, but in a way that the test harness can recover
+  // later.
+  int temp_dir_perms = 0;
+  ASSERT_TRUE(base::GetPosixFilePermissions(temp_dir_.path(), &temp_dir_perms));
+  ASSERT_TRUE(
+      base::SetPosixFilePermissions(temp_dir_.path(),
+                                    base::FILE_PERMISSION_READ_BY_USER |
+                                        base::FILE_PERMISSION_WRITE_BY_USER));
+  // The listener should fail to start up.
+  listener.StartListening();
+
+  ASSERT_TRUE(base::SetPosixFilePermissions(temp_dir_.path(), temp_dir_perms));
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketDirNonexistent) {
+  base::FilePath nonexistent_dir(temp_dir_.path().Append("dir").Append("file"));
+
+  ListeningFailsDelegate fail_delegate(net::ERR_FILE_NOT_FOUND);
+  IncomingConnectionListenerPosix listener(nonexistent_dir, &fail_delegate);
+
+  // The listener should fail to start up.
+  listener.StartListening();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/init.cc b/mojo/shell/init.cc
new file mode 100644
index 0000000..481e5ee
--- /dev/null
+++ b/mojo/shell/init.cc
@@ -0,0 +1,24 @@
+// 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/shell/init.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace shell {
+
+void InitializeLogging() {
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+  // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+  logging::SetLogItems(false,    // Process ID
+                       false,    // Thread ID
+                       false,    // Timestamp
+                       false);   // Tick count
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/init.h b/mojo/shell/init.h
new file mode 100644
index 0000000..c83134a
--- /dev/null
+++ b/mojo/shell/init.h
@@ -0,0 +1,18 @@
+// 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_SHELL_INIT_H_
+#define MOJO_SHELL_INIT_H_
+
+namespace mojo {
+namespace shell {
+
+// Initialization routines shared by desktop and Android main functions.
+
+void InitializeLogging();
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_INIT_H_
diff --git a/mojo/shell/mojo_url_resolver.cc b/mojo/shell/mojo_url_resolver.cc
new file mode 100644
index 0000000..c399854
--- /dev/null
+++ b/mojo/shell/mojo_url_resolver.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium 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/shell/mojo_url_resolver.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "net/base/filename_util.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+std::string MakeSharedLibraryName(const std::string& host_name) {
+#if defined(OS_WIN)
+  return host_name + ".dll";
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+  return "lib" + host_name + ".so";
+#elif defined(OS_MACOSX)
+  return host_name + ".so";
+#else
+  NOTREACHED() << "dynamic loading of services not supported";
+  return std::string();
+#endif
+}
+
+GURL AddTrailingSlashIfNeeded(const GURL& url) {
+  if (!url.has_path() || *url.path().rbegin() == '/')
+    return url;
+
+  std::string path(url.path() + '/');
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
+  return url.ReplaceComponents(replacements);
+}
+
+}  // namespace
+
+MojoURLResolver::MojoURLResolver() {
+  // Needed to treat first component of mojo URLs as host, not path.
+  url::AddStandardScheme("mojo");
+
+  // By default, resolve mojo URLs to files living alongside the shell.
+  base::FilePath path;
+  PathService::Get(base::DIR_MODULE, &path);
+  default_base_url_ = AddTrailingSlashIfNeeded(net::FilePathToFileURL(path));
+}
+
+MojoURLResolver::~MojoURLResolver() {
+}
+
+void MojoURLResolver::SetBaseURL(const GURL& base_url) {
+  DCHECK(base_url.is_valid());
+  // Force a trailing slash on the base_url to simplify resolving
+  // relative files and URLs below.
+  base_url_ = AddTrailingSlashIfNeeded(base_url);
+}
+
+void MojoURLResolver::AddCustomMapping(const GURL& mojo_url,
+                                       const GURL& resolved_url) {
+  url_map_[mojo_url] = resolved_url;
+}
+
+void MojoURLResolver::AddLocalFileMapping(const GURL& mojo_url) {
+  local_file_set_.insert(mojo_url);
+}
+
+GURL MojoURLResolver::Resolve(const GURL& mojo_url) const {
+  std::map<GURL, GURL>::const_iterator it = url_map_.find(mojo_url);
+  if (it != url_map_.end())
+    return it->second;
+
+  std::string lib = MakeSharedLibraryName(mojo_url.host());
+
+  if (!base_url_.is_valid() ||
+      local_file_set_.find(mojo_url) != local_file_set_.end()) {
+    // Resolve to a local file URL.
+    return default_base_url_.Resolve(lib);
+  }
+
+  // Otherwise, resolve to an URL relative to base_url_.
+  return base_url_.Resolve(lib);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/mojo_url_resolver.h b/mojo/shell/mojo_url_resolver.h
new file mode 100644
index 0000000..ccd9bf4
--- /dev/null
+++ b/mojo/shell/mojo_url_resolver.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_MOJO_URL_RESOLVER_H_
+#define MOJO_SHELL_MOJO_URL_RESOLVER_H_
+
+#include <map>
+#include <set>
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// This class resolves "mojo:" URLs to physical URLs (e.g., "file:" and
+// "https:" URLs). By default, "mojo:" URLs resolve to a file location, but
+// that resolution can be customized via the AddCustomMapping method.
+class MojoURLResolver {
+ public:
+  MojoURLResolver();
+  ~MojoURLResolver();
+
+  // If specified, then unknown "mojo:" URLs will be resolved relative to this
+  // base URL. That is, the portion after the colon will be appeneded to
+  // |base_url| with platform-specific shared library prefix and suffix
+  // inserted.
+  void SetBaseURL(const GURL& base_url);
+
+  // Add a custom mapping for a particular "mojo:" URL.
+  void AddCustomMapping(const GURL& mojo_url, const GURL& resolved_url);
+
+  // Add a local file mapping for a particular "mojo:" URL. This causes the
+  // "mojo:" URL to be resolved to a base::DIR_MODULE-relative shared library.
+  void AddLocalFileMapping(const GURL& mojo_url);
+
+  // Resolve the given "mojo:" URL to the URL that should be used to fetch the
+  // code for the corresponding Mojo App.
+  GURL Resolve(const GURL& mojo_url) const;
+
+ private:
+  std::map<GURL, GURL> url_map_;
+  std::set<GURL> local_file_set_;
+  GURL default_base_url_;
+  GURL base_url_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_MOJO_URL_RESOLVER_H_
diff --git a/mojo/shell/network_application_loader.cc b/mojo/shell/network_application_loader.cc
new file mode 100644
index 0000000..dbe70b7
--- /dev/null
+++ b/mojo/shell/network_application_loader.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/shell/network_application_loader.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/network/network_service_impl.h"
+
+namespace {
+base::FilePath GetBasePath() {
+  base::FilePath path;
+  CHECK(PathService::Get(base::DIR_TEMP, &path));
+  return path.Append(FILE_PATH_LITERAL("network_service"));
+}
+}
+
+namespace mojo {
+namespace shell {
+
+NetworkApplicationLoader::NetworkApplicationLoader() {
+}
+
+NetworkApplicationLoader::~NetworkApplicationLoader() {
+}
+
+void NetworkApplicationLoader::Load(ApplicationManager* manager,
+                                    const GURL& url,
+                                    scoped_refptr<LoadCallbacks> callbacks) {
+  ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+  if (!shell_handle.is_valid())
+    return;
+
+  uintptr_t key = reinterpret_cast<uintptr_t>(manager);
+  if (apps_.find(key) == apps_.end()) {
+    scoped_ptr<ApplicationImpl> app(
+        new ApplicationImpl(this, shell_handle.Pass()));
+    apps_.add(key, app.Pass());
+  }
+}
+
+void NetworkApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                                  const GURL& url) {
+  apps_.erase(reinterpret_cast<uintptr_t>(manager));
+}
+
+void NetworkApplicationLoader::Initialize(ApplicationImpl* app) {
+  // The context must be created on the same thread as the network service.
+  context_.reset(new NetworkContext(GetBasePath()));
+}
+
+bool NetworkApplicationLoader::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(this);
+  return true;
+}
+
+void NetworkApplicationLoader::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<NetworkService> request) {
+  BindToRequest(new NetworkServiceImpl(connection, context_.get()), &request);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/network_application_loader.h b/mojo/shell/network_application_loader.h
new file mode 100644
index 0000000..0f426b0
--- /dev/null
+++ b/mojo/shell/network_application_loader.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_
+#define MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/network/network_context.h"
+
+namespace mojo {
+
+class ApplicationImpl;
+class NetworkService;
+
+namespace shell {
+
+// ApplicationLoader responsible for creating connections to the NetworkService.
+class NetworkApplicationLoader : public ApplicationLoader,
+                                 public ApplicationDelegate,
+                                 public InterfaceFactory<NetworkService> {
+ public:
+  NetworkApplicationLoader();
+  virtual ~NetworkApplicationLoader();
+
+ private:
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+  // ApplicationDelegate overrides.
+  virtual void Initialize(ApplicationImpl* app) override;
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+  // InterfaceFactory<NetworkService> overrides.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NetworkService> request) override;
+
+  base::ScopedPtrHashMap<uintptr_t, ApplicationImpl> apps_;
+  scoped_ptr<NetworkContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkApplicationLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_
diff --git a/mojo/shell/out_of_process_dynamic_service_runner.cc b/mojo/shell/out_of_process_dynamic_service_runner.cc
new file mode 100644
index 0000000..c1243a3
--- /dev/null
+++ b/mojo/shell/out_of_process_dynamic_service_runner.cc
@@ -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.
+
+#include "mojo/shell/out_of_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+
+namespace mojo {
+namespace shell {
+
+OutOfProcessDynamicServiceRunner::OutOfProcessDynamicServiceRunner(
+    Context* context)
+    : context_(context) {
+}
+
+OutOfProcessDynamicServiceRunner::~OutOfProcessDynamicServiceRunner() {
+  if (app_child_process_host_) {
+    // TODO(vtl): Race condition: If |AppChildProcessHost::DidStart()| hasn't
+    // been called yet, we shouldn't call |Join()| here. (Until |DidStart()|, we
+    // may not have a child process to wait on.) Probably we should fix
+    // |Join()|.
+    app_child_process_host_->Join();
+  }
+}
+
+void OutOfProcessDynamicServiceRunner::Start(
+    const base::FilePath& app_path,
+    ScopedMessagePipeHandle service_handle,
+    const base::Closure& app_completed_callback) {
+  app_path_ = app_path;
+
+  DCHECK(!service_handle_.is_valid());
+  service_handle_ = service_handle.Pass();
+
+  DCHECK(app_completed_callback_.is_null());
+  app_completed_callback_ = app_completed_callback;
+
+  app_child_process_host_.reset(new AppChildProcessHost(context_, this));
+  app_child_process_host_->Start();
+
+  // TODO(vtl): |app_path.AsUTF8Unsafe()| is unsafe.
+  app_child_process_host_->controller()->StartApp(
+      app_path.AsUTF8Unsafe(),
+      ScopedMessagePipeHandle(MessagePipeHandle(
+          service_handle.release().value())));
+}
+
+void OutOfProcessDynamicServiceRunner::AppCompleted(int32_t result) {
+  DVLOG(2) << "OutOfProcessDynamicServiceRunner::AppCompleted(" << result
+           << ")";
+
+  app_completed_callback_.Run();
+  app_completed_callback_.Reset();
+  app_child_process_host_.reset();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/out_of_process_dynamic_service_runner.h b/mojo/shell/out_of_process_dynamic_service_runner.h
new file mode 100644
index 0000000..71272e7
--- /dev/null
+++ b/mojo/shell/out_of_process_dynamic_service_runner.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_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "mojo/shell/app_child_process.mojom.h"
+#include "mojo/shell/app_child_process_host.h"
+#include "mojo/shell/dynamic_service_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) in a separate process (of its own).
+class OutOfProcessDynamicServiceRunner
+    : public DynamicServiceRunner,
+      public AppChildControllerClient {
+ public:
+  explicit OutOfProcessDynamicServiceRunner(Context* context);
+  virtual ~OutOfProcessDynamicServiceRunner();
+
+  // |DynamicServiceRunner| method:
+  virtual void Start(const base::FilePath& app_path,
+                     ScopedMessagePipeHandle service_handle,
+                     const base::Closure& app_completed_callback) override;
+
+ private:
+  // |AppChildControllerClient| method:
+  virtual void AppCompleted(int32_t result) override;
+
+  Context* const context_;
+
+  base::FilePath app_path_;
+  ScopedMessagePipeHandle service_handle_;
+  base::Closure app_completed_callback_;
+
+  scoped_ptr<AppChildProcessHost> app_child_process_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(OutOfProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<OutOfProcessDynamicServiceRunner>
+    OutOfProcessDynamicServiceRunnerFactory;
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/mojo/shell/shell_test_base.cc b/mojo/shell/shell_test_base.cc
new file mode 100644
index 0000000..296db5e
--- /dev/null
+++ b/mojo/shell/shell_test_base.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/shell_test_base.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "build/build_config.h"
+#include "net/base/filename_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+
+ShellTestBase::ShellTestBase() {
+}
+
+ShellTestBase::~ShellTestBase() {
+}
+
+void ShellTestBase::SetUp() {
+  shell_context_.Init();
+  test_server_.reset(new net::test_server::EmbeddedTestServer());
+  ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady());
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+  test_server_->ServeFilesFromDirectory(service_dir);
+}
+
+ScopedMessagePipeHandle ShellTestBase::ConnectToService(
+    const GURL& application_url,
+    const std::string& service_name) {
+  // Set the MojoURLResolver origin to be the same as the base file path for
+  // local files. This is primarily for test convenience, so that references
+  // to unknown mojo: urls that do not have specific local file or custom
+  // mappings registered on the URL resolver are treated as shared libraries.
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+  shell_context_.mojo_url_resolver()->SetBaseURL(
+      net::FilePathToFileURL(service_dir));
+
+  return shell_context_.ConnectToServiceByName(
+      application_url, service_name).Pass();
+}
+
+ScopedMessagePipeHandle ShellTestBase::ConnectToServiceViaNetwork(
+    const GURL& application_url,
+    const std::string& service_name) {
+  shell_context_.mojo_url_resolver()->SetBaseURL(
+      test_server_->base_url());
+
+  return shell_context_.ConnectToServiceByName(
+      application_url, service_name).Pass();
+}
+
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/shell_test_base.h b/mojo/shell/shell_test_base.h
new file mode 100644
index 0000000..4017315
--- /dev/null
+++ b/mojo/shell/shell_test_base.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_SHELL_SHELL_TEST_BASE_H_
+#define MOJO_SHELL_SHELL_TEST_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/shell/context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class GURL;
+
+namespace net {
+namespace test_server {
+class EmbeddedTestServer;
+}
+}  // namespace net
+
+namespace mojo {
+namespace shell {
+namespace test {
+
+class ShellTestBase : public testing::Test {
+ public:
+  ShellTestBase();
+  virtual ~ShellTestBase();
+
+  virtual void SetUp() override;
+
+  // |application_url| should typically be a mojo: URL (the origin will be set
+  // to an "appropriate" file: URL).
+  // TODO(tim): Should the test base be a ServiceProvider?
+  ScopedMessagePipeHandle ConnectToService(
+      const GURL& application_url,
+      const std::string& service_name);
+
+  ScopedMessagePipeHandle ConnectToServiceViaNetwork(
+      const GURL& application_url,
+      const std::string& service_name);
+
+  template <typename Interface>
+  void ConnectToService(const GURL& application_url,
+                        InterfacePtr<Interface>* ptr) {
+    ptr->Bind(ConnectToService(application_url, Interface::Name_).Pass());
+  }
+
+  template <typename Interface>
+  void ConnectToServiceViaNetwork(const GURL& application_url,
+                        InterfacePtr<Interface>* ptr) {
+    ptr->Bind(
+        ConnectToServiceViaNetwork(application_url, Interface::Name_).Pass());
+  }
+
+  base::MessageLoop* message_loop() { return &message_loop_; }
+  Context* shell_context() { return &shell_context_; }
+
+ private:
+  scoped_ptr<net::test_server::EmbeddedTestServer> test_server_;
+  Context shell_context_;
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellTestBase);
+};
+
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_SHELL_TEST_BASE_H_
diff --git a/mojo/shell/shell_test_base_unittest.cc b/mojo/shell/shell_test_base_unittest.cc
new file mode 100644
index 0000000..8a0bdd4
--- /dev/null
+++ b/mojo/shell/shell_test_base_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright 2014 The Chromium 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/shell/shell_test_base.h"
+
+#include "base/bind.h"
+#include "base/i18n/time_formatting.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using mojo::test::ServiceReport;
+using mojo::test::ServiceReportPtr;
+using mojo::test::TestService;
+using mojo::test::TestTimeService;
+using mojo::test::TestServicePtr;
+using mojo::test::TestTimeServicePtr;
+using mojo::test::TestTrackedRequestService;
+using mojo::test::TestTrackedRequestServicePtr;
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+void GetReportCallback(base::MessageLoop* loop,
+                       std::vector<ServiceReport>* reports_out,
+                       mojo::Array<ServiceReportPtr> report) {
+  for (size_t i = 0; i < report.size(); i++)
+    reports_out->push_back(*report[i]);
+  loop->QuitWhenIdle();
+}
+
+class ShellTestBaseTest : public ShellTestBase {
+ public:
+  // Convenience helpers for use as callbacks in tests.
+  template <typename T>
+  base::Callback<void()> SetAndQuit(T* val, T result) {
+    return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
+        base::Unretained(this), val, result);
+  }
+  template <typename T>
+  base::Callback<void(T result)> SetAndQuit(T* val) {
+      return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
+          base::Unretained(this), val);
+  }
+  static GURL test_app_url() {
+    return GURL("mojo:mojo_test_app");
+  }
+
+  void GetReport(std::vector<ServiceReport>* report) {
+    ConnectToService(GURL("mojo:mojo_test_request_tracker_app"),
+                     &request_tracking_);
+    request_tracking_->GetReport(base::Bind(&GetReportCallback,
+        base::Unretained(message_loop()),
+        base::Unretained(report)));
+    message_loop()->Run();
+  }
+
+ private:
+  template<typename T>
+  void SetAndQuitImpl(T* val, T result) {
+    *val = result;
+    message_loop()->QuitWhenIdle();
+  }
+  TestTrackedRequestServicePtr request_tracking_;
+};
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+  QuitMessageLoopErrorHandler() {}
+  virtual ~QuitMessageLoopErrorHandler() {}
+
+  // |ErrorHandler| implementation:
+  virtual void OnConnectionError() override {
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+// Tests that we can connect to a single service within a single app.
+TEST_F(ShellTestBaseTest, ConnectBasic) {
+  InterfacePtr<TestService> service;
+  ConnectToService(test_app_url(), &service);
+
+  bool was_run = false;
+  service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(was_run);
+  EXPECT_FALSE(service.encountered_error());
+
+  service.reset();
+
+  // This will run until the test app has actually quit (which it will,
+  // since we killed the only connection to it).
+  message_loop()->Run();
+}
+
+// Tests that trying to connect to a service fails properly if the service
+// doesn't exist. Implicit in this test is verification that the shell
+// terminates if no services are running.
+TEST_F(ShellTestBaseTest, ConnectInvalidService) {
+  InterfacePtr<TestService> test_service;
+  ConnectToService(GURL("mojo:non_existent_service"), &test_service);
+
+  bool was_run = false;
+  test_service->Ping(SetAndQuit<bool>(&was_run, true));
+
+  // This will quit because there's nothing running.
+  message_loop()->Run();
+  EXPECT_FALSE(was_run);
+
+  // It may have quit before an error was processed.
+  if (!test_service.encountered_error()) {
+    QuitMessageLoopErrorHandler quitter;
+    test_service.set_error_handler(&quitter);
+    message_loop()->Run();
+    EXPECT_TRUE(test_service.encountered_error());
+  }
+
+  test_service.reset();
+}
+
+// Tests that we can connect to a single service within a single app using
+// a network based loader instead of local files.
+// TODO(tim): Disabled because network service leaks NSS at exit, meaning
+// subsequent tests can't init properly.
+TEST_F(ShellTestBaseTest, DISABLED_ConnectBasicNetwork) {
+  InterfacePtr<TestService> service;
+  ConnectToService(test_app_url(), &service);
+
+  bool was_run = false;
+  service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(was_run);
+  EXPECT_FALSE(service.encountered_error());
+
+  // Note that use of the network service is implicit in this test.
+  // Since TestService is not the only service in use, the shell won't auto
+  // magically exit when TestService is destroyed (unlike ConnectBasic).
+  // Tearing down the shell context will kill connections. The shell loop will
+  // exit as soon as no more apps are connected.
+  // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
+  // necessary once the shell terminates if the primordial app exits, which
+  // we could enforce here by resetting |service|.
+  shell_context()->application_manager()->TerminateShellConnections();
+  message_loop()->Run();  // Waits for all connections to die.
+}
+
+// Tests that trying to connect to a service over network fails preoprly
+// if the service doesn't exist.
+// TODO(tim): Disabled because network service leaks NSS at exit, meaning
+// subsequent tests can't init properly.
+TEST_F(ShellTestBaseTest, DISABLED_ConnectInvalidServiceNetwork) {
+  InterfacePtr<TestService> test_service;
+  ConnectToServiceViaNetwork(GURL("mojo:non_existent_service"), &test_service);
+  QuitMessageLoopErrorHandler quitter;
+  test_service.set_error_handler(&quitter);
+  bool was_run = false;
+  test_service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(test_service.encountered_error());
+
+  // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
+  // necessary once the shell terminates if the primordial app exits, which
+  // we could enforce here by resetting |service|.
+  shell_context()->application_manager()->TerminateShellConnections();
+  message_loop()->Run();  // Waits for all connections to die.
+}
+
+// Similar to ConnectBasic, but causes the app to instantiate multiple
+// service implementation objects and verifies the shell can reach both.
+TEST_F(ShellTestBaseTest, ConnectMultipleInstancesPerApp) {
+  {
+    TestServicePtr service1, service2;
+    ConnectToService(test_app_url(), &service1);
+    ConnectToService(test_app_url(), &service2);
+
+    bool was_run1 = false;
+    bool was_run2 = false;
+    service1->Ping(SetAndQuit<bool>(&was_run1, true));
+    message_loop()->Run();
+    service2->Ping(SetAndQuit<bool>(&was_run2, true));
+    message_loop()->Run();
+    EXPECT_TRUE(was_run1);
+    EXPECT_TRUE(was_run2);
+    EXPECT_FALSE(service1.encountered_error());
+    EXPECT_FALSE(service2.encountered_error());
+  }
+  message_loop()->Run();
+}
+
+// Tests that service A and service B, both in App 1, can talk to each other
+// and parameters are passed around properly.
+TEST_F(ShellTestBaseTest, ConnectDifferentServicesInSingleApp) {
+  // Have a TestService GetPartyTime on a TestTimeService in the same app.
+  int64 time_message;
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->ConnectToAppAndGetTime(test_app_url().spec(),
+                                  SetAndQuit<int64>(&time_message));
+  message_loop()->Run();
+
+  // Verify by hitting the TimeService directly.
+  TestTimeServicePtr time_service;
+  ConnectToService(test_app_url(), &time_service);
+  int64 party_time;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  EXPECT_EQ(time_message, party_time);
+}
+
+// Tests that a service A in App 1 can talk to service B in App 2 and
+// parameters are passed around properly.
+TEST_F(ShellTestBaseTest, ConnectDifferentServicesInDifferentApps) {
+  int64 time_message;
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->ConnectToAppAndGetTime("mojo:mojo_test_request_tracker_app",
+                                  SetAndQuit<int64>(&time_message));
+  message_loop()->Run();
+
+  // Verify by hitting the TimeService in the request tracker app directly.
+  TestTimeServicePtr time_service;
+  ConnectToService(GURL("mojo:mojo_test_request_tracker_app"), &time_service);
+  int64 party_time;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  EXPECT_EQ(time_message, party_time);
+}
+
+// Tests that service A in App 1 can be a client of service B in App 2.
+TEST_F(ShellTestBaseTest, ConnectServiceAsClientOfSeparateApp) {
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  service->Ping(mojo::Callback<void()>());
+  message_loop()->Run();
+
+  for (int i = 0; i < 8; i++)
+    service->Ping(mojo::Callback<void()>());
+  service->Ping(message_loop()->QuitWhenIdleClosure());
+  message_loop()->Run();
+
+  // If everything worked properly, the tracking service should report
+  // 10 pings to TestService.
+  std::vector<ServiceReport> reports;
+  GetReport(&reports);
+  ASSERT_EQ(1U, reports.size());
+  EXPECT_EQ(TestService::Name_, reports[0].service_name);
+  EXPECT_EQ(10U, reports[0].total_requests);
+}
+
+// Connect several services together and use the tracking service to verify
+// communication.
+TEST_F(ShellTestBaseTest, ConnectManyClientsAndServices) {
+  TestServicePtr service;
+  TestTimeServicePtr time_service;
+
+  // Make a request to the TestService and have it contact TimeService in the
+  // tracking app. Do all this with tracking enabled, meaning both services
+  // are connected as clients of the TrackedRequestService.
+  ConnectToService(test_app_url(), &service);
+  service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  message_loop()->Run();
+  for (int i = 0; i < 5; i++)
+    service->Ping(mojo::Callback<void()>());
+  int64 time_result;
+  service->ConnectToAppAndGetTime("mojo:mojo_test_request_tracker_app",
+                                  SetAndQuit<int64>(&time_result));
+  message_loop()->Run();
+
+  // Also make a few requests to the TimeService in the test_app.
+  ConnectToService(test_app_url(), &time_service);
+  time_service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
+  message_loop()->Run();
+  for (int i = 0; i < 18; i++)
+    time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
+  // Flush the tasks with one more to quit.
+  int64 party_time = 0;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  std::vector<ServiceReport> reports;
+  GetReport(&reports);
+  ASSERT_EQ(3U, reports.size());
+  EXPECT_EQ(TestService::Name_, reports[0].service_name);
+  EXPECT_EQ(6U, reports[0].total_requests);
+  EXPECT_EQ(TestTimeService::Name_, reports[1].service_name);
+  EXPECT_EQ(1U, reports[1].total_requests);
+  EXPECT_EQ(TestTimeService::Name_, reports[2].service_name);
+  EXPECT_EQ(20U, reports[2].total_requests);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/shell_test_helper.cc b/mojo/shell/shell_test_helper.cc
new file mode 100644
index 0000000..d0f0f7a
--- /dev/null
+++ b/mojo/shell/shell_test_helper.cc
@@ -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.
+
+#include "mojo/shell/shell_test_helper.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "mojo/shell/init.h"
+#include "net/base/filename_util.h"
+
+namespace mojo {
+namespace shell {
+
+ShellTestHelper::ShellTestHelper() {
+  base::CommandLine::Init(0, NULL);
+  mojo::shell::InitializeLogging();
+}
+
+ShellTestHelper::~ShellTestHelper() {
+}
+
+void ShellTestHelper::Init() {
+  context_.Init();
+  test_api_.reset(
+      new ApplicationManager::TestAPI(context_.application_manager()));
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+  context_.mojo_url_resolver()->SetBaseURL(
+      net::FilePathToFileURL(service_dir));
+}
+
+void ShellTestHelper::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
+                                      const GURL& url) {
+  context_.application_manager()->SetLoaderForURL(loader.Pass(), url);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/shell_test_helper.h b/mojo/shell/shell_test_helper.h
new file mode 100644
index 0000000..38fb008
--- /dev/null
+++ b/mojo/shell/shell_test_helper.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_SHELL_TEST_HELPER_
+#define MOJO_SHELL_SHELL_TEST_HELPER_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/shell/context.h"
+
+class GURL;
+
+namespace mojo {
+
+class ApplicationLoader;
+
+namespace shell {
+
+// ShellTestHelper is useful for tests to establish a connection to the
+// ApplicationManager. Invoke Init() to establish the connection. Once done,
+// application_manager() returns the ApplicationManager.
+class ShellTestHelper {
+ public:
+  ShellTestHelper();
+  ~ShellTestHelper();
+
+  void Init();
+
+  ApplicationManager* application_manager() {
+    return context_.application_manager();
+  }
+
+  // Sets a ApplicationLoader for the specified URL. |loader| is ultimately used
+  // on
+  // the thread this class spawns.
+  void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url);
+
+ private:
+  Context context_;
+  base::MessageLoop shell_loop_;
+  scoped_ptr<ApplicationManager::TestAPI> test_api_;
+  DISALLOW_COPY_AND_ASSIGN(ShellTestHelper);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_SHELL_TEST_HELPER_
diff --git a/mojo/shell/shell_test_main.cc b/mojo/shell/shell_test_main.cc
new file mode 100644
index 0000000..7f057f7
--- /dev/null
+++ b/mojo/shell/shell_test_main.cc
@@ -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.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/shell/child_process.h"
+#include "mojo/shell/switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  if (command_line.HasSwitch(switches::kChildProcessType)) {
+    scoped_ptr<mojo::shell::ChildProcess> child_process =
+        mojo::shell::ChildProcess::Create(command_line);
+    CHECK(child_process);
+    child_process->Main();
+    return 0;
+  }
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv, base::Bind(&base::TestSuite::Run,
+                             base::Unretained(&test_suite)));
+}
diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc
new file mode 100644
index 0000000..61a93e2
--- /dev/null
+++ b/mojo/shell/switches.cc
@@ -0,0 +1,43 @@
+// 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/shell/switches.h"
+
+namespace switches {
+
+// Specify configuration arguments for a Mojo application URL. For example:
+// --args-for='mojo://mojo_wget http://www.google.com'
+const char kArgsFor[] = "args-for";
+
+// Used to specify the type of child process (switch values from
+// |ChildProcess::Type|).
+const char kChildProcessType[] = "child-process-type";
+
+// Comma separated list like:
+// text/html,mojo://mojo_html_viewer,application/bravo,https://abarth.com/bravo
+const char kContentHandlers[] = "content-handlers";
+
+// Force dynamically loaded apps / services to be loaded irrespective of cache
+// instructions.
+const char kDisableCache[] = "disable-cache";
+
+// Allow externally-running applications to discover, connect to, and register
+// themselves with the shell.
+// TODO(cmasone): Work in progress. Once we're sure this works, remove.
+const char kEnableExternalApplications[] = "enable-external-applications";
+
+// Load apps in separate processes.
+// TODO(vtl): Work in progress; doesn't work. Flip this to "disable" (or maybe
+// change it to "single-process") when it works.
+const char kEnableMultiprocess[] = "enable-multiprocess";
+
+// Map mojo: URLs to a shared library of similar name at this origin. See
+// mojo_url_resolver.cc for details.
+const char kOrigin[] = "origin";
+
+// Enables the mojo spy, which acts as a man-in-the-middle inspector for
+// message pipes and other activities. This is work in progress.
+const char kSpy[] = "spy";
+
+}  // namespace switches
diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h
new file mode 100644
index 0000000..33f5946
--- /dev/null
+++ b/mojo/shell/switches.h
@@ -0,0 +1,22 @@
+// 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_SHELL_SWITCHES_H_
+#define MOJO_SHELL_SWITCHES_H_
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kArgsFor[];
+extern const char kChildProcessType[];
+extern const char kContentHandlers[];
+extern const char kDisableCache[];
+extern const char kEnableExternalApplications[];
+extern const char kEnableMultiprocess[];
+extern const char kOrigin[];
+extern const char kSpy[];
+}  // namespace switches
+
+#endif  // MOJO_SHELL_SWITCHES_H_
diff --git a/mojo/shell/task_runners.cc b/mojo/shell/task_runners.cc
new file mode 100644
index 0000000..cb157ee
--- /dev/null
+++ b/mojo/shell/task_runners.cc
@@ -0,0 +1,39 @@
+// 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/shell/task_runners.h"
+
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+const size_t kMaxBlockingPoolThreads = 3;
+
+scoped_ptr<base::Thread> CreateIOThread(const char* name) {
+  scoped_ptr<base::Thread> thread(new base::Thread(name));
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  thread->StartWithOptions(options);
+  return thread.Pass();
+}
+
+}  // namespace
+
+TaskRunners::TaskRunners(
+    const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner)
+    : shell_runner_(shell_runner),
+      io_thread_(CreateIOThread("io_thread")),
+      blocking_pool_(new base::SequencedWorkerPool(kMaxBlockingPoolThreads,
+                                                   "blocking_pool")) {
+}
+
+TaskRunners::~TaskRunners() {
+  blocking_pool_->Shutdown();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/task_runners.h b/mojo/shell/task_runners.h
new file mode 100644
index 0000000..d5a0cec
--- /dev/null
+++ b/mojo/shell/task_runners.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_SHELL_TASK_RUNNERS_H_
+#define MOJO_SHELL_TASK_RUNNERS_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread.h"
+
+namespace base {
+class SequencedWorkerPool;
+}
+
+namespace mojo {
+namespace shell {
+
+// A context object that contains the common task runners for the shell's main
+// process.
+class TaskRunners {
+ public:
+  explicit TaskRunners(
+      const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner);
+  ~TaskRunners();
+
+  base::SingleThreadTaskRunner* shell_runner() const {
+    return shell_runner_.get();
+  }
+
+  base::SingleThreadTaskRunner* io_runner() const {
+    return io_thread_->message_loop_proxy().get();
+  }
+
+  base::SequencedWorkerPool* blocking_pool() const {
+    return blocking_pool_.get();
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> shell_runner_;
+  scoped_ptr<base::Thread> io_thread_;
+
+  scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskRunners);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_TASK_RUNNERS_H_
diff --git a/mojo/shell/test_child_process.cc b/mojo/shell/test_child_process.cc
new file mode 100644
index 0000000..fe187a5
--- /dev/null
+++ b/mojo/shell/test_child_process.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/shell/test_child_process.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+
+namespace mojo {
+namespace shell {
+
+TestChildProcess::TestChildProcess() {
+}
+
+TestChildProcess::~TestChildProcess() {
+}
+
+void TestChildProcess::Main() {
+  VLOG(2) << "TestChildProcess::Main()";
+
+  CHECK(!base::MessageLoop::current());
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/test_child_process.h b/mojo/shell/test_child_process.h
new file mode 100644
index 0000000..c3acb9b
--- /dev/null
+++ b/mojo/shell/test_child_process.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_SHELL_TEST_CHILD_PROCESS_H_
+#define MOJO_SHELL_TEST_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "mojo/shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+class TestChildProcess : public ChildProcess {
+ public:
+  TestChildProcess();
+  virtual ~TestChildProcess();
+
+  virtual void Main() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_TEST_CHILD_PROCESS_H_
diff --git a/mojo/shell/ui_application_loader_android.cc b/mojo/shell/ui_application_loader_android.cc
new file mode 100644
index 0000000..1e4e5aa
--- /dev/null
+++ b/mojo/shell/ui_application_loader_android.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium 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/shell/ui_application_loader_android.h"
+
+#include "base/bind.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/shell/context.h"
+
+namespace mojo {
+
+class UIApplicationLoader::UILoader {
+ public:
+  explicit UILoader(ApplicationLoader* loader) : loader_(loader) {}
+  ~UILoader() {}
+
+  void Load(ApplicationManager* manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle) {
+    scoped_refptr<LoadCallbacks> callbacks(
+        new ApplicationLoader::SimpleLoadCallbacks(shell_handle.Pass()));
+    loader_->Load(manager, url, callbacks);
+  }
+
+  void OnApplicationError(ApplicationManager* manager, const GURL& url) {
+    loader_->OnApplicationError(manager, url);
+  }
+
+ private:
+  ApplicationLoader* loader_;  // Owned by UIApplicationLoader
+
+  DISALLOW_COPY_AND_ASSIGN(UILoader);
+};
+
+UIApplicationLoader::UIApplicationLoader(
+    scoped_ptr<ApplicationLoader> real_loader,
+    shell::Context* context)
+    : loader_(real_loader.Pass()), context_(context) {
+}
+
+UIApplicationLoader::~UIApplicationLoader() {
+  context_->ui_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&UIApplicationLoader::ShutdownOnUIThread,
+                 base::Unretained(this)));
+}
+
+void UIApplicationLoader::Load(ApplicationManager* manager,
+                               const GURL& url,
+                               scoped_refptr<LoadCallbacks> callbacks) {
+  ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+  if (!shell_handle.is_valid())
+    return;
+  context_->ui_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &UIApplicationLoader::LoadOnUIThread,
+          base::Unretained(this),
+          manager,
+          url,
+          base::Owned(new ScopedMessagePipeHandle(shell_handle.Pass()))));
+}
+
+void UIApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                             const GURL& url) {
+  context_->ui_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&UIApplicationLoader::OnApplicationErrorOnUIThread,
+                 base::Unretained(this),
+                 manager,
+                 url));
+}
+
+void UIApplicationLoader::LoadOnUIThread(
+    ApplicationManager* manager,
+    const GURL& url,
+    ScopedMessagePipeHandle* shell_handle) {
+  if (!ui_loader_)
+    ui_loader_ = new UILoader(loader_.get());
+  ui_loader_->Load(manager, url, shell_handle->Pass());
+}
+
+void UIApplicationLoader::OnApplicationErrorOnUIThread(
+    ApplicationManager* manager,
+    const GURL& url) {
+  if (!ui_loader_)
+    ui_loader_ = new UILoader(loader_.get());
+  ui_loader_->OnApplicationError(manager, url);
+}
+
+void UIApplicationLoader::ShutdownOnUIThread() {
+  delete ui_loader_;
+  // Destroy |loader_| on the thread it's actually used on.
+  loader_.reset();
+}
+
+}  // namespace mojo
diff --git a/mojo/shell/ui_application_loader_android.h b/mojo/shell/ui_application_loader_android.h
new file mode 100644
index 0000000..bed36ca
--- /dev/null
+++ b/mojo/shell/ui_application_loader_android.h
@@ -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.
+
+#ifndef MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_
+#define MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+
+namespace mojo {
+
+class ApplicationManager;
+
+namespace shell {
+class Context;
+}
+
+// ApplicationLoader implementation that creates a background thread and issues
+// load
+// requests there.
+class UIApplicationLoader : public ApplicationLoader {
+ public:
+  UIApplicationLoader(scoped_ptr<ApplicationLoader> real_loader,
+                      shell::Context* context);
+  virtual ~UIApplicationLoader();
+
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+ private:
+  class UILoader;
+
+  // These functions are exected on the background thread. They call through
+  // to |background_loader_| to do the actual loading.
+  // TODO: having this code take a |manager| is fragile (as ApplicationManager
+  // isn't thread safe).
+  void LoadOnUIThread(ApplicationManager* manager,
+                      const GURL& url,
+                      ScopedMessagePipeHandle* shell_handle);
+  void OnApplicationErrorOnUIThread(ApplicationManager* manager,
+                                    const GURL& url);
+  void ShutdownOnUIThread();
+
+  scoped_ptr<ApplicationLoader> loader_;
+  shell::Context* context_;
+
+  // Lives on the UI thread. Trivial interface that calls through to |loader_|.
+  UILoader* ui_loader_;
+
+  DISALLOW_COPY_AND_ASSIGN(UIApplicationLoader);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_
diff --git a/mojo/shell/view_manager_loader.cc b/mojo/shell/view_manager_loader.cc
new file mode 100644
index 0000000..9128084
--- /dev/null
+++ b/mojo/shell/view_manager_loader.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 "mojo/shell/view_manager_loader.h"
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+
+using service::ViewManagerInitServiceImpl;
+
+namespace shell {
+
+ViewManagerLoader::ViewManagerLoader() {
+}
+
+ViewManagerLoader::~ViewManagerLoader() {
+}
+
+void ViewManagerLoader::Load(ApplicationManager* manager,
+                             const GURL& url,
+                             scoped_refptr<LoadCallbacks> callbacks) {
+  ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+  if (!shell_handle.is_valid())
+    return;
+
+  // TODO(sky): this needs some sort of authentication as well as making sure
+  // we only ever have one active at a time.
+  scoped_ptr<ApplicationImpl> app(
+      new ApplicationImpl(this, shell_handle.Pass()));
+  apps_.push_back(app.release());
+}
+
+void ViewManagerLoader::OnApplicationError(ApplicationManager* manager,
+                                           const GURL& url) {
+}
+
+bool ViewManagerLoader::ConfigureIncomingConnection(
+    ApplicationConnection* connection)  {
+  context_.ConfigureIncomingConnection(connection);
+  connection->AddService(this);
+  return true;
+}
+
+void ViewManagerLoader::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<ViewManagerInitService> request) {
+  BindToRequest(new ViewManagerInitServiceImpl(connection, &context_),
+                &request);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/mojo/shell/view_manager_loader.h b/mojo/shell/view_manager_loader.h
new file mode 100644
index 0000000..83c5f31
--- /dev/null
+++ b/mojo/shell/view_manager_loader.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_VIEW_MANAGER_LOADER_H_
+#define MOJO_SHELL_VIEW_MANAGER_LOADER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/view_manager_init_service_context.h"
+
+namespace mojo {
+
+class Application;
+
+namespace shell {
+
+// ApplicationLoader responsible for creating connections to the ViewManager.
+class ViewManagerLoader : public ApplicationLoader,
+                          public ApplicationDelegate,
+                          public InterfaceFactory<ViewManagerInitService> {
+ public:
+  ViewManagerLoader();
+  virtual ~ViewManagerLoader();
+
+ private:
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    scoped_refptr<LoadCallbacks> callbacks) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+  // ApplicationDelegate overrides.
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // InterfaceFactory<ViewManagerInitService> overrides.
+  virtual void Create(
+      ApplicationConnection* connection,
+      InterfaceRequest<ViewManagerInitService> request) override;
+
+  ScopedVector<Application> apps_;
+  service::ViewManagerInitServiceContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewManagerLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_VIEW_MANAGER_LOADER_H_
diff --git a/mojo/spy/BUILD.gn b/mojo/spy/BUILD.gn
new file mode 100644
index 0000000..c89b5a1
--- /dev/null
+++ b/mojo/spy/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("spy") {
+  deps = [
+    ":spy_bindings",
+    "//base",
+    "//net:http_server",
+    "//url",
+    "//mojo/application_manager",
+  ]
+
+  sources = [
+    "common.h",
+    "spy.cc",
+    "spy.h",
+    "spy_server_impl.h",
+    "spy_server_impl.cc",
+    "websocket_server.cc",
+    "websocket_server.h",
+  ]
+}
+
+mojom("spy_bindings") {
+  sources = [
+    "public/spy.mojom",
+  ]
+}
diff --git a/mojo/spy/DEPS b/mojo/spy/DEPS
new file mode 100644
index 0000000..8fa9d48
--- /dev/null
+++ b/mojo/spy/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+net",
+]
diff --git a/mojo/spy/PRESUBMIT.py b/mojo/spy/PRESUBMIT.py
new file mode 100644
index 0000000..5090fb8
--- /dev/null
+++ b/mojo/spy/PRESUBMIT.py
@@ -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.
+import os
+import sys
+
+def _CommonChecks(input_api, output_api):
+  results = []
+  # Importing ui actually brings tvcm into the path.
+  import ui
+  from tvcm import presubmit_checker
+  checker = presubmit_checker.PresubmitChecker(input_api, output_api)
+  results += checker.RunChecks()
+  return results
+
+def GetPathsToPrepend(input_api):
+  return [input_api.PresubmitLocalPath()]
+
+def RunWithPrependedPath(prepended_path, fn, *args):
+  old_path = sys.path
+
+  try:
+    sys.path = prepended_path + old_path
+    return fn(*args)
+  finally:
+    sys.path = old_path
+
+def CheckChangeOnUpload(input_api, output_api):
+  def go():
+    results = []
+    results.extend(_CommonChecks(input_api, output_api))
+    return results
+  return RunWithPrependedPath(GetPathsToPrepend(input_api), go)
+
+def CheckChangeOnCommit(input_api, output_api):
+  def go():
+    results = []
+    results.extend(_CommonChecks(input_api, output_api))
+    return results
+  return RunWithPrependedPath(GetPathsToPrepend(input_api), go)
diff --git a/mojo/spy/common.h b/mojo/spy/common.h
new file mode 100644
index 0000000..f39f283
--- /dev/null
+++ b/mojo/spy/common.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SPY_COMMON_H_
+#define MOJO_SPY_COMMON_H_
+
+#include <stdint.h>
+
+namespace mojo {
+
+#pragma pack(push, 1)
+
+// Mojo message header structures. These are based off the Mojo spec.
+
+enum {
+  kMessageExpectsResponse = 1 << 0,
+  kMessageIsResponse      = 1 << 1
+};
+
+struct MojoCommonHeader {
+  uint32_t num_bytes;
+  uint32_t num_fields;
+};
+
+struct MojoMessageHeader : public MojoCommonHeader {
+  uint32_t name;
+  uint32_t flags;
+};
+
+struct MojoRequestHeader : public MojoMessageHeader {
+  uint64_t request_id;
+};
+
+struct MojoMessageData  {
+  MojoRequestHeader header;
+};
+
+#pragma pack(pop)
+
+}  // namespace mojo
+
+#endif  // MOJO_SPY_COMMON_H_
diff --git a/mojo/spy/public/spy.mojom b/mojo/spy/public/spy.mojom
new file mode 100644
index 0000000..fd5fffe
--- /dev/null
+++ b/mojo/spy/public/spy.mojom
@@ -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.
+
+module mojo.spy_api {
+
+enum Result {
+  ALL_OK,
+  INTERNAL_ERROR,
+  INVALID_ID,
+  NO_MORE_IDS,
+  INVALID_CALL,
+  INVALID_PARAMS,
+  BAD_STATE,
+  RESOURCE_LIMIT
+};
+
+struct Version {
+  uint32 v_major;
+  uint32 v_minor;
+};
+
+enum ConnectionOptions {
+  SKIP,
+  PAUSE,
+  RESUME,
+  PEEK_MESSAGES
+};
+
+struct Message {
+  uint32 id;
+  uint32 time;
+  array<uint8>? data;
+};
+
+[Client=SpyClient]
+interface SpyServer {
+  StartSession(Version? version) => (Result result, string? name);
+  StopSession() => (Result result);
+  TrackConnection(uint32 id, ConnectionOptions options) => (Result result);
+};
+
+interface SpyClient {
+  OnFatalError(Result result);
+  OnSessionEnd(Result result);
+  OnClientConnection(string? name, uint32 id, ConnectionOptions options);
+  OnMessage(Message? message);
+};
+
+}
diff --git a/mojo/spy/run_ui_dev_server b/mojo/spy/run_ui_dev_server
new file mode 100755
index 0000000..5dd294f
--- /dev/null
+++ b/mojo/spy/run_ui_dev_server
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Starts the mojo spy dev server.
+
+During normal usage of mojo spy, the spy files are compiled into standalone
+HTML+JS+CSS snippets that are then embedded in the mojo shell.
+
+The dev server allows edit-reload style development of the spy UI in isolation
+of the c++ bits. To use, start the dev server, navigate to the URL the script
+prints, and run any of the tests listed. Reloading in the browser loads the
+latest content from disk, enabling a traditional web development workflow.
+"""
+import sys
+
+from ui import dev_server
+
+COMPONENTS_PORT = 8015
+
+if __name__ == '__main__':
+  sys.exit(dev_server.Main(COMPONENTS_PORT, sys.argv[1:]))
diff --git a/mojo/spy/run_ui_tests b/mojo/spy/run_ui_tests
new file mode 100755
index 0000000..00f8ba1
--- /dev/null
+++ b/mojo/spy/run_ui_tests
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Starts the mojo spy dev server.
+
+During normal usage of mojo spy, the spy files are compiled into standalone
+HTML+JS+CSS snippets that are then embedded in the mojo shell.
+
+The dev server allows edit-reload style development of the spy UI in isolation
+of the c++ bits. To use, start the dev server, navigate to the URL the script
+prints, and run any of the tests listed. Reloading in the browser loads the
+latest content from disk, enabling a traditional web development workflow.
+"""
+import sys
+
+import ui
+from tvcm import test_runner
+
+if __name__ == '__main__':
+  runner = test_runner.TestRunner()
+  runner.AddModule(ui)
+  sys.exit(runner.Main())
diff --git a/mojo/spy/spy.cc b/mojo/spy/spy.cc
new file mode 100644
index 0000000..67ae56a
--- /dev/null
+++ b/mojo/spy/spy.cc
@@ -0,0 +1,338 @@
+// Copyright 2014 The Chromium 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/spy/spy.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread.h"
+#include "base/threading/worker_pool.h"
+#include "base/time/time.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/spy/common.h"
+#include "mojo/spy/public/spy.mojom.h"
+#include "mojo/spy/spy_server_impl.h"
+#include "mojo/spy/websocket_server.h"
+#include "url/gurl.h"
+
+namespace {
+
+mojo::WebSocketServer* ws_server = NULL;
+
+const size_t kMessageBufSize = 2 * 1024;
+const size_t kHandleBufSize = 64;
+const int kDefaultWebSocketPort = 42424;
+
+void CloseHandles(MojoHandle* handles, size_t count) {
+  for (size_t ix = 0; ix != count; ++count)
+    MojoClose(handles[ix]);
+}
+
+// In charge of processing messages that flow over a
+// single message pipe.
+class MessageProcessor :
+    public base::RefCountedThreadSafe<MessageProcessor> {
+ public:
+  MessageProcessor(base::MessageLoopProxy* control_loop_proxy)
+      : last_result_(MOJO_RESULT_OK),
+        bytes_transfered_(0),
+        control_loop_proxy_(control_loop_proxy) {
+    message_count_[0] = 0;
+    message_count_[1] = 0;
+    handle_count_[0] = 0;
+    handle_count_[1] = 0;
+  }
+
+  void Start(mojo::ScopedMessagePipeHandle client,
+             mojo::ScopedMessagePipeHandle interceptor,
+             const GURL& url) {
+    std::vector<mojo::MessagePipeHandle> pipes;
+    pipes.push_back(client.get());
+    pipes.push_back(interceptor.get());
+    std::vector<MojoHandleSignals> handle_signals;
+    handle_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+    handle_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+
+    scoped_ptr<char[]> mbuf(new char[kMessageBufSize]);
+    scoped_ptr<MojoHandle[]> hbuf(new MojoHandle[kHandleBufSize]);
+
+    // Main processing loop:
+    // 1- Wait for an endpoint to have a message.
+    // 2- Read the message
+    // 3- Log data
+    // 4- Wait until the opposite port is ready for writting
+    // 4- Write the message to opposite port.
+
+    for (;;) {
+      int r = WaitMany(pipes, handle_signals, MOJO_DEADLINE_INDEFINITE);
+      if ((r < 0) || (r > 1)) {
+        last_result_ = r;
+        break;
+      }
+
+      uint32_t bytes_read = kMessageBufSize;
+      uint32_t handles_read = kHandleBufSize;
+
+      if (!CheckResult(ReadMessageRaw(pipes[r],
+                                      mbuf.get(), &bytes_read,
+                                      hbuf.get(), &handles_read,
+                                      MOJO_READ_MESSAGE_FLAG_NONE)))
+        break;
+
+      if (!bytes_read && !handles_read)
+        continue;
+
+      if (handles_read) {
+        handle_count_[r] += handles_read;
+
+        // Intercept message pipes which are returned via the ReadMessageRaw
+        // call
+        for (uint32_t i = 0; i < handles_read; i++) {
+          // Hack to determine if a handle is a message pipe.
+          // TODO(ananta)
+          // We should have an API which given a handle returns additional
+          // information about the handle which includes its type, etc.
+          if (MojoReadMessage(hbuf[i], NULL, NULL, NULL, NULL,
+                              MOJO_READ_MESSAGE_FLAG_NONE) !=
+                  MOJO_RESULT_INVALID_ARGUMENT) {
+            mojo::ScopedMessagePipeHandle message_pipe_handle;
+            message_pipe_handle.reset(mojo::MessagePipeHandle(hbuf[i]));
+
+            mojo::ScopedMessagePipeHandle faux_client;
+            mojo::ScopedMessagePipeHandle interceptor;
+            CreateMessagePipe(NULL, &faux_client, &interceptor);
+
+            base::WorkerPool::PostTask(
+                FROM_HERE,
+                base::Bind(&MessageProcessor::Start,
+                            this,
+                            base::Passed(&message_pipe_handle),
+                              base::Passed(&interceptor),
+                            url),
+                true);
+            hbuf.get()[i] = faux_client.release().value();
+          }
+        }
+      }
+      ++message_count_[r];
+      bytes_transfered_ += bytes_read;
+
+      LogMessageInfo(mbuf.get(), url);
+
+      mojo::MessagePipeHandle write_handle = (r == 0) ? pipes[1] : pipes[0];
+      if (!CheckResult(Wait(write_handle,
+                            MOJO_HANDLE_SIGNAL_WRITABLE,
+                            MOJO_DEADLINE_INDEFINITE)))
+        break;
+
+      if (!CheckResult(WriteMessageRaw(write_handle,
+                                       mbuf.get(), bytes_read,
+                                       hbuf.get(), handles_read,
+                                       MOJO_WRITE_MESSAGE_FLAG_NONE))) {
+        // On failure we own the handles. For now just close them.
+        if (handles_read)
+          CloseHandles(hbuf.get(), handles_read);
+        break;
+      }
+    }
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<MessageProcessor>;
+  virtual ~MessageProcessor() {}
+
+  bool CheckResult(MojoResult mr) {
+    if (mr == MOJO_RESULT_OK)
+      return true;
+    last_result_ = mr;
+    return false;
+  }
+
+  void LogInvalidMessage(const mojo::MojoMessageHeader& header) {
+    LOG(ERROR) << "Invalid message: Number of Fields: "
+               << header.num_fields
+               << " Number of bytes: "
+               << header.num_bytes
+               << " Flags: "
+               << header.flags;
+  }
+
+  // Validates the message as per the mojo spec.
+  bool IsValidMessage(const mojo::MojoMessageHeader& header) {
+    if (header.num_fields == 2) {
+      if (header.num_bytes != sizeof(mojo::MojoMessageHeader)) {
+        LogInvalidMessage(header);
+        return false;
+      }
+    } else if (header.num_fields == 3) {
+      if (header.num_bytes != sizeof(mojo::MojoRequestHeader)) {
+        LogInvalidMessage(header);
+      }
+    } else if (header.num_fields > 3) {
+      if (header.num_bytes < sizeof(mojo::MojoRequestHeader)) {
+        LogInvalidMessage(header);
+        return false;
+      }
+    }
+    // These flags should be specified in request or response messages.
+    if (header.num_fields < 3 &&
+          ((header.flags & mojo::kMessageExpectsResponse) ||
+           (header.flags & mojo::kMessageIsResponse))) {
+      LOG(ERROR) << "Invalid request message.";
+      LogInvalidMessage(header);
+      return false;
+    }
+    // These flags are mutually exclusive.
+    if ((header.flags & mojo::kMessageExpectsResponse) &&
+        (header.flags & mojo::kMessageIsResponse)) {
+      LOG(ERROR) << "Invalid flags combination in request message.";
+      LogInvalidMessage(header);
+      return false;
+    }
+    return true;
+  }
+
+  void LogMessageInfo(void* data, const GURL& url) {
+    mojo::MojoMessageData* message_data =
+        reinterpret_cast<mojo::MojoMessageData*>(data);
+    if (IsValidMessage(message_data->header)) {
+      control_loop_proxy_->PostTask(
+          FROM_HERE,
+          base::Bind(&mojo::WebSocketServer::LogMessageInfo,
+                      base::Unretained(ws_server),
+                      message_data->header, url, base::Time::Now()));
+    }
+  }
+
+  MojoResult last_result_;
+  uint32_t bytes_transfered_;
+  uint32_t message_count_[2];
+  uint32_t handle_count_[2];
+  scoped_refptr<base::MessageLoopProxy> control_loop_proxy_;
+};
+
+// In charge of intercepting access to the service manager.
+class SpyInterceptor : public mojo::ApplicationManager::Interceptor {
+ public:
+  explicit SpyInterceptor(
+      scoped_refptr<mojo::SpyServerImpl> spy_server,
+      const scoped_refptr<base::MessageLoopProxy>& control_loop_proxy)
+      : spy_server_(spy_server),
+        proxy_(base::MessageLoopProxy::current()),
+        control_loop_proxy_(control_loop_proxy) {}
+
+ private:
+  virtual mojo::ServiceProviderPtr OnConnectToClient(
+    const GURL& url, mojo::ServiceProviderPtr real_client) override {
+      if (!MustIntercept(url))
+        return real_client.Pass();
+
+      // You can get an invalid handle if the app (or service) is
+      // created by unconventional means, for example the command line.
+      if (!real_client)
+        return real_client.Pass();
+
+      mojo::ScopedMessagePipeHandle faux_client;
+      mojo::ScopedMessagePipeHandle interceptor;
+      CreateMessagePipe(NULL, &faux_client, &interceptor);
+
+      scoped_refptr<MessageProcessor> processor =
+          new MessageProcessor(control_loop_proxy_.get());
+      mojo::ScopedMessagePipeHandle real_handle = real_client.PassMessagePipe();
+      base::WorkerPool::PostTask(
+          FROM_HERE,
+          base::Bind(&MessageProcessor::Start,
+                     processor,
+                     base::Passed(&real_handle), base::Passed(&interceptor),
+                     url),
+          true);
+
+      mojo::ServiceProviderPtr faux_provider;
+      faux_provider.Bind(faux_client.Pass());
+      return faux_provider.Pass();
+  }
+
+  bool MustIntercept(const GURL& url) {
+    // TODO(cpu): manage who and when to intercept.
+    proxy_->PostTask(
+        FROM_HERE,
+        base::Bind(&mojo::SpyServerImpl::OnIntercept, spy_server_, url));
+    return true;
+  }
+
+  scoped_refptr<mojo::SpyServerImpl> spy_server_;
+  scoped_refptr<base::MessageLoopProxy> proxy_;
+  scoped_refptr<base::MessageLoopProxy> control_loop_proxy_;
+};
+
+void StartWebServer(int port, mojo::ScopedMessagePipeHandle pipe) {
+  // TODO(cpu) figure out lifetime of the server. See Spy() dtor.
+  ws_server = new mojo::WebSocketServer(port, pipe.Pass());
+  ws_server->Start();
+}
+
+struct SpyOptions {
+  int websocket_port;
+
+  SpyOptions()
+      : websocket_port(kDefaultWebSocketPort) {
+  }
+};
+
+SpyOptions ProcessOptions(const std::string& options) {
+  SpyOptions spy_options;
+  if (options.empty())
+    return spy_options;
+  base::StringPairs kv_pairs;
+  base::SplitStringIntoKeyValuePairs(options, ':', ',', &kv_pairs);
+  base::StringPairs::iterator it = kv_pairs.begin();
+  for (; it != kv_pairs.end(); ++it) {
+    if (it->first == "port") {
+      int port;
+      if (base::StringToInt(it->second, &port))
+        spy_options.websocket_port = port;
+    }
+  }
+  return spy_options;
+}
+
+}  // namespace
+
+namespace mojo {
+
+Spy::Spy(mojo::ApplicationManager* application_manager,
+         const std::string& options) {
+  SpyOptions spy_options = ProcessOptions(options);
+
+  spy_server_ = new SpyServerImpl();
+
+  // Start the tread what will accept commands from the frontend.
+  control_thread_.reset(new base::Thread("mojo_spy_control_thread"));
+  base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
+  control_thread_->StartWithOptions(thread_options);
+  control_thread_->message_loop_proxy()->PostTask(
+      FROM_HERE, base::Bind(&StartWebServer,
+                            spy_options.websocket_port,
+                            base::Passed(spy_server_->ServerPipe())));
+
+  // Start intercepting mojo services.
+  application_manager->SetInterceptor(
+      new SpyInterceptor(spy_server_, control_thread_->message_loop_proxy()));
+}
+
+Spy::~Spy() {
+  // TODO(cpu): Do not leak the interceptor. Lifetime between the
+  // application_manager and the spy is still unclear hence the leak.
+}
+
+}  // namespace mojo
diff --git a/mojo/spy/spy.h b/mojo/spy/spy.h
new file mode 100644
index 0000000..f2b6be9
--- /dev/null
+++ b/mojo/spy/spy.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_SPY_SPY_H_
+#define MOJO_SPY_SPY_H_
+
+#include <string>
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class Thread;
+}
+
+namespace mojo {
+
+class ApplicationManager;
+class SpyServerImpl;
+
+// mojo::Spy is a troubleshooting and debugging aid. It helps tracking
+// the mojo system core activities like messages, service creation, etc.
+//
+// The |options| parameter in the constructor comes from the command
+// line of the mojo_shell. Which takes --spy=<options>. Each option is
+// separated by ',' and each option is a key+ value pair separated by ':'.
+//
+// For example --spy=port:13333
+//
+class Spy {
+ public:
+  Spy(mojo::ApplicationManager* application_manager,
+      const std::string& options);
+  ~Spy();
+
+ private:
+  scoped_refptr<SpyServerImpl> spy_server_;
+  // This thread runs the code that talks to the frontend.
+  scoped_ptr<base::Thread> control_thread_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SPY_SPY_H_
diff --git a/mojo/spy/spy_server_impl.cc b/mojo/spy/spy_server_impl.cc
new file mode 100644
index 0000000..25e640e
--- /dev/null
+++ b/mojo/spy/spy_server_impl.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/spy/spy_server_impl.h"
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace {
+
+bool NextId(uint32_t* out_id) {
+  static uint32_t id = 1;
+  if (!++id)
+    return false;
+  *out_id = id;
+  return true;
+}
+
+}  // namespace
+
+namespace mojo {
+
+struct SpyServerImpl::Item {
+  enum Type {
+    kServiceIntercept,
+    kMessage
+  };
+
+  uint32_t id;
+  Type type;
+
+  Item(uint32_t id, Type type) : id(id), type(type) {}
+};
+
+SpyServerImpl::SpyServerImpl() : has_session_(false) {
+  BindToPipe(this, pipe_.handle0.Pass());
+}
+
+SpyServerImpl::~SpyServerImpl() {
+}
+
+void SpyServerImpl::StartSession(
+    spy_api::VersionPtr version,
+    const mojo::Callback<void(spy_api::Result, mojo::String)>& callback) {
+  if (has_session_) {
+    callback.Run(spy_api::RESULT_RESOURCE_LIMIT, "");
+    return;
+  }
+  callback.Run(spy_api::RESULT_ALL_OK, "session 0");
+  has_session_ = true;
+}
+
+void SpyServerImpl::StopSession(
+  const mojo::Callback<void(spy_api::Result)>& callback) {
+  if (!has_session_) {
+    callback.Run(spy_api::RESULT_INVALID_CALL);
+    return;
+  }
+  callback.Run(spy_api::RESULT_ALL_OK);
+  has_session_ = false;
+}
+
+void SpyServerImpl::TrackConnection(
+  uint32_t id,
+  spy_api::ConnectionOptions options,
+  const mojo::Callback<void(spy_api::Result)>& callback) {
+}
+
+void SpyServerImpl::OnConnectionError() {
+  // Pipe got disconnected.
+}
+
+void SpyServerImpl::OnIntercept(const GURL& url) {
+  if (!has_session_)
+    return;
+  uint32_t id;
+  if (!NextId(&id)) {
+    client()->OnFatalError(spy_api::RESULT_NO_MORE_IDS);
+    return;
+  }
+
+  items_[id] = new Item(id, Item::kServiceIntercept);
+  client()->OnClientConnection(url.possibly_invalid_spec(),
+                               id,
+                               spy_api::CONNECTION_OPTIONS_PEEK_MESSAGES);
+}
+
+ScopedMessagePipeHandle SpyServerImpl::ServerPipe() {
+  return ScopedMessagePipeHandle(pipe_.handle1.Pass()).Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/spy/spy_server_impl.h b/mojo/spy/spy_server_impl.h
new file mode 100644
index 0000000..20d355e
--- /dev/null
+++ b/mojo/spy/spy_server_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_SPY_SPY_SERVER_IMPL_H_
+#define MOJO_SPY_SPY_SERVER_IMPL_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/spy/public/spy.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class SpyServerImpl :
+    public base::RefCounted<SpyServerImpl>,
+    public InterfaceImpl<spy_api::SpyServer> {
+ public:
+  SpyServerImpl();
+
+  // spy_api::SpyServer implementation.
+  virtual void StartSession(
+      spy_api::VersionPtr version,
+      const mojo::Callback<void(spy_api::Result,
+                                mojo::String)>& callback) override;
+
+  virtual void StopSession(
+      const mojo::Callback<void(spy_api::Result)>& callback) override;
+
+  virtual void TrackConnection(
+      uint32_t id,
+      spy_api::ConnectionOptions options,
+      const mojo::Callback<void(spy_api::Result)>& callback) override;
+
+  virtual void OnConnectionError() override;
+
+  // SpyServerImpl own methods.
+  void OnIntercept(const GURL& url);
+
+  ScopedMessagePipeHandle ServerPipe();
+
+ private:
+  friend class base::RefCounted<SpyServerImpl>;
+  virtual ~SpyServerImpl();
+
+  // Item models the entities that we track by IDs.
+  struct Item;
+
+  MessagePipe pipe_;
+  bool has_session_;
+  std::map<uint32_t, Item*> items_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SPY_SPY_SERVER_IMPL_H_
diff --git a/mojo/spy/test/spy_repl_test.html b/mojo/spy/test/spy_repl_test.html
new file mode 100644
index 0000000..a709a54
--- /dev/null
+++ b/mojo/spy/test/spy_repl_test.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+<html lang="en">
+<head>
+  <meta charset="utf-8" />
+  <title>Spy WS test</title>
+  <style>
+    hr {color:sienna;}
+    body {
+      background-color:#b0c4de;
+      font:normal 16px/20px "Helvetica Neue", Helvetica, sans-serif;
+    }
+    #command {
+      width:70%;
+    }
+    #status {
+      background-color:#0094ff;
+      width:50%;
+      padding:4px;
+    }
+  </style>
+</head>
+<body>
+<header><h1>mojo spy</h1></header>
+  <form>
+    <input type="text" id="command" placeholder="enter spy command + enter" />
+  </form>
+  <p id="status">status: no connection</p>
+  <p id="log">...</p>
+  <script>
+    function openConnection() {
+      if (conn.readyState === undefined || conn.readyState > 1) {
+        conn = new WebSocket('ws://127.0.0.1:42424');
+        conn.onopen = function () {
+          state.innerHTML = 'connected @port 42424';
+        };
+        conn.onmessage = function (event) {
+          var message = event.data;
+          log.innerHTML += "<br/>" + message;
+        };
+        conn.onclose = function (event) {
+          state.innerHTML = 'connection closed';
+        };
+        conn.onerror = function (event) {
+          state.innerHTML = 'got error';
+        };
+      }
+    }
+
+    var addEvent = (function () {
+      if (document.addEventListener) {
+        return function (el, type, fn) {
+          if (el && el.nodeName || el === window) {
+            el.addEventListener(type, fn, false);
+          } else if (el && el.length) {
+            for (var i = 0; i < el.length; i++) {
+              addEvent(el[i], type, fn);
+            }
+          }
+        };
+      } else {
+        return function (el, type, fn) {
+          if (el && el.nodeName || el === window) {
+            el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
+          } else if (el && el.length) {
+            for (var i = 0; i < el.length; i++) {
+              addEvent(el[i], type, fn);
+            }
+          }
+        };
+      }
+    })();
+
+    var log = document.getElementById('log');
+    var cmd = document.getElementById('command');
+    var form = cmd.form;
+    var conn = {};
+    var state = document.getElementById('status');
+
+    if (window.WebSocket === undefined) {
+      state.innerHTML = 'websockets not supported';
+    } else {
+      addEvent(form, 'submit', function (event) {
+        event.preventDefault();
+        if (conn.readyState === 1) {
+          conn.send(JSON.stringify(cmd.value));
+          log.innerHTML = 'data sent';
+          cmd.value = '';
+        }
+      });
+
+      openConnection();
+    }
+  </script>
+</body>
+</html>
diff --git a/mojo/spy/ui/__init__.py b/mojo/spy/ui/__init__.py
new file mode 100644
index 0000000..bfecdb2
--- /dev/null
+++ b/mojo/spy/ui/__init__.py
@@ -0,0 +1,5 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from ui import tvcm_stub
diff --git a/mojo/spy/ui/dev_server.py b/mojo/spy/ui/dev_server.py
new file mode 100644
index 0000000..2a83032
--- /dev/null
+++ b/mojo/spy/ui/dev_server.py
@@ -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.
+
+import optparse
+import tvcm
+
+from ui import spy_project
+
+
+def Main(port, args):
+  parser = optparse.OptionParser()
+  _, args = parser.parse_args(args)
+
+  project = spy_project.SpyProject()
+  server = tvcm.DevServer(
+      port=port, project=project)
+
+  def IsTestModuleResourcePartOfSpy(module_resource):
+    return module_resource.absolute_path.startswith(project.spy_path)
+
+  server.test_module_resource_filter = IsTestModuleResourcePartOfSpy
+  return server.serve_forever()
diff --git a/mojo/spy/ui/spy.html b/mojo/spy/ui/spy.html
new file mode 100644
index 0000000..79aeb7f
--- /dev/null
+++ b/mojo/spy/ui/spy.html
@@ -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.
+-->
+<style>
+  x-spy-log-message {
+    display: block;
+    border-style: 1px solid black;
+    margin-top: 4px;
+    margin-bottom: 4px;
+  }
+
+  x-spy {
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+  }
+  x-spy > messages {
+    display: block;
+    -webkit-flex: 1 1 auto;
+  }
+  x-spy > form {
+    -webkit-flex: 0 0 auto;
+  }
+  x-spy > form > input {
+    width: 300px;
+  }
+</style>
+
+
+<template id="x-spy-template">
+  <messages>
+  </messages>
+  <input type="text" id="command" placeholder="enter spy command + enter" />
+</template>
diff --git a/mojo/spy/ui/spy.js b/mojo/spy/ui/spy.js
new file mode 100644
index 0000000..e3f47e1
--- /dev/null
+++ b/mojo/spy/ui/spy.js
@@ -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.
+
+'use strict';
+
+tvcm.require('tvcm.utils');
+tvcm.require('tvcm.ui');
+tvcm.requireTemplate('ui.spy');
+
+tvcm.exportTo('ui', function() {
+  /**
+   * @constructor
+   */
+  var LogMessage = tvcm.ui.define('x-spy-log-message');
+
+  LogMessage.prototype = {
+    __proto__: HTMLUnknownElement.prototype,
+
+    decorate: function() {
+    },
+
+    get message() {
+      return message_;
+    },
+
+    set message(message) {
+      this.message_ = message;
+      this.textContent = JSON.stringify(message);
+    }
+  };
+
+
+  /**
+   * @constructor
+   */
+  var Spy = tvcm.ui.define('x-spy');
+
+  Spy.prototype = {
+    __proto__: HTMLUnknownElement.prototype,
+
+    decorate: function() {
+      var node = tvcm.instantiateTemplate('#x-spy-template');
+      this.appendChild(node);
+
+      this.channel_ = undefined;
+      this.onMessage_ = this.onMessage_.bind(this);
+
+      var commandEl = this.querySelector('#command');
+      commandEl.addEventListener('keydown', function(e) {
+        if (e.keyCode == 13) {
+          e.stopPropagation();
+          this.onCommandEntered_();
+        }
+      }.bind(this));
+
+      this.updateDisabledStates_();
+    },
+
+    get channel() {
+      return channel_;
+    },
+
+    set channel(channel) {
+      if (this.channel_)
+        this.channel_.removeEventListener('message', this.onMessage_);
+      this.channel_ = channel;
+      if (this.channel_)
+        this.channel_.addEventListener('message', this.onMessage_);
+      this.updateDisabledStates_();
+    },
+
+    updateDisabledStates_: function() {
+      var connected = this.channel_ !== undefined;
+
+      this.querySelector('#command').disabled = !connected;
+    },
+
+    onCommandEntered_: function(cmd) {
+      var commandEl = this.querySelector('#command');
+      this.channel_.send(JSON.stringify(commandEl.value));
+      commandEl.value = '';
+    },
+
+    onMessage_: function(message) {
+      var messageEl = new LogMessage();
+      messageEl.message = message.data;
+      this.querySelector('messages').appendChild(messageEl);
+    }
+
+  };
+
+  return {
+    Spy: Spy
+  };
+});
diff --git a/mojo/spy/ui/spy_project.py b/mojo/spy/ui/spy_project.py
new file mode 100644
index 0000000..409ecdf
--- /dev/null
+++ b/mojo/spy/ui/spy_project.py
@@ -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 os
+
+import tvcm_stub
+
+from trace_viewer import trace_viewer_project
+
+
+class SpyProject(trace_viewer_project.TraceViewerProject):
+  spy_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                            '..'))
+
+  def __init__(self):
+    super(SpyProject, self).__init__(
+      [self.spy_path])
diff --git a/mojo/spy/ui/spy_shell.html b/mojo/spy/ui/spy_shell.html
new file mode 100644
index 0000000..c46a24f
--- /dev/null
+++ b/mojo/spy/ui/spy_shell.html
@@ -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.
+-->
+<style>
+  html, body {
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    height: 100%;
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+  }
+  body > x-spy-shell {
+    -webkit-flex: 1 1 auto;
+  }
+
+  x-spy-shell {
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+  }
+
+  x-spy-shell > #status {
+    -webkit-flex: 0 0 auto;
+    border-bottom: 1px solid black;
+  }
+
+  x-spy-shell > x-spy {
+    -webkit-flex: 1 1 auto;
+  }
+</style>
+
+
+<template id="x-spy-shell-template">
+  <div id="status"></div>
+  <x-spy></x-spy>
+</template>
diff --git a/mojo/spy/ui/spy_shell.js b/mojo/spy/ui/spy_shell.js
new file mode 100644
index 0000000..bcf0430
--- /dev/null
+++ b/mojo/spy/ui/spy_shell.js
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+tvcm.require('ui.spy');
+tvcm.require('tvcm.ui');
+tvcm.require('tvcm.ui.dom_helpers');
+tvcm.requireTemplate('ui.spy_shell');
+
+tvcm.exportTo('ui', function() {
+  /**
+   * @constructor
+   */
+  var SpyShell = tvcm.ui.define('x-spy-shell');
+
+  SpyShell.prototype = {
+    __proto__: HTMLUnknownElement.prototype,
+
+    decorate: function(socketURL) {
+      var node = tvcm.instantiateTemplate('#x-spy-shell-template');
+      this.appendChild(node);
+
+      this.socketURL_ = socketURL;
+      this.conn_ = undefined;
+
+      this.statusEl_ = this.querySelector('#status');
+      this.statusEl_.textContent = 'Not connected';
+
+      this.spy_ = this.querySelector('x-spy');
+      tvcm.ui.decorate(this.spy_, ui.Spy);
+
+      this.openConnection_();
+    },
+
+    get socketURL() {
+      return this.socketURL_;
+    },
+
+    openConnection_: function() {
+      if (!(this.conn_ == undefined ||
+            this.conn_.readyState === undefined ||
+            conn.readyState > 1)) {
+        return;
+      }
+
+      this.conn_ = new WebSocket(this.socketURL_);
+      this.conn_.onopen = function() {
+        this.statusEl_.textContent = 'connected at ' + this.socketURL_;
+        this.spy_.connection = this.conn_;
+      }.bind(this);
+
+      this.conn_.onclose = function(event) {
+        this.statusEl_.textContent = 'connection closed';
+        this.spy_.connection = undefined;
+      }.bind(this);
+      this.conn_.onerror = function(event) {
+        this.statusEl_.innerHTML = 'got error';
+      }.bind(this);
+    }
+
+  };
+
+  return {
+    SpyShell: SpyShell
+  };
+});
diff --git a/mojo/spy/ui/spy_shell_to_html b/mojo/spy/ui/spy_shell_to_html
new file mode 100755
index 0000000..68ba089
--- /dev/null
+++ b/mojo/spy/ui/spy_shell_to_html
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium 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 os
+import sys
+
+if __name__ == '__main__':
+  top_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+  sys.path.append(top_dir)
+  from ui import spy_shell_to_html
+  sys.exit(spy_shell_to_html.Main(sys.argv))
diff --git a/mojo/spy/ui/spy_shell_to_html.py b/mojo/spy/ui/spy_shell_to_html.py
new file mode 100644
index 0000000..293b9fb
--- /dev/null
+++ b/mojo/spy/ui/spy_shell_to_html.py
@@ -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 sys
+import os
+import optparse
+
+from ui import spy_project
+from tvcm import generate
+
+def Main(args):
+  parser = optparse.OptionParser()
+  parser.add_option('--output-file', '-o')
+  options,args = parser.parse_args(args)
+
+  if options.output_file:
+    ofile = open(options.output_file, 'w')
+  else:
+    ofile = sys.stdout
+  GenerateHTML(ofile)
+  if ofile != sys.stdout:
+    ofile.close()
+
+def GenerateHTML(ofile):
+  project = spy_project.SpyProject()
+  load_sequence = project.CalcLoadSequenceForModuleNames(
+    ['ui.spy_shell'])
+  bootstrap_js = """
+
+  document.addEventListener('DOMContentLoaded', function() {
+    document.body.appendChild(new ui.SpyShell('ws://127.0.0.1:42424'));
+
+  });
+"""
+  bootstrap_script = generate.ExtraScript(text_content=bootstrap_js)
+  generate.GenerateStandaloneHTMLToFile(
+    ofile, load_sequence,
+    title='Mojo spy',
+    extra_scripts=[bootstrap_script])
diff --git a/mojo/spy/ui/spy_test.js b/mojo/spy/ui/spy_test.js
new file mode 100644
index 0000000..b70ecd9
--- /dev/null
+++ b/mojo/spy/ui/spy_test.js
@@ -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.
+
+'use strict';
+
+tvcm.require('ui.spy');
+tvcm.require('tvcm.event_target');
+
+tvcm.unittest.testSuite('ui.spy_test', function() {
+  /**
+   * @constructor
+   */
+  function FakeChannel() {
+    tvcm.EventTarget.call(this);
+  }
+
+  FakeChannel.prototype = {
+    __proto__: tvcm.EventTarget.prototype,
+
+    send: function(msg) {
+    },
+
+    dispatchMessage: function(msg) {
+      var event = new Event('message', false, false);
+      event.data = msg;
+      this.dispatchEvent(event);
+    }
+  };
+
+  test('basic', function() {
+    var channel = new FakeChannel();
+
+    var spy = new ui.Spy();
+    spy.style.width = '600px';
+    spy.style.height = '400px';
+    spy.style.border = '1px solid black';
+    this.addHTMLOutput(spy);
+    spy.channel = channel;
+
+    channel.dispatchMessage({data: 'alo there'});
+
+    // Fake out echo reply
+    channel.send = function(msg) {
+      setTimeout(function() {
+        channel.dispatchMessage({data: {type: 'reply', msg: msg}});
+      }, 10);
+    }
+  });
+
+});
diff --git a/mojo/spy/ui/tvcm_stub.py b/mojo/spy/ui/tvcm_stub.py
new file mode 100644
index 0000000..8c7c084
--- /dev/null
+++ b/mojo/spy/ui/tvcm_stub.py
@@ -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 os
+import sys
+
+_CHROME_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                            '..', '..', '..'))
+
+# Bring in tvcm module for basic JS components capabilities.
+sys.path.append(os.path.join(_CHROME_PATH,
+    'third_party', 'trace-viewer', 'third_party', 'tvcm'))
+
+# Bring in trace_viewer module for the UI features that are part of the trace
+# viewer.
+sys.path.append(os.path.join(_CHROME_PATH,
+    'third_party', 'trace-viewer'))
diff --git a/mojo/spy/ui/ui_unittest.py b/mojo/spy/ui/ui_unittest.py
new file mode 100644
index 0000000..ffaffad
--- /dev/null
+++ b/mojo/spy/ui/ui_unittest.py
@@ -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.
+
+from ui import spy_project
+from tvcm import module_test_case
+
+
+def load_tests(_, _2, _3):
+  project = spy_project.SpyProject()
+  suite = module_test_case.DiscoverTestsInModule(
+      project,
+      project.spy_path)
+  assert suite.countTestCases() > 0, 'Expected to find at least one test.'
+  return suite
diff --git a/mojo/spy/websocket_server.cc b/mojo/spy/websocket_server.cc
new file mode 100644
index 0000000..20e2da6
--- /dev/null
+++ b/mojo/spy/websocket_server.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 The Chromium 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/spy/websocket_server.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
+#include "net/socket/tcp_server_socket.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+const int kNotConnected = -1;
+
+#define MOJO_DEBUGGER_MESSAGE_FORMAT "\"url: %s\n," \
+                                     "time: %02d:%02d:%02d\n,"\
+                                     "bytes: %u\n,"\
+                                     "fields: %u\n,"\
+                                     "name: %u\n,"\
+                                     "requestId: %llu\n,"\
+                                     "type: %s\n,"\
+                                     "end:\n\""
+
+WebSocketServer::WebSocketServer(int port,
+                                 mojo::ScopedMessagePipeHandle server_pipe)
+    : port_(port),
+      connection_id_(kNotConnected),
+      spy_server_(MakeProxy<spy_api::SpyServer>(server_pipe.Pass())) {
+  spy_server_.set_client(this);
+}
+
+WebSocketServer::~WebSocketServer() {
+}
+
+bool WebSocketServer::Start() {
+  scoped_ptr<net::ServerSocket> server_socket(
+      new net::TCPServerSocket(NULL, net::NetLog::Source()));
+  server_socket->ListenWithAddressAndPort("0.0.0.0", port_, 1);
+  web_server_.reset(new net::HttpServer(server_socket.Pass(), this));
+  net::IPEndPoint address;
+  int error = web_server_->GetLocalAddress(&address);
+  port_ = address.port();
+  return (error == net::OK);
+}
+
+void WebSocketServer::LogMessageInfo(
+    const mojo::MojoRequestHeader& message_header,
+    const GURL& url,
+    const base::Time& message_time) {
+  base::Time::Exploded exploded;
+  message_time.LocalExplode(&exploded);
+
+  std::string output_message = base::StringPrintf(
+      MOJO_DEBUGGER_MESSAGE_FORMAT,
+      url.spec().c_str(),
+      exploded.hour,
+      exploded.minute,
+      exploded.second,
+      static_cast<unsigned>(message_header.num_bytes),
+      static_cast<unsigned>(message_header.num_fields),
+      static_cast<unsigned>(message_header.name),
+      static_cast<unsigned long long>(
+          message_header.num_fields == 3 ? message_header.request_id
+              : 0),
+      message_header.flags != 0 ?
+        (message_header.flags & mojo::kMessageExpectsResponse ?
+            "Expects response" : "Response message")
+        : "Not a request or response message");
+  if (Connected()) {
+    web_server_->SendOverWebSocket(connection_id_, output_message.c_str());
+  } else {
+    DVLOG(1) << output_message;
+  }
+}
+
+void WebSocketServer::OnHttpRequest(
+    int connection_id,
+    const net::HttpServerRequestInfo& info) {
+  web_server_->Send500(connection_id, "websockets protocol only");
+}
+
+void WebSocketServer::OnWebSocketRequest(
+    int connection_id,
+    const net::HttpServerRequestInfo& info) {
+  if (connection_id_ != kNotConnected) {
+    // Reject connection since we already have our client.
+    web_server_->Close(connection_id);
+    return;
+  }
+  // Accept the connection.
+  web_server_->AcceptWebSocket(connection_id, info);
+  connection_id_ = connection_id;
+}
+
+void WebSocketServer::OnWebSocketMessage(
+    int connection_id,
+    const std::string& data) {
+
+  if (data == "\"start\"") {
+    spy_api::VersionPtr ver = spy_api::Version::New();
+    ver->v_major = 0;
+    ver->v_minor = 1;
+    spy_server_->StartSession(
+        ver.Pass(),
+        base::Bind(&WebSocketServer::OnStartSession, base::Unretained(this)));
+  } else if (data == "\"stop\"") {
+    spy_server_->StopSession(
+        base::Bind(&WebSocketServer::OnSessionEnd, base::Unretained(this)));
+  }
+}
+
+void WebSocketServer::OnFatalError(spy_api::Result result) {
+  web_server_->SendOverWebSocket(connection_id_, "\"fatal error\"");
+}
+
+void WebSocketServer::OnClose(
+    int connection_id) {
+  if (connection_id != connection_id_)
+    return;
+  connection_id_ = kNotConnected;
+
+  spy_server_->StopSession(
+      base::Bind(&WebSocketServer::OnSessionEnd, base::Unretained(this)));
+}
+
+void WebSocketServer::OnSessionEnd(spy_api::Result result) {
+  // Called when the spy session (not the websocket) ends.
+}
+
+void WebSocketServer::OnClientConnection(
+  const mojo::String& name,
+  uint32_t id,
+  spy_api::ConnectionOptions options) {
+  std::string cc("\"");
+  cc += name.To<std::string>() + "\"";
+  web_server_->SendOverWebSocket(connection_id_, cc);
+}
+
+void WebSocketServer::OnMessage(spy_api::MessagePtr message) {
+}
+
+void WebSocketServer::OnStartSession(spy_api::Result, mojo::String) {
+  web_server_->SendOverWebSocket(connection_id_, "\"ok start\"");
+}
+
+bool WebSocketServer::Connected() const {
+  return connection_id_ !=  kNotConnected;
+}
+
+}  // namespace mojo
diff --git a/mojo/spy/websocket_server.h b/mojo/spy/websocket_server.h
new file mode 100644
index 0000000..c6dc076
--- /dev/null
+++ b/mojo/spy/websocket_server.h
@@ -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.
+
+#ifndef MOJO_SPY_WEBSOCKET_SERVER_H_
+#define MOJO_SPY_WEBSOCKET_SERVER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "mojo/spy/common.h"
+#include "mojo/spy/public/spy.mojom.h"
+#include "net/server/http_server.h"
+
+namespace base {
+class Time;
+};
+
+class GURL;
+
+namespace mojo {
+
+class WebSocketServer : public net::HttpServer::Delegate,
+                        public spy_api::SpyClient {
+ public:
+  // Pass 0 in |port| to listen in one available port.
+  explicit WebSocketServer(int port, ScopedMessagePipeHandle server_pipe);
+  virtual ~WebSocketServer();
+  // Begin accepting HTTP requests. Must be called from an IO MessageLoop.
+  bool Start();
+  // Returns the listening port, useful if 0 was passed to the contructor.
+  int port() const { return port_; }
+
+  // Maintains a log of the message passed in.
+  void LogMessageInfo(
+      const mojo::MojoRequestHeader& message_header,
+      const GURL& url,
+      const base::Time& message_time);
+
+ protected:
+  // Overridden from net::HttpServer::Delegate.
+  virtual void OnConnect(int connection_id) override {}
+  virtual void OnHttpRequest(
+      int connection_id,
+      const net::HttpServerRequestInfo& info) override;
+  virtual void OnWebSocketRequest(
+      int connection_id,
+      const net::HttpServerRequestInfo& info) override;
+  virtual void OnWebSocketMessage(
+      int connection_id,
+      const std::string& data) override;
+  virtual void OnClose(int connection_id) override;
+
+  // Overriden form spy_api::SpyClient.
+  virtual void OnFatalError(spy_api::Result result) override;
+  virtual void OnSessionEnd(spy_api::Result result) override;
+  virtual void OnClientConnection(
+      const mojo::String& name,
+      uint32_t id,
+      spy_api::ConnectionOptions options) override;
+  virtual void OnMessage(spy_api::MessagePtr message) override;
+
+  // Callbacks from calling spy_api::SpyServer.
+  void OnStartSession(spy_api::Result, mojo::String);
+
+  bool Connected() const;
+
+ private:
+  int port_;
+  int connection_id_;
+  scoped_ptr<net::HttpServer> web_server_;
+  spy_api::SpyServerPtr spy_server_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketServer);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SPY_WEBSOCKET_SERVER_H_
diff --git a/mojo/tools/BUILD.gn b/mojo/tools/BUILD.gn
new file mode 100644
index 0000000..de38e6c
--- /dev/null
+++ b/mojo/tools/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_base.gyp:mojo_message_generator
+executable("message_generator") {
+  testonly = true
+  output_name = "mojo_message_generator"
+
+  sources = [
+    "message_generator.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//testing/gtest",
+  ]
+}
+
diff --git a/mojo/tools/check_mojom_golden_files.py b/mojo/tools/check_mojom_golden_files.py
new file mode 100755
index 0000000..9af7a86
--- /dev/null
+++ b/mojo/tools/check_mojom_golden_files.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium 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 argparse
+import os.path
+import sys
+from filecmp import dircmp
+from shutil import rmtree
+from tempfile import mkdtemp
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+_mojo_dir = os.path.join(_script_dir, os.pardir)
+_chromium_src_dir = os.path.join(_mojo_dir, os.pardir)
+sys.path.insert(0, os.path.join(_mojo_dir, "public", "tools", "bindings",
+                                "pylib"))
+from mojom_tests.support.find_files import FindFiles
+from mojom_tests.support.run_bindings_generator import RunBindingsGenerator
+
+
+def _ProcessDircmpResults(results, verbose=False):
+  """Prints results of directory comparison and returns true if they are
+  identical (note: the "left" directory should be the golden directory)."""
+  rv = not (bool(results.left_only) or bool(results.right_only) or \
+            bool(results.common_funny) or bool(results.funny_files) or \
+            bool(results.diff_files))
+  if verbose:
+    for f in results.left_only:
+      print "%s exists in golden directory but not in current output" % f
+    for f in results.right_only:
+      print "%s exists in current output but not in golden directory" % f
+    for f in results.common_funny + results.funny_files:
+      print "Unable to compare %s between golden directory and current output" \
+          % f
+    for f in results.diff_files:
+      print "%s differs between golden directory and current output" % f
+  for r in results.subdirs.values():
+    # If we're being verbose, check subdirectories even if we know that there
+    # are differences. Note that it's "... and rv" to avoid the short-circuit.
+    if rv or verbose:
+      rv = _ProcessDircmpResults(r, verbose=verbose) and rv
+  return rv
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--generate_golden_files", action="store_true",
+                      help=("generate golden files (does not obliterate "
+                            "directory"))
+  parser.add_argument("--keep_temp_dir", action="store_true",
+                      help="don't delete the temporary directory")
+  parser.add_argument("--verbose", action="store_true",
+                      help="spew excess verbiage")
+  parser.add_argument("golden_dir", metavar="GOLDEN_DIR",
+                      help="directory with the golden files")
+  args = parser.parse_args()
+
+  if args.generate_golden_files:
+    if os.path.exists(args.golden_dir):
+      print "WARNING: golden directory %s already exists" % args.golden_dir
+    out_dir = args.golden_dir
+  else:
+    if not os.path.exists(args.golden_dir):
+      print "ERROR: golden directory %s does not exist" % args.golden_dir
+      return 1
+    out_dir = mkdtemp()
+  if args.verbose:
+    print "Generating files to %s ..." % out_dir
+
+  mojom_files = FindFiles(_mojo_dir, "*.mojom")
+  for mojom_file in mojom_files:
+    if args.verbose:
+      print "  Processing %s ..." % os.path.relpath(mojom_file, _mojo_dir)
+    # TODO(vtl): This may wrong, since the path can be overridden in the .gyp
+    # file.
+    RunBindingsGenerator(out_dir, _mojo_dir, mojom_file,
+                         ["-I", os.path.abspath(_chromium_src_dir)])
+
+  if args.generate_golden_files:
+    return 0
+
+  identical = _ProcessDircmpResults(dircmp(args.golden_dir, out_dir, ignore=[]),
+                                    verbose=args.verbose)
+
+  if args.keep_temp_dir:
+    if args.verbose:
+      print "Not removing %s ..." % out_dir
+  else:
+    if args.verbose:
+      print "Removing %s ..." % out_dir
+    rmtree(out_dir)
+
+  if not identical:
+    print "FAILURE: current output differs from golden files"
+    return 1
+
+  print "SUCCESS: current output identical to golden files"
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/tools/data/unittests b/mojo/tools/data/unittests
new file mode 100644
index 0000000..0bafba4
--- /dev/null
+++ b/mojo/tools/data/unittests
@@ -0,0 +1,27 @@
+# This file contains a list of Mojo gtest unit tests.
+# Prepend * to indicate that results shouldn't be cached (e.g., if the test has
+# other data dependencies).
+# TODO(vtl): Add a way of specifying data dependencies instead.
+
+# System tests:
+mojo_system_unittests
+
+# Public tests:
+*mojo_public_bindings_unittests
+mojo_public_environment_unittests
+mojo_public_system_unittests
+mojo_public_utility_unittests
+
+# Non-system, non-public tests:
+mojo_application_manager_unittests
+mojo_common_unittests
+mojo_view_manager_lib_unittests
+mojo_view_manager_unittests
+mojo_surfaces_lib_unittests
+
+# JavaScript tests:
+*mojo_apps_js_unittests
+*mojo_js_unittests
+
+# Shell integration tests:
+*mojo_shell_tests
diff --git a/mojo/tools/generate_java_callback_interfaces.py b/mojo/tools/generate_java_callback_interfaces.py
new file mode 100644
index 0000000..257a540
--- /dev/null
+++ b/mojo/tools/generate_java_callback_interfaces.py
@@ -0,0 +1,69 @@
+"""Generate the org.chromium.mojo.bindings.Callbacks interface"""
+
+import argparse
+import sys
+
+CALLBACK_TEMPLATE = ("""
+    /**
+     * A generic %d-argument callback.
+     *
+     * %s
+     */
+    interface Callback%d<%s> {
+        /**
+         * Call the callback.
+         */
+        public void call(%s);
+    }
+""")
+
+INTERFACE_TEMPLATE = (
+"""// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file was generated using
+//     mojo/tools/generate_java_callback_interfaces.py
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Contains a generic interface for callbacks.
+ */
+public interface Callbacks {
+
+    /**
+     * A generic callback.
+     */
+    interface Callback0 {
+        /**
+         * Call the callback.
+         */
+        public void call();
+    }
+%s
+}""")
+
+def GenerateCallback(nb_args):
+  params = '\n      * '.join(
+      ['@param <T%d> the type of argument %d.' % (i+1, i+1)
+       for i in xrange(nb_args)])
+  template_parameters = ', '.join(['T%d' % (i+1) for i in xrange(nb_args)])
+  callback_parameters = ', '.join(['T%d arg%d' % ((i+1), (i+1))
+                                   for i in xrange(nb_args)])
+  return CALLBACK_TEMPLATE % (nb_args, params, nb_args, template_parameters,
+                              callback_parameters)
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Generate org.chromium.mojo.bindings.Callbacks")
+  parser.add_argument("max_args", nargs=1, type=int,
+      help="maximal number of arguments to generate callbacks for")
+  args = parser.parse_args()
+  max_args = args.max_args[0]
+  print INTERFACE_TEMPLATE % ''.join([GenerateCallback(i+1)
+                                      for i in xrange(max_args)])
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/mojo/tools/message_generator.cc b/mojo/tools/message_generator.cc
new file mode 100644
index 0000000..08b7dcc
--- /dev/null
+++ b/mojo/tools/message_generator.cc
@@ -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.
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+// This file is used to generate various files corresponding to mojo
+// messages. The various binding implementations can parse these to verify they
+// correctly decode messages.
+//
+// The output consists of each byte of the message encoded in a hex string with
+// a newline after it.
+namespace mojo {
+namespace {
+
+std::string BinaryToHex(const base::StringPiece& piece) {
+  std::string result("// File generated by mojo_message_generator.\n");;
+  result.reserve(result.size() + (piece.size() * 5));
+  for (size_t i = 0; i < piece.size(); ++i)
+    base::StringAppendF(&result, "0X%.2X\n", static_cast<int>(piece.data()[i]));
+  return result;
+}
+
+void WriteMessageToFile(const Message& message, const base::FilePath& path) {
+  const std::string hex_message(BinaryToHex(
+      base::StringPiece(reinterpret_cast<const char*>(message.data()),
+                        message.data_num_bytes())));
+  CHECK_EQ(static_cast<int>(hex_message.size()),
+           base::WriteFile(path, hex_message.data(),
+                           static_cast<int>(hex_message.size())));
+}
+
+// Generates a message of type MessageData. The message uses the name 21,
+// with 4 bytes of payload: 0x9, 0x8, 0x7, 0x6.
+void GenerateMessageDataMessage() {
+  internal::MessageBuilder builder(static_cast<uint32_t>(21),
+                                   static_cast<size_t>(4));
+  char* data = static_cast<char*>(builder.buffer()->Allocate(4));
+  DCHECK(data);
+  data[0] = 9;
+  data[1] = 8;
+  data[2] = 7;
+  data[3] = 6;
+
+  Message message;
+  builder.Finish(&message);
+  WriteMessageToFile(message,
+                     base::FilePath(FILE_PATH_LITERAL("message_data")));
+}
+
+}  // namespace
+}  // namespace mojo
+
+int main(int argc, char** argv) {
+  mojo::GenerateMessageDataMessage();
+  return 0;
+}
diff --git a/mojo/tools/mojob.sh b/mojo/tools/mojob.sh
new file mode 100755
index 0000000..e0804d1
--- /dev/null
+++ b/mojo/tools/mojob.sh
@@ -0,0 +1,238 @@
+#!/bin/bash
+# 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.
+
+# This a simple script to make building/testing Mojo components easier (on
+# Linux).
+
+# TODO(vtl): Maybe make the test runner smart and not run unchanged test
+# binaries.
+# TODO(vtl) Maybe also provide a way to pass command-line arguments to the test
+# binaries.
+
+do_help() {
+  cat << EOF
+Usage: $(basename "$0") [command|option ...]
+
+command should be one of:
+  build - Build.
+  test - Run unit tests (does not build).
+  perftest - Run perf tests (does not build).
+  pytest - Run Python unit tests.
+  gyp - Run gyp for mojo (does not sync).
+  gypall - Run gyp for all of chromium (does not sync).
+  sync - Sync using gclient (does not run gyp).
+  show-bash-alias - Outputs an appropriate bash alias for mojob. In bash do:
+      \$ eval \`mojo/tools/mojob.sh show-bash-alias\`
+
+option (which will only apply to following commands) should be one of:
+  Build/test options (specified before build/test/perftest):
+    --debug - Build/test in Debug mode.
+    --release - Build/test in Release mode.
+    --debug-and-release - Build/test in both Debug and Release modes (default).
+  Compiler options (specified before gyp):
+    --clang - Use clang (default).
+    --gcc - Use gcc.
+  Component options:
+    --shared Build components as shared libraries (default).
+    --static Build components as static libraries.
+  Use goma:
+    --use-goma - Use goma if \$GOMA_DIR is set or \$HOME/goma exists (default).
+    --no-use-goma - Do not use goma.
+
+Note: It will abort on the first failure (if any).
+EOF
+}
+
+do_build() {
+  echo "Building in out/$1 ..."
+  if [ "$GOMA" = "auto" -a -v GOMA_DIR ]; then
+    ninja -j 1000 -l 100 -C "out/$1" mojo || exit 1
+  else
+    ninja -C "out/$1" mojo || exit 1
+  fi
+}
+
+do_unittests() {
+  echo "Running unit tests in out/$1 ..."
+  mojo/tools/test_runner.py mojo/tools/data/unittests "out/$1" \
+      mojob_test_successes || exit 1
+}
+
+do_perftests() {
+  echo "Running perf tests in out/$1 ..."
+  "out/$1/mojo_public_system_perftests" || exit 1
+}
+
+do_pytests() {
+  python mojo/tools/run_mojo_python_tests.py || exit 1
+}
+
+do_gyp() {
+  local gyp_defines="$(make_gyp_defines)"
+  echo "Running gyp for mojo with GYP_DEFINES=$gyp_defines ..."
+  GYP_DEFINES="$gyp_defines" build/gyp_chromium mojo/mojo.gyp || exit 1
+}
+
+do_gypall() {
+  local gyp_defines="$(make_gyp_defines)"
+  echo "Running gyp for everything with GYP_DEFINES=$gyp_defines ..."
+  GYP_DEFINES="$gyp_defines" build/gyp_chromium || exit 1
+}
+
+do_sync() {
+  # Note: sync only (with hooks, but no gyp-ing).
+  GYP_CHROMIUM_NO_ACTION=1 gclient sync || exit 1
+}
+
+# Valid values: Debug, Release, or Debug_and_Release.
+BUILD_TEST_TYPE=Debug_and_Release
+should_do_Debug() {
+  test "$BUILD_TEST_TYPE" = Debug -o "$BUILD_TEST_TYPE" = Debug_and_Release
+}
+should_do_Release() {
+  test "$BUILD_TEST_TYPE" = Release -o "$BUILD_TEST_TYPE" = Debug_and_Release
+}
+
+# Valid values: clang or gcc.
+COMPILER=clang
+# Valid values: shared or static.
+COMPONENT=shared
+# Valid values: auto or disabled.
+GOMA=auto
+make_gyp_defines() {
+  local options=()
+  # Always include these options.
+  options+=("use_aura=1")
+  case "$COMPILER" in
+    clang)
+      options+=("clang=1")
+      ;;
+    gcc)
+      options+=("clang=0")
+      ;;
+  esac
+  case "$COMPONENT" in
+    shared)
+      options+=("component=shared_library")
+      ;;
+    static)
+      options+=("component=static_library")
+      ;;
+  esac
+  case "$GOMA" in
+    auto)
+      if [ -v GOMA_DIR ]; then
+        options+=("use_goma=1" "gomadir=\"${GOMA_DIR}\"")
+      else
+        options+=("use_goma=0")
+      fi
+      ;;
+    disabled)
+      options+=("use_goma=0")
+      ;;
+  esac
+  echo "${options[*]}"
+}
+
+set_goma_dir_if_necessary() {
+  if [ "$GOMA" = "auto" -a ! -v GOMA_DIR ]; then
+    if [ -d "${HOME}/goma" ]; then
+      GOMA_DIR="${HOME}/goma"
+    fi
+  fi
+}
+
+start_goma_if_necessary() {
+  if [ "$GOMA" = "auto" -a -v GOMA_DIR ]; then
+    "${GOMA_DIR}/goma_ctl.py" ensure_start
+  fi
+}
+
+# We're in src/mojo/tools. We want to get to src.
+cd "$(realpath "$(dirname "$0")")/../.."
+
+if [ $# -eq 0 ]; then
+  do_help
+  exit 0
+fi
+
+for arg in "$@"; do
+  case "$arg" in
+    # Commands -----------------------------------------------------------------
+    help|--help)
+      do_help
+      exit 0
+      ;;
+    build)
+      set_goma_dir_if_necessary
+      start_goma_if_necessary
+      should_do_Debug && do_build Debug
+      should_do_Release && do_build Release
+      ;;
+    test)
+      should_do_Debug && do_unittests Debug
+      should_do_Release && do_unittests Release
+      ;;
+    perftest)
+      should_do_Debug && do_perftests Debug
+      should_do_Release && do_perftests Release
+      ;;
+    pytest)
+      do_pytests
+      ;;
+    gyp)
+      set_goma_dir_if_necessary
+      do_gyp
+      ;;
+    gypall)
+      set_goma_dir_if_necessary
+      do_gypall
+      ;;
+    sync)
+      do_sync
+      ;;
+    show-bash-alias)
+      # You want to type something like:
+      #   alias mojob=\
+      #       '"$(pwd | sed '"'"'s/\(.*\/src\).*/\1/'"'"')/mojo/tools/mojob.sh"'
+      # This is quoting hell, so we simply escape every non-alphanumeric
+      # character.
+      echo alias\ mojob\=\'\"\$\(pwd\ \|\ sed\ \'\"\'\"\'s\/\\\(\.\*\\\/src\\\)\
+\.\*\/\\1\/\'\"\'\"\'\)\/mojo\/tools\/mojob\.sh\"\'
+      ;;
+    # Options ------------------------------------------------------------------
+    --debug)
+      BUILD_TEST_TYPE=Debug
+      ;;
+    --release)
+      BUILD_TEST_TYPE=Release
+      ;;
+    --debug-and-release)
+      BUILD_TEST_TYPE=Debug_and_Release
+      ;;
+    --clang)
+      COMPILER=clang
+      ;;
+    --gcc)
+      COMPILER=gcc
+      ;;
+    --shared)
+      COMPONENT=shared
+      ;;
+    --static)
+      COMPONENT=static
+      ;;
+    --use-goma)
+      GOMA=auto
+      ;;
+    --no-use-goma)
+      GOMA=disabled
+      ;;
+    *)
+      echo "Unknown command \"${arg}\". Try \"$(basename "$0") help\"."
+      exit 1
+      ;;
+  esac
+done
diff --git a/mojo/tools/mojosh.sh b/mojo/tools/mojosh.sh
new file mode 100755
index 0000000..bb8a2cd
--- /dev/null
+++ b/mojo/tools/mojosh.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This a simple script to make running Mojo shell easier (on Linux).
+
+DIRECTORY="$(dirname "$0")"/../../out/Debug
+PORT=$(($RANDOM % 8192 + 2000))
+
+do_help() {
+  cat << EOF
+Usage: $(basename "$0") [-d DIRECTORY] [-|--] <mojo_shell arguments ...>
+
+DIRECTORY defaults to $DIRECTORY.
+
+Example:
+  $(basename "$0") mojo:mojo_sample_app
+EOF
+}
+
+kill_http_server() {
+  echo "Killing SimpleHTTPServer ..."
+  kill $HTTP_SERVER_PID
+  wait $HTTP_SERVER_PID
+}
+
+while [ $# -gt 0 ]; do
+  case "$1" in
+    -h|--help)
+      do_help
+      exit 0
+      ;;
+    -d)
+      shift
+      if [ $# -eq 0 ]; then
+        do_help
+        exit 1
+      fi
+      DIRECTORY="$1"
+      ;;
+    --show-bash-alias)
+      echo alias\ mojosh\=\'\"\$\(pwd\ \|\ sed\ \'\"\'\"\'s\/\\\(\.\*\\\/src\\\
+\)\.\*\/\\1\/\'\"\'\"\'\)\/mojo\/tools\/mojosh\.sh\"\'
+      exit 0
+      ;;
+    # Separate arguments to mojo_shell (e.g., in case you want to pass it -d).
+    -|--)
+      shift
+      break
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+
+echo "Base directory: $DIRECTORY"
+
+echo "Running SimpleHTTPServer in directory $DIRECTORY/lib on port $PORT"
+cd $DIRECTORY/lib || exit 1
+python -m SimpleHTTPServer $PORT &
+# Kill the HTTP server on exit (even if the user kills everything using ^C).
+HTTP_SERVER_PID=$!
+trap kill_http_server EXIT
+cd ..
+
+echo "Running:"
+echo "./mojo_shell --origin=http://127.0.0.1:$PORT --disable-cache $*"
+./mojo_shell --origin=http://127.0.0.1:$PORT --disable-cache $*
diff --git a/mojo/tools/package_manager/BUILD.gn b/mojo/tools/package_manager/BUILD.gn
new file mode 100644
index 0000000..76b5771
--- /dev/null
+++ b/mojo/tools/package_manager/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.
+
+shared_library("package_manager") {
+  output_name = "mojo_package_manager"
+
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+    "package_fetcher.cc",
+    "package_fetcher.h",
+    "package_manager.cc",
+    "package_manager_application.cc",
+    "package_manager_application.h",
+    "unpacker.cc",
+    "unpacker.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/public/c/system:for_shared_library",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/public/interfaces/network",
+    "//third_party/zlib:zip",
+    "//url",
+  ]
+}
diff --git a/mojo/tools/package_manager/DEPS b/mojo/tools/package_manager/DEPS
new file mode 100644
index 0000000..784b7fb
--- /dev/null
+++ b/mojo/tools/package_manager/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/zlib',
+]
diff --git a/mojo/tools/package_manager/manifest.cc b/mojo/tools/package_manager/manifest.cc
new file mode 100644
index 0000000..5a0e346
--- /dev/null
+++ b/mojo/tools/package_manager/manifest.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium 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/tools/package_manager/manifest.h"
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/values.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+Manifest::Manifest() {
+}
+
+Manifest::~Manifest() {
+}
+
+bool Manifest::Parse(const std::string& str, std::string* err_msg) {
+  int err_code = base::JSONReader::JSON_NO_ERROR;
+  scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError(
+      str,
+      base::JSON_ALLOW_TRAILING_COMMAS,
+      &err_code, err_msg));
+  if (err_code != base::JSONReader::JSON_NO_ERROR)
+    return false;
+
+  const base::DictionaryValue* root_dict;
+  if (!root->GetAsDictionary(&root_dict)) {
+    *err_msg = "Manifest is not a dictionary.";
+    return false;
+  }
+
+  if (!PopulateDeps(root_dict, err_msg))
+    return false;
+
+  return true;
+}
+
+bool Manifest::ParseFromFile(const base::FilePath& file_name,
+                             std::string* err_msg) {
+  std::string data;
+  if (!base::ReadFileToString(file_name, &data)) {
+    *err_msg = "Couldn't read manifest file " + file_name.AsUTF8Unsafe();
+    return false;
+  }
+  return Parse(data, err_msg);
+}
+
+bool Manifest::PopulateDeps(const base::DictionaryValue* root,
+                            std::string* err_msg) {
+  const base::Value* deps_value;
+  if (!root->Get("deps", &deps_value))
+    return true;  // No deps, that's OK.
+
+  const base::ListValue* deps;
+  if (!deps_value->GetAsList(&deps)) {
+    *err_msg = "Deps is not a list. Should be \"deps\": [ \"...\", \"...\" ]";
+    return false;
+  }
+
+  deps_.reserve(deps->GetSize());
+  for (size_t i = 0; i < deps->GetSize(); i++) {
+    std::string cur_dep;
+    if (!deps->GetString(i, &cur_dep)) {
+      *err_msg = "Dependency list item wasn't a string.";
+      return false;
+    }
+
+    GURL cur_url(cur_dep);
+    if (!cur_url.is_valid()) {
+      *err_msg = "Dependency entry isn't a valid URL: " + cur_dep;
+      return false;
+    }
+
+    deps_.push_back(cur_url);
+  }
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/tools/package_manager/manifest.h b/mojo/tools/package_manager/manifest.h
new file mode 100644
index 0000000..f87040d
--- /dev/null
+++ b/mojo/tools/package_manager/manifest.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_TOOLS_PACKAGE_MANAGER_MANIFEST_H_
+#define MOJO_TOOLS_PACKAGE_MANAGER_MANIFEST_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/system/macros.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+}
+
+namespace mojo {
+
+class Manifest {
+ public:
+  Manifest();
+  ~Manifest();
+
+  // Parses the manifest from raw data. Returns true on success. On failure,
+  // populates the "err_msg" string with an error.
+  bool Parse(const std::string& str, std::string* err_msg);
+
+  // Like Parse but reads the data from a file.
+  bool ParseFromFile(const base::FilePath& file_name, std::string* err_msg);
+
+  const std::vector<GURL>& deps() const { return deps_; }
+
+ private:
+  bool PopulateDeps(const base::DictionaryValue* root, std::string* err_msg);
+
+  std::vector<GURL> deps_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Manifest);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_TOOLS_PACKAGE_MANAGER_MANIFEST_H_
diff --git a/mojo/tools/package_manager/package_fetcher.cc b/mojo/tools/package_manager/package_fetcher.cc
new file mode 100644
index 0000000..b5a47e5
--- /dev/null
+++ b/mojo/tools/package_manager/package_fetcher.cc
@@ -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.
+
+#include "mojo/tools/package_manager/package_fetcher.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace mojo {
+
+PackageFetcher::PackageFetcher(NetworkService* network_service,
+                               PackageFetcherDelegate* delegate,
+                               const GURL& url)
+    : delegate_(delegate),
+      url_(url) {
+  network_service->CreateURLLoader(Get(&loader_));
+
+  URLRequestPtr request(URLRequest::New());
+  request->url = url.spec();
+  request->auto_follow_redirects = true;
+
+  loader_->Start(request.Pass(),
+                 base::Bind(&PackageFetcher::OnReceivedResponse,
+                            base::Unretained(this)));
+}
+
+PackageFetcher::~PackageFetcher() {
+}
+
+void PackageFetcher::OnReceivedResponse(URLResponsePtr response) {
+  if (response->error) {
+    printf("Got error %d (%s) for %s\n",
+           response->error->code,
+           response->error->description.get().c_str(),
+           url_.spec().c_str());
+    delegate_->FetchFailed(url_);
+    return;
+  }
+
+  if (!base::CreateTemporaryFile(&output_file_name_)) {
+    delegate_->FetchFailed(url_);
+    return;
+  }
+  output_file_.Initialize(output_file_name_,
+      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  if (!output_file_.IsValid()) {
+    base::DeleteFile(output_file_name_, false);
+    delegate_->FetchFailed(url_);
+    // Danger: may be deleted now.
+    return;
+  }
+
+  body_ = response->body.Pass();
+  ReadData(MOJO_RESULT_OK);
+  // Danger: may be deleted now.
+}
+
+void PackageFetcher::ReadData(MojoResult) {
+  char buf[4096];
+  uint32_t num_bytes = sizeof(buf);
+  MojoResult result = ReadDataRaw(body_.get(), buf, &num_bytes,
+                                  MOJO_READ_DATA_FLAG_NONE);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    WaitToReadMore();
+  } else if (result == MOJO_RESULT_OK) {
+    if (output_file_.WriteAtCurrentPos(buf, num_bytes) !=
+        static_cast<int>(num_bytes)) {
+      // Clean up the output file.
+      output_file_.Close();
+      base::DeleteFile(output_file_name_, false);
+
+      delegate_->FetchFailed(url_);
+      // Danger: may be deleted now.
+      return;
+    }
+    WaitToReadMore();
+  } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // Done.
+    output_file_.Close();
+    delegate_->FetchSucceeded(url_, output_file_name_);
+    // Danger: may be deleted now.
+  }
+}
+
+void PackageFetcher::WaitToReadMore() {
+  handle_watcher_.Start(
+      body_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_DEADLINE_INDEFINITE,
+      base::Bind(&PackageFetcher::ReadData, base::Unretained(this)));
+}
+
+}  // namespace mojo
diff --git a/mojo/tools/package_manager/package_fetcher.h b/mojo/tools/package_manager/package_fetcher.h
new file mode 100644
index 0000000..3cbd7a7
--- /dev/null
+++ b/mojo/tools/package_manager/package_fetcher.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_TOOLS_PACKAGE_MANAGER_FETCHER_H_
+#define MOJO_TOOLS_PACKAGE_MANAGER_FETCHER_H_
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "url/gurl.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace mojo {
+
+class PackageFetcherDelegate;
+
+class PackageFetcher {
+ public:
+  PackageFetcher(NetworkService* network_service,
+                 PackageFetcherDelegate* delegate,
+                 const GURL& url);
+  virtual ~PackageFetcher();
+
+ private:
+  void OnReceivedResponse(URLResponsePtr response);
+
+  void ReadData(MojoResult);
+  void WaitToReadMore();
+
+  PackageFetcherDelegate* delegate_;
+
+  // URL of the package.
+  GURL url_;
+
+  URLLoaderPtr loader_;
+
+  // Valid once file has started streaming.
+  ScopedDataPipeConsumerHandle body_;
+  common::HandleWatcher handle_watcher_;
+
+  base::FilePath output_file_name_;
+  base::File output_file_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(PackageFetcher);
+};
+
+class PackageFetcherDelegate {
+ public:
+  virtual void FetchSucceeded(const GURL& url, const base::FilePath& name) = 0;
+
+  virtual void FetchFailed(const GURL& url) = 0;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_TOOLS_PACKAGE_MANAGER_FETCHER_H_
diff --git a/mojo/tools/package_manager/package_manager.cc b/mojo/tools/package_manager/package_manager.cc
new file mode 100644
index 0000000..b091f6d
--- /dev/null
+++ b/mojo/tools/package_manager/package_manager.cc
@@ -0,0 +1,12 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/tools/package_manager/package_manager_application.h"
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunnerChromium runner(new mojo::PackageManagerApplication);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/tools/package_manager/package_manager_application.cc b/mojo/tools/package_manager/package_manager_application.cc
new file mode 100644
index 0000000..ae4d4a4
--- /dev/null
+++ b/mojo/tools/package_manager/package_manager_application.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium 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/tools/package_manager/package_manager_application.h"
+
+#include "base/files/file_util.h"
+#include "mojo/tools/package_manager/manifest.h"
+#include "mojo/tools/package_manager/unpacker.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/application/application_impl.h"
+
+namespace mojo {
+
+namespace {
+
+const base::FilePath::CharType kManifestFileName[] =
+    FILE_PATH_LITERAL("manifest.json");
+
+}  // namespace
+
+PackageManagerApplication::PendingLoad::PendingLoad() {
+}
+
+PackageManagerApplication::PendingLoad::~PendingLoad() {
+}
+
+PackageManagerApplication::PackageManagerApplication() {
+}
+
+PackageManagerApplication::~PackageManagerApplication() {
+  STLDeleteContainerPairSecondPointers(pending_.begin(), pending_.end());
+}
+
+void PackageManagerApplication::Initialize(ApplicationImpl* app) {
+  app->ConnectToService("mojo:mojo_network_service", &network_service_);
+
+  printf("Enter URL> ");
+  char buf[1024];
+  if (scanf("%1023s", buf) != 1) {
+    printf("No input, exiting\n");
+    base::MessageLoop::current()->Quit();
+    return;
+  }
+  GURL url(buf);
+  if (!url.is_valid()) {
+    printf("Invalid URL\n");
+    base::MessageLoop::current()->Quit();
+    return;
+  }
+
+  StartLoad(url);
+}
+
+void PackageManagerApplication::StartLoad(const GURL& url) {
+  if (completed_.find(url) != completed_.end() ||
+      pending_.find(url) != pending_.end())
+    return;  // Already loaded or are loading this one.
+
+  PendingLoad* load = new PendingLoad;
+  load->fetcher.reset(new mojo::PackageFetcher(
+      network_service_.get(), this, url));
+  pending_[url] = load;
+}
+
+void PackageManagerApplication::LoadDeps(const Manifest& manifest) {
+  for (size_t i = 0; i < manifest.deps().size(); i++)
+    StartLoad(manifest.deps()[i]);
+}
+
+void PackageManagerApplication::PendingLoadComplete(const GURL& url) {
+  pending_.erase(pending_.find(url));
+  completed_.insert(url);
+  if (pending_.empty())
+    base::MessageLoop::current()->Quit();
+}
+
+void PackageManagerApplication::FetchSucceeded(
+    const GURL& url,
+    const base::FilePath& name) {
+  Unpacker unpacker;
+  if (!unpacker.Unpack(name)) {
+    base::DeleteFile(name, false);
+    printf("Failed to unpack\n");
+    PendingLoadComplete(url);
+    return;
+  }
+  // The downloaded .zip file is no longer necessary.
+  base::DeleteFile(name, false);
+
+  // Load the manifest.
+  base::FilePath manifest_path = unpacker.dir().Append(kManifestFileName);
+  Manifest manifest;
+  std::string err_msg;
+  if (!manifest.ParseFromFile(manifest_path, &err_msg)) {
+    printf("%s\n", err_msg.c_str());
+    PendingLoadComplete(url);
+    return;
+  }
+
+  // Enqueue loads for any deps.
+  LoadDeps(manifest);
+
+  printf("Loaded %s\n", url.spec().c_str());
+  PendingLoadComplete(url);
+}
+
+void PackageManagerApplication::FetchFailed(const GURL& url) {
+  PendingLoadComplete(url);
+}
+
+}  // namespace mojo
diff --git a/mojo/tools/package_manager/package_manager_application.h b/mojo/tools/package_manager/package_manager_application.h
new file mode 100644
index 0000000..87d4fcd
--- /dev/null
+++ b/mojo/tools/package_manager/package_manager_application.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_PACKAGE_MANAGER_PACKAGE_MANAGER_APPLICATION_H_
+#define MOJO_PACKAGE_MANAGER_PACKAGE_MANAGER_APPLICATION_H_
+
+#include <map>
+#include <set>
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/tools/package_manager/package_fetcher.h"
+
+namespace mojo {
+
+class Manifest;
+
+class PackageManagerApplication
+    : public ApplicationDelegate,
+      public PackageFetcherDelegate {
+ public:
+  PackageManagerApplication();
+  virtual ~PackageManagerApplication();
+
+ private:
+  struct PendingLoad {
+    PendingLoad();
+    ~PendingLoad();
+
+    scoped_ptr<PackageFetcher> fetcher;
+  };
+  typedef std::map<GURL, PendingLoad*> PendingLoadMap;
+
+  void StartLoad(const GURL& url);
+
+  void LoadDeps(const Manifest& manifest);
+
+  // Deletes the pending load entry for the given URL and possibly exits the
+  // message loop if all loads are done.
+  void PendingLoadComplete(const GURL& url);
+
+  // ApplicationDelegate implementation.
+  virtual void Initialize(ApplicationImpl* app) override;
+
+  // PackageFetcher.
+  virtual void FetchSucceeded(const GURL& url,
+                              const base::FilePath& name) override;
+  virtual void FetchFailed(const GURL& url) override;
+
+  mojo::NetworkServicePtr network_service_;
+
+  PendingLoadMap pending_;  // Owning pointers.
+  std::set<GURL> completed_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(PackageManagerApplication);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PACKAGE_MANAGER_PACKAGE_MANAGER_APPLICATION_H
diff --git a/mojo/tools/package_manager/unpacker.cc b/mojo/tools/package_manager/unpacker.cc
new file mode 100644
index 0000000..8ed34d5
--- /dev/null
+++ b/mojo/tools/package_manager/unpacker.cc
@@ -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.
+
+#include "mojo/tools/package_manager/unpacker.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "third_party/zlib/google/zip.h"
+
+namespace mojo {
+
+Unpacker::Unpacker() {
+}
+
+Unpacker::~Unpacker() {
+  if (!dir_.empty())
+    DeleteFile(dir_, true);
+}
+
+bool Unpacker::Unpack(const base::FilePath& zip_file) {
+  DCHECK(zip_file_.empty());
+  zip_file_ = zip_file;
+
+  DCHECK(dir_.empty());
+  if (!CreateNewTempDirectory(base::FilePath::StringType(), &dir_))
+    return false;
+  if (!zip::Unzip(zip_file, dir_)) {
+    dir_ = base::FilePath();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/tools/package_manager/unpacker.h b/mojo/tools/package_manager/unpacker.h
new file mode 100644
index 0000000..2f7acc5
--- /dev/null
+++ b/mojo/tools/package_manager/unpacker.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_TOOLS_PACKAGE_MANAGER_UNPACKER_H_
+#define MOJO_TOOLS_PACKAGE_MANAGER_UNPACKER_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// Unzips a package into a temporary folder. The temporary folder will be
+// deleted when the object is destroyed.
+//
+// In the future, this class would probably manage doing the unzip operation on
+// a background thread.
+class Unpacker {
+ public:
+  Unpacker();
+  ~Unpacker();
+
+  // Actually does the unpacking, returns true on success.
+  bool Unpack(const base::FilePath& zip_file);
+
+  // The root directory where the package has been unpacked.
+  const base::FilePath& dir() const { return dir_; }
+
+ private:
+  base::FilePath zip_file_;
+
+  base::FilePath dir_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Unpacker);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_TOOLS_PACKAGE_MANAGER_UNPACKER_H_
diff --git a/mojo/tools/pylib/mojo_python_tests_runner.py b/mojo/tools/pylib/mojo_python_tests_runner.py
new file mode 100644
index 0000000..e63a2a4
--- /dev/null
+++ b/mojo/tools/pylib/mojo_python_tests_runner.py
@@ -0,0 +1,147 @@
+# Copyright 2014 The Chromium 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 argparse
+import json
+import os
+import sys
+import time
+import unittest
+
+
+class MojoPythonTestRunner(object):
+  """Helper class to run python tests on the bots."""
+
+  def __init__(self, test_dir):
+    self._test_dir = test_dir
+
+  def run(self):
+    parser = argparse.ArgumentParser()
+    parser.usage = 'run_mojo_python_tests.py [options] [tests...]'
+    parser.add_argument('-v', '--verbose', action='count', default=0)
+    parser.add_argument('--metadata', action='append', default=[],
+                        help=('optional key=value metadata that will be stored '
+                              'in the results files (can be used for revision '
+                              'numbers, etc.)'))
+    parser.add_argument('--write-full-results-to', metavar='FILENAME',
+                        action='store',
+                        help='path to write the list of full results to.')
+    parser.add_argument('tests', nargs='*')
+
+    self.add_custom_commandline_options(parser)
+    args = parser.parse_args()
+    self.apply_customization(args)
+
+    bad_metadata = False
+    for val in args.metadata:
+      if '=' not in val:
+        print >> sys.stderr, ('Error: malformed metadata "%s"' % val)
+        bad_metadata = True
+    if bad_metadata:
+      print >> sys.stderr
+      parser.print_help()
+      return 2
+
+    chromium_src_dir = os.path.join(os.path.dirname(__file__),
+                                    os.pardir,
+                                    os.pardir,
+                                    os.pardir)
+
+    loader = unittest.loader.TestLoader()
+    print "Running Python unit tests under %s..." % self._test_dir
+
+    pylib_dir = os.path.abspath(os.path.join(chromium_src_dir, self._test_dir))
+    if args.tests:
+      if pylib_dir not in sys.path:
+        sys.path.append(pylib_dir)
+      suite = unittest.TestSuite()
+      for test_name in args.tests:
+        suite.addTests(loader.loadTestsFromName(test_name))
+    else:
+      suite = loader.discover(pylib_dir, pattern='*_unittest.py')
+
+    runner = unittest.runner.TextTestRunner(verbosity=(args.verbose + 1))
+    result = runner.run(suite)
+
+    full_results = _FullResults(suite, result, args.metadata)
+    if args.write_full_results_to:
+      with open(args.write_full_results_to, 'w') as fp:
+        json.dump(full_results, fp, indent=2)
+        fp.write("\n")
+
+    return 0 if result.wasSuccessful() else 1
+
+  def add_custom_commandline_options(self, parser):
+    """Allow to add custom option to the runner script."""
+    pass
+
+  def apply_customization(self, args):
+    """Allow to apply any customization to the runner."""
+    pass
+
+
+TEST_SEPARATOR = '.'
+
+
+def _FullResults(suite, result, metadata):
+  """Convert the unittest results to the Chromium JSON test result format.
+
+  This matches run-webkit-tests (the layout tests) and the flakiness dashboard.
+  """
+
+  full_results = {}
+  full_results['interrupted'] = False
+  full_results['path_delimiter'] = TEST_SEPARATOR
+  full_results['version'] = 3
+  full_results['seconds_since_epoch'] = time.time()
+  for md in metadata:
+    key, val = md.split('=', 1)
+    full_results[key] = val
+
+  all_test_names = _AllTestNames(suite)
+  failed_test_names = _FailedTestNames(result)
+
+  full_results['num_failures_by_type'] = {
+      'FAIL': len(failed_test_names),
+      'PASS': len(all_test_names) - len(failed_test_names),
+  }
+
+  full_results['tests'] = {}
+
+  for test_name in all_test_names:
+    value = {}
+    value['expected'] = 'PASS'
+    if test_name in failed_test_names:
+      value['actual'] = 'FAIL'
+      value['is_unexpected'] = True
+    else:
+      value['actual'] = 'PASS'
+    _AddPathToTrie(full_results['tests'], test_name, value)
+
+  return full_results
+
+
+def _AllTestNames(suite):
+  test_names = []
+  # _tests is protected  pylint: disable=W0212
+  for test in suite._tests:
+    if isinstance(test, unittest.suite.TestSuite):
+      test_names.extend(_AllTestNames(test))
+    else:
+      test_names.append(test.id())
+  return test_names
+
+
+def _FailedTestNames(result):
+  return set(test.id() for test, _ in result.failures + result.errors)
+
+
+def _AddPathToTrie(trie, path, value):
+  if TEST_SEPARATOR not in path:
+    trie[path] = value
+    return
+  directory, rest = path.split(TEST_SEPARATOR, 1)
+  if directory not in trie:
+    trie[directory] = {}
+  _AddPathToTrie(trie[directory], rest, value)
diff --git a/mojo/tools/pylib/transitive_hash.py b/mojo/tools/pylib/transitive_hash.py
new file mode 100644
index 0000000..93e8dc4
--- /dev/null
+++ b/mojo/tools/pylib/transitive_hash.py
@@ -0,0 +1,89 @@
+# Copyright 2014 The Chromium 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 logging
+import subprocess
+import sys
+
+from hashlib import sha256
+from os.path import basename, realpath
+
+_logging = logging.getLogger()
+
+# Based on/taken from
+#   http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/
+# (with cosmetic changes).
+def _memoize(f):
+  """Memoization decorator for a function taking a single argument."""
+  class Memoize(dict):
+    def __missing__(self, key):
+      rv = self[key] = f(key)
+      return rv
+  return Memoize().__getitem__
+
+@_memoize
+def _file_hash(filename):
+  """Returns a string representing the hash of the given file."""
+  _logging.debug("Hashing %s ...", filename)
+  rv = subprocess.check_output(['sha256sum', '-b', filename]).split(None, 1)[0]
+  _logging.debug("  => %s", rv)
+  return rv
+
+@_memoize
+def _get_dependencies(filename):
+  """Returns a list of filenames for files that the given file depends on."""
+  _logging.debug("Getting dependencies for %s ...", filename)
+  lines = subprocess.check_output(['ldd', filename]).splitlines()
+  rv = []
+  for line in lines:
+    i = line.find('/')
+    if i < 0:
+      _logging.debug("  => no file found in line: %s", line)
+      continue
+    rv.append(line[i:].split(None, 1)[0])
+  _logging.debug("  => %s", rv)
+  return rv
+
+def transitive_hash(filename):
+  """Returns a string that represents the "transitive" hash of the given
+  file. The transitive hash is a hash of the file and all the shared libraries
+  on which it depends (done in an order-independent way)."""
+  hashes = set()
+  to_hash = [filename]
+  while to_hash:
+    current_filename = realpath(to_hash.pop())
+    current_hash = _file_hash(current_filename)
+    if current_hash in hashes:
+      _logging.debug("Already seen %s (%s) ...", current_filename, current_hash)
+      continue
+    _logging.debug("Haven't seen %s (%s) ...", current_filename, current_hash)
+    hashes.add(current_hash)
+    to_hash.extend(_get_dependencies(current_filename))
+  return sha256('|'.join(sorted(hashes))).hexdigest()
+
+def main(argv):
+  logging.basicConfig()
+  # Uncomment to debug:
+  # _logging.setLevel(logging.DEBUG)
+
+  if len(argv) < 2:
+    print """\
+Usage: %s [file] ...
+
+Prints the \"transitive\" hash of each (executable) file. The transitive
+hash is a hash of the file and all the shared libraries on which it
+depends (done in an order-independent way).""" % basename(argv[0])
+    return 0
+
+  rv = 0
+  for filename in argv[1:]:
+    try:
+      print transitive_hash(filename), filename
+    except:
+      print "ERROR", filename
+      rv = 1
+  return rv
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/tools/run_mojo_python_bindings_tests.py b/mojo/tools/run_mojo_python_bindings_tests.py
new file mode 100755
index 0000000..916c68d
--- /dev/null
+++ b/mojo/tools/run_mojo_python_bindings_tests.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium 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 os
+import sys
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, os.path.join(_script_dir, "pylib"))
+
+from mojo_python_tests_runner import MojoPythonTestRunner
+
+
+class PythonBindingsTestRunner(MojoPythonTestRunner):
+
+  def add_custom_commandline_options(self, parser):
+    parser.add_argument('--build-dir', action='store',
+                        help='path to the build output directory')
+
+  def apply_customization(self, args):
+    if args.build_dir:
+      python_build_dir = os.path.join(args.build_dir, 'python')
+      if python_build_dir not in sys.path:
+        sys.path.append(python_build_dir)
+      python_gen_dir = os.path.join(
+          args.build_dir,
+          'gen', 'mojo', 'public', 'interfaces', 'bindings', 'tests')
+      if python_gen_dir not in sys.path:
+        sys.path.append(python_gen_dir)
+
+
+def main():
+  runner = PythonBindingsTestRunner(os.path.join('mojo', 'python', 'tests'))
+  sys.exit(runner.run())
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/tools/run_mojo_python_tests.py b/mojo/tools/run_mojo_python_tests.py
new file mode 100755
index 0000000..d83ca54
--- /dev/null
+++ b/mojo/tools/run_mojo_python_tests.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium 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 os
+import sys
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, os.path.join(_script_dir, "pylib"))
+
+from mojo_python_tests_runner import MojoPythonTestRunner
+
+
+def main():
+  runner = MojoPythonTestRunner(os.path.join('mojo', 'public', 'tools',
+                                             'bindings', 'pylib'))
+  sys.exit(runner.run())
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/tools/test_runner.py b/mojo/tools/test_runner.py
new file mode 100755
index 0000000..b0bb4d9
--- /dev/null
+++ b/mojo/tools/test_runner.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A "smart" test runner for gtest unit tests (that caches successes)."""
+
+import logging
+import os
+import subprocess
+import sys
+
+_logging = logging.getLogger()
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, os.path.join(_script_dir, "pylib"))
+
+from transitive_hash import transitive_hash
+
+def main(argv):
+  logging.basicConfig()
+  # Uncomment to debug:
+  # _logging.setLevel(logging.DEBUG)
+
+  if len(argv) < 3 or len(argv) > 4:
+    print "Usage: %s gtest_list_file root_dir [successes_cache_file]" % \
+        os.path.basename(argv[0])
+    return 0 if len(argv) < 2 else 1
+
+  _logging.debug("Test list file: %s", argv[1])
+  with open(argv[1], 'rb') as f:
+    gtest_list = [y for y in [x.strip() for x in f.readlines()] \
+                      if y and y[0] != '#']
+  _logging.debug("Test list: %s" % gtest_list)
+
+  print "Running tests in directory: %s" % argv[2]
+  os.chdir(argv[2])
+
+  if len(argv) == 4 and argv[3]:
+    successes_cache_filename = argv[3]
+    print "Successes cache file: %s" % successes_cache_filename
+  else:
+    successes_cache_filename = None
+    print "No successes cache file (will run all tests unconditionally)"
+
+  if successes_cache_filename:
+    # This file simply contains a list of transitive hashes of tests that
+    # succeeded.
+    try:
+      _logging.debug("Trying to read successes cache file: %s",
+                     successes_cache_filename)
+      with open(argv[3], 'rb') as f:
+        successes = set([x.strip() for x in f.readlines()])
+      _logging.debug("Successes: %s", successes)
+    except:
+      # Just assume that it didn't exist, or whatever.
+      print "Failed to read successes cache file %s (will create)" % argv[3]
+      successes = set()
+
+  # Run gtests with color if we're on a TTY (and we're not being told explicitly
+  # what to do).
+  if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ:
+    _logging.debug("Setting GTEST_COLOR=yes")
+    os.environ['GTEST_COLOR'] = 'yes'
+
+  # TODO(vtl): We may not close this file on failure.
+  successes_cache_file = open(successes_cache_filename, 'ab') \
+      if successes_cache_filename else None
+  for gtest in gtest_list:
+    if gtest[0] == '*':
+      gtest = gtest[1:]
+      _logging.debug("%s is marked as non-cacheable" % gtest)
+      cacheable = False
+    else:
+      cacheable = True
+
+    if successes_cache_file and cacheable:
+      _logging.debug("Getting transitive hash for %s ... " % gtest)
+      try:
+        gtest_hash = transitive_hash(gtest)
+      except:
+        print "Failed to get transitive hash for %s" % gtest
+        return 1
+      _logging.debug("  Transitive hash: %s" % gtest_hash)
+
+      if gtest_hash in successes:
+        print "Skipping %s (previously succeeded)" % gtest
+        continue
+
+    print "Running %s...." % gtest,
+    sys.stdout.flush()
+    try:
+      subprocess.check_output(["./" + gtest], stderr=subprocess.STDOUT)
+      print "Succeeded"
+      # Record success.
+      if successes_cache_filename and cacheable:
+        successes.add(gtest_hash)
+        successes_cache_file.write(gtest_hash + '\n')
+        successes_cache_file.flush()
+    except subprocess.CalledProcessError as e:
+      print "Failed with exit code %d and output:" % e.returncode
+      print 72 * '-'
+      print e.output
+      print 72 * '-'
+      return 1
+    except OSError as e:
+      print "  Failed to start test"
+      return 1
+  print "All tests succeeded"
+  if successes_cache_file:
+    successes_cache_file.close()
+
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/views/BUILD.gn b/mojo/views/BUILD.gn
new file mode 100644
index 0000000..005654a
--- /dev/null
+++ b/mojo/views/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+assert(use_aura)
+
+# GYP version: mojo/mojo.gyp:mojo_views_support
+source_set("views") {
+  sources = [
+    "input_method_mojo_linux.cc",
+    "input_method_mojo_linux.h",
+    "native_widget_view_manager.cc",
+    "native_widget_view_manager.h",
+    "views_init.cc",
+    "views_init.h",
+  ]
+
+  deps = [
+    ":views_internal",
+    "//base",
+    "//base:i18n",
+    "//skia",
+    "//third_party/icu",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/views",
+    "//ui/wm",
+    "//mojo/aura",
+  ]
+}
+
+# GYP version: mojo/mojo.gyp:mojo_views_support_internal
+component("views_internal") {
+  output_name = "mojo_views_support_internal"
+
+  visibility = [ ":views" ]
+
+  sources = [
+    "mojo_views_export.h",
+    "views_init_internal.cc",
+    "views_init_internal.h",
+  ]
+
+  defines = [ "MOJO_VIEWS_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//base/third_party/dynamic_annotations",
+    "//skia",
+    "//third_party/icu",
+    "//ui/base",
+    "//ui/gfx",
+  ]
+}
diff --git a/mojo/views/DEPS b/mojo/views/DEPS
new file mode 100644
index 0000000..a182521
--- /dev/null
+++ b/mojo/views/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+  "+base",
+  "+skia",
+  "+ui/aura",
+  "+ui/base",
+  "+ui/events",
+  "+ui/views",
+  "+ui/wm",
+]
diff --git a/mojo/views/input_method_mojo_linux.cc b/mojo/views/input_method_mojo_linux.cc
new file mode 100644
index 0000000..e54b644
--- /dev/null
+++ b/mojo/views/input_method_mojo_linux.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/views/input_method_mojo_linux.h"
+
+#include "ui/base/ime/text_input_client.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+
+InputMethodMojoLinux::InputMethodMojoLinux(
+    ui::internal::InputMethodDelegate* delegate)
+    : ui::InputMethodAuraLinux(delegate) {
+}
+
+InputMethodMojoLinux::~InputMethodMojoLinux() {}
+
+bool InputMethodMojoLinux::DispatchKeyEvent(const ui::KeyEvent& event) {
+  DCHECK(event.type() == ui::ET_KEY_PRESSED ||
+         event.type() == ui::ET_KEY_RELEASED);
+  DCHECK(system_toplevel_window_focused());
+
+  // If no text input client, do nothing.
+  if (!GetTextInputClient())
+    return DispatchKeyEventPostIME(event);
+
+  // Here is where we change the differ from our base class's logic. Instead of
+  // always dispatching a key down event, and then sending a synthesized
+  // character event, we instead check to see if this is a character event and
+  // send out the key if it is. (We fallback to normal dispatch if it isn't.)
+  if (event.is_char()) {
+    const uint16 ch = event.GetCharacter();
+    if (GetTextInputClient())
+      GetTextInputClient()->InsertChar(ch, event.flags());
+
+    return false;
+  }
+
+  return DispatchKeyEventPostIME(event);
+}
+
+}  // namespace mojo
diff --git a/mojo/views/input_method_mojo_linux.h b/mojo/views/input_method_mojo_linux.h
new file mode 100644
index 0000000..0072a29
--- /dev/null
+++ b/mojo/views/input_method_mojo_linux.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_VIEWS_INPUT_METHOD_MOJO_LINUX_H_
+#define MOJO_VIEWS_INPUT_METHOD_MOJO_LINUX_H_
+
+#include "ui/base/ime/input_method_auralinux.h"
+#include "ui/base/ime/input_method_delegate.h"
+
+namespace mojo {
+
+// An input method for linux that does absolutely no translation.
+//
+// The current InputMethodMinimal makes assumptions that a system will only
+// input/output keydown/keyup events; it assumes that things don't work like
+// Windows does. When it gets a keydown event, it then tries to insert a
+// character at the same time.
+//
+// However, we're standardizing on Windows' WM_CHAR style events. This tries to
+// follow InputMethodWin::DispatchKeyEvent() instead, because PlatformViewX11
+// now synthesizes a character events so that we have one behaviour across our
+// platforms.
+class InputMethodMojoLinux : public ui::InputMethodAuraLinux {
+ public:
+  explicit InputMethodMojoLinux(ui::internal::InputMethodDelegate* delegate);
+  virtual ~InputMethodMojoLinux();
+
+  // Overriden from ui::InputMethodAuraLinux:
+  virtual bool DispatchKeyEvent(const ui::KeyEvent& event) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InputMethodMojoLinux);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_VIEWS_INPUT_METHOD_MOJO_LINUX_H_
diff --git a/mojo/views/mojo_views_export.h b/mojo/views/mojo_views_export.h
new file mode 100644
index 0000000..0ca8985
--- /dev/null
+++ b/mojo/views/mojo_views_export.h
@@ -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.
+
+#ifndef MOJO_VIEWS_MOJO_VIEWS_EXPORT_H_
+#define MOJO_VIEWS_MOJO_VIEWS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_VIEWS_IMPLEMENTATION)
+#define MOJO_VIEWS_EXPORT __declspec(dllexport)
+#else
+#define MOJO_VIEWS_EXPORT __declspec(dllimport)
+#endif  // defined(MOJO_VIEWS_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(MOJO_VIEWS_IMPLEMENTATION)
+#define MOJO_VIEWS_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_VIEWS_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define MOJO_VIEWS_EXPORT
+#endif
+
+#endif  // MOJO_VIEWS_MOJO_VIEWS_EXPORT_H_
diff --git a/mojo/views/native_widget_view_manager.cc b/mojo/views/native_widget_view_manager.cc
new file mode 100644
index 0000000..6907ea6
--- /dev/null
+++ b/mojo/views/native_widget_view_manager.cc
@@ -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.
+
+#include "mojo/views/native_widget_view_manager.h"
+
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/input_method_base.h"
+#include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/input_method_factory.h"
+#include "ui/base/ime/text_input_client.h"
+#include "ui/wm/core/base_focus_rules.h"
+#include "ui/wm/core/capture_controller.h"
+#include "ui/wm/core/focus_controller.h"
+
+#if defined(OS_LINUX)
+#include "mojo/views/input_method_mojo_linux.h"
+#endif
+
+namespace mojo {
+namespace {
+
+// TODO: figure out what this should be.
+class FocusRulesImpl : public wm::BaseFocusRules {
+ public:
+  FocusRulesImpl() {}
+  virtual ~FocusRulesImpl() {}
+
+  virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FocusRulesImpl);
+};
+
+class MinimalInputEventFilter : public ui::internal::InputMethodDelegate,
+                                public ui::EventHandler {
+ public:
+  explicit MinimalInputEventFilter(aura::Window* root)
+      : root_(root) {
+    ui::InitializeInputMethodForTesting();
+#if defined(OS_LINUX)
+    input_method_.reset(new InputMethodMojoLinux(this));
+#else
+    input_method_ = ui::CreateInputMethod(this, gfx::kNullAcceleratedWidget);
+#endif
+    input_method_->Init(true);
+    root_->AddPreTargetHandler(this);
+    root_->SetProperty(aura::client::kRootWindowInputMethodKey,
+                       input_method_.get());
+  }
+
+  virtual ~MinimalInputEventFilter() {
+    root_->RemovePreTargetHandler(this);
+    root_->SetProperty(aura::client::kRootWindowInputMethodKey,
+                       static_cast<ui::InputMethod*>(NULL));
+  }
+
+ private:
+  // ui::EventHandler:
+  virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+    // See the comment in InputMethodEventFilter::OnKeyEvent() for details.
+    if (event->IsTranslated()) {
+      event->SetTranslated(false);
+    } else {
+      if (input_method_->DispatchKeyEvent(*event))
+        event->StopPropagation();
+    }
+  }
+
+  // ui::internal::InputMethodDelegate:
+  virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
+    // See the comment in InputMethodEventFilter::DispatchKeyEventPostIME() for
+    // details.
+    ui::KeyEvent aura_event(event);
+    aura_event.SetTranslated(true);
+    ui::EventDispatchDetails details =
+        root_->GetHost()->dispatcher()->OnEventFromSource(&aura_event);
+    return aura_event.handled() || details.dispatcher_destroyed;
+  }
+
+  aura::Window* root_;
+  scoped_ptr<ui::InputMethod> input_method_;
+
+  DISALLOW_COPY_AND_ASSIGN(MinimalInputEventFilter);
+};
+
+}  // namespace
+
+NativeWidgetViewManager::NativeWidgetViewManager(
+    views::internal::NativeWidgetDelegate* delegate, View* view)
+    : NativeWidgetAura(delegate),
+      view_(view) {
+  view_->AddObserver(this);
+  window_tree_host_.reset(new WindowTreeHostMojo(view_, this));
+  window_tree_host_->InitHost();
+
+  ime_filter_.reset(
+      new MinimalInputEventFilter(window_tree_host_->window()));
+
+  focus_client_.reset(new wm::FocusController(new FocusRulesImpl));
+
+  aura::client::SetFocusClient(window_tree_host_->window(),
+                               focus_client_.get());
+  aura::client::SetActivationClient(window_tree_host_->window(),
+                                    focus_client_.get());
+  window_tree_host_->window()->AddPreTargetHandler(focus_client_.get());
+
+  capture_client_.reset(
+      new aura::client::DefaultCaptureClient(window_tree_host_->window()));
+}
+
+NativeWidgetViewManager::~NativeWidgetViewManager() {
+  if (view_)
+    view_->RemoveObserver(this);
+}
+
+void NativeWidgetViewManager::InitNativeWidget(
+    const views::Widget::InitParams& in_params) {
+  views::Widget::InitParams params(in_params);
+  params.parent = window_tree_host_->window();
+  NativeWidgetAura::InitNativeWidget(params);
+}
+
+void NativeWidgetViewManager::CompositorContentsChanged(
+    const SkBitmap& bitmap) {
+  if (view_)
+    view_->SetContents(bitmap);
+}
+
+void NativeWidgetViewManager::OnViewDestroyed(View* view) {
+  DCHECK_EQ(view, view_);
+  view->RemoveObserver(this);
+  view_ = NULL;
+}
+
+void NativeWidgetViewManager::OnViewBoundsChanged(View* view,
+                                                  const gfx::Rect& old_bounds,
+                                                  const gfx::Rect& new_bounds) {
+  GetWidget()->SetBounds(gfx::Rect(view->bounds().size()));
+}
+
+void NativeWidgetViewManager::OnViewInputEvent(View* view,
+                                               const EventPtr& event) {
+  scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event> >());
+  if (ui_event)
+    window_tree_host_->SendEventToProcessor(ui_event.get());
+}
+
+}  // namespace mojo
diff --git a/mojo/views/native_widget_view_manager.h b/mojo/views/native_widget_view_manager.h
new file mode 100644
index 0000000..a9e8220
--- /dev/null
+++ b/mojo/views/native_widget_view_manager.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_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
+#define MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
+
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "ui/views/widget/native_widget_aura.h"
+
+namespace aura {
+namespace client {
+class DefaultCaptureClient;
+}
+}
+
+namespace ui {
+namespace internal {
+class InputMethodDelegate;
+}
+}
+
+namespace wm {
+class FocusController;
+}
+
+namespace mojo {
+
+class WindowTreeHostMojo;
+
+class NativeWidgetViewManager : public views::NativeWidgetAura,
+                                public WindowTreeHostMojoDelegate,
+                                public ViewObserver {
+ public:
+  NativeWidgetViewManager(views::internal::NativeWidgetDelegate* delegate,
+                          View* view);
+  virtual ~NativeWidgetViewManager();
+
+ private:
+  // Overridden from internal::NativeWidgetAura:
+  virtual void InitNativeWidget(
+      const views::Widget::InitParams& in_params) OVERRIDE;
+
+  // WindowTreeHostMojoDelegate:
+  virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE;
+
+  // ViewObserver:
+  virtual void OnViewDestroyed(View* view) OVERRIDE;
+  virtual void OnViewBoundsChanged(View* view,
+                                   const gfx::Rect& old_bounds,
+                                   const gfx::Rect& new_bounds) OVERRIDE;
+  virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE;
+
+  scoped_ptr<WindowTreeHostMojo> window_tree_host_;
+
+  scoped_ptr<wm::FocusController> focus_client_;
+
+  scoped_ptr<ui::internal::InputMethodDelegate> ime_filter_;
+
+  View* view_;
+
+  scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeWidgetViewManager);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
diff --git a/mojo/views/views_init.cc b/mojo/views/views_init.cc
new file mode 100644
index 0000000..edd8af3
--- /dev/null
+++ b/mojo/views/views_init.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/views/views_init.h"
+
+#include "mojo/views/views_init_internal.h"
+
+namespace mojo {
+
+ViewsInit::ViewsInit() {
+  InitViews();
+}
+
+ViewsInit::~ViewsInit() {
+}
+
+}  // namespace mojo
diff --git a/mojo/views/views_init.h b/mojo/views/views_init.h
new file mode 100644
index 0000000..ad3b95e
--- /dev/null
+++ b/mojo/views/views_init.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_VIEWS_VIEWS_INIT_H_
+#define MOJO_VIEWS_VIEWS_INIT_H_
+
+#include "mojo/aura/aura_init.h"
+
+namespace mojo {
+
+// Sets up necessary state for views when run with the viewmanager.
+class ViewsInit {
+ public:
+  ViewsInit();
+  ~ViewsInit();
+
+ private:
+  AuraInit aura_init_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewsInit);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_VIEWS_VIEWS_INIT_H_
diff --git a/mojo/views/views_init_internal.cc b/mojo/views/views_init_internal.cc
new file mode 100644
index 0000000..6d92c7c
--- /dev/null
+++ b/mojo/views/views_init_internal.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/views/views_init_internal.h"
+
+#include "base/i18n/icu_util.h"
+#include "base/lazy_instance.h"
+#include "base/path_service.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+
+namespace mojo {
+
+namespace {
+
+// Used to ensure we only init once.
+class Setup {
+ public:
+  Setup() {
+    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);
+
+    // There is a bunch of static state in gfx::Font, by running this now,
+    // before any other apps load, we ensure all the state is set up.
+    gfx::Font();
+  }
+
+  ~Setup() {
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+void InitViews() {
+  setup.Get();
+}
+
+}  // namespace mojo
diff --git a/mojo/views/views_init_internal.h b/mojo/views/views_init_internal.h
new file mode 100644
index 0000000..5ec66e7
--- /dev/null
+++ b/mojo/views/views_init_internal.h
@@ -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.
+
+#ifndef MOJO_VIEWS_VIEWS_INIT_INTERNAL_H_
+#define MOJO_VIEWS_VIEWS_INIT_INTERNAL_H_
+
+#include "mojo/views/mojo_views_export.h"
+
+namespace mojo {
+
+// This is in a separate target so that we only initialize some common state
+// once.
+// Do not use this, it is called internally by ViewsInit.
+MOJO_VIEWS_EXPORT void InitViews();
+
+}  // namespace mojo
+
+#endif  // MOJO_VIEWS_VIEWS_INIT_INTERNAL_H_